Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/portable-agent-plugins-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agent-bundle": minor
---

portable: complete the Agent Plugins 1.0.0 adoption (#307) — author the standard's §5.4 manifest metadata (`author`, `homepage`, `repository`, `license`, `keywords`) and §5.6 reverse-domain `extensions` under the `portable` config key and emit them into the root `plugin.json` (omitted fields leave the manifest byte-identical to the previous contract; malformed values fail closed with `portable.manifest.<field>.invalid`); add the pinned Agent Plugins byte lane (`validatePortablePlugin`, `AB6035`–`AB6038`: pinned schemas plus the normative command/cwd/URL/header/placeholder/version/skill-layout/symlink-containment rules) to `validate --artifact --host-validation`, to `doctor` for installed Cursor local plugins that declare the standard's `$schema` (`AB7320`), and to the portable host-install proof; record dated capability rows for every standard feature (manifest metadata, extensions, extension directories, legacy SSE); re-verify the schema pins against the live 1.0.0 schemas and the specification repository (2026-09-02); adapterRevision 1.5.0 → 1.6.0.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ npx agent-bundle dev --root . # local workbench with live rebu

`targets: ['plugin']` emits one multi-host bundle at `dist/plugin/`: `.claude-plugin/`, `.codex-plugin/`, and `.cursor-plugin/` manifests over shared `skills/`, `hooks/`, `mcp/`, and `scripts/` directories. The bundle's generated `AGENTS.md` explains how to install it into each host. Per-host layouts are available as the `claude`, `codex`, `cursor`, and `portable` targets.

The `portable` target is the [Agent Plugins open standard](https://agent-plugins.org/specification) (specification 1.0.0) adapter — the default target, and the layout Cursor, Codex, VS Code, GitHub Copilot, Kiro, and ChatGPT load natively (Claude Code consumes it only through CLI translation). It emits the closed root `plugin.json` (canonical `$schema`, `name`, `version`, `description`, plus `author`, `homepage`, `repository`, `license`, `keywords`, and reverse-domain `extensions` authored under the `portable` config key), `skills/<name>/SKILL.md`, and `mcp.json` with stdio and Streamable HTTP servers whose `args`, `env` values, and `cwd` use the standard's `${PLUGIN_ROOT}`/`${PLUGIN_DATA}` placeholders. Rules, commands, hooks, marketplaces, and client extension directories are honestly unavailable there because the v1 standard packages only skills and MCP servers. Both documents are validated against the vendored, hash-pinned 1.0.0 schemas and the normative text at plan time, after every build (`AB6011`/`AB6012`), under `validate --artifact --host-validation` (`AB6035`–`AB6038`), and by `agent-bundle doctor` for installed Cursor local plugins that declare the standard's `$schema` (`AB7320`); see [Diagnostics](docs/diagnostics.md#agent-plugins-portable-validation-ab6035ab6038). Pins live in `packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json`; the capability table `packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json` carries a dated row for every standard feature.

Claude Code language servers are declared under `claude.lspServers`; the `claude` target and the Claude half of `plugin` emit the record as plugin-root `.lsp.json`. Agent Bundle expands path tokens only in `command`, `args`, `env`, and `workspaceFolder`, and it does not include the language-server binary — install that separately so the declared command is available on `PATH`. Codex, Cursor, and the portable format do not currently receive this host-scoped configuration.

Claude Code plugin defaults are declared under `claude.settings` and emitted as plugin-root `settings.json`, which Claude Code applies when the plugin is enabled. The pinned contract supports only `agent` and `subagentStatusLine`; Agent Bundle rejects any other key rather than shipping a default Claude Code would silently ignore, and it expands no path tokens here because `settings.json` is absent from the host's placeholder-substitution table. Because the plugin `agents/` component is still deferred, declaring `agent` also raises a warning: the referenced agent has to reach the plugin root some other way, such as a prebuilt payload.
Expand Down
38 changes: 36 additions & 2 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ gate a build, a validation, or a dev rebuild.
| `AB473x` | Migration nudges (informational; see below). |
| `AB474x`/`AB4750` | Prebuilt payloads and prebuilt entries (see below). |
| `AB5000` | General CLI and adapter failures. |
| `AB60xx` | Built-artifact validation, including schema documents and referenced files (`AB6025`: a manifest-declared `logo` path is missing from the artifact or escapes the deploy tree; `AB6034`: emitted Skill Markdown has no instruction body). |
| `AB60xx` | Built-artifact validation, including schema documents and referenced files (`AB6011`/`AB6012`: a target's required pinned-schema document is missing or invalid; `AB6025`: a manifest-declared `logo` path is missing from the artifact or escapes the deploy tree; `AB6034`: emitted Skill Markdown has no instruction body; `AB6035`–`AB6038`: Agent Plugins portable validation, see below). |
| `AB700x` | Host installation: bundle identity, host availability, scope, command failure, and collision checks. |
| `AB7010`–`AB7013` | npm prepack inventory, artifact freshness, package bin targets, and release-version agreement. |
| `AB7xxx` | Project preparation and development rebuilds. |
Expand Down Expand Up @@ -74,6 +74,40 @@ locally against emitted bytes.
| --- | --- | --- | --- |
| `AB6034` | error | An emitted `SKILL.md` has valid YAML frontmatter but no Markdown instruction body after it. The pinned Agent Skills specification requires frontmatter followed by Markdown content. | Add Markdown instructions after the Skill frontmatter, then rebuild the artifact. |

## Agent Plugins portable validation (`AB6035`–`AB6038`)

The `portable` target is the [Agent Plugins open standard](https://agent-plugins.org/specification)
(specification 1.0.0) adapter. Its contract is pinned in
`packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json` (schema
hashes, specification repository commit, retrieval and re-verification dates).
The standard publishes machine-readable schemas plus normative text the schemas
cannot express; the text wins on conflict, so validation runs both lanes and
never spawns a client CLI (the standard publishes no reference validator).

Validation happens at three moments, all fail-closed:

1. **Plan time** (`agent-bundle build`/`validate`): the emitted `plugin.json`
and `mcp.json` are validated against the pinned schemas before they are
written (`portable.schema.plugin`, `portable.schema.mcp`), authored manifest
metadata is checked field by field (`portable.manifest.<field>.invalid`), and
MCP path tokens are refused where the standard forbids them
(`portable.mcp.token.*`). These target-scoped codes are errors.
2. **Artifact time** (`agent-bundle build`, `validate --artifact`): the generic
target-contract pass reports a missing required document as `AB6011` and a
pinned-schema rejection as `AB6012`; `validate --artifact --host-validation`
additionally runs the byte lane below and returns a `portable` host
validation report.
3. **Installed bytes** (`agent-bundle doctor`): a Cursor local plugin whose root
`plugin.json` declares an Agent Plugins `$schema` is validated with the same
byte lane and reported under `AB7320` (an error marks the entry `corrupt`).

| Code | Severity | Meaning | Recovery |
| --- | --- | --- | --- |
| `AB6035` | error | The root `plugin.json` is missing, or a present `plugin.json`/`mcp.json` is unreadable, not valid JSON, or rejected by its pinned Agent Plugins 1.0.0 schema (closed manifest fields, plugin name constraints, reserved `PLUGIN_ROOT`/`PLUGIN_DATA` env keys, closed server variants). | Repair the generated Agent Plugins document so it satisfies the pinned 1.0.0 schema, then rebuild. |
| `AB6036` | error | A normative-text rule the schemas cannot express is violated: `plugin.json` and `mcp.json` declare different Agent Plugins versions (§10.1); a stdio `command` is neither a bare executable name nor a bundled plugin-relative `./` file, or carries a placeholder (§7.2.1); a `./`, `${PLUGIN_ROOT}`, or `${PLUGIN_DATA}` `cwd` escapes its root after resolution (§4.1/§7.2.1); a remote `url` is not an absolute HTTP(S) URL, carries user information or a fragment, uses plain HTTP against a non-loopback host, or carries a placeholder; header names are invalid, repeat under different casing, or carry placeholders (§7.2.1); an `env` key carries a placeholder (§9.2); `skills/` or `mcp.json` is present with the wrong filesystem kind (§6.2); or a `skills/<name>/` directory has no regular `SKILL.md` (§7.1). | Repair the generated portable layout or MCP entry to satisfy the Agent Plugins 1.0.0 normative text, then rebuild. |
| `AB6037` | error | A symlink inside the plugin resolves outside the plugin root, or cannot be resolved at all (§4.1 containment). | Replace the escaping symlink with a file or a link that resolves inside the plugin root, then rebuild. |
| `AB6038` | info | Every portable host-validation report states that Agent Plugins publishes no reference validator and names the pinned schema provenance (specification repository commit, retrieval and re-verification dates) used for local validation. | Review the pinned Agent Plugins provenance before changing the local validator contract. |

## npm prepack gate (`AB7010`–`AB7013`)

| Code | Meaning |
Expand Down Expand Up @@ -411,7 +445,7 @@ host CLI, repair a bundle, or perform a live protocol exchange.
| Code | Severity | Trigger | Recovery |
| --- | --- | --- | --- |
| `AB7319` | error | A host tree resolved from `doctor --from` violates its pinned document schemas or process-free loader rules. The message retains the originating build-validator code and detail. | Rebuild that host bundle from valid source bytes, then rerun Doctor. |
| `AB7320` | error / info | Error when a `.cursor-plugin/plugin.json` install violates Cursor's pinned document schemas or token-location rules, or when any local plugin contains a symlink that escapes `~/.cursor/plugins/local`; the inventory entry is reported as `corrupt`. Info when a `.claude-plugin/plugin.json` or root `plugin.json` install has no Cursor-side pinned static document contract; the loader-recognized entry remains `installed`. | Reinstall an invalid Cursor plugin or repair an escaping symlink. For other manifest flavors, use that ecosystem's validator when static document proof is required. |
| `AB7320` | error / info | Error when a `.cursor-plugin/plugin.json` install violates Cursor's pinned document schemas or token-location rules, when a root `plugin.json` install that declares an Agent Plugins `$schema` violates the pinned Agent Plugins 1.0.0 contract (`AB6035`–`AB6037`, retained in the message), or when any local plugin contains a symlink that escapes `~/.cursor/plugins/local`; the inventory entry is reported as `corrupt`. Info naming the contract applied to an Agent Plugins install, or stating that a `.claude-plugin/plugin.json` (or schema-less root `plugin.json`) install has no Cursor-side pinned static document contract; loader-recognized entries remain `installed`. | Reinstall an invalid Cursor plugin, rebuild an invalid portable bundle, or repair an escaping symlink. For other manifest flavors, use that ecosystem's validator when static document proof is required. |

## Development package build (`AB7103`)

Expand Down
16 changes: 12 additions & 4 deletions docs/framework-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,18 @@ this format natively alongside Cursor Plugins; Codex, VS Code, GitHub Copilot,
Kiro, and ChatGPT are native clients too. Claude Code consumes the standard
only through CLI translation, so its dedicated target remains necessary. The
standard packages only skills and MCP servers, leaving rules, commands, and
hooks honestly unavailable on the portable target. A dogfood proof against the
real Cursor IDE plugin loader (discovery, skill listing, MCP launch, and three
observed Cursor 3.18.25 placeholder-expansion conformance gaps) is recorded in
`docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md`.
hooks honestly unavailable on the portable target. The standard's manifest
metadata (`author`, `homepage`, `repository`, `license`, `keywords`) and
reverse-domain `extensions` are authored under the `portable` config key and
land in the root `plugin.json`; omitting them leaves the manifest exactly as
before. Emitted bytes are validated against the pinned schemas and the
normative text at plan time, after every build, under
`validate --artifact --host-validation`, and by `doctor` for installed Cursor
local plugins that declare the standard's `$schema`
(`AB6035`–`AB6038`, `AB7320`; see `docs/diagnostics.md`). A dogfood proof
against the real Cursor IDE plugin loader (discovery, skill listing, MCP
launch, and three observed Cursor 3.18.25 placeholder-expansion conformance
gaps) is recorded in `docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md`.

The framework CLI performs those same operations:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
"2026-09-02: Cursor 3.18.25 does not expand ${PLUGIN_ROOT} in cwd or args and does not default an omitted cwd to the plugin root (spec 7.2.1/9.2 MUSTs), so spec-conformant stdio servers fail to launch there; its proprietary ${CURSOR_PLUGIN_ROOT} expands and connects.",
"2026-09-02: With a launchable configuration the Cursor IDE completed the stdio handshake (connect_success with stable heartbeat), so the launch pipeline itself consumes this format."
],
"legacySse": {
"reason": "2026-09-02: Agent Plugins 1.0.0 §7.2.1 defines the deprecated HTTP+SSE variant as OPTIONAL for clients; the compiler emits only the required modern transports and rejects a legacy transport with AB4339 (https://agent-plugins.org/specification).",
"state": "unavailable"
},
"pathTokens": {
"args": [
"${PLUGIN_DATA}",
Expand Down Expand Up @@ -120,7 +124,41 @@
},
"observedSpecificationVersion": "1.0.0",
"plugin": {
"extensionDirectories": {
"reason": "2026-09-02: Agent Plugins 1.0.0 §8.2 reserves top-level reverse-domain directories for client-owned files; the compiler emits no client extension directory because no pinned client publishes a file-based namespace contract for this format (https://agent-plugins.org/specification).",
"state": "unavailable"
},
"extensions": {
"configKey": "portable.extensions",
"evidence": [
"2026-09-02: Agent Plugins 1.0.0 §5.6/§8.1 — `extensions` maps reverse-domain client namespaces to opaque objects; clients ignore unimplemented namespaces without validating their contents (https://agent-plugins.org/specification)."
],
"state": "supported"
},
"manifest": "plugin.json",
"manifestMetadata": {
"configKey": "portable",
"evidence": [
"2026-09-02: Agent Plugins 1.0.0 §5.4 metadata fields author, homepage, repository, license, and keywords are validated only by JSON type by clients; the compiler additionally refuses malformed URLs, emails, and empty strings before emission (https://agent-plugins.org/specification)."
],
"fields": [
"author",
"homepage",
"keywords",
"license",
"repository"
],
"state": "supported"
},
"skills": true
},
"specificationSections": {
"clientExtensions": "8",
"componentDiscovery": "6",
"componentTypes": "7",
"manifest": "5",
"packageModel": "4",
"placeholderExpansion": "9",
"versioning": "10"
}
}
Loading
Loading