Lifecycle
APM has five lifecycle steps. Most projects use all five; small ones use three.
init -> install -> compile -> run | v audit | +--> back to install (fix drift)init scaffolds the project. install resolves dependencies, scans them, and writes the lockfile. compile transforms primitives into the formats each agent harness expects. run invokes a script declared in apm.yml. audit rebuilds the deployed context in scratch and diffs it against your working tree to catch drift before it ships.
You will use install and run daily, audit in CI, and init and compile rarely.
1. INIT
Section titled “1. INIT”apm init [project-name]Scaffolds a new APM project in the current directory.
apm init writes three things: an apm.yml manifest, a .apm/ directory for your local primitives (instructions, prompts, skills, agents, hooks), and the agent target directories you select (for example .github/, .claude/, .cursor/). It auto-detects sensible defaults for name, author, and description from your git config and surrounding directory; pass -y to accept them without prompts.
Targets are picked in priority order. An explicit --target copilot,claude flag wins. Otherwise an interactive checklist runs. Otherwise APM scans the working tree for signal directories (.github/, .claude/, .cursor/, .opencode/, .codex/, .gemini/, .windsurf/) and pre-checks every harness it finds. With -y and no flag, all detected harnesses are written into apm.yml. See primitives and targets for what each target actually receives.
Common surprises
- Re-running
apm initin a directory that already hasapm.ymlwarns and exits unless you pass-y(which overwrites the manifest). - If you set
targets:to an empty list inapm.yml, target detection runs again at compile time. To pin targets, list them explicitly.
Read more: apm init reference, package anatomy.
2. INSTALL
Section titled “2. INSTALL”apm install [packages...]Resolves the dependency graph declared in apm.yml, runs the security scan, and writes apm.lock.yaml.
Order of operations is deterministic and worth memorizing:
- Resolve — walk
dependenciesanddevDependencies(APM packages, MCP servers, Claude skills, plugin collections), follow transitive deps, pick versions. - Policy gate — if
apm-policy.ymlis discovered (locally or via your repo’s org), every resolved dependency is checked against the allow-list before anything touches disk. Pass--no-policyto skip the org policy gate for one invocation; this does not bypassapm audit --ci. - Scan — the pre-deploy security scan inspects every primitive for hidden Unicode (zero-width characters, bidi controls, tag characters). Critical findings block the install. Pass
--forceto deploy anyway. - Integrate — write primitives into each target harness’s native directory (
.github/,.claude/, etc.) and merge MCP server configs into the harness-specific config files. - Lockfile — write
apm.lock.yamlwith pinned versions, content hashes, and the resolved MCP server set.
apm install with no arguments installs from the existing manifest. apm install <package> adds a new dependency, re-runs the full pipeline, and updates both apm.yml and apm.lock.yaml. --dry-run runs steps 1 and 2 only and prints the plan.
Common surprises
- The scan is not optional in normal operation. If you need to land an install with a known critical finding (for example, an upstream package you cannot patch yet), use
--forceand document the exception. - Transitive MCP servers are gated behind explicit trust. If a deep dependency declares a new MCP server, install pauses to ask you to re-declare it in your top-level
apm.yml. Use--trust-transitive-mcpto skip this in trusted environments.
Read more: apm install reference, security, policy reference.
3. COMPILE
Section titled “3. COMPILE”apm compile [--target <list>]Transforms the primitives in .apm/ (and dependencies under apm_modules/) into harness-native files: AGENTS.md for Codex, GEMINI.md for Gemini, populated .cursor/, .opencode/, .windsurf/ directories, and so on.
Most users never call apm compile directly. apm install runs it as part of the integrate phase, and apm run auto-compiles any .prompt.md files referenced by a script just before execution. Reach for apm compile when you want to inspect what will be deployed without changing dependencies, when you are iterating on local primitives between installs, or when you only need output for one harness.
The --target flag accepts a comma-separated list (copilot,claude,cursor,opencode,codex,gemini,windsurf,agent-skills) or all. --dry-run prints placement decisions without writing files. --validate checks primitive frontmatter and structure without producing output. --watch re-runs compilation on every change.
Common surprises
- Running
apm compiledoes not re-run the security scan. The scan happens at install time. If you hand-edit primitives between installs, runapm auditto scan them. --cleanremoves orphanedAGENTS.mdfiles from previous compilations. Without it, removed primitives can leave stale output behind.
Read more: apm compile reference, compilation guide.
4. RUN
Section titled “4. RUN”apm run <script-name> [--param key=value ...]Executes a named script from the scripts: block in apm.yml.
The scripts: block is a flat string-to-string mapping, mirroring package.json:
name: my-projectversion: 0.1.0scripts: start: copilot --prompt .apm/prompts/review.prompt.md review: copilot --prompt .apm/prompts/review.prompt.md test: pytest tests/apm run with no script name runs start, matching npm. Before invoking the command, APM scans it for .prompt.md references, compiles each one to .apm/compiled/<name>.txt, and substitutes the compiled path into the command line. Use --param key=value (repeatable) to pass parameters that get interpolated into prompt frontmatter.
To preview what will run without executing, use apm preview <script-name>. It prints the original command, the rewritten command after prompt compilation, and the list of compiled files.
Common surprises
- A script that does not reference any
.prompt.mdfile runs as-is. APM only rewrites the command when it finds.prompt.mdarguments. - Parameters passed with
--paramonly reach prompt files. They do not become shell environment variables.
Read more: apm run reference, agent workflows guide.
5. AUDIT
Section titled “5. AUDIT”apm audit # local: scan deployed files for hidden Unicodeapm audit --ci # CI gate: lockfile consistency + drift replayapm audit --file <path> # standalone: scan an arbitrary fileapm audit is the explicit reporting and remediation tool that complements the built-in scan run by install. It has two modes worth understanding separately.
Local mode (apm audit, optionally with --strip or --file <path>) scans installed primitives — or any file you point at — for hidden Unicode and reports findings as text, JSON, SARIF, or markdown. With --strip, it removes hidden characters in place, preserving emoji and whitespace. Use --dry-run to preview the strip.
CI mode (apm audit --ci) runs the eight baseline consistency checks in order: lockfile-exists, ref-consistency, deployed-files-present, no-orphaned-packages, skill-subset-consistency, config-consistency, content-integrity, and includes-consent. After those pass, it performs an install-replay drift check. APM rebuilds the deployed context in a scratch directory and diffs it against your working tree, catching hand-edits to apm_modules/ or generated files before they ship. Pass --no-drift to skip the replay in performance-constrained loops; pass --no-fail-fast to run all checks even after a failure. With --policy <source> it also evaluates org policy against the lockfile.
Common surprises
apm audit --ciexits 1 on any failure — this is the gate you wire into branch protection. The localapm auditexits 0 even when findings exist, unless you also pass--stripand writes fail.- The drift check rebuilds the full context from scratch; on large repos, expect a few seconds of overhead. If your CI loop cannot afford it, narrow with
--no-driftand accept reduced coverage.
Read more: apm audit reference, policy reference for the full check list, security.