Package Types
APM supports six source layouts plus catalog-only marketplace packages. Pick the form that matches the author’s intent – APM preserves it.
Layout summary
Section titled “Layout summary”| 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.
APM package (.apm/ directory)
Section titled “APM package (.apm/ directory)”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.mdWhat 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.
Skill bundle (SKILL.md at root)
Section titled “Skill bundle (SKILL.md at root)”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 scriptsWhat 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.
Metadata model (HYBRID packages)
Section titled “Metadata model (HYBRID packages)”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.descriptionis a short human-facing tagline rendered byapm view,apm search,apm deps list, and registry/marketplace listings.SKILL.mddescription(frontmatter) is the agent-runtime invocation matcher consumed by Claude, Copilot, and other runtimes per the agentskills.io spec. APM copiesSKILL.mdbyte-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.
Skill collection (skills/<name>/SKILL.md)
Section titled “Skill collection (skills/<name>/SKILL.md)”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 # optionalWhat 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.
# 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 - functionsThe 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
namefield (if present) must match the directory name. - Frontmatter
descriptionshould 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.
Hook package (hooks/*.json only)
Section titled “Hook package (hooks/*.json only)”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.jsonWhat 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.
Catalog-only marketplace package
Section titled “Catalog-only marketplace package”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.
Plugin collection (plugin.json)
Section titled “Plugin collection (plugin.json)”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.mdWhat 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.jsonWhat 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.
See also
Section titled “See also”- Your First Package – hands-on walkthrough for scaffolding and publishing.
apm installandapm pack– install, package, and validation options.- Manifest Schema – full
apm.ymlfield reference.