palm migrate
palm migrate reads the palm 0.1 palm.yaml, lockfile and ~/.palm/config.yaml, and writes the 0.2 palm.yaml and lock.
It ships with palm 0.2 and is removed in 0.3.
palm migrate [--dry-run] [--review] [--allow-exec <list>]palm migrate -g [--dry-run]| Command | What it does |
|---|---|
palm migrate --dry-run |
Print the new palm.yaml, the files palm would change and the programs that need consent. Write nothing. |
palm migrate --dry-run --review |
The same, with every script those programs run. |
palm migrate |
Ask for consent, then migrate the project, adopt matching files, run palm check and print what to commit. |
palm migrate --allow-exec <list> |
Migrate without a terminal, with the consent line a previous run printed. |
palm migrate -g |
Migrate ~/.palm, with tokens for harness homes and a list of literal secrets. |
$ palm installx palm.yaml is in the 0.1 format palm migrate$ palm migrate --dry-runtargets: [claude, codex]sources: mattpocock/skills: ref: v1.2.3 alias: mattpocock skills: [tdd, handoff] mattpocock-2c0db90: url: https://github.com/mattpocock/skills.git ref: v1.2.0 skills: [grill-me]- would remove .claude/skills/grill-me/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skills- would remove .claude/skills/handoff/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skills- would remove .claude/skills/tdd/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skillsi skill tdd: claude gets its own copy in .claude/skillsi skill handoff: claude gets its own copy in .claude/skillsi skill grill-me: claude gets its own copy in .claude/skills
Warnings ! skill grill-me was pinned to v1.2.0; palm.yaml declares source mattpocock-2c0db90 for it at that ref (mattpocock/skills tracks v1.2.3), as --as mattpocock-2c0db90 would$ palm migratei palm.yaml: source mattpocock/skills added from ~/.palm/config.yaml, needed by 2 entries; commit iti palm.yaml: source mattpocock-2c0db90 added from ~/.palm/config.yaml, needed by 1 entry; commit iti keep ~/.palm/config.yaml until every project is migrated; palm migrate reads it~ palm.yaml and palm.lock.yaml now use the palm 0.2 format- removed .claude/skills/grill-me/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skills- removed .claude/skills/handoff/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skills- removed .claude/skills/tdd/agents/openai.yaml: palm 0.1 copied it; palm 0.2 writes agents/openai.yaml only into .agents/skillsi skill grill-me: claude gets its own copy in .claude/skillsi skill handoff: claude gets its own copy in .claude/skillsi skill tdd: claude gets its own copy in .claude/skills~ .gitignore: .palm/ is now .palm/local/, so .palm/assets/ is committedCommit palm.yaml, palm.lock.yaml, .claude/ and .gitignore together.
palm check:✓ manifest and lock agree✓ palm.yaml targets match the lock✓ every entry is rendered✓ every entry is on all its targets✓ generated files match the lock✓ no stray file in a folder palm owns- applied files outside the lock: skipped (global scope only)✓ in-repo sources match the lock✓ no generated path lies inside a source✓ every program palm installed is trusted✓ no foreign command in the hook files palm manages✓ no foreign stdio server in the MCP files palm manages✓ every hook script exists✓ no literal secret in generated files✓ every variable the servers need is set✓ generated files are committed✓ every source is declared✓ no link leaves the scope✓ no hidden Unicode in generated files✓ no harness loads an entity twice✓ no two agents or commands share a name in a harness folder✓ every skill an agent preloads is installed✓ instruction files are within harness limitsno problems
Warnings ! skill grill-me was pinned to v1.2.0; palm.yaml declares source mattpocock-2c0db90 for it at that ref (mattpocock/skills tracks v1.2.3), as --as mattpocock-2c0db90 would$ git status --short D .claude/skills/grill-me/agents/openai.yaml D .claude/skills/handoff/agents/openai.yaml D .claude/skills/tdd/agents/openai.yaml M .gitignore M palm.lock.yaml M palm.yamlOptions
Section titled “Options”| Option | Meaning |
|---|---|
--review |
print the scripts of every program first; with –dry-run, only print |
The global options also apply.
Behavior
Section titled “Behavior”palm plans the whole migration in memory, validates it, and asks for consent before it writes anything.
A refusal, a failed fetch or a declined consent leaves every file as it was, so running palm migrate again works.
- Every repository alias the old lock references becomes a source in
palm.yaml, with the URL, root, layout and locked ref. The key isowner/repofor a GitHub URL, else the alias, and an alias that differs from the key is kept asalias:. A local origin with apathand arootbecomes./<path>/<root>. name@alias#refbecomes an entry under its source. When entries of one source disagree on refs, the most common ref wins, and palm prints one line per other entry.- A plugin becomes a
plugins:entry, withexclude:for members you had uninstalled. A command becomes a skill of the same name. - An MCP server from the MCP registry is copied from the rendered
.mcp.json, or the harness file that has it, intomcp:. - An entry on a target outside the project’s set becomes per-entry
targets:, with a note. An entry from~/.palm/minewarns and names the folder to copy the file into. - A hook keeps its trust under its 0.2 name, which palm finds by source and entity path, and palm prints each rename.
- palm prints the programs it re-copies, once, for one consent. The trust goes into the new lock.
- palm writes the new
palm.yamlwith your comments kept, and the new lock. .palm/hooks/<name>/is re-copied from the locked commit into.palm/assets/<source>/<name>/, then.palm/hooks/is removed. A hook from an in-repo source is not copied; it runs in place. The.palm/line in.gitignorebecomes.palm/local/andpalm.local.yaml.- A bare install adopts every file that already matches, and every merged entry by its key. palm never deletes or unlinks a harness file, and writes through symlinks.
- palm runs
palm check, prints its result, and lists every file it changed and every output folder to commit. An output folder.gitignorehides is a warning here, not a failure, with the exact lines that re-include it, as Gitignored outputs shows.
A source that came from ~/.palm/config.yaml prints ~ palm.yaml: source <name> added from ~/.palm/config.yaml, needed by <n> entries; commit it.
Global scope
Section titled “Global scope”Under -g, palm migrates ~/.palm/palm.yaml and its lock.
It rewrites absolute paths as tokens such as <claude>/skills/tdd, including paths written under another machine’s home.
It lists every literal secret in the harness files with the advice to rotate it, and leaves ~/.palm/config.yaml in place.
Only under -g does palm say that ~/.palm/config.yaml can go, once every project is migrated.
Reads and writes
Section titled “Reads and writes”| File | Reads | Writes |
|---|---|---|
palm.yaml (0.1) |
yes | the 0.2 format, comments kept |
palm.lock.yaml (version 1 or 2) |
yes | version 3 |
~/.palm/config.yaml |
yes | never |
.palm/hooks/ |
yes | removed after the copy |
.palm/assets/ |
no | the re-copied scripts |
.gitignore |
yes | the new ignore lines |
| harness files | yes, to adopt | missing files and merged entries only; never deleted or unlinked |
Every other palm command stops on the old files with x palm.yaml is in the 0.1 format or x palm.lock.yaml is version 2 (palm 0.1), and the hint palm migrate.
Exit codes
Section titled “Exit codes”| Code | When |
|---|---|
0 |
The scope was migrated and palm check passed, or --dry-run printed the plan. |
1 |
A repository could not be fetched, a program needs consent without a terminal, a file could not be adopted, or the final palm check failed on anything but ignored output folders. |
2 |
An unknown option. |
130 |
You declined consent or pressed Ctrl-C. palm wrote nothing. |