This is the full developer documentation for Agent Package Manager
# Agent Package Manager
> A dependency manager for AI agents -- like npm for agent context.
APM is a dependency manager for AI agents. Declare the skills, prompts, instructions, plugins, and MCP servers your project needs in one `apm.yml`, then any developer runs `apm install` to deploy each primitive to the harnesses that support it. See the [targets matrix](./reference/targets-matrix/) for support across GitHub Copilot, Claude Code, Grok Build, Cursor, OpenCode, Codex, Gemini, Windsurf, and Kiro.
Portable by manifest
One `apm.yml` across supported harnesses and machines. The lockfile pins exact versions and content hashes so a fresh clone resolves the same package content byte-for-byte.
Secure by default
Every install scans for hidden Unicode, pins content hashes, and blocks transitive MCP servers unless they are explicitly declared or trusted. No opt-in required.
Governed by policy
`apm-policy.yml` is enforced at install time, including transitive MCP. Tighten-only inheritance flows enterprise -> org -> repo. Runtime behavior is your harness’s domain. See the [Governance Guide](/apm/enterprise/governance-guide/).
## Install APM
[Section titled “Install APM”](#install-apm)
* macOS (Homebrew)
Already use Homebrew?
```bash
brew install apm
```
No custom tap needed. Update with `brew upgrade apm`.
* Linux / macOS (no Homebrew)
```bash
curl -sSL https://aka.ms/apm-unix | sh
```
* Windows
```powershell
irm https://aka.ms/apm-windows | iex
```
Homebrew is optional. See [all installation options](./getting-started/installation/) for pip, Scoop, and manual installs. For standalone Unix installs, follow the installer’s shell-specific `PATH` guidance if the current terminal cannot find `apm`.
Then try it on a sample package:
```bash
apm install microsoft/apm-sample-package
```
## The manifest
[Section titled “The manifest”](#the-manifest)
```yaml
# apm.yml -- ships with your repo, like package.json
name: my-project
version: 1.0.0
dependencies:
apm:
# Skills, prompts, agents, plugins -- from any GitHub repo, with version pinning
- anthropics/skills/skills/frontend-design
- microsoft/apm-sample-package
- github/awesome-copilot/plugins/context-engineering#v2.1
- github/awesome-copilot/agents/api-architect.agent.md
# GitLab, Azure DevOps, Bitbucket, Gitea, any git server
- git: https://gitlab.com/acme/coding-standards.git
path: instructions/security
ref: v2.0
- dev.azure.com/acme/platform/_git/prompts/review.prompt.md
- bitbucket.org/team/agent-rules#main
mcp:
# MCP servers governed by the same manifest
- io.github.github/github-mcp-server
- io.github.microsoft/playwright-mcp
```
```bash
git clone && cd && apm install
```
That’s it. Each selected harness receives the primitives it supports in one command; every dependency is pinned, and every MCP server is policy-checked before it touches disk.
## Pick your path
[Section titled “Pick your path”](#pick-your-path)
Use a package
Install someone else’s primitives and run them on your harness. Start in the [consumer ramp](/apm/consumer/).
Author and publish
Build skills, prompts, plugins, or full packages others can install. Start in the [producer ramp](/apm/producer/).
Govern at fleet scale
Policy, audit, and CI gating across the org. Start in the [enterprise ramp](/apm/enterprise/).
***
APM is open source under the [microsoft](https://github.com/microsoft/apm) org, MIT-licensed. Built on [AGENTS.md](https://agents.md), [Agent Skills](https://agentskills.io), [MCP](https://modelcontextprotocol.io). See the [changelog](https://github.com/microsoft/apm/blob/main/CHANGELOG.md) for what shipped recently.
# 404
> Page not found. Check the URL or use the navigation to find what you're looking for.
# Glossary
> Every overloaded APM term, resolved in one paragraph. Skim alphabetically.
Every overloaded APM term, resolved in one paragraph. Skim alphabetically. “What it is NOT” lines disambiguate the most common collisions.
### apm.lock.yaml
[Section titled “apm.lock.yaml”](#apmlockyaml)
The lockfile APM writes after a successful resolve. Pins exact commit SHAs and per-file content hashes so every `apm install` from the same lockfile produces byte-identical output. Lives at the project root next to `apm.yml`.
NOT the manifest. The manifest declares what you want; the lockfile records what you got.
Source: `src/apm_cli/deps/lockfile.py`.
### apm.yml
[Section titled “apm.yml”](#apmyml)
The package manifest. A YAML file at the package root that declares `name`, `version`, `dependencies`, `scripts`, `targets` / `target`, and metadata. Both the unit of authoring and the unit of consumption – a directory becomes an APM package the moment it has an `apm.yml`.
NOT the lockfile, and NOT a `plugin.json`. The manifest is human-edited; the lockfile is generated; `plugin.json` is the local-bundle descriptor.
Source: `src/apm_cli/models/apm_package.py`.
### audit
[Section titled “audit”](#audit)
The `apm audit` command. Scans installed primitives for hidden Unicode that could embed invisible instructions, and (with `--ci`) re-derives content from the lockfile to detect drift before it ships. Emits SARIF, JSON, or rendered output. Standalone scanning of arbitrary files is available via `--file`.
NOT the same thing as install-time scanning. Install-time scanning is automatic and blocks critical findings; `audit` is the explicit reporting and remediation surface.
See: [Security](/apm/enterprise/security/). Source: `src/apm_cli/commands/audit.py`.
### bundle
[Section titled “bundle”](#bundle)
A local-install artifact produced by `apm pack`. Either a directory or a `.zip` (or legacy `.tar.gz`) containing `plugin.json` at the root and (in current versions) an embedded `apm.lock.yaml` with per-file SHA-256 hashes. Installed via `apm install `.
NOT a package source repository. A bundle is the packed, hash-verified output of one; you ship bundles, you author packages.
Source: `src/apm_cli/bundle/local_bundle.py`.
### compile
[Section titled “compile”](#compile)
The `apm compile` command. Takes resolved primitives in `apm_modules/` and writes them into each declared harness location (`.github/`, `.claude/`, `.cursor/`, etc.) using the format that harness expects. Runs automatically as the final phase of `apm install`.
NOT a build step that produces an artifact. Compile only deploys to local harness directories.
See: [Compilation guide](/apm/producer/compile/). Source: `src/apm_cli/commands/compile/`.
### dev-only primitive
[Section titled “dev-only primitive”](#dev-only-primitive)
A dependency listed under `devDependencies:` in `apm.yml` (mirroring `package.json`). Installed locally for authoring and testing but excluded from the bundle that `apm pack` ships. The lockfile records the `is_dev` flag per package.
NOT a separate primitive type. Any package or primitive can be marked dev-only; it is a visibility flag, not a category.
Source: `src/apm_cli/deps/lockfile.py`, `src/apm_cli/deps/installed_package.py`.
### GitHub APM PAT
[Section titled “GitHub APM PAT”](#github-apm-pat)
The personal access token APM reads to authenticate against GitHub when resolving private packages. Resolution order: `GITHUB_APM_PAT_` (per-org), then `GITHUB_APM_PAT`, then `GITHUB_TOKEN`, then `GH_TOKEN`. Public packages need no token.
NOT a separate token type. It is a standard GitHub PAT; the `GITHUB_APM_PAT` name exists so APM-scoped tokens do not collide with other tooling.
See: [Authentication](/apm/consumer/authentication/). Source: `src/apm_cli/core/auth.py`.
### harness
[Section titled “harness”](#harness)
The agent runtime that executes primitives: GitHub Copilot (CLI + IDE), Claude Code, Grok Build, Cursor, Codex, Gemini, Antigravity, OpenCode, Windsurf, and Kiro. Each harness has its own primitive directory layout and file format.
NOT the same as a target. The target is the manifest or CLI selector for which harnesses to compile for; the harness is the runtime itself.
Source: `src/apm_cli/integration/targets.py` (see `KNOWN_TARGETS`).
### hook
[Section titled “hook”](#hook)
A primitive type whose contents run at a defined lifecycle event in the host harness (for example `PreToolUse`). Supported on every harness except OpenCode – see the matrix in [primitives and targets](/apm/concepts/primitives-and-targets/).
NOT an APM CLI lifecycle event. Hooks fire inside the agent runtime, not inside `apm install` or `apm compile`.
Source: `src/apm_cli/integration/hook_integrator.py`.
### install
[Section titled “install”](#install)
The `apm install` command. Resolves dependencies declared in `apm.yml`, downloads them into `apm_modules/`, runs the policy gate, scans for hidden Unicode, writes `apm.lock.yaml`, and compiles primitives into each declared harness directory.
All major verbs match npm semantics: `apm install` deploys, `apm update` refreshes dependencies, `apm install --frozen` is the lockfile-only CI install (mirrors `npm ci`). CLI upgrades use your package manager (`brew upgrade apm` for Homebrew), or `apm self-update` for standalone installs, not `apm update`.
Source: `src/apm_cli/commands/install.py`.
### lockfile
[Section titled “lockfile”](#lockfile)
See [apm.lock.yaml](#apmlockyaml).
### manifest
[Section titled “manifest”](#manifest)
See [apm.yml](#apmyml).
### marketplace
[Section titled “marketplace”](#marketplace)
A curated index of packages, hosted as a Git repository with a `marketplace.json` at its root. Lists packages by handle and points each one at a Git source. Authors publish their packages to a marketplace so consumers can discover and install them by short name.
NOT a registry. A marketplace is human-curated discovery; the registry is the resolution backend that APM actually downloads from.
See: [Marketplaces guide](/apm/consumer/private-and-org-packages/). Source: `src/apm_cli/commands/marketplace/`.
### MCP server
[Section titled “MCP server”](#mcp-server)
A Model Context Protocol server declared as a dependency under `mcp:` in `apm.yml`. APM resolves MCP servers transitively, applies the same policy gate, and writes the runtime config into each harness that supports MCP.
NOT an APM primitive type. MCP servers are external processes; APM declares and gates them but does not ship their code.
Source: `src/apm_cli/install/mcp/`, `src/apm_cli/integration/mcp_integrator.py`.
### package
[Section titled “package”](#package)
The APM unit of distribution: a directory whose root contains an `apm.yml`. Packages declare primitives, dependencies, scripts, and targets. A repository may contain one package at the root or several in subdirectories.
NOT a plugin (see below) and NOT a bundle. A package is the source-form unit; the bundle is its packed form; `plugin.json` is a separate descriptor format.
Source: `src/apm_cli/models/apm_package.py`.
### plugin
[Section titled “plugin”](#plugin)
A local-install artifact whose root contains a `plugin.json` (Claude Code / Copilot CLI plugin format). APM detects plugins on `apm install `, treats them as packages by synthesising an `apm.yml` from the `plugin.json`, then installs them through the standard pipeline.
NOT a different thing from a package at runtime. Plugin format is the input shape; once detected, APM handles plugins exactly like packages.
See: [Plugins guide](/apm/producer/author-primitives/). Source: `src/apm_cli/bundle/local_bundle.py`, `src/apm_cli/commands/install.py`.
### policy
[Section titled “policy”](#policy)
The `apm-policy.yml` file plus the install-time enforcement gate that reads it. Lets a security team allow-list sources, scopes, and primitive kinds. Tightens-only across enterprise -> org -> repo. Runs before any file is written to disk, including for transitive MCP servers.
NOT the same as `audit`. Policy enforces at install time; audit reports after the fact.
See: [Governance guide](/apm/enterprise/governance-guide/). Source: `src/apm_cli/policy/`.
### primitive
[Section titled “primitive”](#primitive)
The atomic unit APM ships. The supported kinds are: instructions, skills, prompts, agents, hooks, commands, plugins, MCP servers, and experimental canvas extensions. Each kind has its own integrator that knows how to deploy it into each harness.
NOT every file in a package. Only files matching the primitive layout under recognised directories (`agents/`, `skills/`, `prompts/`, `instructions/`, `hooks/`, `commands/`, `extensions/`) are deployed.
See: [Primitives and targets](/apm/concepts/primitives-and-targets/). Source: `src/apm_cli/integration/`.
### registry
[Section titled “registry”](#registry)
The resolution backend APM downloads packages from. In the current implementation this is GitHub (or any Git host reachable over HTTPS or SSH); enterprise customers can pin to GHES, ADO, or GitLab.
NOT a marketplace. The registry is where bytes come from; a marketplace is a curated index that points at registry locations.
Source: `src/apm_cli/core/auth.py`, `src/apm_cli/utils/github_host.py`.
### script
[Section titled “script”](#script)
An entry under `scripts:` in `apm.yml`, mapped to a shell command. Invoked with `apm run `. Used for the post-install workflow that launches an agent against the compiled primitives (for example `apm run start`).
NOT a primitive. Scripts are project-level commands; they do not deploy into harness directories.
Source: `src/apm_cli/models/apm_package.py`, `src/apm_cli/commands/run.py`.
### target
[Section titled “target”](#target)
The `targets:` field in `apm.yml` (or legacy `target:`). Names which harnesses the package compiles for (`copilot`, `claude`, `grok-build`, `cursor`, `codex`, `gemini`, `antigravity`, `opencode`, `windsurf`, `kiro`, or `agent-skills`). `all` is a CLI `--target` value only. Drives which integrator runs and which directories receive output during `apm compile`.
NOT the harness itself. Target is the declaration; the harness is the runtime that consumes the compiled output.
See: [Primitives and targets](/apm/concepts/primitives-and-targets/). Source: `src/apm_cli/integration/targets.py`, `src/apm_cli/core/target_detection.py`.
### transitive dependency
[Section titled “transitive dependency”](#transitive-dependency)
A dependency that another dependency pulls in. APM resolves the full transitive closure (packages and MCP servers), applies the policy gate to every node, and records each one in the lockfile. Insecure transitive deps trigger an explicit error unless allow-listed.
NOT silent. Every transitive node is policy-gated and lockfile-recorded; nothing slips in below the manifest layer.
Source: `src/apm_cli/install/context.py`, `src/apm_cli/install/insecure_policy.py`.
### trust prompt
[Section titled “trust prompt”](#trust-prompt)
The install-time consent step before APM writes a new MCP server config to disk. Required because an MCP server pulled in transitively by a deep dependency can introduce a new outbound integration the user did not explicitly request. Today this is enforced via `--trust-transitive-mcp` opt-in plus `apm-policy.yml` allow-listing; an interactive prompt is on the Promise 2 roadmap.
Source: `src/apm_cli/install/mcp/`, `src/apm_cli/install/insecure_policy.py`.
### self-update
[Section titled “self-update”](#self-update)
The `apm self-update` command. Downloads the latest release of the `apm` CLI from the official installer URL and replaces the binary in place. Supports `--check` to report availability without installing. Disabled in package-manager distributions (for example, Homebrew), which print a distributor-defined upgrade message instead.
NOT a dependency refresh. `self-update` only touches the CLI binary; your project’s `apm.yml`, lockfile, and `apm_modules/` are untouched. For dependency refresh, see [update](#update).
Source: `src/apm_cli/commands/self_update.py`.
### update
[Section titled “update”](#update)
The `apm update` command. Re-resolves every dependency in `apm.yml` to its latest matching Git ref, prints a structured plan (added / updated / removed / unchanged), and prompts for consent before rewriting `apm.lock.yaml`. Defaults to **No** on the prompt; declining exits cleanly with no writes. `--yes` skips the prompt for CI; `--dry-run` prints the plan without prompting or writing.
Mirrors `npm update`. To pin to the existing lockfile in CI without refreshing, use `apm install --frozen` (mirrors `npm ci`). To upgrade the CLI binary itself, see [self-update](#self-update).
Source: `src/apm_cli/commands/update.py`.
# Lifecycle
> The five steps every APM project moves through, from init to audit.
APM has five lifecycle steps. Most projects use all five; small ones use three.
```plaintext
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”](#1-init)
```bash
apm init [project-name]
```
Scaffolds a new APM project in the current directory.
`apm init` writes an `apm.yml` manifest with sensible defaults for `name`, `author`, and `description`, plus empty dependency and script blocks. It records selected targets in `targets:`; author `.apm/` primitives yourself and run `apm install` or `apm compile` to create target output directories.
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 recognized [filesystem signals](../../reference/cli/targets/#detection-signals) and pre-checks every harness it finds. With `-y` and no flag, all detected harnesses are written into `apm.yml`. See [primitives and targets](../primitives-and-targets/) for what each target receives.
**Common surprises**
* Re-running `apm init` in a directory that already has `apm.yml` warns and exits unless you pass `-y` (which overwrites the manifest).
* `targets:` must contain at least one target. Omit the field (or leave legacy `target:` blank) when you want auto-detection at compile time.
**Read more:** [`apm init` reference](/apm/reference/cli/init/), [package anatomy](/apm/concepts/package-anatomy/).
## 2. INSTALL
[Section titled “2. INSTALL”](#2-install)
```bash
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:
1. **Resolve** – walk `dependencies` and `devDependencies` (APM packages, MCP servers, Claude skills, plugin collections), follow transitive deps, pick versions.
2. **Policy gate** – if `apm-policy.yml` is discovered (locally or via your repo’s org), every resolved dependency is checked against the allow-list before integration writes deployed files. Pass `--no-policy` to skip the org policy gate for one invocation; this does not bypass `apm audit --ci`.
3. **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 `--force` to deploy anyway.
4. **Integrate** – write primitives into each target harness’s native directory (`.github/`, `.claude/`, etc.) and merge MCP server configs into the harness-specific config files.
5. **Lockfile** – write `apm.lock.yaml` with pinned versions, content hashes, and the resolved MCP server set.
`apm install` with no arguments installs from the existing manifest. `apm install ` 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. If that command bootstraps a new project, it keeps the generated `apm.yml` and explicit target selection while rolling back package and deployment writes.
After narrowing `targets:`, run `apm install` to reconcile obsolete target output. APM removes only unchanged files it owns; edited files remain tracked so `apm audit --ci` can surface them for review.
Coming from npm?
`apm install` mirrors `npm install` deliberately. The big difference: APM also runs a security scan and, if present, an org policy gate before writing deployed files.
**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 `--force` and document the exception.
* Transitive MCP servers are gated behind explicit trust. If a deep dependency declares a new MCP server, re-declare it in your top-level `apm.yml` or use `--trust-transitive-mcp` in trusted environments.
**Read more:** [`apm install` reference](/apm/reference/cli/install/), [security](/apm/enterprise/security/), [policy reference](/apm/enterprise/policy-reference/).
## 3. COMPILE
[Section titled “3. COMPILE”](#3-compile)
```bash
apm compile [--target ]
```
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/`, `.kiro/` directories, and so on.
`apm install` deploys individual primitives but does not run aggregate compilation. Run `apm compile` explicitly to generate root or distributed context files such as `AGENTS.md`, `CLAUDE.md`, and `GEMINI.md`. `apm run` still compiles any `.prompt.md` files referenced by a script immediately before execution.
The `--target` flag accepts comma-separated catalog values; stable examples include `copilot`, `claude`, and `grok-build`. `--all` selects the default stable set. `--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 compile` does not re-run the security scan. The scan happens at install time. If you hand-edit primitives between installs, run `apm audit` to scan them.
* `--clean` removes orphaned `AGENTS.md` files from previous compilations. Without it, removed primitives can leave stale output behind.
**Read more:** [`apm compile` reference](../../reference/cli/compile/), [compilation guide](../../producer/compile/).
## 4. RUN
[Section titled “4. RUN”](#4-run)
```bash
apm run [--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`:
```yaml
name: my-project
version: 0.1.0
scripts:
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/.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 `. It prints the original command, the rewritten command after prompt compilation, and the list of compiled files.
Coming from npm?
The `scripts:` shape is intentionally identical to `package.json`. Object-form scripts (with `description`, `env`, etc.) are not supported; keep them strings.
**Common surprises**
* A script that does not reference any `.prompt.md` file runs as-is. APM only rewrites the command when it finds `.prompt.md` arguments.
* Parameters passed with `--param` only reach prompt files. They do not become shell environment variables.
**Read more:** [`apm run` reference](/apm/reference/cli/run/), [agent workflows guide](/apm/producer/author-primitives/instructions-and-agents/).
## 5. AUDIT
[Section titled “5. AUDIT”](#5-audit)
```bash
apm audit # local: scan deployed files for hidden Unicode
apm audit --ci # CI gate: lockfile consistency + drift replay
apm audit --file # standalone: scan an arbitrary file
```
`apm 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 `) 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 nine baseline consistency checks in order: `lockfile-exists`, `ref-consistency`, `deployment-ledger-owners`, `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. When `apm_modules/` is absent but the lockfile is present, `--ci` self-hydrates a lock-pinned scratch install first instead of reporting a green drift skip. 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 ` it also evaluates org policy against the lockfile.
**Common surprises**
* `apm audit --ci` exits 1 on any failure – this is the gate you wire into branch protection. The local `apm audit` exits 0 even when findings exist, unless you also pass `--strip` and 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-drift` and accept reduced coverage.
**Read more:** [`apm audit` reference](/apm/reference/cli/audit/), [policy reference](/apm/enterprise/policy-reference/) for the full check list, [security](/apm/enterprise/security/).
# Package anatomy
> The file layout of an APM package, field by field.
An APM package is a directory with two things: an `apm.yml` manifest and a `.apm/` source tree. Everything else – the lockfile, compiled output, MCP configs, runtime-specific folders – is generated, optional, or both.
This page walks the file tree top-down so you can recognize every piece on sight.
## The minimal package
[Section titled “The minimal package”](#the-minimal-package)
Three lines on disk is enough:
```plaintext
my-pkg/
+-- apm.yml
+-- .apm/
+-- skills/hello/SKILL.md
```
apm.yml
```yaml
name: my-pkg
version: 1.0.0
```
`name` and `version` are the only required fields. Both must be non-empty strings; quote a numeric version so YAML does not parse it as a number. `apm install` will validate the manifest, generate `apm.lock.yaml`, and deploy `hello` to whatever harnesses you target.
## Full file tree
[Section titled “Full file tree”](#full-file-tree)
A mature package looks closer to this. One line per file; deeper pages own the detail.
```plaintext
my-pkg/
+-- apm.yml # The manifest. Required. See below.
+-- apm.lock.yaml # Resolved versions + content hashes. Generated.
+-- apm_modules/ # Installed dependencies. Generated. Gitignore.
+-- .apm/ # Source primitives you author.
| +-- instructions/ # Always-on rules attached to file globs.
| +-- skills/ # Multi-file capabilities (SKILL.md + assets).
| +-- prompts/ # Reusable prompt templates.
| +-- agents/ # Named agents (model + system prompt + tools).
| +-- context/ # Shared context fragments.
| +-- hooks/ # Lifecycle hooks (pre/post events).
+-- .github/ # Compiled output for Copilot. Generated.
| +-- instructions/
| +-- agents/
| +-- copilot-instructions.md
+-- .claude/ # Compiled output for Claude Code. Generated.
+-- .cursor/ # Compiled output for Cursor. Generated.
+-- .codex/ # Compiled output for Codex. Generated.
+-- AGENTS.md # Compiled context for agents-family targets. Generated.
+-- GEMINI.md # Compiled context for Gemini. Generated.
+-- apm-policy.yml # Optional org/repo policy. See enterprise docs.
+-- scripts/ # Optional helper scripts you author.
+-- tests/ # Optional tests for your primitives.
```
Anything under `apm_modules/`, `.github/`, `.claude/`, `.cursor/`, or `.codex/` is build output. Edit the source under `.apm/` and re-run `apm install` – never edit the deployed copy.
`apm init` only writes `apm.yml`. The rest appears as you author primitives or run `apm install`.
For why `.apm/` exists at all (instead of writing straight into `.github/`), see [Primitives and targets](/apm/concepts/primitives-and-targets/).
## Anatomy of `apm.yml`
[Section titled “Anatomy of apm.yml”](#anatomy-of-apmyml)
A realistic example, every field annotated:
```yaml
# Required identity
name: my-pkg # Package name. Required.
version: 1.0.0 # SemVer string. Required.
# Optional metadata
description: Code review skills for Python services
author: Jane Doe # plain string, or {name, email?, url?} object
license: MIT
homepage: https://example.com/my-pkg
repository: https://github.com/org/my-pkg
keywords: [ai, review, python]
# Optional content type: one of instructions, skill, hybrid, prompts.
# Constrains what `.apm/` may contain. Useful for single-purpose packages.
type: skill
# Optional target list. Pins which harnesses this package compiles to.
# Prefer plural targets: as a YAML list; legacy target: CSV is still accepted.
targets:
- copilot
- claude
# Optional. "auto" publishes the authoritative local source layout, or list
# explicit repo paths to define the complete publication set.
includes: auto
# Optional. Runtime dependencies, grouped by kind.
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0 # Pinned to a tag
- github/awesome-copilot/skills/review-and-refactor # Single primitive
mcp:
- microsoft/azure-devops-mcp # MCP server dependency
# Optional. Same shape as `dependencies`, but excluded from the shipped
# artifact. Use for dev-only tooling and tests.
devDependencies:
apm:
- my-org/internal-test-skills
# Optional. Named scripts you can run with `apm run `.
scripts:
start: copilot -p hello.prompt.md
codex: codex --skip-git-repo-check hello.prompt.md
```
### Field reference
[Section titled “Field reference”](#field-reference)
| Field | Required | Notes |
| -------------------- | -------- | --------------------------------------------------------------- |
| `name` | yes | Package name. |
| `version` | yes | SemVer string. |
| `description` | no | |
| `author` | no | Plain string or `{name, email?, url?}` object. |
| `license` | no | SPDX identifier recommended. |
| `homepage` | no | URL; passed through to `plugin.json` by `apm pack`. |
| `repository` | no | URL; passed through to `plugin.json` by `apm pack`. |
| `keywords` | no | List of strings; passed through to `plugin.json` by `apm pack`. |
| `type` | no | `instructions`, `skill`, `hybrid`, or `prompts`. |
| `targets` / `target` | no | Preferred YAML list, or legacy string/list of harness slugs. |
| `includes` | no | `"auto"` or list of repo paths. |
| `dependencies` | no | Mapping with `apm:` and/or `mcp:` keys. |
| `devDependencies` | no | Same shape as `dependencies`. Excluded from `apm pack`. |
| `scripts` | no | Mapping of name to shell command. Run via `apm run `. |
Coming from npm?
The shape mirrors `package.json` on purpose: `name`, `version`, `dependencies`, `devDependencies`, `scripts`. The verbs match too: `apm install` deploys, `apm update` refreshes dependencies, and `apm install --frozen` is the lockfile-only CI install (mirrors `npm ci`). CLI upgrades use your package manager (`brew upgrade apm` for Homebrew), or `apm self-update` for standalone installs.
## Anatomy of `apm.lock.yaml`
[Section titled “Anatomy of apm.lock.yaml”](#anatomy-of-apmlockyaml)
The lockfile pins every resolved dependency to an exact commit and content hash so two clones of the repo install byte-identical primitives. Generated by `apm install`; commit it.
```yaml
lockfile_version: '1'
apm_version: 0.22.0
dependencies:
- repo_url: https://github.com/microsoft/apm-sample-package
resolved_commit: a1b2c3d4e5f6... # Exact SHA installed
resolved_ref: v1.0.0 # Tag/branch the SHA came from
version: 1.0.0 # SemVer if available
depth: 1 # 1 = direct, 2+ = transitive
package_type: apm_package
content_hash: sha256:9f... # Hash of the package file tree
deployed_files: # What this dep wrote to disk
- .github/skills/review/SKILL.md
deployed_file_hashes:
.github/skills/review/SKILL.md: sha256:c4...
# A single-primitive (virtual) import looks like this:
- repo_url: https://github.com/github/awesome-copilot
virtual_path: skills/review-and-refactor
is_virtual: true
resolved_commit: 7e8f9a...
depth: 1
mcp_servers:
- microsoft/azure-devops-mcp
# The package's own local content. Same hashing logic as deps; lets
# `apm audit` detect hand-edits to deployed files.
local_deployed_files:
- .github/instructions/python.instructions.md
local_deployed_file_hashes:
.github/instructions/python.instructions.md: sha256:45...
```
### Field reference
[Section titled “Field reference”](#field-reference-1)
Top-level fields:
| Field | Notes |
| ----------------------------- | --------------------------------------------------------- |
| `lockfile_version` | Schema version of the lockfile. |
| `apm_version` | CLI version that generated the file. |
| `dependencies` | List of `LockedDependency` entries. |
| `mcp_servers` | Resolved MCP server identifiers. |
| `mcp_configs` | Per-harness MCP configuration blobs. |
| `local_deployed_files` | Files this package wrote to deployed dirs. |
| `local_deployed_file_hashes` | SHA-256 of each local-deployed file. |
| *(Deprecated)* `generated_at` | Write timestamp. Remove from lockfile to avoid conflicts. |
Each dependency stores canonical identity and resolution data. For case-insensitive providers, `repo_url` is the canonical comparison value while the optional `materialization_repo_url` retains the repository display spelling used under `apm_modules/` and in generated links. The [lockfile specification](../../reference/lockfile-spec/#per-entry-fields) is the single field reference, including package-type-specific `name` and `version` semantics.
`apm audit` rehashes everything in `deployed_file_hashes` and `local_deployed_file_hashes` to detect hand-edits before they ship.
## The `.apm/` directory
[Section titled “The .apm/ directory”](#the-apm-directory)
`.apm/` is the conventional source root for APM packages. APM also recognizes package forms such as root `SKILL.md`, `plugin.json`, and nested `skills//SKILL.md`. Each subdirectory holds one primitive type; file naming conventions are documented per type.
* **`instructions/`** – Always-on rules attached to file globs (e.g. “for every `*.py`, follow PEP 8”). One Markdown file per rule. Compiled into `.github/instructions/`, `.cursor/rules/`, and the equivalent for other harnesses.
* **`skills//SKILL.md`** – Multi-file capabilities. The `SKILL.md` is the entry point; sibling files (templates, scripts, references) ship alongside it. Loaded on demand by harnesses that support skills.
* **`prompts/`** – Reusable prompt templates, one `.prompt.md` per prompt. Invocable via `apm run