Hey Squad team — first off, thanks for shipping #876 and following up so cleanly with #963. Squad reaching for APM as the distribution layer is a milestone, and the framing in the PR description (".squad/ remains the source of truth — APM is an export layer") nails the architecture I'd hoped third-party tools would land on.
I'm Daniel, the maintainer of microsoft/apm. Working through the merged code, I noticed the integration drifts from APM's current contract in a handful of spots. Each one has a documented APM-side answer, and most would simplify Squad's code in the process. Writing this up so it's all in one place — happy to send a PR for the mechanical bits if it's useful.
Summary
| # |
Area |
Squad today |
APM contract |
| 1 |
apm.yml schema |
Top-level skills: / instructions: / prompts: |
Not valid keys; primitives are discovered from .apm/ |
| 2 |
Install |
Reimplemented via gh api + a regex YAML parser |
Shell out to apm install owner/repo[/skill-name] |
| 3 |
Publish/portability |
Custom apm.yml regen |
apm pack --format plugin produces a portable bundle |
| 4 |
Distribution model |
"APM registry" / apm publish |
Git-native; no registry; apm publish doesn't exist |
| 5 |
Skill source path |
.copilot/skills/<name>/skill.md |
.apm/skills/<name>/SKILL.md (then APM deploys to .copilot/, .github/, etc.) |
1. apm.yml doesn't have skills: / instructions: / prompts: top-level keys
The manifest only recognizes name, version, description, author, license, target, type, scripts, dependencies, devDependencies, compilation (manifest-schema §2). Primitives aren't declared in the manifest — they're discovered from .apm/<primitive>/ directories (primitive-types). The blocks Squad emits are silently ignored by apm install, and the target: paths inside them aren't honored — APM's integrators decide deployment paths based on the consumer's target: setting.
Suggested: emit only the fields APM reads — typically name, version, and (when relevant) a dependencies.apm: block. Skill content goes in .apm/skills/<name>/SKILL.md and is picked up automatically.
2. squad skill install reimplements apm install
The current path uses gh api to fetch the upstream apm.yml, parses it with a regex YAML reader, then gh api-fetches each skill.md. APM's apm install already does this and a lot more (cli-commands):
apm install owner/repo # whole package
apm install owner/repo/skill-name # virtual subdirectory (selective)
apm install owner/repo/path/skill.md # single file
You also get lockfile pinning (apm.lock.yaml), transitive resolution, content-hash verification, and security scanning — none of which are reachable via the gh api path. The lockfile also tracks provenance, so .apm-source.json becomes redundant.
Suggested: swap the install internals for execFile('apm', ['install', source]). Drops ~200 LOC including the YAML parser flagged in #963.
3. The actual feature for "make this Squad portable" is apm pack --format plugin
squad skill publish regenerates apm.yml to advertise local skills. The intent — "make this skill bundle portable so others can use it" — is exactly what apm pack --format plugin solves (pack-distribute §plugin format):
"apm pack --format plugin transforms your project into a standalone plugin directory consumable by Copilot CLI, Claude Code, or other plugin hosts. The output contains no APM-specific files."
Mapping:
| Source |
Plugin output |
.apm/skills/*/SKILL.md |
skills/*/SKILL.md |
.apm/agents/*.agent.md |
agents/*.agent.md |
.apm/prompts/*.prompt.md |
commands/*.md |
.apm/instructions/*.instructions.md |
instructions/*.instructions.md |
Suggested: squad skill publish shells out to apm pack --format plugin after laying skills out under .apm/skills/<name>/SKILL.md. Users git push the resulting bundle. This also unlocks "hybrid mode" (guides/plugins) — Squad packages can declare dev-only deps that get excluded from the published bundle.
4. There's no APM registry and no apm publish
A few user-facing strings reference both:
skill.ts:173 — "push to GitHub to share via APM registry"
skill.ts:183 — "Run 'apm publish' to push to the APM registry"
skill.ts:298 (help) — "APM registry: https://github.com/microsoft/apm"
APM is git-native by design (what-is-apm):
"Distribute. Any git repository is a valid APM package. Publish by pushing to a git remote — no registry required."
microsoft/apm is the source repo, not a registry endpoint. Marketplaces (guides/marketplaces) exist as optional curated indexes for discovery, but there's no apm publish and no plan to add one — the model is intentionally git push.
Suggested: update the strings to "Run apm pack --format plugin to produce a portable bundle, then git push to share."
5. Skill source location: .apm/skills/, not .copilot/skills/
Worth being precise on the directory model since the doc has been ambiguous on this:
.apm/<primitive>/ is where a project's own primitives live (the project itself is a package). APM discovers them automatically.
.copilot/, .github/, .claude/, .cursor/ are deploy targets that apm install populates, both from local .apm/ content and from installed dependencies. They're outputs, not sources.
Today resolveSkillsDir writes skills to .copilot/skills/<name>/skill.md as the source location, which inverts that model. (Side note: @Omzig already noted in #924 that Copilot CLI doesn't actually read .copilot/skills/ either, so users probably aren't getting what they expect on the consumer side.)
Suggested: treat .apm/skills/<name>/SKILL.md as the source of truth. APM's integrators handle the per-target deployment.
On the APM side
A few things shipping in parallel that should make this easier going forward:
- JSON Schema for
apm.yml (warns rather than errors on unknown top-level keys, so existing Squad-generated manifests in the wild won't break overnight).
- A short doc page aimed at framework authors integrating with APM, with Squad as the worked example if you're OK with that.
Offer
Items 1, 2, 4, 5 are mechanical and I'd estimate ~150 LOC net delta (negative once the YAML parser comes out). Happy to PR them in one or split across a few. Item 3 is a UX shift for squad skill publish so I'd defer to you on direction before opening anything.
Let me know what's useful — no obligation either way, and again, really nice to see APM integration in Squad.
— Daniel
Hey Squad team — first off, thanks for shipping #876 and following up so cleanly with #963. Squad reaching for APM as the distribution layer is a milestone, and the framing in the PR description (".squad/ remains the source of truth — APM is an export layer") nails the architecture I'd hoped third-party tools would land on.
I'm Daniel, the maintainer of microsoft/apm. Working through the merged code, I noticed the integration drifts from APM's current contract in a handful of spots. Each one has a documented APM-side answer, and most would simplify Squad's code in the process. Writing this up so it's all in one place — happy to send a PR for the mechanical bits if it's useful.
Summary
apm.ymlschemaskills:/instructions:/prompts:.apm/gh api+ a regex YAML parserapm install owner/repo[/skill-name]apm.ymlregenapm pack --format pluginproduces a portable bundleapm publishapm publishdoesn't exist.copilot/skills/<name>/skill.md.apm/skills/<name>/SKILL.md(then APM deploys to.copilot/,.github/, etc.)1.
apm.ymldoesn't haveskills:/instructions:/prompts:top-level keysThe manifest only recognizes
name,version,description,author,license,target,type,scripts,dependencies,devDependencies,compilation(manifest-schema §2). Primitives aren't declared in the manifest — they're discovered from.apm/<primitive>/directories (primitive-types). The blocks Squad emits are silently ignored byapm install, and thetarget:paths inside them aren't honored — APM's integrators decide deployment paths based on the consumer'starget:setting.Suggested: emit only the fields APM reads — typically
name,version, and (when relevant) adependencies.apm:block. Skill content goes in.apm/skills/<name>/SKILL.mdand is picked up automatically.2.
squad skill installreimplementsapm installThe current path uses
gh apito fetch the upstreamapm.yml, parses it with a regex YAML reader, thengh api-fetches eachskill.md. APM'sapm installalready does this and a lot more (cli-commands):You also get lockfile pinning (
apm.lock.yaml), transitive resolution, content-hash verification, and security scanning — none of which are reachable via thegh apipath. The lockfile also tracks provenance, so.apm-source.jsonbecomes redundant.Suggested: swap the install internals for
execFile('apm', ['install', source]). Drops ~200 LOC including the YAML parser flagged in #963.3. The actual feature for "make this Squad portable" is
apm pack --format pluginsquad skill publishregeneratesapm.ymlto advertise local skills. The intent — "make this skill bundle portable so others can use it" — is exactly whatapm pack --format pluginsolves (pack-distribute §plugin format):Mapping:
.apm/skills/*/SKILL.mdskills/*/SKILL.md.apm/agents/*.agent.mdagents/*.agent.md.apm/prompts/*.prompt.mdcommands/*.md.apm/instructions/*.instructions.mdinstructions/*.instructions.mdSuggested:
squad skill publishshells out toapm pack --format pluginafter laying skills out under.apm/skills/<name>/SKILL.md. Usersgit pushthe resulting bundle. This also unlocks "hybrid mode" (guides/plugins) — Squad packages can declare dev-only deps that get excluded from the published bundle.4. There's no APM registry and no
apm publishA few user-facing strings reference both:
skill.ts:173— "push to GitHub to share via APM registry"skill.ts:183— "Run 'apm publish' to push to the APM registry"skill.ts:298(help) — "APM registry: https://github.com/microsoft/apm"APM is git-native by design (what-is-apm):
microsoft/apmis the source repo, not a registry endpoint. Marketplaces (guides/marketplaces) exist as optional curated indexes for discovery, but there's noapm publishand no plan to add one — the model is intentionallygit push.Suggested: update the strings to "Run
apm pack --format pluginto produce a portable bundle, thengit pushto share."5. Skill source location:
.apm/skills/, not.copilot/skills/Worth being precise on the directory model since the doc has been ambiguous on this:
.apm/<primitive>/is where a project's own primitives live (the project itself is a package). APM discovers them automatically..copilot/,.github/,.claude/,.cursor/are deploy targets thatapm installpopulates, both from local.apm/content and from installed dependencies. They're outputs, not sources.Today
resolveSkillsDirwrites skills to.copilot/skills/<name>/skill.mdas the source location, which inverts that model. (Side note: @Omzig already noted in #924 that Copilot CLI doesn't actually read.copilot/skills/either, so users probably aren't getting what they expect on the consumer side.)Suggested: treat
.apm/skills/<name>/SKILL.mdas the source of truth. APM's integrators handle the per-target deployment.On the APM side
A few things shipping in parallel that should make this easier going forward:
apm.yml(warns rather than errors on unknown top-level keys, so existing Squad-generated manifests in the wild won't break overnight).Offer
Items 1, 2, 4, 5 are mechanical and I'd estimate ~150 LOC net delta (negative once the YAML parser comes out). Happy to PR them in one or split across a few. Item 3 is a UX shift for
squad skill publishso I'd defer to you on direction before opening anything.Let me know what's useful — no obligation either way, and again, really nice to see APM integration in Squad.
— Daniel