Skip to content
Open
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `apm install --target intellij` now configures JetBrains Copilot MCP support
while routing package file primitives through the Copilot profile.
(by @sergio-sisternes-epam; closes #1957) (#2041)

### Fixed

- Azure DevOps marketplace checks now preserve suffix-free `/_git/<repo>` URLs
and pass Azure CLI bearer authentication through to `git ls-remote`. (closes #2119)
- `apm pack` now treats `.apm/` as the authoritative local source, warns on
mixed layouts, and enforces explicit `includes:` lists exhaustively while
preserving root-only Claude plugin directories, including after `apm init`.
(#2122)

## [0.24.1] - 2026-07-10

Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/concepts/package-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ targets:
- copilot
- claude

# Optional. "auto" auto-publishes every primitive under .apm/, or list
# explicit repo paths to publish a subset.
# 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.
Expand Down
19 changes: 14 additions & 5 deletions docs/src/content/docs/getting-started/first-package.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ cd team-skills
`apm init` creates exactly one file -- the manifest. The `.apm/` source tree
is yours to author.

:::tip[Already have a Claude plugin?]
If `skills/`, `agents/`, or `commands/` already exists at the project root,
`apm init` leaves that layout in place. It writes `includes: auto` without
creating `.apm/`, and those plugin-native directories remain sources for
`apm pack`. Create `.apm/` later when you want `apm pack` to source from that
directory instead of the project root. See [source layout and install-time
discovery](../../producer/pack-a-bundle/#source-layout-and-install-time-discovery).
:::

```
team-skills/
+-- apm.yml
Expand All @@ -52,11 +61,11 @@ includes: auto
scripts: {}
```

`includes: auto` is the field that makes step 4 work: with no remote
dependencies declared, `apm install` walks your local `.apm/` tree
and deploys what it finds. Set `includes: []` (or omit the field) and
local content stops deploying. Override with an explicit list of
paths to gate exactly what ships.
`includes: auto` records explicit consent to deploy or pack local content.
Omitting the field preserves legacy implicit consent and produces an audit
advisory. Use an explicit list of paths when you need an exhaustive publication
boundary. Source layout is independent: `.apm/` is authoritative when present;
otherwise supported plugin-native root directories remain pack sources.

## 2. Add a skill

Expand Down
39 changes: 28 additions & 11 deletions docs/src/content/docs/producer/pack-a-bundle.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Pack a bundle
description: Build a plugin-format bundle from your .apm/ source so others can deploy it with a single apm install command.
description: Build a plugin-format bundle from APM-native or plugin-native source so others can deploy it with one apm install command.
---

A bundle is the artifact you hand to a consumer when you do not want to publish
to a registry. It is a directory (or archive -- `.zip` by default, `.tar.gz` via
`--archive-format tar.gz`) containing a
`plugin.json`, your primitive folders, and an embedded `apm.lock.yaml` that
pins every file by SHA-256. Build it with one command from a project that has
`.apm/` and `apm.yml`:
`apm.yml`:

```bash
apm pack
Expand Down Expand Up @@ -127,10 +127,22 @@ For the consumer flags that apply (`--target`, `--global`, `--force`,

## Source layout and install-time discovery

`apm pack` is intentionally liberal: it collects primitives from both
`.apm/<type>/` subdirectories and from convention directories at the
package root (`agents/`, `skills/`, `instructions/`, etc.). This lets
you author in whichever layout feels natural during development.
When `.apm/` exists, local primitive content is sourced from `.apm/`.
Without `.apm/`, supported plugin-native root directories such as
`agents/`, `skills/`, `commands/`, and `hooks/` remain pack sources.
`includes: auto` grants publication consent but does not select either
layout. An explicit `includes` list is exhaustive and may deliberately
name an APM-native or root path.

If both layouts exist, `.apm/` wins and packing succeeds. A warning names
each skipped root source and tells you to move it under `.apm/` or remove
it:

```text
[!] Skipping root-level skills/ because .apm/ is present.
Move publishable files to .apm/skills/ or remove skills/ to silence
this warning.
```

When packing git dependencies, `apm pack` emits **only** what the
lockfile attests, in every format (`--format plugin` and the default
Expand All @@ -150,9 +162,11 @@ pack` fails and tells you to run `apm install` to record provenance.
Dependency **hooks-config and MCP-config** (the `hooks.json` / `.mcp.json`
entries `apm install` merges into shared host settings) are *not*
attested in `deployed_files`, so they are not packed; `apm pack` warns
loudly (`[!]`) and names the dependency when this happens. First-party
root hooks/MCP authored by the packaging project itself **are** packed --
only unattested dependency config is dropped. Hook *scripts* recorded in
loudly (`[!]`) and names the dependency when this happens. First-party hooks
authored by the packaging project follow the selected local layout: root
`hooks/` and `hooks.json` are skipped when `.apm/` is present. The packaging
project's root `.mcp.json` is packed independently of source layout. Only
unattested dependency config is dropped. Hook *scripts* recorded in
`deployed_files` still pack normally.

`apm install` is per-primitive and stricter. Each integrator has its own
Expand Down Expand Up @@ -180,8 +194,11 @@ Source: `src/apm_cli/integration/instruction_integrator.py`,
### Canonical layout for marketplace publishers

:::caution[Silent install drops can remove intended guardrails]
`apm pack` accepts primitives from both `.apm/<type>/` and root convention
directories (for example, an `instructions/` folder at the plugin root).
When `.apm/` exists, it is the authoritative local source. Without `.apm/`,
supported plugin-native root directories remain pack sources, including after
`apm init` writes `includes: auto`. An explicit [`includes`
list](../reference/manifest-schema/#39-includes) is exhaustive regardless
of layout.
`apm install` does NOT discover instructions, commands, or prompts placed
in root convention directories. Packages that rely on these primitives for
security guardrails or policy enforcement will install silently incomplete,
Expand Down
13 changes: 7 additions & 6 deletions docs/src/content/docs/producer/repo-shapes.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,13 @@ my-monorepo/
```

> **Important -- use `.apm/<type>/` for every primitive in each plugin.**
> `apm pack` accepts primitives from both `.apm/<type>/` and root
> convention directories (e.g. `instructions/` at the plugin root), but
> `apm install` only discovers instructions, commands, and prompts under
> `.apm/<type>/`. Authoring `packages/plugin-a/instructions/style.instructions.md`
> instead of `packages/plugin-a/.apm/instructions/style.instructions.md`
> will produce a bundle that packs correctly but installs silently
> When `.apm/` exists, it is the authoritative local pack source. Without
> `.apm/`, supported plugin-native root directories remain pack sources,
> including after `apm init` writes `includes: auto`. Mixed layouts pack from
> `.apm/` and warn about each skipped root source. `apm install` only discovers
> instructions, commands, and prompts under `.apm/<type>/`, so authoring
> `packages/plugin-a/instructions/style.instructions.md` instead of
> `packages/plugin-a/.apm/instructions/style.instructions.md` can install
> incomplete. See [Pack a bundle -- source layout and install-time
> discovery](./pack-a-bundle/#source-layout-and-install-time-discovery)
> for the full per-primitive scan-path reference.
Expand Down
4 changes: 3 additions & 1 deletion docs/src/content/docs/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ Three fields matter day one:
- `scripts` -- optional named shell commands you run with the
experimental `apm run` (see [Run scripts](/apm/consumer/run-scripts/)).

`includes: auto` ships any local `.apm/` content you author. The
`includes: auto` ships local content from the authoritative source layout:
`.apm/` when that directory exists, or supported plugin-native directories at
the project root otherwise. The
`targets:` block is commented out so APM auto-detects the harnesses
present in your repo (`.github/`, `.claude/`, `.cursor/`, etc.). For the
full schema, see [Package anatomy](/apm/concepts/package-anatomy/).
Expand Down
4 changes: 4 additions & 0 deletions docs/src/content/docs/reference/cli/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ $ apm init --yes --target copilot,claude,cursor
pre-checks targets read from its existing `target:` field.
- **Codex hint:** if `.codex/` is present, suggests
`--target agent-skills` to also deploy skills to `.agents/skills/`.
- **Existing plugin sources:** when plugin-native directories such as
`skills/`, `agents/`, or `commands/` exist at the project root and `.apm/`
does not, warns that they remain packable. `apm init` does not create
`.apm/` automatically.
- **agentrc suggestion:** when no agent instruction files are found
(`.github/copilot-instructions.md`, `AGENTS.md`, `.github/instructions/`),
the Next Steps panel suggests generating agent instructions:
Expand Down
5 changes: 4 additions & 1 deletion docs/src/content/docs/reference/cli/pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ apm pack --archive --dry-run -v
A Claude Code plugin directory under `--output`. Contains:

- `plugin.json` -- schema-conformant manifest. Convention-dir keys are stripped because Claude Code auto-discovers them.
- Plugin-native subdirs populated from your `.apm/` content and from installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`, `extensions/` (canvas extensions, when the `canvas` experimental flag is enabled).
- Plugin-native subdirs populated from local source and installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`, `extensions/` (canvas extensions, when the `canvas` experimental flag is enabled).
- When `.apm/` exists, local primitives and hooks are sourced from `.apm/`. Root convention sources are skipped with actionable warnings.
- Without `.apm/`, supported plugin-native root directories remain pack sources, including after `apm init` writes [`includes: auto`](../manifest-schema/#39-includes).
- An explicit `includes:` list is exhaustive. A missing or unpackable listed path stops packing instead of falling back to implicit discovery.
- Installed dependencies are packed exclusively from lockfile-attested `deployed_files`; the `apm_modules` cache is never packed (it has no provenance or integrity guarantee). Each attested file is verified against its `deployed_file_hashes` SHA-256 before inclusion.
- If the dependency declares `skills:`, only the named skills are included; the cache cannot add extras.
- If a dependency has cached primitives but no `deployed_files`, `apm pack` fails and tells you to run `apm install`.
Expand Down
37 changes: 30 additions & 7 deletions docs/src/content/docs/reference/manifest-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,24 @@ scripts:
| **Default** | Undeclared (legacy implicit auto-publish; flagged by `apm audit`). |
| **Allowed values** | `auto` or a list of paths relative to the project root. |

Declares which local `.apm/` content the project consents to publish when packing or deploying. Three forms are supported:

1. **Undeclared** (field omitted). Legacy behaviour: all local `.apm/` content is published as if `auto` were set. `apm audit` emits an `includes-consent` advisory whenever local content is deployed under this form.
2. **`includes: auto`**. Explicit consent to publish all local `.apm/` content via the file scanner. No path enumeration required. Default for newly initialised projects.
3. **`includes: [<path>, ...]`**. Explicit allow-list of paths the project consents to publish. Strongest governance form; changes are reviewable in PR diffs.
Records consent to publish local content during deployment and controls local
content selection when producing plugin bundles with `apm pack`. This field
does not select the source layout: `.apm/` presence makes the APM-native layout
authoritative, while its absence preserves supported plugin-native root
sources. Three forms are supported:

1. **Undeclared** (field omitted). Legacy implicit consent for content in the
selected source layout. `apm audit` emits an `includes-consent` advisory
whenever local content is deployed under this form.
2. **`includes: auto`**. Explicit consent to publish all local content from the
selected source layout. No path enumeration required. Default for newly
initialised projects.
3. **`includes: [<path>, ...]`**. Explicit consent list for deployment and the
exhaustive allow-list for plugin packing. Listed paths may be under `.apm/`
or a plugin-native root directory. Missing, unsafe, symlinked, or unpackable
paths stop `apm pack`; no implicit source fallback occurs. During
`apm install`, the list satisfies explicit-consent policy but does not filter
integrator discovery.

```yaml
# Form 1: undeclared (legacy; audit advisory)
Expand All @@ -247,7 +260,10 @@ includes: auto
# - .apm/skills/my-skill/
```

`includes:` is allow-list only. There is no `exclude:` form. To keep maintainer-only primitives out of shipped artifacts, author them OUTSIDE `.apm/` and reference them via a local-path devDependency. See [Dev-only Primitives](../concepts/primitives-and-targets/#dev-only-primitives).
For plugin packing, `includes:` is allow-list only. There is no `exclude:`
form. To keep maintainer-only primitives out of shipped artifacts, author them
outside the selected source layout and reference them via a local-path
devDependency. See [Dev-only Primitives](../concepts/primitives-and-targets/#dev-only-primitives).

When `policy.manifest.require_explicit_includes` is `true` (see [Policy reference](../enterprise/policy-reference/)), only form 3 passes; `auto` and undeclared are rejected at install/audit time by the `explicit-includes` check (not at YAML parse time).

Expand Down Expand Up @@ -713,7 +729,14 @@ Created automatically by [`apm plugin init`](./cli/plugin/). Use [`apm install -
apm install --dev owner/test-helpers
```

Plain `apm install` (no flag) deploys both `dependencies` and `devDependencies`. There is no `--omit=dev` flag today; the dev/prod separation kicks in at `apm pack` (plugin format, the default). The local-content scanner that builds plugin bundles operates on `.apm/` only and does not consult the devDep marker. To keep maintainer-only primitives out of shipped artifacts, author them outside `.apm/` and reference them via a local-path devDependency. See [Dev-only Primitives](../concepts/primitives-and-targets/#dev-only-primitives).
Plain `apm install` (no flag) deploys both `dependencies` and
`devDependencies`. There is no `--omit=dev` flag today; the dev/prod separation
kicks in at `apm pack` (plugin format, the default). The local-content scanner
uses `.apm/` when present, otherwise supported plugin-native root directories;
it does not consult the devDep marker. To keep maintainer-only primitives out
of shipped artifacts, author them outside the selected source layout and
reference them via a local-path devDependency. See [Dev-only
Primitives](../concepts/primitives-and-targets/#dev-only-primitives).

Local-path devDependency example:

Expand Down
Loading
Loading