Skip to content

Commit f37f0ff

Browse files
Add the Copilot Agent Plugins 1.0 marketplace bundle (R2)
vscode_copilot models only VS Code's repo-local hooks (.github/hooks/agentseam.json, "no bundle format"). GitHub Copilot's marketplace intake is a different artifact entirely: Agent Plugins 1.0, plugin.json at bundle root plus com.github.copilot/hooks/hooks.json. Both known consumers emit Copilot marketplace plugins, so this bundle format cannot stay a consumer concern without breaking standalone. New distinct `copilot` PACKAGING row (unit=plugin), sourced from microsoft/vscode-docs' own agent-plugins.md: skills and mcp.json are the standard's portable component types, found by location exactly like every other plugin format here (skills/{name}/SKILL.md is now shared by five formats, not four); everything else Copilot can hold lives under the client-specific com.github.copilot/ namespace. plugin.json requires a fixed $schema field to be recognized as this format at all -- packaging.plan()'s manifest renderer gained `manifest_fixed` to render it, since omitting it silently falls back to an unrelated legacy format. Slash commands degrade honestly to a PART_LIMITS refusal: the folder is documented, the file format inside it is not. The honesty gate: whether Copilot actually executes these hooks once installed. Two primary vendor sources establish that it does -- VS Code's own docs state plugin hooks "fire in addition to any other hooks configured for the same event," and separately that Copilot CLI and the Copilot app read the same com.github.copilot namespace; Copilot CLI's own hooks-reference.md independently lists "hooks contributed by installed plugins" in its combined, executed hook-loading order. Recorded as docs-basis, not live-verified, with an open, unconfirmed bug report (github/copilot-cli#2540) noted rather than omitted. No separate adapter is registered for "copilot": vscode_copilot's adapter already claims this exact wire dialect (verified: VS Code and Copilot CLI share one hook engine, two event-name spellings), so a second adapter with an identical claims() would make every VS Code/Copilot CLI payload ambiguous to adapters.detect() and break dispatch for both. The matrix row (tier=TIER_UNADAPTED, events={}) reflects that honestly: no capability claimed under this identity that dispatch cannot actually deliver, with the real, cited evidence in notes/verified rather than silently omitted. instructions.py and permissions_data.py both updated for the new matrix membership: instruction files point at the same repo files vscode_copilot already uses (same product, same repo), and the permissions capability stays unrecorded (a distribution-only identity has no live settings surface of its own to duplicate vscode_copilot's). packaging_data.py split: PART_LIMITS/ALSO_READS/UNRECORDED moved to packaging_limits.py (re-exported) to keep both files under the 300-line budget. Marketplace INDEX aggregation stays out of scope per the spec -- this is the bundle FORMAT only. Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 71b9162 commit f37f0ff

13 files changed

Lines changed: 323 additions & 77 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ updated AGENTS.md
8888
created CLAUDE.md
8989
created .junie/guidelines.md
9090
...
91-
covered by AGENTS.md: aider, codex_cli, cursor, gemini_cli, kimi_code, vscode_copilot, windsurf, zed
91+
covered by AGENTS.md: codex_cli, copilot, cursor, gemini_cli, kimi_code, vscode_copilot, windsurf, zed
9292
```
9393

94-
15 agents reached with 9 files written, because 7 of them read `AGENTS.md` natively and a
94+
16 agents reached with 9 files written, because 8 of them read `AGENTS.md` natively and a
9595
second copy would only drift. Content is written as a marker-delimited block, so anything a
9696
human wrote in those files is preserved untouched — and `instructions --list` shows what
9797
a repo is already telling its agents.

src/agentseam/instructions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@
4141
"shared": True,
4242
"imports": None,
4343
},
44+
# Not a second product: a repo running an installed Agent Plugins 1.0 "copilot" bundle
45+
# (packaging_data.PACKAGING) is running actual GitHub Copilot, which reads the same
46+
# repo-level instructions file regardless of which surface installed the plugin.
47+
"copilot": {
48+
"files": [".github/copilot-instructions.md", ".github/agents/agentseam.agent.md"],
49+
"shared": True,
50+
"imports": None,
51+
},
4452
"gemini_cli": {"files": ["GEMINI.md", ".gemini/GEMINI.md"], "shared": True, "imports": None},
4553
"windsurf": {"files": [".windsurf/rules/agentseam.md", ".windsurfrules"], "shared": True, "imports": None},
4654
# Aider discovers nothing by convention: it reads the files listed under `read:` in

src/agentseam/matrix_evidence.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,32 @@
7070
"stop",
7171
),
7272
},
73+
"copilot": {
74+
# Not the vscode_copilot row's product -- this is the honesty gate for hooks shipped
75+
# inside an Agent Plugins 1.0 MARKETPLACE bundle specifically (packaging_data.py's
76+
# "copilot" row). No adapter is registered under this name (see matrix_gaps.py's
77+
# "copilot" row for why), so this evidence documents what was found, not a dispatch
78+
# path reachable from here.
79+
"basis": BASIS_DOCS,
80+
"version": "VS Code 1.110+ (Agent Plugins 1.0); Copilot CLI per docs, no build pinned",
81+
"date": "2026-08-29",
82+
"method": (
83+
"vendor docs read from a clone: microsoft/vscode-docs "
84+
"docs/agent-customization/agent-plugins.md states plainly, for VS Code's own "
85+
"engine, 'When a plugin is enabled, its hooks fire in addition to any other hooks "
86+
"configured for the same event,' and separately that VS Code 'reads custom agents, "
87+
"slash commands, rules, and hooks from the com.github.copilot namespace, which "
88+
"GitHub Copilot CLI and the GitHub Copilot app also read.' github/docs "
89+
"content/copilot/reference/hooks-reference.md corroborates for the CLI side: its "
90+
"documented hook-loading order combines policy, user, and project hooks with "
91+
"'hooks contributed by installed plugins.' Neither document was read against a "
92+
"running build, and an open, unconfirmed bug report (github/copilot-cli#2540, "
93+
"v1.0.18) says a plugin's hooks.json hooks did not fire for one reporter -- filed "
94+
"against the Legacy Copilot format's root hooks.json, not the Agent Plugins 1.0 "
95+
"com.github.copilot/hooks/hooks.json path this row is about, and not confirmed by "
96+
"a maintainer, but recorded rather than omitted."
97+
),
98+
},
7399
"cursor": {
74100
"basis": BASIS_LIVE_PARTIAL,
75101
"version": "3.17.8",

src/agentseam/matrix_gaps.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@
2121
from .matrix_terms import TIER_NONE, TIER_UNADAPTED
2222

2323
GAPS = {
24+
"copilot": {
25+
# Not "no surface" (TIER_NONE would say that, and it would be false -- see
26+
# EVIDENCE/NOTES) and not "nobody built the adapter yet" in the usual TIER_UNADAPTED
27+
# sense either: an adapter under this name would collide with vscode_copilot's, which
28+
# already dispatches this exact wire dialect. This row exists so
29+
# packaging_data.PACKAGING's "copilot" identity (the Agent Plugins 1.0 marketplace
30+
# bundle) has a matrix row at all, per test_packaging's completeness invariant.
31+
"display": "GitHub Copilot (Agent Plugins 1.0 marketplace bundle)",
32+
"tier": TIER_UNADAPTED,
33+
"config": None,
34+
"verified": EVIDENCE["copilot"],
35+
"events": {},
36+
"notes": NOTES["copilot"],
37+
},
2438
"replit": {
2539
"display": "Replit Agent",
2640
"tier": TIER_UNADAPTED,

src/agentseam/matrix_notes.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@
99
from __future__ import annotations
1010

1111
NOTES = {
12+
"copilot": "TIER_UNADAPTED and events={} are deliberate, not a placeholder for an adapter "
13+
"that has not been written yet: a real adapter under this name would claim every payload "
14+
"vscode_copilot already claims (the wire dialect is documented as identical), and "
15+
"adapters.detect() treats a payload two adapters both claim as unidentified -- registering "
16+
"one here would silently stop dispatch working for every existing VS Code/Copilot CLI "
17+
"user. A hook that ships inside a copilot-format bundle is dispatched through the "
18+
"vscode_copilot adapter at runtime; 'copilot' exists only as packaging_data.PACKAGING's "
19+
"identity for the Agent Plugins 1.0 marketplace bundle FORMAT, a genuinely different "
20+
"artifact from vscode_copilot's repo-local .github/hooks/agentseam.json (own manifest, "
21+
"own namespaced paths, published to a marketplace rather than committed to a repo). "
22+
"The honest capability answer is NOT 'none': see EVIDENCE['copilot'] for what was found "
23+
"-- VS Code documents plugin hooks as firing alongside workspace hooks, and documents "
24+
"Copilot CLI and the Copilot app as reading the same com.github.copilot namespace; "
25+
"Copilot CLI's own hooks-reference.md separately lists installed-plugin hooks as one of "
26+
"its combined, executed sources. Recorded here as docs-basis evidence, not a live "
27+
"capability claim, because nothing here was run against a live build.",
1228
"junie": "Strongest gate here: PreToolUse returns allow/ask/block and carries "
1329
"updatedInput, so block, ask and rewrite are all native. Its event names and field "
1430
"names are Claude Code's by design; project_path is what separates the two payloads. "

src/agentseam/packaging.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,20 @@ def _manifest(agent, bundle, kinds):
210210
211211
Only the parts the bundle actually has are declared. Pointing at `./skills` in a bundle
212212
with no skills advertises a directory that is not there.
213+
214+
`manifest_fixed` is a third thing again: fields a format requires on every manifest
215+
regardless of what the bundle holds -- Agent Plugins 1.0's `$schema` is the only one
216+
today, and it is load-bearing rather than decorative: without it the host falls back to
217+
reading the file as an unrelated legacy format, silently mislabeling the whole bundle.
213218
"""
214219
row = PACKAGING[agent]
215220
if not row["manifest"]:
216221
return None
217222
body = {"name": bundle.name, "version": bundle.version}
218223
if bundle.description:
219224
body["description"] = bundle.description
225+
for key, value in (row.get("manifest_fixed") or {}).items():
226+
body[key] = value
220227
for kind, (key, value) in sorted((row.get("declares") or {}).items()):
221228
if kind in kinds:
222229
body[key] = value

src/agentseam/packaging_data.py

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -224,69 +224,55 @@
224224
"date": "2026-08-26",
225225
},
226226
},
227-
}
228-
229-
#: Why a specific agent cannot hold a specific part. Without these the generic message
230-
#: ("this format has no place for it") would understate Gemini, which supports MCP servers
231-
#: perfectly well -- just in the manifest rather than in a file of its own.
232-
PART_LIMITS = {
233-
("codex_cli", MCP): "MCP servers are declared inside .codex-plugin/plugin.json under "
234-
"`mcp_servers`, not in a file of their own",
235-
("codex_cli", COMMAND): "the manifest reads command paths from a `commands` field, but the "
236-
"command FILE format was not established here -- a guessed format ships a plugin whose "
237-
"commands silently do not load",
238-
("codex_cli", SUBAGENT): "no subagent field exists in the plugin manifest",
239-
("codex_cli", EXECUTABLE): "RawPluginManifest's field list is exhaustive (name, version, "
240-
"description, keywords, skills, mcp_servers, apps, hooks, interface) and none names a "
241-
"bundled executable or scripts path; whether an undeclared file elsewhere in the plugin "
242-
"directory survives installation to be referenced by a hook command was not established here",
243-
("cursor", MCP): "no MCP declaration was established for this plugin format",
244-
("cursor", COMMAND): "no command format was established for this plugin format",
245-
("cursor", SUBAGENT): "no subagent format was established for this plugin format",
246-
("cursor", EXECUTABLE): "no scripts/executable location was established for this closed-source "
247-
"plugin format; the documented layout (.cursor-plugin/, skills/, rules/, mcp.json) names "
248-
"no place for one",
249-
("gemini_cli", MCP): "MCP servers are declared inside gemini-extension.json, not in a file "
250-
"of their own; a rendered .mcp.json would simply be ignored",
251-
("vscode_copilot", MCP): "MCP servers are configured by the editor rather than shipped alongside these parts",
252-
}
253-
254-
#: Folders an agent reads that belong to *another* agent. This is the interop surface:
255-
#: a repository that ships .claude/skills is already shipping skills to VS Code.
256-
ALSO_READS = {
257-
"vscode_copilot": {
258-
SKILL: (SHARED_SKILL_DIR, ".claude/skills", "~/.agents/skills", "~/.copilot/skills", "~/.claude/skills"),
259-
SUBAGENT: (".claude/agents", "~/.copilot/agents", "~/.claude/agents"),
260-
HOOKS: (".claude/settings.json", ".claude/settings.local.json", "~/.claude/settings.json"),
227+
"copilot": {
228+
# The Agent Plugins 1.0 MARKETPLACE bundle -- a distinct artifact from vscode_copilot's
229+
# repo-local .github/hooks/agentseam.json, per the spec: skills and MCP are the
230+
# standard's own portable component types, found by location; everything else Copilot
231+
# can hold is client-specific and lives under the com.github.copilot/ namespace inside
232+
# the bundle. No "declares": both portable and namespaced parts are discovered by
233+
# location, never resolved from the manifest.
234+
"unit": "plugin",
235+
"manifest": "plugin.json",
236+
"manifest_format": "json",
237+
# Required and fixed, not bundle-varying: without it a rendered plugin.json is
238+
# auto-detected as the unrelated Legacy "Copilot" format instead (VS Code's own
239+
# fallback rule -- see the row's "verified" -- silently mislabeling the whole bundle).
240+
"manifest_fixed": {"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"},
241+
"project_root": "{bundle}",
242+
"plugin_root": ("${PLUGIN_ROOT}",),
243+
"parts": {
244+
SKILL: "skills/{name}/SKILL.md",
245+
SUBAGENT: "com.github.copilot/agents/{name}.agent.md",
246+
COMMAND: None,
247+
HOOKS: "com.github.copilot/hooks/hooks.json",
248+
MCP: "mcp.json",
249+
EXECUTABLE: "scripts/{name}",
250+
},
251+
"notes": (
252+
"skills/ and mcp.json are the standard's own portable component types -- "
253+
"byte-identical skills/{name}/SKILL.md to every other plugin format here -- found "
254+
"by location like the manifest fields say. Everything else Copilot can hold "
255+
"(agents, hooks, and slash commands) is client-specific and lives under "
256+
"com.github.copilot/, which other clients ignore so the bundle stays portable. "
257+
"scripts/ sits at the bundle root, outside the namespace, exactly as the vendor's "
258+
"own example places a hook's script -- referenced via ${PLUGIN_ROOT}, the token "
259+
"this format defines (NOT ${CLAUDE_PLUGIN_ROOT}, which is the Legacy Copilot "
260+
"format's spelling, alongside ${PLUGIN_ROOT}, per the same vendor table). "
261+
"See MATRIX['copilot'] for whether these hooks actually run once installed -- "
262+
"the honesty gate this row exists to pass."
263+
),
264+
"verified": {
265+
"method": "vendor source read from a clone: microsoft/vscode-docs "
266+
"docs/agent-customization/agent-plugins.md -- the directory example, the plugin "
267+
"manifest field table ($schema required), the plugin-format auto-detection rule "
268+
"(no $schema falls back to the unrelated Legacy Copilot format), and the "
269+
"plugin-root token table (Agent Plugins 1.0: ${PLUGIN_ROOT} only)",
270+
"date": "2026-08-29",
271+
},
261272
},
262273
}
263274

264-
#: Every agent the matrix knows that has no packaging format recorded here, and why.
265-
#:
266-
#: Exhaustive on purpose, and enforced by a test: recorded plus unrecorded must equal the
267-
#: matrix exactly. A missing agent reads as "nothing to package here" when the truth is
268-
#: "nobody looked", and that is the one thing this module must never say by accident.
269-
#:
270-
#: Several of these are *not* blanks. Where a vendor's own hook documentation proves that
271-
#: skills or subagents exist -- a SubagentStop event, a define_subagent tool, a loader that
272-
#: mentions skills -- that is recorded, because "we could not find the layout" and "there is
273-
#: no such thing" are different answers and only one of them is true.
274-
UNRECORDED = {
275-
"aider": "no packaging format established here",
276-
"antigravity": "subagents provably exist -- define_subagent and invoke_subagent are tools it "
277-
"offers, and its file reader takes an IsSkillFile argument -- but the on-disk layout was "
278-
"not established here",
279-
"devin": "skills provably exist -- its hook loader is documented as following the same "
280-
"discovery rules as skills and rules -- but their layout was not read here",
281-
"grok": "subagents provably exist (SubagentStart and SubagentStop hook events), and it has an "
282-
"extensions system, but neither layout was established here",
283-
"junie": "extensions provably exist and ship hooks at hooks/hooks.json in the Claude "
284-
"plugin layout, with ${JUNIE_EXTENSION_ROOT} aliasing ${CLAUDE_PLUGIN_ROOT} -- but the "
285-
"rest of the extension layout, and how skills and commands sit in it, was not read here",
286-
"kimi_code": "subagents provably exist (a SubagentStop hook event, and a documented agents "
287-
"and sub-agents feature) but their layout was not read here",
288-
"replit": "no packaging format established here",
289-
"tabnine": "no packaging format established here; vendor documentation is unreachable from this environment",
290-
"windsurf": "vendor documentation unreachable from the environment this was written in",
291-
"zed": "no packaging format established here",
292-
}
275+
#: PART_LIMITS, ALSO_READS, and UNRECORDED moved to packaging_limits.py to keep this module
276+
#: under the line budget; re-exported here so `from .packaging_data import ...` still works
277+
#: everywhere it already did.
278+
from .packaging_limits import ALSO_READS, PART_LIMITS, UNRECORDED # noqa: E402,F401

0 commit comments

Comments
 (0)