From 6ed40bcac4ab4499f9fdd942211cc31d0f2e215c Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Fri, 6 Mar 2026 12:20:59 +0100 Subject: [PATCH 1/4] docs: add apm.yml manifest schema reference for integrators Add a spec-grade manifest schema reference (docs/manifest-schema.md) documenting the complete apm.yml format including: - Top-level fields (name, version, description, author, license, target, type, scripts) - APM dependency syntax (string and object forms, virtual packages, normalization) - MCP dependency syntax (string and object forms, validation rules) - Compilation configuration (CompilationConfig fields and defaults) - Lockfile specification (apm-lock.json format) - Integrator contract for tooling interoperability This is intended as a reference for external integrators (e.g. GitHub Agentic Workflows team) who need to parse or generate apm.yml files. All fields verified against source code (apm_package.py, agents_compiler.py, lockfile.py, target_detection.py). --- docs/index.md | 1 + docs/manifest-schema.md | 393 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+) create mode 100644 docs/manifest-schema.md diff --git a/docs/index.md b/docs/index.md index ab701808..69e4d050 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,6 +18,7 @@ New to APM? Follow this learning path for the fastest way to get productive: ### Command Line Interface - **[CLI Reference](cli-reference.md)** - Complete command documentation with examples +- **[Manifest Schema](manifest-schema.md)** - Complete `apm.yml` schema reference for integrators - **[Integration Guide](integrations.md)** - VSCode, Spec-kit, AI runtimes, and tool compatibility ### Framework Implementation diff --git a/docs/manifest-schema.md b/docs/manifest-schema.md new file mode 100644 index 00000000..86ffee6e --- /dev/null +++ b/docs/manifest-schema.md @@ -0,0 +1,393 @@ +# `apm.yml` Manifest Specification + +> **Version:** 0.1  |  **Status:** Draft  |  **Format:** YAML 1.2 + +The `apm.yml` manifest declares the full closure of agent primitive dependencies, MCP servers, scripts, and compilation settings for a project. It is the contract between package authors, runtimes, and integrators — any conforming resolver can consume this format to install, compile, and run agentic workflows. + +--- + +## Document Structure + +```yaml +# apm.yml +name: # REQUIRED +version: # REQUIRED +description: +author: +license: +target: +type: +scripts: > +dependencies: + apm: > + mcp: > +compilation: +``` + +--- + +## Top-Level Fields + +### `name` + +| | | +|---|---| +| **Type** | `string` | +| **Required** | Yes | +| **Description** | Package identifier. Free-form string (no pattern enforced at parse time). Convention: alphanumeric, dots, hyphens, underscores. | + +### `version` + +| | | +|---|---| +| **Type** | `string` | +| **Required** | Yes | +| **Pattern** | `^\d+\.\d+\.\d+` (semver; pre-release/build suffixes allowed) | +| **Description** | Semantic version. A value that does not match the pattern produces a validation warning (non-blocking). | + +### `description` + +| | | +|---|---| +| **Type** | `string` | +| **Required** | No | +| **Description** | Brief human-readable description. | + +### `author` + +| | | +|---|---| +| **Type** | `string` | +| **Required** | No | +| **Description** | Package author or organization. | + +### `license` + +| | | +|---|---| +| **Type** | `string` | +| **Required** | No | +| **Description** | SPDX license identifier (e.g. `MIT`, `Apache-2.0`). | + +### `target` + +| | | +|---|---| +| **Type** | `enum` | +| **Required** | No | +| **Default** | `all` | +| **Allowed values** | `vscode` · `agents` · `claude` · `all` | + +Controls which output targets are generated during compilation. Unknown values are silently ignored (auto-detection takes over). + +| Value | Effect | +|---|---| +| `vscode` | Emits `AGENTS.md` files into `.github/` | +| `agents` | Alias for `vscode` | +| `claude` | Emits `CLAUDE.md` into `.claude/` | +| `all` | Both targets | + +### `type` + +| | | +|---|---| +| **Type** | `enum` | +| **Required** | No | +| **Default** | None (unset — behaviour depends on package content) | +| **Allowed values** | `instructions` · `skill` · `hybrid` · `prompts` | + +Declares how the package's content is processed during install and compile: + +| Value | Behaviour | +|---|---| +| `instructions` | Compiled into AGENTS.md only. No skill directory created. | +| `skill` | Installed as a native skill only. No AGENTS.md output. | +| `hybrid` | Both AGENTS.md compilation and skill installation. | +| `prompts` | Commands/prompts only. No instructions or skills. | + +### `scripts` + +| | | +|---|---| +| **Type** | `map` | +| **Required** | No | +| **Key pattern** | Script name (free-form string) | +| **Value** | Shell command string | +| **Description** | Named commands executed via `apm run `. Supports `--param key=value` substitution. | + +--- + +## `dependencies` + +| | | +|---|---| +| **Type** | `object` | +| **Required** | No | +| **Allowed keys** | `apm`, `mcp` | + +Contains two optional lists: `apm` for agent primitive packages and `mcp` for MCP servers. Each list entry is either a string shorthand or a typed object. + +--- + +### `dependencies.apm` — `list` + +Each element is one of two forms: **string** or **object**. + +#### String Form + +Grammar: + +``` +dependency = url_form | shorthand_form +url_form = ("https://" | "git@") +shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["@" alias] ["#" ref] +``` + +| Segment | Required | Pattern | Description | +|---|---|---|---| +| `host` | No | FQDN (e.g. `gitlab.com`) | Git host. Defaults to `github.com`. | +| `owner/repo` | **Yes** | `^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$` | Repository path. Nested groups supported for non-GitHub hosts (e.g. `gitlab.com/group/sub/repo`). | +| `virtual_path` | No | Path segments after repo | Subdirectory or file within the repo. See *Virtual Packages* below. | +| `alias` | No | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Parsed before `ref`. | +| `ref` | No | Branch, tag, or commit SHA | Git reference. Commit SHAs matched by `^[a-f0-9]{7,40}$`. Semver tags matched by `^v?\d+\.\d+\.\d+`. | + +**Examples:** + +```yaml +dependencies: + apm: + # GitHub shorthand (default host) + - microsoft/apm-sample-package + - microsoft/apm-sample-package#v1.0.0 + - microsoft/apm-sample-package@standards + + # Non-GitHub hosts (FQDN preserved) + - gitlab.com/acme/coding-standards + - bitbucket.org/team/repo#main + + # Full URLs + - https://github.com/microsoft/apm-sample-package.git + - git@github.com:microsoft/apm-sample-package.git + + # Virtual packages + - ComposioHQ/awesome-claude-skills/brand-guidelines # subdirectory + - contoso/prompts/review.prompt.md # single file + + # Azure DevOps + - dev.azure.com/org/project/_git/repo +``` + +#### Object Form + +Required when the shorthand is ambiguous (e.g. nested-group repos with virtual paths). + +| Field | Type | Required | Pattern / Constraint | Description | +|---|---|---|---|---| +| `git` | `string` | **Yes** | HTTPS URL, SSH URL, or FQDN shorthand | Clone URL of the repository. | +| `path` | `string` | No | Relative path within the repo | Subdirectory or file (virtual package). | +| `ref` | `string` | No | Branch, tag, or commit SHA | Git reference to checkout. | +| `alias` | `string` | No | `^[a-zA-Z0-9._-]+$` | Local alias. | + +```yaml +- git: https://gitlab.com/acme/repo.git + path: instructions/security + ref: v2.0 + alias: acme-sec +``` + +#### Virtual Packages + +A dependency that targets a subdirectory or file within a repository rather than the whole repo. + +| Kind | Detection rule | Example | +|---|---|---| +| **File** | `virtual_path` ends in `.prompt.md`, `.instructions.md`, `.agent.md`, or `.chatmode.md` | `owner/repo/prompts/review.prompt.md` | +| **Subdirectory** | `virtual_path` does not match any file extension above | `owner/repo/skills/security` | + +#### Canonical Normalisation + +Conforming writers MUST normalise entries to canonical form on write. `github.com` is the default host and is stripped; all other hosts are preserved as FQDN. + +| Input | Canonical form | +|---|---| +| `https://github.com/microsoft/apm-sample-package.git` | `microsoft/apm-sample-package` | +| `git@github.com:microsoft/apm-sample-package.git` | `microsoft/apm-sample-package` | +| `gitlab.com/acme/repo` | `gitlab.com/acme/repo` | + +--- + +### `dependencies.mcp` — `list` + +Each element is one of two forms: **string** or **object**. + +#### String Form + +A plain registry reference: `io.github.github/github-mcp-server` + +#### Object Form + +| Field | Type | Required | Constraint | Description | +|---|---|---|---|---| +| `name` | `string` | **Yes** | Non-empty | Server identifier (registry name or custom name). | +| `transport` | `enum` | Conditional | `stdio` · `sse` · `http` · `streamable-http` | Transport protocol. **Required** when `registry: false`. | +| `env` | `map` | No | | Environment variable overrides. | +| `args` | `dict` or `list` | No | | Dict for overlay variable overrides (registry), list for positional args (self-defined). | +| `version` | `string` | No | | Pin to a specific server version. | +| `registry` | `bool` or `string` | No | Default: `true` (public registry) | `false` = self-defined (private) server. String = custom registry URL. | +| `package` | `enum` | No | `npm` · `pypi` · `oci` | Package manager type hint. | +| `headers` | `map` | No | | Custom HTTP headers for remote endpoints. | +| `tools` | `list` | No | Default: `["*"]` | Restrict which tools are exposed. | +| `url` | `string` | Conditional | | Endpoint URL. **Required** when `registry: false` and `transport` is `http`, `sse`, or `streamable-http`. | +| `command` | `string` | Conditional | | Binary path. **Required** when `registry: false` and `transport` is `stdio`. | + +**Validation rules for self-defined servers (`registry: false`):** +- `transport` MUST be present. +- If `transport` is `stdio`, `command` MUST be present. +- If `transport` is `http`, `sse`, or `streamable-http`, `url` MUST be present. + +```yaml +dependencies: + mcp: + # Registry reference (string) + - io.github.github/github-mcp-server + + # Registry with overlays (object) + - name: io.github.github/github-mcp-server + tools: ["repos", "issues"] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Self-defined server (object, registry: false) + - name: my-private-server + registry: false + transport: stdio + command: ./bin/my-server + args: ["--port", "3000"] + env: + API_KEY: ${{ secrets.KEY }} +``` + +--- + +## `compilation` — `CompilationConfig` + +Optional section controlling `apm compile` behaviour. All fields have sensible defaults; omitting the entire section is valid. + +| Field | Type | Default | Constraint | Description | +|---|---|---|---|---| +| `target` | `enum` | `all` | `vscode` · `agents` · `claude` · `all` | Output target (same values as top-level `target`). | +| `strategy` | `enum` | `distributed` | `distributed` · `single-file` | `distributed` generates per-directory AGENTS.md files. `single-file` generates one monolithic file. | +| `single_file` | `bool` | `false` | | Legacy alias. When `true`, overrides `strategy` to `single-file`. | +| `output` | `string` | `AGENTS.md` | File path | Custom output path for the compiled file. | +| `chatmode` | `string` | — | | Chatmode filter for compilation. | +| `resolve_links` | `bool` | `true` | | Resolve relative Markdown links in primitives. | +| `source_attribution` | `bool` | `true` | | Include source-file origin comments in compiled output. | +| `exclude` | `list` or `string` | `[]` | Glob patterns | Directories to skip during compilation (e.g. `apm_modules/**`). | +| `placement` | `object` | — | | Placement tuning. See sub-fields below. | + +#### `compilation.placement` + +| Field | Type | Default | Description | +|---|---|---|---| +| `min_instructions_per_file` | `int` | `1` | Minimum instruction count to warrant a separate AGENTS.md file. | + +```yaml +compilation: + target: all + strategy: distributed + source_attribution: true + exclude: + - "apm_modules/**" + - "tmp/**" + placement: + min_instructions_per_file: 1 +``` + +--- + +## Complete Example + +```yaml +name: my-project +version: 1.0.0 +description: AI-native web application +author: Contoso +license: MIT +target: all +type: hybrid # instructions | skill | hybrid | prompts + +scripts: + review: "copilot -p 'code-review.prompt.md'" + impl: "copilot -p 'implement-feature.prompt.md'" + +dependencies: + apm: + - microsoft/apm-sample-package + - gitlab.com/acme/coding-standards + - git: https://gitlab.com/acme/repo.git + path: instructions/security + ref: v2.0 + alias: acme-sec + mcp: + - io.github.github/github-mcp-server + - name: my-private-server + registry: false + transport: stdio + command: ./bin/my-server + env: + API_KEY: ${{ secrets.KEY }} + +compilation: + target: all + strategy: distributed + exclude: + - "apm_modules/**" + placement: + min_instructions_per_file: 1 +``` + +--- + +## Lockfile Specification (`apm.lock`) + +After successful dependency resolution, a conforming resolver MUST write a lockfile capturing the exact resolved state. The lockfile is a separate YAML file committed to version control. + +### Structure + +```yaml +lockfile_version: "1" +generated_at: +apm_version: +dependencies: + : + repo_url: # Resolved clone URL + host: # Git host (optional, e.g. "gitlab.com") + resolved_commit: # Full commit SHA + resolved_ref: # Branch/tag that was resolved + version: # Package version from its apm.yml + virtual_path: # Virtual package path (if applicable) + is_virtual: # True for virtual (file/subdirectory) packages + depth: # 1 = direct, 2+ = transitive + resolved_by: # Parent dependency (transitive only) + deployed_files: > # Workspace-relative paths of installed files +``` + +### Resolver Behaviour + +1. **First install** — resolve all dependencies, write `apm.lock`. +2. **Subsequent installs** — read `apm.lock`, use locked commit SHAs. Skip download if local checkout already matches. +3. **`--update` flag** — re-resolve from `apm.yml`, overwrite lockfile. + +--- + +## Integrator Contract + +Any runtime adopting this format (e.g. GitHub Agentic Workflows, CI systems, IDEs) should implement these steps: + +1. **Parse** — Read `apm.yml` as YAML. Validate the two required fields (`name`, `version`) and the `dependencies` object shape. +2. **Resolve `dependencies.apm`** — For each entry, clone/fetch the git repo (respecting `ref`), locate the `.apm/` directory (or virtual path), and extract primitives. +3. **Resolve `dependencies.mcp`** — For each entry, resolve from the MCP registry or validate self-defined transport config. +4. **Transitive resolution** — Resolved packages may contain their own `apm.yml` with further dependencies, forming a dependency tree. Resolve transitively. Conflicts are merged at instruction level (by `applyTo` pattern), not file level. +5. **Write lockfile** — Record exact commit SHAs and deployed file paths in `apm.lock` for reproducibility. + +The schema (this document) is the contract. Implementations — resolver, downloader, installer, compiler — are decoupled. Each runtime builds its own against this spec. From 0dd3cd85591c1356115a67d0e04fa6e86486df4b Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Fri, 6 Mar 2026 12:33:43 +0100 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20address=20PR=20review=20=E2=80=94?= =?UTF-8?q?=20fix=20grammar=20order,=20URL=20forms,=20collections,=20targe?= =?UTF-8?q?t=20effects,=20lockfile=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix dependency grammar: string format is #ref@alias (not @alias#ref) - Add ssh:// and http:// to supported URL forms - Add virtual collection packages (dir and manifest kinds) - Fix target effects: AGENTS.md/CLAUDE.md at project root, not .github/.claude - Fix target default: auto-detect based on folder presence, not fixed 'all' - Fix lockfile structure: dependencies is a YAML list, not a map - Clarify compilation.target default when set explicitly --- docs/manifest-schema.md | 50 ++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/docs/manifest-schema.md b/docs/manifest-schema.md index 86ffee6e..c55c4a68 100644 --- a/docs/manifest-schema.md +++ b/docs/manifest-schema.md @@ -75,17 +75,18 @@ compilation: |---|---| | **Type** | `enum` | | **Required** | No | -| **Default** | `all` | +| **Default** | Auto-detect: `vscode` if `.github/` exists, `claude` if `.claude/` exists, `all` if both, `minimal` if neither | | **Allowed values** | `vscode` · `agents` · `claude` · `all` | -Controls which output targets are generated during compilation. Unknown values are silently ignored (auto-detection takes over). +Controls which output targets are generated during compilation. When unset, the CLI auto-detects based on `.github/` and `.claude/` folder presence. Unknown values are silently ignored (auto-detection takes over). | Value | Effect | |---|---| -| `vscode` | Emits `AGENTS.md` files into `.github/` | +| `vscode` | Emits `AGENTS.md` at the project root (and per-directory files in distributed mode) | | `agents` | Alias for `vscode` | -| `claude` | Emits `CLAUDE.md` into `.claude/` | -| `all` | Both targets | +| `claude` | Emits `CLAUDE.md` at the project root | +| `all` | Both `vscode` and `claude` targets | +| `minimal` | AGENTS.md only at project root (fallback when no `.github/` or `.claude/` detected) | ### `type` @@ -139,8 +140,8 @@ Grammar: ``` dependency = url_form | shorthand_form -url_form = ("https://" | "git@") -shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["@" alias] ["#" ref] +url_form = ("https://" | "http://" | "ssh://git@" | "git@") +shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["#" ref] ["@" alias] ``` | Segment | Required | Pattern | Description | @@ -148,8 +149,8 @@ shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["@" alias] ["#" r | `host` | No | FQDN (e.g. `gitlab.com`) | Git host. Defaults to `github.com`. | | `owner/repo` | **Yes** | `^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$` | Repository path. Nested groups supported for non-GitHub hosts (e.g. `gitlab.com/group/sub/repo`). | | `virtual_path` | No | Path segments after repo | Subdirectory or file within the repo. See *Virtual Packages* below. | -| `alias` | No | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Parsed before `ref`. | | `ref` | No | Branch, tag, or commit SHA | Git reference. Commit SHAs matched by `^[a-f0-9]{7,40}$`. Semver tags matched by `^v?\d+\.\d+\.\d+`. | +| `alias` | No | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Appears after `#ref` in the string. | **Examples:** @@ -167,7 +168,9 @@ dependencies: # Full URLs - https://github.com/microsoft/apm-sample-package.git + - http://github.com/microsoft/apm-sample-package.git - git@github.com:microsoft/apm-sample-package.git + - ssh://git@github.com/microsoft/apm-sample-package.git # Virtual packages - ComposioHQ/awesome-claude-skills/brand-guidelines # subdirectory @@ -197,12 +200,14 @@ Required when the shorthand is ambiguous (e.g. nested-group repos with virtual p #### Virtual Packages -A dependency that targets a subdirectory or file within a repository rather than the whole repo. +A dependency that targets a subdirectory, file, or collection within a repository rather than the whole repo. | Kind | Detection rule | Example | |---|---|---| | **File** | `virtual_path` ends in `.prompt.md`, `.instructions.md`, `.agent.md`, or `.chatmode.md` | `owner/repo/prompts/review.prompt.md` | -| **Subdirectory** | `virtual_path` does not match any file extension above | `owner/repo/skills/security` | +| **Collection (dir)** | `virtual_path` contains `/collections/` (no collection extension) | `owner/repo/collections/security` | +| **Collection (manifest)** | `virtual_path` contains `/collections/` and ends with `.collection.yml` or `.collection.yaml` | `owner/repo/collections/security.collection.yml` | +| **Subdirectory** | `virtual_path` does not match any file, collection, or extension rule above | `owner/repo/skills/security` | #### Canonical Normalisation @@ -275,7 +280,7 @@ Optional section controlling `apm compile` behaviour. All fields have sensible d | Field | Type | Default | Constraint | Description | |---|---|---|---|---| -| `target` | `enum` | `all` | `vscode` · `agents` · `claude` · `all` | Output target (same values as top-level `target`). | +| `target` | `enum` | `all` | `vscode` · `agents` · `claude` · `all` | Output target (same values as top-level `target`). Defaults to `all` when set explicitly in compilation config. | | `strategy` | `enum` | `distributed` | `distributed` · `single-file` | `distributed` generates per-directory AGENTS.md files. `single-file` generates one monolithic file. | | `single_file` | `bool` | `false` | | Legacy alias. When `true`, overrides `strategy` to `single-file`. | | `output` | `string` | `AGENTS.md` | File path | Custom output path for the compiled file. | @@ -358,18 +363,17 @@ After successful dependency resolution, a conforming resolver MUST write a lockf lockfile_version: "1" generated_at: apm_version: -dependencies: - : - repo_url: # Resolved clone URL - host: # Git host (optional, e.g. "gitlab.com") - resolved_commit: # Full commit SHA - resolved_ref: # Branch/tag that was resolved - version: # Package version from its apm.yml - virtual_path: # Virtual package path (if applicable) - is_virtual: # True for virtual (file/subdirectory) packages - depth: # 1 = direct, 2+ = transitive - resolved_by: # Parent dependency (transitive only) - deployed_files: > # Workspace-relative paths of installed files +dependencies: # YAML list (not a map) + - repo_url: # Resolved clone URL + host: # Git host (optional, e.g. "gitlab.com") + resolved_commit: # Full commit SHA + resolved_ref: # Branch/tag that was resolved + version: # Package version from its apm.yml + virtual_path: # Virtual package path (if applicable) + is_virtual: # True for virtual (file/subdirectory) packages + depth: # 1 = direct, 2+ = transitive + resolved_by: # Parent dependency (transitive only) + deployed_files: > # Workspace-relative paths of installed files ``` ### Resolver Behaviour From d411c237537f4298846f3278da77b406f208a82d Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Fri, 6 Mar 2026 12:37:39 +0100 Subject: [PATCH 3/4] docs: adopt W3C/RFC spec style for manifest schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Status of This Document preamble - Add Abstract section - Add Conformance section with RFC 2119 keywords - Number all sections (1-7) and subsections (e.g. 4.1.3) - Use normative language (MUST/SHOULD/MAY/OPTIONAL/REQUIRED) - Cross-reference sections using § notation - Add metadata block (version, date, editors, repository) - Move example to Appendix A - Add Appendix B (Revision History) --- docs/manifest-schema.md | 263 +++++++++++++++++++++++----------------- 1 file changed, 149 insertions(+), 114 deletions(-) diff --git a/docs/manifest-schema.md b/docs/manifest-schema.md index c55c4a68..37952357 100644 --- a/docs/manifest-schema.md +++ b/docs/manifest-schema.md @@ -1,12 +1,38 @@ -# `apm.yml` Manifest Specification +# APM Manifest Format Specification -> **Version:** 0.1  |  **Status:** Draft  |  **Format:** YAML 1.2 +
+
Version
0.1 (Working Draft)
+
Date
2026-03-06
+
Editors
Daniel Meppiel (Microsoft)
+
Repository
https://github.com/microsoft/apm
+
Format
YAML 1.2
+
+ +## Status of This Document + +This is a **Working Draft**. It may be updated, replaced, or made obsolete at any time. It is inappropriate to cite this document as other than work in progress. + +This specification defines the manifest format (`apm.yml`) used by the Agent Package Manager (APM). Feedback is welcome via [GitHub Issues](https://github.com/microsoft/apm/issues). + +--- + +## Abstract The `apm.yml` manifest declares the full closure of agent primitive dependencies, MCP servers, scripts, and compilation settings for a project. It is the contract between package authors, runtimes, and integrators — any conforming resolver can consume this format to install, compile, and run agentic workflows. --- -## Document Structure +## 1. Conformance + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). + +A conforming manifest is a YAML 1.2 document that satisfies all MUST-level requirements in this specification. A conforming resolver is a program that correctly parses conforming manifests and performs dependency resolution as described herein. + +--- + +## 2. Document Structure + +A conforming manifest MUST be a YAML mapping at the top level with the following shape: ```yaml # apm.yml @@ -26,59 +52,59 @@ compilation: --- -## Top-Level Fields +## 3. Top-Level Fields -### `name` +### 3.1. `name` | | | |---|---| | **Type** | `string` | -| **Required** | Yes | +| **Required** | MUST be present | | **Description** | Package identifier. Free-form string (no pattern enforced at parse time). Convention: alphanumeric, dots, hyphens, underscores. | -### `version` +### 3.2. `version` | | | |---|---| | **Type** | `string` | -| **Required** | Yes | +| **Required** | MUST be present | | **Pattern** | `^\d+\.\d+\.\d+` (semver; pre-release/build suffixes allowed) | -| **Description** | Semantic version. A value that does not match the pattern produces a validation warning (non-blocking). | +| **Description** | Semantic version. A value that does not match the pattern SHOULD produce a validation warning (non-blocking). | -### `description` +### 3.3. `description` | | | |---|---| | **Type** | `string` | -| **Required** | No | +| **Required** | OPTIONAL | | **Description** | Brief human-readable description. | -### `author` +### 3.4. `author` | | | |---|---| | **Type** | `string` | -| **Required** | No | +| **Required** | OPTIONAL | | **Description** | Package author or organization. | -### `license` +### 3.5. `license` | | | |---|---| | **Type** | `string` | -| **Required** | No | +| **Required** | OPTIONAL | | **Description** | SPDX license identifier (e.g. `MIT`, `Apache-2.0`). | -### `target` +### 3.6. `target` | | | |---|---| | **Type** | `enum` | -| **Required** | No | +| **Required** | OPTIONAL | | **Default** | Auto-detect: `vscode` if `.github/` exists, `claude` if `.claude/` exists, `all` if both, `minimal` if neither | | **Allowed values** | `vscode` · `agents` · `claude` · `all` | -Controls which output targets are generated during compilation. When unset, the CLI auto-detects based on `.github/` and `.claude/` folder presence. Unknown values are silently ignored (auto-detection takes over). +Controls which output targets are generated during compilation. When unset, a conforming resolver SHOULD auto-detect based on `.github/` and `.claude/` folder presence. Unknown values MUST be silently ignored (auto-detection takes over). | Value | Effect | |---|---| @@ -88,12 +114,12 @@ Controls which output targets are generated during compilation. When unset, the | `all` | Both `vscode` and `claude` targets | | `minimal` | AGENTS.md only at project root (fallback when no `.github/` or `.claude/` detected) | -### `type` +### 3.7. `type` | | | |---|---| | **Type** | `enum` | -| **Required** | No | +| **Required** | OPTIONAL | | **Default** | None (unset — behaviour depends on package content) | | **Allowed values** | `instructions` · `skill` · `hybrid` · `prompts` | @@ -106,51 +132,51 @@ Declares how the package's content is processed during install and compile: | `hybrid` | Both AGENTS.md compilation and skill installation. | | `prompts` | Commands/prompts only. No instructions or skills. | -### `scripts` +### 3.8. `scripts` | | | |---|---| | **Type** | `map` | -| **Required** | No | +| **Required** | OPTIONAL | | **Key pattern** | Script name (free-form string) | | **Value** | Shell command string | -| **Description** | Named commands executed via `apm run `. Supports `--param key=value` substitution. | +| **Description** | Named commands executed via `apm run `. MUST support `--param key=value` substitution. | --- -## `dependencies` +## 4. Dependencies | | | |---|---| | **Type** | `object` | -| **Required** | No | +| **Required** | OPTIONAL | | **Allowed keys** | `apm`, `mcp` | -Contains two optional lists: `apm` for agent primitive packages and `mcp` for MCP servers. Each list entry is either a string shorthand or a typed object. +Contains two OPTIONAL lists: `apm` for agent primitive packages and `mcp` for MCP servers. Each list entry is either a string shorthand or a typed object. --- -### `dependencies.apm` — `list` +### 4.1. `dependencies.apm` — `list` -Each element is one of two forms: **string** or **object**. +Each element MUST be one of two forms: **string** or **object**. -#### String Form +#### 4.1.1. String Form -Grammar: +Grammar (ABNF-style): ``` -dependency = url_form | shorthand_form -url_form = ("https://" | "http://" | "ssh://git@" | "git@") +dependency = url_form / shorthand_form +url_form = ("https://" / "http://" / "ssh://git@" / "git@") clone-url shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["#" ref] ["@" alias] ``` | Segment | Required | Pattern | Description | |---|---|---|---| -| `host` | No | FQDN (e.g. `gitlab.com`) | Git host. Defaults to `github.com`. | -| `owner/repo` | **Yes** | `^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$` | Repository path. Nested groups supported for non-GitHub hosts (e.g. `gitlab.com/group/sub/repo`). | -| `virtual_path` | No | Path segments after repo | Subdirectory or file within the repo. See *Virtual Packages* below. | -| `ref` | No | Branch, tag, or commit SHA | Git reference. Commit SHAs matched by `^[a-f0-9]{7,40}$`. Semver tags matched by `^v?\d+\.\d+\.\d+`. | -| `alias` | No | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Appears after `#ref` in the string. | +| `host` | OPTIONAL | FQDN (e.g. `gitlab.com`) | Git host. Defaults to `github.com`. | +| `owner/repo` | REQUIRED | `^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$` | Repository path. Nested groups supported for non-GitHub hosts (e.g. `gitlab.com/group/sub/repo`). | +| `virtual_path` | OPTIONAL | Path segments after repo | Subdirectory, file, or collection within the repo. See §4.1.3. | +| `ref` | OPTIONAL | Branch, tag, or commit SHA | Git reference. Commit SHAs matched by `^[a-f0-9]{7,40}$`. Semver tags matched by `^v?\d+\.\d+\.\d+`. | +| `alias` | OPTIONAL | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Appears after `#ref` in the string. | **Examples:** @@ -180,16 +206,16 @@ dependencies: - dev.azure.com/org/project/_git/repo ``` -#### Object Form +#### 4.1.2. Object Form -Required when the shorthand is ambiguous (e.g. nested-group repos with virtual paths). +REQUIRED when the shorthand is ambiguous (e.g. nested-group repos with virtual paths). | Field | Type | Required | Pattern / Constraint | Description | |---|---|---|---|---| -| `git` | `string` | **Yes** | HTTPS URL, SSH URL, or FQDN shorthand | Clone URL of the repository. | -| `path` | `string` | No | Relative path within the repo | Subdirectory or file (virtual package). | -| `ref` | `string` | No | Branch, tag, or commit SHA | Git reference to checkout. | -| `alias` | `string` | No | `^[a-zA-Z0-9._-]+$` | Local alias. | +| `git` | `string` | REQUIRED | HTTPS URL, SSH URL, or FQDN shorthand | Clone URL of the repository. | +| `path` | `string` | OPTIONAL | Relative path within the repo | Subdirectory, file, or collection (virtual package). | +| `ref` | `string` | OPTIONAL | Branch, tag, or commit SHA | Git reference to checkout. | +| `alias` | `string` | OPTIONAL | `^[a-zA-Z0-9._-]+$` | Local alias. | ```yaml - git: https://gitlab.com/acme/repo.git @@ -198,9 +224,9 @@ Required when the shorthand is ambiguous (e.g. nested-group repos with virtual p alias: acme-sec ``` -#### Virtual Packages +#### 4.1.3. Virtual Packages -A dependency that targets a subdirectory, file, or collection within a repository rather than the whole repo. +A dependency MAY target a subdirectory, file, or collection within a repository rather than the whole repo. Conforming resolvers MUST classify virtual packages using the following rules, evaluated in order: | Kind | Detection rule | Example | |---|---|---| @@ -209,9 +235,9 @@ A dependency that targets a subdirectory, file, or collection within a repositor | **Collection (manifest)** | `virtual_path` contains `/collections/` and ends with `.collection.yml` or `.collection.yaml` | `owner/repo/collections/security.collection.yml` | | **Subdirectory** | `virtual_path` does not match any file, collection, or extension rule above | `owner/repo/skills/security` | -#### Canonical Normalisation +#### 4.1.4. Canonical Normalisation -Conforming writers MUST normalise entries to canonical form on write. `github.com` is the default host and is stripped; all other hosts are preserved as FQDN. +Conforming writers MUST normalise entries to canonical form on write. `github.com` is the default host and MUST be stripped; all other hosts MUST be preserved as FQDN. | Input | Canonical form | |---|---| @@ -221,34 +247,37 @@ Conforming writers MUST normalise entries to canonical form on write. `github.co --- -### `dependencies.mcp` — `list` +### 4.2. `dependencies.mcp` — `list` -Each element is one of two forms: **string** or **object**. +Each element MUST be one of two forms: **string** or **object**. -#### String Form +#### 4.2.1. String Form A plain registry reference: `io.github.github/github-mcp-server` -#### Object Form +#### 4.2.2. Object Form | Field | Type | Required | Constraint | Description | |---|---|---|---|---| -| `name` | `string` | **Yes** | Non-empty | Server identifier (registry name or custom name). | -| `transport` | `enum` | Conditional | `stdio` · `sse` · `http` · `streamable-http` | Transport protocol. **Required** when `registry: false`. | -| `env` | `map` | No | | Environment variable overrides. | -| `args` | `dict` or `list` | No | | Dict for overlay variable overrides (registry), list for positional args (self-defined). | -| `version` | `string` | No | | Pin to a specific server version. | -| `registry` | `bool` or `string` | No | Default: `true` (public registry) | `false` = self-defined (private) server. String = custom registry URL. | -| `package` | `enum` | No | `npm` · `pypi` · `oci` | Package manager type hint. | -| `headers` | `map` | No | | Custom HTTP headers for remote endpoints. | -| `tools` | `list` | No | Default: `["*"]` | Restrict which tools are exposed. | -| `url` | `string` | Conditional | | Endpoint URL. **Required** when `registry: false` and `transport` is `http`, `sse`, or `streamable-http`. | -| `command` | `string` | Conditional | | Binary path. **Required** when `registry: false` and `transport` is `stdio`. | - -**Validation rules for self-defined servers (`registry: false`):** -- `transport` MUST be present. -- If `transport` is `stdio`, `command` MUST be present. -- If `transport` is `http`, `sse`, or `streamable-http`, `url` MUST be present. +| `name` | `string` | REQUIRED | Non-empty | Server identifier (registry name or custom name). | +| `transport` | `enum` | Conditional | `stdio` · `sse` · `http` · `streamable-http` | Transport protocol. REQUIRED when `registry: false`. | +| `env` | `map` | OPTIONAL | | Environment variable overrides. | +| `args` | `dict` or `list` | OPTIONAL | | Dict for overlay variable overrides (registry), list for positional args (self-defined). | +| `version` | `string` | OPTIONAL | | Pin to a specific server version. | +| `registry` | `bool` or `string` | OPTIONAL | Default: `true` (public registry) | `false` = self-defined (private) server. String = custom registry URL. | +| `package` | `enum` | OPTIONAL | `npm` · `pypi` · `oci` | Package manager type hint. | +| `headers` | `map` | OPTIONAL | | Custom HTTP headers for remote endpoints. | +| `tools` | `list` | OPTIONAL | Default: `["*"]` | Restrict which tools are exposed. | +| `url` | `string` | Conditional | | Endpoint URL. REQUIRED when `registry: false` and `transport` is `http`, `sse`, or `streamable-http`. | +| `command` | `string` | Conditional | | Binary path. REQUIRED when `registry: false` and `transport` is `stdio`. | + +#### 4.2.3. Validation Rules for Self-Defined Servers + +When `registry` is `false`, the following constraints apply: + +1. `transport` MUST be present. +2. If `transport` is `stdio`, `command` MUST be present. +3. If `transport` is `http`, `sse`, or `streamable-http`, `url` MUST be present. ```yaml dependencies: @@ -274,13 +303,13 @@ dependencies: --- -## `compilation` — `CompilationConfig` +## 5. Compilation -Optional section controlling `apm compile` behaviour. All fields have sensible defaults; omitting the entire section is valid. +The `compilation` key is OPTIONAL. It controls `apm compile` behaviour. All fields have sensible defaults; omitting the entire section is valid. | Field | Type | Default | Constraint | Description | |---|---|---|---|---| -| `target` | `enum` | `all` | `vscode` · `agents` · `claude` · `all` | Output target (same values as top-level `target`). Defaults to `all` when set explicitly in compilation config. | +| `target` | `enum` | `all` | `vscode` · `agents` · `claude` · `all` | Output target (same values as §3.6). Defaults to `all` when set explicitly in compilation config. | | `strategy` | `enum` | `distributed` | `distributed` · `single-file` | `distributed` generates per-directory AGENTS.md files. `single-file` generates one monolithic file. | | `single_file` | `bool` | `false` | | Legacy alias. When `true`, overrides `strategy` to `single-file`. | | `output` | `string` | `AGENTS.md` | File path | Custom output path for the compiled file. | @@ -288,9 +317,9 @@ Optional section controlling `apm compile` behaviour. All fields have sensible d | `resolve_links` | `bool` | `true` | | Resolve relative Markdown links in primitives. | | `source_attribution` | `bool` | `true` | | Include source-file origin comments in compiled output. | | `exclude` | `list` or `string` | `[]` | Glob patterns | Directories to skip during compilation (e.g. `apm_modules/**`). | -| `placement` | `object` | — | | Placement tuning. See sub-fields below. | +| `placement` | `object` | — | | Placement tuning. See §5.1. | -#### `compilation.placement` +### 5.1. `compilation.placement` | Field | Type | Default | Description | |---|---|---|---| @@ -310,7 +339,50 @@ compilation: --- -## Complete Example +## 6. Lockfile (`apm.lock`) + +After successful dependency resolution, a conforming resolver MUST write a lockfile capturing the exact resolved state. The lockfile MUST be a YAML file named `apm.lock` at the project root. It SHOULD be committed to version control. + +### 6.1. Structure + +```yaml +lockfile_version: "1" +generated_at: +apm_version: +dependencies: # YAML list (not a map) + - repo_url: # Resolved clone URL + host: # Git host (OPTIONAL, e.g. "gitlab.com") + resolved_commit: # Full commit SHA + resolved_ref: # Branch/tag that was resolved + version: # Package version from its apm.yml + virtual_path: # Virtual package path (if applicable) + is_virtual: # True for virtual (file/subdirectory) packages + depth: # 1 = direct, 2+ = transitive + resolved_by: # Parent dependency (transitive only) + deployed_files: > # Workspace-relative paths of installed files +``` + +### 6.2. Resolver Behaviour + +1. **First install** — Resolve all dependencies, write `apm.lock`. +2. **Subsequent installs** — Read `apm.lock`, use locked commit SHAs. A resolver SHOULD skip download if local checkout already matches. +3. **`--update` flag** — Re-resolve from `apm.yml`, overwrite lockfile. + +--- + +## 7. Integrator Contract + +Any runtime adopting this format (e.g. GitHub Agentic Workflows, CI systems, IDEs) MUST implement these steps: + +1. **Parse** — Read `apm.yml` as YAML. Validate the two REQUIRED fields (`name`, `version`) and the `dependencies` object shape. +2. **Resolve `dependencies.apm`** — For each entry, clone/fetch the git repo (respecting `ref`), locate the `.apm/` directory (or virtual path), and extract primitives. +3. **Resolve `dependencies.mcp`** — For each entry, resolve from the MCP registry or validate self-defined transport config per §4.2.3. +4. **Transitive resolution** — Resolved packages MAY contain their own `apm.yml` with further dependencies, forming a dependency tree. Resolvers MUST resolve transitively. Conflicts are merged at instruction level (by `applyTo` pattern), not file level. +5. **Write lockfile** — Record exact commit SHAs and deployed file paths in `apm.lock` per §6. + +--- + +## Appendix A. Complete Example ```yaml name: my-project @@ -353,45 +425,8 @@ compilation: --- -## Lockfile Specification (`apm.lock`) - -After successful dependency resolution, a conforming resolver MUST write a lockfile capturing the exact resolved state. The lockfile is a separate YAML file committed to version control. - -### Structure - -```yaml -lockfile_version: "1" -generated_at: -apm_version: -dependencies: # YAML list (not a map) - - repo_url: # Resolved clone URL - host: # Git host (optional, e.g. "gitlab.com") - resolved_commit: # Full commit SHA - resolved_ref: # Branch/tag that was resolved - version: # Package version from its apm.yml - virtual_path: # Virtual package path (if applicable) - is_virtual: # True for virtual (file/subdirectory) packages - depth: # 1 = direct, 2+ = transitive - resolved_by: # Parent dependency (transitive only) - deployed_files: > # Workspace-relative paths of installed files -``` - -### Resolver Behaviour - -1. **First install** — resolve all dependencies, write `apm.lock`. -2. **Subsequent installs** — read `apm.lock`, use locked commit SHAs. Skip download if local checkout already matches. -3. **`--update` flag** — re-resolve from `apm.yml`, overwrite lockfile. - ---- - -## Integrator Contract +## Appendix B. Revision History -Any runtime adopting this format (e.g. GitHub Agentic Workflows, CI systems, IDEs) should implement these steps: - -1. **Parse** — Read `apm.yml` as YAML. Validate the two required fields (`name`, `version`) and the `dependencies` object shape. -2. **Resolve `dependencies.apm`** — For each entry, clone/fetch the git repo (respecting `ref`), locate the `.apm/` directory (or virtual path), and extract primitives. -3. **Resolve `dependencies.mcp`** — For each entry, resolve from the MCP registry or validate self-defined transport config. -4. **Transitive resolution** — Resolved packages may contain their own `apm.yml` with further dependencies, forming a dependency tree. Resolve transitively. Conflicts are merged at instruction level (by `applyTo` pattern), not file level. -5. **Write lockfile** — Record exact commit SHAs and deployed file paths in `apm.lock` for reproducibility. - -The schema (this document) is the contract. Implementations — resolver, downloader, installer, compiler — are decoupled. Each runtime builds its own against this spec. +| Version | Date | Changes | +|---|---|---| +| 0.1 | 2026-03-06 | Initial Working Draft. | From 4e368d8618c0ea6a514a837c1aea57cf42c99888 Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Fri, 6 Mar 2026 12:51:10 +0100 Subject: [PATCH 4/4] docs: address second review round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §3.6: Mark 'minimal' target as auto-detected only, MUST NOT be explicit - §4: Change 'Allowed keys' to 'Known keys' with forward-compat note - §4.1.1: Relax owner/repo pattern to support nested groups on non-GitHub hosts --- docs/manifest-schema.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/manifest-schema.md b/docs/manifest-schema.md index 37952357..a13ff9eb 100644 --- a/docs/manifest-schema.md +++ b/docs/manifest-schema.md @@ -112,7 +112,7 @@ Controls which output targets are generated during compilation. When unset, a co | `agents` | Alias for `vscode` | | `claude` | Emits `CLAUDE.md` at the project root | | `all` | Both `vscode` and `claude` targets | -| `minimal` | AGENTS.md only at project root (fallback when no `.github/` or `.claude/` detected) | +| `minimal` | AGENTS.md only at project root. **Auto-detected only** — this value MUST NOT be set explicitly in manifests; it is an internal fallback when no `.github/` or `.claude/` folder is detected. | ### 3.7. `type` @@ -150,9 +150,9 @@ Declares how the package's content is processed during install and compile: |---|---| | **Type** | `object` | | **Required** | OPTIONAL | -| **Allowed keys** | `apm`, `mcp` | +| **Known keys** | `apm`, `mcp` | -Contains two OPTIONAL lists: `apm` for agent primitive packages and `mcp` for MCP servers. Each list entry is either a string shorthand or a typed object. +Contains two OPTIONAL lists: `apm` for agent primitive packages and `mcp` for MCP servers. Each list entry is either a string shorthand or a typed object. Additional keys MAY be present for future dependency types; conforming resolvers MUST ignore unknown keys for resolution but MUST preserve them when reading and rewriting manifests, to allow forward compatibility. --- @@ -173,7 +173,7 @@ shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["#" ref] ["@" ali | Segment | Required | Pattern | Description | |---|---|---|---| | `host` | OPTIONAL | FQDN (e.g. `gitlab.com`) | Git host. Defaults to `github.com`. | -| `owner/repo` | REQUIRED | `^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$` | Repository path. Nested groups supported for non-GitHub hosts (e.g. `gitlab.com/group/sub/repo`). | +| `owner/repo` | REQUIRED | 2+ path segments of `[a-zA-Z0-9._-]+` | Repository path. GitHub uses exactly 2 segments (`owner/repo`). Non-GitHub hosts MAY use nested groups (e.g. `gitlab.com/group/sub/repo`). | | `virtual_path` | OPTIONAL | Path segments after repo | Subdirectory, file, or collection within the repo. See §4.1.3. | | `ref` | OPTIONAL | Branch, tag, or commit SHA | Git reference. Commit SHAs matched by `^[a-f0-9]{7,40}$`. Semver tags matched by `^v?\d+\.\d+\.\d+`. | | `alias` | OPTIONAL | `^[a-zA-Z0-9._-]+$` | Local alias for the dependency. Appears after `#ref` in the string. |