/ documentation

maleta install

Resolve and materialize skills and plugins from a Maleta, with destinations, collisions, dry runs, idempotency, and clear exit codes.

#Usage and options

Standard installation
bash
maleta install --file ./maleta.json --tool claude
Plan without writing
bash
maleta install --file ./maleta.json --tool claude --dry-run

Without --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>. The shared folder 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. Each resolve prints [info] resolving GitHub skill <name> from <identity>, and when a mixed document fails on a GitHub skill the error ends with ; local skills were not installed either so it never half-installs.
  • An inline source reads its files from maleta.json and therefore makes no GitHub request. Content is written as opaque UTF-8 data and never executed. A changed write reports [ok] install inline:<name> -> <dest>.
  • The destination for claude is ~/.claude/skills/<skill.name>/. The destinations for codex and agents are ~/.agents/skills/<skill.name>/. all expands 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

A skill name that is not a safe directory name is rejected by document validation, before any plan is built: Invalid skill name '<x>'. At write time, a symlinked destination is refused with an explanation and the MALETA_CLI_HOME workaround; a destination that is not a directory reports destination is not a directory: <path>; a staged file that is not a regular file reports destination is not a regular file: <dest>; and a read-only destination reports permission denied writing to <dir>; check its ownership and permissions.

#Dry run

With --dry-run, the CLI makes no network requests and writes nothing to disk. An inline action uses its source identity as the label: [dry-run] would install inline:<name> -> <dest>. The general materialization line is:

Materialization line in a dry run
text
[dry-run] would install <label> -> <dest>
Plugin lines in a dry run
text
[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.

#Local ownership record

After a run, install and sync record what they wrote for that exact absolute document path in <home>/.maleta/local-state.json (mode 0600, localStateVersion: 1). The record lets a later run tell its own files from a file you edited: a file that differs and was not last written by the CLI is kept with [warn] kept <path>: changed locally; delete it to take the new version, while files the CLI wrote are still updated and adopted files stay adopted. It also lets maleta prune remove a skill, including an inline:<name> resource, once the recorded document stops declaring it.

A skill whose destination already contains a symbolic link is not written through: the CLI prints [skip] <label> -> <dest>: existing symbolic link at <path>; remove it and rerun and continues with the other skills.

#How it differs from sync

Exact differences
Aspectinstallsync
Plugin pre-checkNone.Runs claude plugin list before installation.
Plugin already installedReinstalls it.Skips it with [ok] plugin '<id>' already installed.
Changed file[ok] install <label> -> <dest>[ok] sync <label> -> <dest>
Footerinstall: no relevant changes or install: applied local selectionsync: no relevant changes or sync: applied local selection

#Exit codes

CodeCondition
0No failures (`failures === 0`); skipped or unsupported items also result in 0.
1Document, plan, GitHub resolution, limit, collision, staging/transfer, or plugin error.
2Invalid command, option, or flag value.
4The file's targetOs differs from the host: [error] targetOs '<x>' does not match the local host '<y>'; refusing install.

Privacy preferences

Choose what this origin may measure. You can change it here at any time.

Essential storage

This browser always keeps your theme, the Maletas you save here and this choice so the site works. They stay on this origin.

Optional analytics

Maleta.dev measures aggregate traffic and real-user performance only after you allow it.

Current choice: not made yet, so measurement stays off.

Privacy policy