palm install
palm install has four forms: list a source, install from it, sync the whole scope, or declare an MCP server.
palm install <source> [--grep <text>]palm install <source> [[kind:]name...] [--all] [--as <name>] [--targets <ids>] [--layout <kind=glob>]... [--at <dir>]palm installpalm 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. |
$ palm install mattpocock/skills tdd handoffi 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.32 installed. Commit palm.yaml, palm.lock.yaml, .agents/ and .claude/ together.$ palm install obra/superpowers plugin:superpowers --dry-runThis 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 mergeddry 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.Options
Section titled “Options”| 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 |
MCP servers (palm install mcp)
Section titled “MCP servers (palm install mcp)”| 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.
List a source
Section titled “List a source”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=codepalm 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.
Install from a source
Section titled “Install from a source”With names or --all, palm installs and records.
- It resolves the source, asking the remote for its current tags. An undeclared source is declared in
palm.yamlonce something installs, as Sources describes. - 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, andE_AMBIGUOUSwithout one. The error lists every such name at once, and its hint is your command with each name askind:name, with the same source,#refand options. - It refuses an entity whose kind and name another source already installed in this scope, with
E_CONFLICTnaming the owner. - It takes the targets from
palm.yaml. With none there, it detects them, writes them, and prints one line. - It renders every entity for every target without writing, and computes the render hashes.
- It shows every program the install adds and asks for consent. A trusted hash passes silently.
- It applies each target as a transaction. A failure on one target restores that target’s files and leaves the others.
- It saves
palm.yamland 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.
Sync the scope
Section titled “Sync the scope”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.
MCP servers
Section titled “MCP servers”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’sserversform from stdin.--snippet server.jsonreads a file.--urlwith--headerdeclares a remote server.--commandwith--argand--envdeclares 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=Vor--header K=Vis written as${K}, and palm prints theexportline. Only--secrets literalwrites the value. - An existing name stops with
E_CONFLICTunless 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.
Reads and writes
Section titled “Reads and writes”| 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 |
What it refuses
Section titled “What it refuses”- A file that exists and that palm did not write, when it differs from the render.
--forcereplaces 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.
--forcerestores 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;
--forcedoes 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.mdorGEMINI.mdpast the harness’s cap.--forceoverrides these. - A source that overlaps an output directory, also through a symlinked output folder, and a local path outside the git worktree.
- A
palm.yamlin the 0.1 format. The hint ispalm migrate.
Exit codes
Section titled “Exit codes”| 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. |