Skip to content

palm install

palm install has four forms: list a source, install from it, sync the whole scope, or declare an MCP server.

Terminal window
palm install <source> [--grep <text>]
palm install <source> [[kind:]name...] [--all] [--as <name>] [--targets <ids>] [--layout <kind=glob>]... [--at <dir>]
palm install
palm install mcp <name> --url <url> [--header <K=V>]... [--transport http|sse]
palm install mcp <name> --command <program> [--arg <value>]... [--env <K=V>]...
palm install mcp --snippet <file|->

Aliases: palm add, palm i.

Command What it does
palm install mattpocock/skills Fetch and index the source, list what it offers with a pasteable next line, save nothing.
palm install github/awesome-copilot --grep review List only the entities whose name or description holds review.
palm install mattpocock/skills tdd grill-me Install two skills, declare the source in palm.yaml, pin it in the lock.
palm install obra/superpowers --all Install every entity that runs no program. Hooks and stdio servers are listed with the command that installs each.
palm install obra/superpowers plugin:superpowers Install every member of a plugin and record the plugin in palm.yaml.
palm install acme-kit db-conventions --targets claude,codex Install for some of the project’s targets only.
palm install https://gitlab.acme.com/platform/agent-kit.git reviewer --as acme-kit Declare a URL under a name you choose.
palm install acme/kit reviewer --layout 'agents=people/*.md' Declare a source with a layout descriptor, for a repository auto-detection misreads.
palm install Make the disk match palm.yaml and the lock.
pbpaste | palm install mcp --snippet - Declare every MCP server in a README snippet.
~/project
$ palm install mattpocock/skills tdd handoff
i ref ^1.2 saved to palm.yaml (latest tag v1.2.3); edit ref: to track main
+ skill tdd .agents/skills/tdd/, .claude/skills/tdd/ 5 files from mattpocock/skills v1.2.3 (claude gets its own copy in .claude/skills)
+ skill handoff .agents/skills/handoff/, .claude/skills/handoff/ 3 files from mattpocock/skills v1.2.3
2 installed. Commit palm.yaml, palm.lock.yaml, .agents/ and .claude/ together.
$ palm install obra/superpowers plugin:superpowers --dry-run
This install adds 1 program that will run on your machine.
1. hook superpowers from obra/superpowers (commit 083545e, v4.0.3, 2026-09-28)
SessionStart startup|clear|compact CLAUDE_PLUGIN_ROOT="$CLAUDE_PROJECT_DIR"/.palm/assets/obra__superpowers/superpowers "$CLAUDE_PROJECT_DIR"/.palm/assets/obra__superpowers/superpowers/hooks/run-hook.cmd session-start
targets: claude (.claude/settings.json), codex (.codex/hooks.json)
scripts: 2 files, 355 B -> .palm/assets/obra__superpowers/superpowers/ (committed with your repo)
hooks/run-hook.cmd 755 50 B sha256:94db96e9
... 1 more (--review shows every script)
palm never runs this itself. If you say yes, its hash goes into palm.lock.yaml,
so teammates and CI install it without being asked; any change asks again.
i ref ^4.0 would be saved to palm.yaml (latest tag v4.0.3); edit ref: to track main
+ would install skill brainstorming .agents/skills/brainstorming/, .claude/skills/brainstorming/ 2 files (claude gets its own copy in .claude/skills)
+ would install skill test-driven-development .agents/skills/test-driven-development/, .claude/skills/test-driven-development/ 6 files
+ would install skill writing-skills .agents/skills/writing-skills/, .claude/skills/writing-skills/ 4 files
+ would install hook superpowers .palm/assets/obra__superpowers/superpowers/, .claude/settings.json +1 2 files +2 merged
dry run: 4 would install; nothing written.
$ rm -r .claude/skills/handoff
$ palm install
↺ skill handoff .agents/skills/handoff/, .claude/skills/handoff/ 3 files (claude gets its own copy in .claude/skills)
1 restored, 1 unchanged.
Argument Meaning
source owner/repo, git URL, directory, or a source in palm.yaml
names [kind:]name of what to install
Option Meaning
--all everything the source offers
--grep <text> list only what matches the text (name or description)
--as <name> the name a URL source gets in palm.yaml
--layout <k=glob> a new source’s layout: skills=packages/* (repeatable)
--targets <ids> only these targets for these entries (kept per entry)
--review print program scripts first; with –dry-run, only print
--allow-local-sources palm.yaml may name file:// sources outside the project
Option Meaning
--url <url> a remote server
--header <K=V> an HTTP header (repeatable)
--command <cmd> a local (stdio) server: the program
--arg <arg> an argument for –command (repeatable)
--env <K=V> an environment variable (repeatable)
--transport <t> stdio, http or sse, when palm cannot tell
--cwd <dir> working directory for –command
--snippet <file> a README’s mcpServers block: a file, or - for stdin

The global options also apply.

With a source and no names, palm fetches the source, indexes it and prints every entity with its kind, name, description and version. It asks the remote for its current tags first, so the listing never shows a stale version. Hooks and stdio MCP servers are marked (a program; asks before installing), and a hook row shows its whole command, such as claude: Stop -> go run .agents/hooks/main.go. A name that means two kinds is marked too, and an entity an install would refuse says why, such as (refused: hidden Unicode); the paste line never offers it. When the source offers only programs, the paste line names them as hook:<name> or mcp:<name> and offers no --all. Notes about what the index left out print in full: files shaped like agents, hooks or servers that it did not index, with the --layout flags that index them, a layout glob that matches nothing, an apm.yml dependency palm does not follow, and links that leave the source. The listing ends with Nothing written. Install some: and two commands you can paste, with the #ref you typed.

--grep <text> keeps the entities whose name or description contains the text, and the paste line takes the first two matches. Index notes you act on print in full: a file the layout misses, a glob that matches nothing, a dependency palm does not follow, a link that leaves the source. The other index notes show as one count line that points to palm describe source <source>, which prints them in full. A source that overlaps an output folder is refused at listing time, before it offers --all.

The first word is a source when it is a name or alias declared in palm.yaml, owner/repo, a URL, a path, or the word mcp. Anything else is a usage error whose first line is the fix.

x "superpowers" is not a repository. palm installs from git repositories:
palm install <owner/repo> [names...] for example palm install obra/superpowers
Not sure which repository? https://github.com/search?q=superpowers+SKILL.md&type=code

palm consults no registry for a name, so nothing times out and no unrelated server appears. The hint tries the word against the project first: a folder becomes ./<word>, and a name a declared source offers gets that source. Troubleshooting lists every case.

With names or --all, palm installs and records.

  1. It resolves the source, asking the remote for its current tags. An undeclared source is declared in palm.yaml once something installs, as Sources describes.
  2. It matches each name within that source, ignoring case. A name that matches nothing fails with E_NOT_FOUND, and the hint is the closest name as a pasteable command. A name that means two kinds is asked about on a terminal, and E_AMBIGUOUS without one. The error lists every such name at once, and its hint is your command with each name as kind:name, with the same source, #ref and options.
  3. It refuses an entity whose kind and name another source already installed in this scope, with E_CONFLICT naming the owner.
  4. It takes the targets from palm.yaml. With none there, it detects them, writes them, and prints one line.
  5. It renders every entity for every target without writing, and computes the render hashes.
  6. It shows every program the install adds and asks for consent. A trusted hash passes silently.
  7. It applies each target as a transaction. A failure on one target restores that target’s files and leaves the others.
  8. It saves palm.yaml and the lock after every entity that installed, and again at the end. When nothing installs, both files keep their bytes.

A plugin name, or plugin:<name>, expands to its members and records the plugin under plugins:. An agent’s skills: and mcpServers: are not installed. palm prints one line with the command that installs them. --at <dir> is recorded and printed as i at <dir> (placed at the root until 0.3). Ctrl-C stops between entities: palm finishes the current one, saves both files, and exits 130 with the count installed so far.

The report has one status line per entity, then every failure with its hint, then the warnings, then a summary. Unchanged entities collapse into one count line, and a note shared by many rows prints once. On the first install in a project, the summary names the files and folders to commit together. A multi-name install where nothing installs ends with Nothing installed.

Status Meaning
+ installed written on every target
~ updated a new commit or new content
~ re-rendered same content, another render, such as a new target or an edited in-repo source
↺ restored a missing generated file put back
= unchanged the render equals the lock and the disk equals the render, on every target; counted in one line
! modified (kept) you changed a generated file; palm kept it; --force restores
! partial the targets ended differently, such as one refused or kept; the lock keeps every target of the entry; exit 1
⊘ skipped a target has nothing for this kind; noted in the lock
! not installed a program --all left out or you declined; a plugin’s program lands in its exclude: in palm.yaml
x failed nothing written

When you decline a changed program you trusted before, the run ends with exit 130 and writes nothing; the line says the previous version stays active, with its hash and the palm remove command.

palm install with no arguments makes the disk match palm.yaml and the lock, offline when the cache holds every locked commit. It installs entries new to palm.yaml, removes entries palm.yaml dropped, restores missing files, re-renders changed in-repo sources and keeps files you edited. palm.yaml and palm.lock.yaml has the full table.

On a clean clone with a current lock, it writes nothing and prints one unchanged count line. A ref: edited by hand in palm.yaml makes it an update: palm prints the plan, asks for consent when a program changes, and needs --yes without a terminal. Under -g, it compares the lock with ~/.palm/applied.yaml, so a removal that arrives through your dotfiles reaches this machine. --dry-run shows that removal as would remove lines, one per file.

palm install mcp declares a server under mcp: in palm.yaml and renders it into every target’s config file.

  • --snippet - reads a { "mcpServers": { ... } } block, the flat form, or VS Code’s servers form from stdin. --snippet server.json reads a file.
  • --url with --header declares a remote server. --command with --arg and --env declares a stdio server.
  • palm install mcp <name> with neither prints both forms and --snippet -, for pasting the block from the server’s README.
  • A literal secret or a fill-in placeholder in a snippet becomes ${NAME}, with a notice, and so does VS Code’s ${input:name}.
  • A value you type with --env K=V or --header K=V is written as ${K}, and palm prints the export line. Only --secrets literal writes the value.
  • An existing name stops with E_CONFLICT unless you pass --force. An identical entry you already wrote by hand is adopted.
  • A stdio server asks for consent. A remote server does not.

--json after mcp takes a file argument. As a global option, --json switches the output to JSON. Add MCP servers walks through each form.

File Reads Writes
palm.yaml yes a new source, entries, targets: on the first install
palm.lock.yaml yes sources, entries, render hashes, trust
harness files yes, to compare every file and merged entry of the installed entities
.palm/assets/<source>/<entity>/ no the scripts hooks and stdio servers run, for git sources
.gitignore yes .palm/local/ and palm.local.yaml, once, when missing
~/.palm/cache/ yes checkouts and indexes, also under --dry-run
~/.palm/applied.yaml under -g under -g
  • A file that exists and that palm did not write, when it differs from the render. --force replaces it. A file that matches the render is adopted: palm compares parsed frontmatter and ignores trailing whitespace.
  • A file you changed since palm wrote it. The entity stays as it was. --force restores the render. To keep your change instead, copy the file into your own in-repo source under a new name and install it from there, as Troubleshooting shows.
  • An entity another source already installed under the same kind and name. The message names the owner; --force does not apply.
  • Hidden Unicode of critical severity, an unresolvable hook reference, or a literal secret from a source. No flag overrides these.
  • A skill folder above 200 files or 5 MB, and a managed block that would push AGENTS.md or GEMINI.md past the harness’s cap. --force overrides these.
  • A source that overlaps an output directory, also through a symlinked output folder, and a local path outside the git worktree.
  • A palm.yaml in the 0.1 format. The hint is palm migrate.
Code When
0 Every entity is installed, updated, restored, re-rendered, unchanged or skipped, including a plugin program you declined. Also a listing.
1 A name matches nothing or two kinds, a source cannot be fetched, another source owns the name, a program needs consent without a terminal, or any entity failed, was kept as modified, or installed only partly.
2 The first word is not a source, an option is unknown, --targets is used without names, --local is used, or palm.yaml is in the 0.1 format.
130 You declined a program you named or a ref change, or pressed Ctrl-C. palm finishes the current entity and saves both files first; a second Ctrl-C saves the lock and stops at once.