Skip to content

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.

  • Commit the current state, so git diff shows every change palm makes.
  • palm, git and Node 22 or later.
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
  1. List what apm.yml installs. Take this file as the example.

    apm.yml
    name: my-service
    version: 1.0.0
    dependencies:
    apm:
    - acme/standards#v1.0.0
    - cursor/plugins/pstack
    mcp:
    - io.github.github/github-mcp-server
  2. Choose the harnesses.

    Terminal window
    palm init --target claude,copilot

    Use the harnesses from APM’s targets: if you set it, or the ones your team runs. palm notices the apm.yml and prints one hint: an import command for it arrives in palm 0.3. Until then, these steps do the job.

  3. Install each package.

    Terminal window
    palm install acme/standards
    palm install acme/standards --all

    The 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.

  4. Install the package’s hook after you read it.

    Terminal window
    palm install acme/standards format-on-save
    ~/project
    $ palm install acme/standards
    i 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.0
    acme/standards v1.0.0 (30fa2b0) 1 plugin, 2 skills, 1 agent, 1 instruction, 1 hook
    plugin my-apm-pkg 2 skills, 1 agent, 1 instruction, 1 hook
    skill lint-fix Fix lint errors.
    skill release Cut a release
    agent reviewer Reviews pull requests
    instruction typescript TS style
    hook 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 release
    palm install acme/standards --all
    $ palm install acme/standards --all
    i 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 files
    from 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 installed
    see it: palm install acme/standards hook:format-on-save --dry-run
    install it: palm install acme/standards hook:format-on-save
    4 installed. Commit palm.yaml, palm.lock.yaml, .agents/, .claude/ and .github/ together.
    $ palm install acme/standards format-on-save
    This 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 runs
    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.
    x 1 program needs your consent and there is no terminal
    review: palm install acme/standards format-on-save --dry-run --review
    then: 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:c9da7ff0d7bf85ec182618dfe63abbeaa8feeb63c5eb5c2a16355f15e4b3ff90
    This 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 runs
    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.
    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:c9da7ff0
    1 installed. Commit palm.yaml, palm.lock.yaml and .palm/ together.

    Without a terminal palm stops and prints the --allow-exec line. On a terminal it shows the same review and asks.

  5. Take single entities where APM installed a whole folder.

    Terminal window
    palm install cursor/plugins/pstack comment-sicko poteto-agent
  6. 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.

  7. Install your project’s own .apm/ folder, if it has one.

    Terminal window
    palm install ./.apm

    The listing shows what palm finds there. Install the names you want, and ./.apm becomes an in-repo source in palm.yaml.

  8. Remove APM’s own files and hook entries, then commit.

    Terminal window
    rm apm.yml apm.lock.yaml
    rm -r apm_modules
    palm check

    Delete the context files apm compile wrote, such as a root AGENTS.md, unless you edit them by hand. The next section lists what else APM left behind. Then commit.

    Terminal window
    git add -A
    git 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.

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.

Terminal window
palm check
git status --short

After the deletions, palm install restores any file palm writes that you removed, and palm check passes.

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.

.gitignore
.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.

Terminal window
palm get
palm check

palm get lists each entity with its source and targets. palm check exits 0 when palm.yaml, the lock and the files agree.