Skip to content

Package Types

APM supports six source layouts plus catalog-only marketplace packages. Pick the form that matches the author’s intent – APM preserves it.

Package signal Author intent Install semantic
.apm/ (with or without apm.yml) “I have N independent primitives” Hoist each primitive into the target’s runtime dirs
SKILL.md (alone or with apm.yml – HYBRID) “I am one skill bundle” Copy the whole bundle to <target>/skills/<name>/
skills/<name>/SKILL.md (nested) “I ship many skills in one repo” Promote each nested skill to <target>/skills/<name>/
hooks/*.json only (no apm.yml or SKILL.md) “I ship a set of harness hooks” Deploy each hook to the target’s hooks/ directory
plugin.json (no $schema, or unrecognized $schema) / .claude-plugin/ Claude plugin collection Dissect via plugin artifact mapping
plugin.json with the recognized Agent Plugins $schema Portable Agent Plugin Installed whole and registered when the effective targets include Copilot
Marketplace entry with inline lspServers or mcpServers Catalog owns server metadata Synthesize and validate apm.yml, then deploy servers

When plugin signals coexist with an eligible apm.yml, the APM layout wins. An apm.yml is eligible when the root also has .apm/ or the manifest declares APM or MCP dependencies. To intentionally select a plugin layout, omit apm.yml or keep it metadata-only, without .apm/ or dependencies.

The classic APM layout. Primitives live under .apm/ in typed subdirectories. apm install hoists each primitive into the consumer’s runtime directories individually.

my-package/
+-- apm.yml
+-- .apm/
+-- skills/
| +-- pr-description/SKILL.md
+-- agents/
| +-- reviewer.agent.md
+-- instructions/
+-- team-standards.instructions.md

What gets installed: each skill, agent, and instruction is copied to its corresponding runtime directory (e.g. .github/skills/, .github/agents/).

When to choose: you are shipping multiple independent primitives that consumers may override or extend individually.

A single skill with co-located resources. The presence of SKILL.md at the package root tells APM: “this entire directory is one skill – install it as a unit.”

An optional apm.yml alongside SKILL.md makes this a HYBRID package. APM still installs it as a skill bundle, but gains dependency resolution, version metadata, and script support from the manifest.

code-review-skill/
+-- SKILL.md
+-- agents/
| +-- reviewer.agent.md
+-- assets/
| +-- checklist.md
+-- scripts/
| +-- lint-check.sh
+-- apm.yml # optional -- enables dependencies and scripts

What gets installed: the entire directory tree is copied to <target>/skills/<name>/, preserving internal structure.

When to choose: you are shipping one cohesive skill that bundles its own agents, assets, or scripts. The skill’s internal layout is part of its contract – APM will not rearrange it.

apm.yml and SKILL.md each own their description field independently – APM never merges or backfills one from the other. The two strings serve different consumers:

  • apm.yml.description is a short human-facing tagline rendered by apm view, apm search, apm deps list, and registry/marketplace listings.
  • SKILL.md description (frontmatter) is the agent-runtime invocation matcher consumed by Claude, Copilot, and other runtimes per the agentskills.io spec. APM copies SKILL.md byte-for-byte into <target>/skills/<name>/ and never reads or mutates this field.

Other apm.yml fields (name, version, license, dependencies, scripts) are owned exclusively by apm.yml – there is no SKILL.md-side equivalent and nothing to merge. allowed-tools lives exclusively in SKILL.md frontmatter and is consumed by the agent runtime.

When you ship a HYBRID package, populate both descriptions independently: keep apm.yml.description to a short tagline (under ~80 characters) and write SKILL.md in whatever length and tone the agent runtime expects. apm pack warns when apm.yml.description is missing so the human-facing surfaces do not degrade silently while the agent runtime keeps working.

A multi-skill package following the agentskills.io / npx skills convention. Each skill lives in its own subdirectory under skills/ with its own SKILL.md.

A Git repo with skills/<name>/SKILL.md needs neither root apm.yml nor root SKILL.md for skills: [name]. An optional root apm.yml supplies metadata and dependencies; otherwise APM synthesizes minimal metadata.

azure-skills/
+-- skills/
| +-- cosmos-db/
| | +-- SKILL.md
| | +-- examples/
| +-- functions/
| | +-- SKILL.md
| +-- aks/
| +-- SKILL.md
+-- apm.yml # optional

What gets installed: each skills/<name>/ directory is promoted to <target>/skills/<name>/, preserving internal structure.

Selective install: use --skill <name> to install only specific skills from the bundle (repeatable). The selection is persisted in apm.yml (as a skills: field) and apm.lock.yaml (as skill_subset), so subsequent bare apm install commands are deterministic. Selection controls deployed skills, not sparse checkout or the package root; use path to select a subdirectory instead. For nested skill bundles, the selector must match a deployable skill path; for example, use productivity/grill-me, not an invented prefix. Use --skill '*' to reset and install all skills. --skill is additive across separate installs (a later --skill X unions onto the existing pin and never removes already-deployed skills) – see apm install.

Terminal window
# Install only two skills (persisted to apm.yml):
apm install microsoft/azure-skills --skill cosmos-db --skill functions
# Bare reinstall respects the persisted selection:
apm install
# Reset to all skills:
apm install microsoft/azure-skills --skill '*'

The apm.yml entry is promoted to dict form with a skills: list:

dependencies:
apm:
- git: microsoft/azure-skills
skills:
- cosmos-db
- functions

The sibling per-dependency targets: list uses the same object form to limit which active harnesses receive a dependency’s target-scoped primitives.

Validation rules:

  • Frontmatter name field (if present) must match the directory name.
  • Frontmatter description should be present (warning if absent).
  • All frontmatter values must be ASCII-only.
  • Directory names must pass path-traversal checks.

When to choose: you maintain a curated collection of independent skills in one repository (e.g. all Azure skills, all Firebase skills). Consumers can install the full set or cherry-pick with --skill.

A package whose root contains hooks/*.json files but no apm.yml, SKILL.md, or plugin.json. APM treats the whole directory as a hook bundle: each hook JSON is deployed to the target’s runtime hooks directory.

my-hooks/
+-- hooks/
+-- pre-commit.json
+-- post-merge.json

What gets installed: every file under hooks/ is deployed to the target’s hooks runtime path (e.g. .github/hooks/ for Copilot, .claude/hooks/ for Claude).

When to choose: you ship a set of harness hooks with no other primitives. If you also ship skills or instructions, prefer the .apm/ layout and put your hooks under .apm/hooks/ so they install alongside the rest.

A marketplace entry can supply a package’s only APM metadata. When its downloaded source has no apm.yml, SKILL.md, or plugin manifest, APM uses one or both inline lspServers and mcpServers fields. It admits only name, description, version, lspServers, and mcpServers; unrelated catalog fields, including dependency fields, cannot add APM dependencies.

APM stages and validates a synthesized apm.yml before committing it. Every declared server must validate. Any failure rejects the package and removes its download. A symlinked package path, apm.yml, or .apm path also fails closed. On warm installs, APM rematerializes the manifest when the admitted catalog metadata variant changes.

A Claude-native plugin layout. A plugin.json with no $schema field, or with an unrecognized $schema, is detected by its structure as this layout. APM dissects the plugin artifacts and maps them into runtime directories. An unrecognized schema is a warning, not a rejection; only the recognized Agent Plugins schema selects the portable Agent Plugin route.

my-plugin/
+-- plugin.json
+-- agents/
| +-- helper.agent.md
+-- skills/
+-- search/SKILL.md

What gets installed: each artifact listed in plugin.json is mapped to the appropriate runtime directory via _map_plugin_artifacts. Use --skill to cherry-pick plugin skills by leaf name or a source-relative path under skills/, such as productivity/grill-me.

A declared key is authoritative for its primitive: it replaces the default directory scan rather than adding to it. "skills": ["./skills/search"] deploys search and nothing else, and a skill declared outside skills/ deploys even though no scan would have found it. Only an omitted key falls back to scanning the conventional directory.

This is a migration point for existing plugin authors. A string or list is exhaustive after normalization, including a declared container with one skill per immediate child. "skills": [] intentionally deploys no skills. When that empty declaration shadows root skills/ entries, APM emits one diagnostic that names the package and tells you to declare the skills (or their container), or remove the key to restore discovery.

Marketplace version checks: a local Plugin collection may omit apm.yml. For apm pack --check-versions, APM uses plugin.json’s version only when apm.yml is absent. See Versioning strategies for manifest precedence, failure behavior, and a plugin-only example.

Declared component paths are requirements, not hints. If an agents, skills, commands, or hooks entry is missing or escapes the plugin root, install exits non-zero before deployment or lockfile commit. Likewise, --skill exits non-zero when none of the manifest-declared skill paths match. Omit an optional field or use an empty list when the plugin has no component of that type.

When to choose: you already have a Claude plugin and want APM to consume it without restructuring. This is still the no-flag default output of apm pack and apm plugin init.

Agent Plugin (plugin.json with an Agent Plugins schema)

Section titled “Agent Plugin (plugin.json with an Agent Plugins schema)”

A plugin.json that declares the exact Agent Plugins v1 "$schema" is a distinct package type from the Claude plugin collection above. Other schema identifiers are identification misses: APM warns, then classifies by structure. A non-string $schema remains a manifest error.

my-plugin/
+-- plugin.json # "$schema": ".../1.0.0/plugin.schema.json"
+-- skills/
| +-- search/SKILL.md
+-- mcp.json

What gets installed: nothing is dissected. With --target copilot, APM keeps the plugin whole under apm_modules/ and registers it with Copilot as a live directory marketplace. APM does not require or inspect a Copilot binary during install. Stable Copilot CLI 1.0.81 or newer is the supported runtime for loading the generated projection. Copilot loads the unit from APM’s bytes – it never copies it. See Install Agent Plugins for Copilot.

When to choose: you are producing a portable package with apm pack --format agent-plugin for GitHub Copilot and other Agent-Plugin-aware hosts that read the Agent Plugins v1 schema directly. See apm pack.