Repo shapes for marketplace producers
A marketplace producer repo is just an apm.yml (or several) plus a
marketplace: block. There is no --shape flag and no scaffold
mode: every layout below emerges from the same two commands,
apm plugin init and apm marketplace init, composed differently.
Two primitive postures cover most repos – you ship your own plugin, or you curate others’ plugins into a marketplace. The third row in the table is what happens when one repo does both at once. Pick the shape that matches how the source code is already organised; you can migrate later by moving directories and re-running the same commands.
| Shape | Source files | When |
|---|---|---|
| Single-plugin | One apm.yml at the repo root |
One plugin per repo. Smallest surface, fewest gotchas. |
| Aggregator | One apm.yml at the root, N remote packages: |
You curate other repos into a marketplace. |
| Monorepo-hybrid (advanced) | Root apm.yml plus per-plugin apm.yml subdirs |
Many plugins live alongside the marketplace in one repo. Composition of the two postures above. |
When the layout is ready, ship it with the recipe in Releasing from any CI.
Single-plugin
Section titled “Single-plugin”One repo, one plugin, one marketplace entry pointing at the local source. The marketplace artifact and the plugin live side by side.
Scaffold:
apm plugin init my-plugin --yesapm marketplace init --owner acme-org --name my-marketplaceapm marketplace package add ./ --name my-plugin --version 0.1.0 --no-verifyResulting apm.yml:
name: my-pluginversion: 0.1.0description: Single plugin shipped through its own marketplace
marketplace: owner: name: acme-org url: https://github.com/acme-org outputs: claude: {} packages: - name: my-plugin source: ./ version: 0.1.0apm pack writes the plugin bundle to ./build/my-plugin/ and the
marketplace artifact to .claude-plugin/marketplace.json. Commit
both. Consumers run apm marketplace add acme-org/<repo>.
Aggregator
Section titled “Aggregator”One repo whose only job is to curate plugins that live in other repos. No plugin source lives here.
Scaffold:
apm marketplace init --owner acme-org --name acme-curatedapm marketplace package add acme-org/skill-pkg-a --version "^1.0.0"apm marketplace package add acme-org/skill-pkg-b --ref v0.4.2Resulting apm.yml:
name: acme-curatedversion: 1.0.0description: Curated APM marketplace for acme-org
marketplace: owner: name: acme-org url: https://github.com/acme-org outputs: claude: {} packages: - name: skill-pkg-a source: acme-org/skill-pkg-a version: "^1.0.0" - name: skill-pkg-b source: acme-org/skill-pkg-b ref: v0.4.2apm pack resolves every remote entry against git ls-remote and
writes marketplace.json only. No bundle is produced because
dependencies: is omitted.
Monorepo-hybrid
Section titled “Monorepo-hybrid”Advanced. Most first-time authors should start with single-plugin or aggregator. Reach for hybrid when you’re shipping your own plugin and curating others.
DevExpGbb/zava-agent-configis the live reference: 7 plugins underplugins/, one rootapm.yml, releases via microsoft/apm-action@v1mode: release.
One repo, many plugins under plugins/, one marketplace at the root
that lists them as local-path entries. Each plugin gets its own
apm.yml so it can be compiled and tested in isolation.
Layout:
my-monorepo/ apm.yml # marketplace + local-path packages plugins/ plugin-a/ apm.yml # plugin-a's manifest .apm/ agents/ expert.agent.md instructions/ style.instructions.md skills/ my-skill/ SKILL.md plugin-b/ apm.yml .apm/ prompts/ review.prompt.md hooks/ pre-tool.jsonImportant – use
.apm/<type>/for every primitive in each plugin. When.apm/exists, it is the authoritative local pack source. Without.apm/, supported plugin-native root directories remain pack sources, including afterapm initwritesincludes: auto. Mixed layouts pack from.apm/and warn about each skipped root source.apm installonly discovers instructions, commands, and prompts under.apm/<type>/, so authoringplugins/plugin-a/instructions/style.instructions.mdinstead ofplugins/plugin-a/.apm/instructions/style.instructions.mdcan install incomplete. See Pack a bundle – source layout and install-time discovery for the full per-primitive scan-path reference.
plugins/ is not an APM CLI repository requirement: a package source: can
point to another directory. This walkthrough uses plugins/ because
microsoft/apm-action with mode: release autodetects aggregator members
only at plugins/<name>/apm.yml.
Scaffold:
# From the empty my-monorepo repository rootmkdir -p pluginscd pluginsapm plugin init plugin-a --yesapm plugin init plugin-b --yescd ..apm marketplace init --owner acme-org --name acme-monorepoapm marketplace package remove example-package --yesapm marketplace package add ./plugins/plugin-a --name plugin-a --version 0.1.0 --no-verifyapm marketplace package add ./plugins/plugin-b --name plugin-b --version 0.1.0 --no-verify--version avoids resolving a Git ref, while --no-verify skips remote
reachability checks for the local sources. The scaffold starts every manifest
at 0.1.0, satisfying the lockstep strategy below.
Resulting root apm.yml:
name: acme-monorepoversion: 0.1.0description: Acme plugins shipped together
marketplace: owner: name: acme-org url: https://github.com/acme-org outputs: claude: {} versioning: strategy: lockstep # see versioning-strategies packages: - name: plugin-a source: ./plugins/plugin-a version: 0.1.0 - name: plugin-b source: ./plugins/plugin-b version: 0.1.0Local-path entries skip remote resolution. Each plugin’s own
apm.yml controls its build; the root apm.yml controls the
marketplace index. Pick a versioning strategy that matches how you
tag releases – see Versioning strategies.
Shipping bin/ executables (Claude Code only)
Section titled “Shipping bin/ executables (Claude Code only)”A plugin may ship a top-level bin/ directory of executable scripts.
When a consumer runs a global install (apm install -g), APM
deploys the plugin as a Claude Code skills-directory plugin (a folder
containing .claude-plugin/plugin.json) under the Claude skills
directory, which puts bin/ on Claude Code’s Bash tool PATH. The
agent can then invoke your scripts as bare commands.
my-plugin/ apm.yml .apm/ bin/ my-tool # executable script (chmod handled by APM)This is a Claude-Code-specific contract – no other harness has an
equivalent – so bin/ deploys only when the consumer has an active
Claude Code skills target. Authoring rules:
- Deploy is user-scope only. A project-scope install (without
-g) skipsbin/and prints a hint to re-run with-g. - APM tightens deployed executable files to
0o700on POSIX systems. Do not rely on source-file permissions or a specific umask. - Deployed executables sit on Claude Code’s
PATHand are invoked without per-call confirmation. Treat them as trusted code: keep them minimal, audited, and free of network side effects you would not want an agent to trigger unprompted. - Enterprises can deny deployment per-package or globally via the org
executables.denypolicy (the legacybin_deployrule remains a deprecated alias) – see the policy schema. - Consumers can pass
--trust-binonapm installto explicitly consent (suppresses the trust-posture warning) or--no-trust-binto skip bin/ deployment for a single invocation. Without either flag, bin/ deploys with a prominent warning.
What to read next
Section titled “What to read next”- Versioning strategies – lockstep vs
per-package and how
apm pack --check-versionsenforces them. - Releasing from any CI – the canonical release pipeline that ships any of the shapes above.
- Publish to a marketplace – the
apm marketplace initwalkthrough and the registry schema.