Migrate from APM
Your project installs agent files with Microsoft APM from an apm.yml.
palm reads the same git repositories, APM packages included, and writes each harness’s own files at install time.
Each dependency becomes a source in palm.yaml, and the files APM already wrote stay byte for byte where they are.
When you finish, an APM package’s skill, prompt, agent (subagent), instruction and hook sit in each harness’s own folder:
Folder.agents/
Folderskills/
Folderlint-fix/
- …
Folderrelease/
- …
Folder.claude/
Folderagents/
- reviewer.md
Folderrules/
- typescript.md
Folderskills/
Folderlint-fix/
- …
Folderrelease/
- …
- settings.json
Folder.github/
Folderagents/
- reviewer.agent.md
Folderhooks/
- format-on-save.json
Folderinstructions/
- typescript.instructions.md
Folder.palm/
Folderassets/
Folderacme__standards/
- …
- palm.lock.yaml
- palm.yaml
The session on this page ran against palm’s APM test fixture, a package with apm.yml and an .apm/ folder.
Before you start
Section titled “Before you start”- Commit the current state, so
git diffshows every change palm makes. - palm, git and Node 22 or later.
How the two map
Section titled “How the two map”| APM | palm |
|---|---|
apm.yml |
palm.yaml |
apm.lock.yaml |
palm.lock.yaml |
apm_modules/ in each project |
~/.palm/cache/, shared by all your projects |
a package in dependencies.apm |
a source, then the entities you want from it, or --all |
an entry in dependencies.mcp |
an mcp: entry, from the server’s README snippet |
compiled AGENTS.md, CLAUDE.md and GEMINI.md |
one native file per entity and harness; managed blocks only in AGENTS.md and GEMINI.md |
executables: approval |
consent pinned by hash in palm.lock.yaml |
| APM command | palm command |
|---|---|
apm init |
palm init |
apm install |
palm install |
apm install <package> |
palm install <owner/repo> <names> or palm install <owner/repo> --all |
apm install --frozen |
palm check |
apm uninstall <package> |
palm remove <source> <names> |
apm update |
palm update |
apm outdated |
palm update --dry-run |
apm deps list |
palm get |
apm audit |
palm check |
apm compile |
none: palm writes native files when it installs |
-
List what
apm.ymlinstalls. Take this file as the example.apm.yml name: my-serviceversion: 1.0.0dependencies:apm:- acme/standards#v1.0.0- cursor/plugins/pstackmcp:- io.github.github/github-mcp-server -
Choose the harnesses.
Terminal window palm init --target claude,copilotUse the harnesses from APM’s
targets:if you set it, or the ones your team runs. palm notices theapm.ymland prints one hint: an import command for it arrives in palm 0.3. Until then, these steps do the job. -
Install each package.
Terminal window palm install acme/standardspalm install acme/standards --allThe first command shows what the package holds. The second installs everything except programs. A file APM already wrote that matches palm’s output is adopted without a change.
-
Install the package’s hook after you read it.
Terminal window palm install acme/standards format-on-save~/project $ palm install acme/standardsi acme/standards: apm.yml: 1 MCP server dependency is not installed (io.github.github/github-mcp-server); palm installs a server with: palm install mcp --snippet <the JSON from io.github.github/github-mcp-server's README>i acme/standards: apm.yml: 2 dependencies are not installed; declare what you need: palm install microsoft/apm-sample-package, palm install github/awesome-copilot/skills/review-and-refactor#v1.2.0acme/standards v1.0.0 (30fa2b0) 1 plugin, 2 skills, 1 agent, 1 instruction, 1 hookplugin my-apm-pkg 2 skills, 1 agent, 1 instruction, 1 hookskill lint-fix Fix lint errors.skill release Cut a releaseagent reviewer Reviews pull requestsinstruction typescript TS stylehook format-on-save claude: PostToolUse -> npx prettier --write $CLAUDE_FILE_PATHS (a program; asks before installing)Nothing written. Install some:palm install acme/standards lint-fix releasepalm install acme/standards --all$ palm install acme/standards --alli ref ^1.0 saved to palm.yaml (latest tag v1.0.0); edit ref: to track main+ skill lint-fix .agents/skills/lint-fix/, .claude/skills/lint-fix/ 2 files (claude gets its own copy in .claude/skills)+ skill release .agents/skills/release/, .claude/skills/release/ 2 filesfrom command release.prompt.md: installed as a skill, which a harness may also load on its own when it looks relevant+ agent reviewer .claude/agents/reviewer.md, .github/agents/reviewer.agent.md 2 files+ instruction typescript .claude/rules/typescript.md, .github/instructions/typescript.instructions.md 2 files! hook format-on-save runs a program on your machine; not installedsee it: palm install acme/standards hook:format-on-save --dry-runinstall it: palm install acme/standards hook:format-on-save4 installed. Commit palm.yaml, palm.lock.yaml, .agents/, .claude/ and .github/ together.$ palm install acme/standards format-on-saveThis install adds 1 program that will run on your machine.1. hook format-on-save from acme/standards (commit 30fa2b0, v1.0.0, 2026-09-28)PostToolUse Write|Edit npx prettier --write "$CLAUDE_FILE_PATHS"targets: claude (.claude/settings.json), copilot (.github/hooks/format-on-save.json)scripts: 1 file, 249 B -> .palm/assets/acme__standards/format-on-save/ (committed with your repo).apm/hooks/format-on-save.json 644 249 B sha256:161d35f7! unpinned: npx resolves prettier@latest at run time; pin an exact version to review what runspalm 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.x 1 program needs your consent and there is no terminalreview: palm install acme/standards format-on-save --dry-run --reviewthen: palm install acme/standards format-on-save --allow-exec hook:format-on-save@acme/standards=sha256:c9da7ff0d7bf85ec182618dfe63abbeaa8feeb63c5eb5c2a16355f15e4b3ff90$ palm install acme/standards format-on-save --allow-exec hook:format-on-save@acme/standards=sha256:c9da7ff0d7bf85ec182618dfe63abbeaa8feeb63c5eb5c2a16355f15e4b3ff90This install adds 1 program that will run on your machine.1. hook format-on-save from acme/standards (commit 30fa2b0, v1.0.0, 2026-09-28)PostToolUse Write|Edit npx prettier --write "$CLAUDE_FILE_PATHS"targets: claude (.claude/settings.json), copilot (.github/hooks/format-on-save.json)scripts: 1 file, 249 B -> .palm/assets/acme__standards/format-on-save/ (committed with your repo).apm/hooks/format-on-save.json 644 249 B sha256:161d35f7! unpinned: npx resolves prettier@latest at run time; pin an exact version to review what runspalm 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.allowed by --allow-exec: hook:format-on-save@acme/standards (sha256:c9da7ff0)+ hook format-on-save .github/hooks/format-on-save.json, .palm/assets/acme__standards/format-on-save/ +1 2 files +1 merged from acme/standards v1.0.0~ trusted hook format-on-save sha256:c9da7ff01 installed. Commit palm.yaml, palm.lock.yaml and .palm/ together.Without a terminal palm stops and prints the
--allow-execline. On a terminal it shows the same review and asks. -
Take single entities where APM installed a whole folder.
Terminal window palm install cursor/plugins/pstack comment-sicko poteto-agent -
Add the MCP servers from
dependencies.mcp.palm has no registry client. Copy the JSON snippet from the server’s README and pipe it in.
Terminal window pbpaste | palm install mcp --snippet -A README without a JSON block works through
palm install mcp <name> --url ...or--command .... Add MCP servers covers snippets, flags and secrets. -
Install your project’s own
.apm/folder, if it has one.Terminal window palm install ./.apmThe listing shows what palm finds there. Install the names you want, and
./.apmbecomes an in-repo source inpalm.yaml. -
Remove APM’s own files and hook entries, then commit.
Terminal window rm apm.yml apm.lock.yamlrm -r apm_modulespalm checkDelete the context files
apm compilewrote, such as a rootAGENTS.md, unless you edit them by hand. The next section lists what else APM left behind. Then commit.Terminal window git add -Agit commit -m "Install agent files with palm"palm check
palm does not follow dependencies: inside a package’s own apm.yml.
It lists them once as a warning, with the command for each, and installs nothing you did not name.
What APM leaves behind
Section titled “What APM leaves behind”APM wrote copies of each package’s files and its own hook entries. palm adopts the copies that match what it writes, and never edits what it did not write.
| APM wrote | What palm does | What you do |
|---|---|---|
skills, agents and instructions in a harness folder, such as .claude/skills/<n>/ or .github/instructions/ |
adopts a file that matches its render byte for byte; refuses one that differs, naming the command with --force |
nothing for adopted files; compare and pass --force for the rest |
a hook entry in .claude/settings.json or .codex/hooks.json that runs a script under .claude/hooks/ or .codex/hooks/ |
writes its own entry, which runs the script from .palm/assets/; palm check warns foreign hook command about APM’s entry |
delete APM’s entry from the file, then its script copies |
hook copies in .claude/hooks/, .codex/hooks/, .github/hooks/ and .claude/apm-hooks.json |
nothing; the lock does not list them | delete them |
compiled AGENTS.md, CLAUDE.md or GEMINI.md, at the root or in folders such as .claude/ or docs/ |
writes its own managed block into the root AGENTS.md and GEMINI.md |
delete APM’s compiled files unless you edit them by hand |
A hook entry APM wrote and palm’s own entry for the same hook run the same script twice.
palm check names each foreign entry with its file and event, so you know which to delete.
palm checkgit status --shortAfter the deletions, palm install restores any file palm writes that you removed, and palm check passes.
Gitignored outputs
Section titled “Gitignored outputs”APM restores its output on every install, so some projects ignore .claude/ on purpose.
palm commits the generated files instead, so a teammate’s clone and CI work without palm.
palm check fails on the git-ignored check while an output folder is ignored.
palm migrate prints the same lines as a warning and does not fail on them.
Both print the exact lines to add, for the output folders palm writes to only.
Git cannot re-include a path whose parent folder is ignored, so a .claude/ line becomes .claude/* first.
.claude/*!.claude/skills/!.claude/rules/Personal files such as .claude/settings.local.json stay ignored.
Commit the files the new lines expose, then run palm check.
A mode that keeps outputs ignored and restores them on a bare install is a candidate for palm 0.3.
Check the result
Section titled “Check the result”palm getpalm checkpalm get lists each entity with its source and targets.
palm check exits 0 when palm.yaml, the lock and the files agree.
Next steps
Section titled “Next steps”- Adopt files you copied by hand handles files that differ from what palm writes.
- Check the setup in CI replaces
apm install --frozenin your pipeline. - Scan rules shows where APM packages rank among the layouts palm detects.