#Usage and options
maleta install --file ./maleta.json --tool claudemaleta install --file ./maleta.json --tool claude --dry-runWithout --file, it reads maleta.json relative to the current directory. Without --tool, it uses targetTool from the file. Accepted values are all, claude, codex, and agents. --dry-run is available only for install and sync.
#Skills and destinations
- A builtin source is looked up in the local assets at
claude/skills/<name>. Thesharedfolder is added automatically when a valid builtin skill exists. - A GitHub source uses the declared repository, path, and optional ref. Content is resolved through the GitHub API from the declared public repository and written as bytes; it is not executed.
- The destination for
claudeis~/.claude/skills/<skill.name>/. The destinations forcodexandagentsare~/.agents/skills/<skill.name>/.allexpands to both directories (Codex and Agents share the second one).
#Plugins
Plugins are applied by the Claude command. If the selection runs with --tool codex or --tool agents, each plugin is skipped with an incompatibility message. The plan can add the marketplace with claude plugin marketplace add <repo> and install with claude plugin install <id>.
[skip] plugin '<id>' is Claude-only and is unsupported for --tool <tool>[skip] plugin '<id>' is not present in the local plugin manifest; not installed[ok] plugin '<id>' installed
#Safe, idempotent writes
The operation is additive: it copies the existing tree to a staging directory and overwrites only files from the selection. It does not prune and preserves unmanaged files. Identical bytes are not rewritten and appear as [ok] <label> already aligned -> <dest>. Replacement uses transactional staging, backup, and rollback; temporary directories are named .maleta-stage-* and <stage>.old during the operation.
#Destination safeguards
The CLI rejects an unsafe directory name with unsafe target directory name: <name>. It also rejects a destination containing a symlink with destination path contains a symlink: <path>, a destination that is not a directory with destination is not a directory: <path>, and a file path that is not a regular file.
#Dry run
With --dry-run, the CLI makes no network requests and writes nothing to disk. The materialization line is:
[dry-run] would install <label> -> <dest>[dry-run] claude plugin marketplace add <repo>
[dry-run] claude plugin install <id>An empty selection prints [warn] no skills or plugins selected; edit the selected Maleta file or export one from https://app.maleta.dev before the usual install: no relevant changes footer, including with --dry-run. Otherwise, with --dry-run and at least one materialization action planned, no footer is printed. With no relevant changes—no files changed and no plugin commands run—the footer is install: no relevant changes; when the local selection is applied, the footer is install: applied local selection.
#How it differs from sync
| Aspect | install | sync |
|---|---|---|
| Plugin pre-check | None. | Runs claude plugin list before installation. |
| Plugin already installed | Reinstalls it. | Skips it with [ok] plugin '<id>' already installed. |
| Changed file | [ok] install <label> -> <dest> | [ok] sync <label> -> <dest> |
| Footer | install: no relevant changes or install: applied local selection | sync: no relevant changes or sync: applied local selection |
#Exit codes
| Code | Condition |
|---|---|
0 | No failures (`failures === 0`); skipped or unsupported items also result in 0. |
1 | Document, plan, GitHub resolution, limit, collision, staging/transfer, or plugin error. |
2 | Invalid command, option, or flag value. |
4 | The file's targetOs differs from the host: [error] targetOs '<x>' does not match the local host '<y>'; refusing install. |