fix: correct Copilot and Antigravity plugin output + polymorphic target registry - #13
Merged
Merged
Conversation
Introduces PluginTargetDefinition — one interface a target implements once (default components, manifest/entry/marketplace builders, hooks and MCP config paths, validation, install snippets, citations) instead of the same target's facts being scattered across components.ts, targets.ts, and validate.ts independently. adapters.ts now bridges old and new: a target resolves through the registry if migrated, falling back to the legacy per-target functions otherwise, so targets migrate one at a time without breaking the build in between. The registry starts empty; this commit is a no-op bridge (verified via the full existing test suite passing unchanged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot's emitted output previously carried no per-plugin plugin.json at all — a comment in the old emitCopilot said "Copilot has no per-plugin manifest," which was true of an earlier, thinner version of the format. The current spec is explicit: "All plugins consist of a plugin directory containing, at minimum, a manifest file named plugin.json" (docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference). Without it, a plugin likely isn't recognized at all once Copilot fetches it from the marketplace's `source`. Migrates the copilot target onto the new PluginTargetDefinition registry (see the scaffolding commit) as part of landing this fix, and adds a validation check that agent files are named NAME.agent.md per docs.github.com/.../plugins-creating, since Copilot derives agent identity from the filename. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two confirmed, verified-twice bugs in the antigravity target:
- Hooks were emitted at hooks/hooks.json (a directory), but Antigravity's
actual convention (confirmed on both docs.google/docs/ide/plugins and
.../docs/cli/plugins) is a single root-level hooks.json file. Hooks
authored for this target were silently inert — the host never looked
where pluginpack put them.
- The plugin.json manifest included a "version" field, but Antigravity's
actual JSON Schema is additionalProperties: false with only "name"
(required) and "description" (optional) — a strict validator would
reject the whole manifest for the extra key, not just ignore it.
The emit engine (src/targets/engine.ts) now relocates a source plugin's
hooks/hooks.json to wherever the target's own hooksPath() says hooks
belong, rather than copying it verbatim — this is the actual fix, and
it generalizes: any future target with a different hooks convention
gets it right by construction instead of by remembering to special-case
it. validateAntigravity also now rejects any manifest field outside
{name, description} as a regression guard, and no longer requires
description (the schema makes it optional).
Also reversed, with a citation: an earlier pass claimed Antigravity
doesn't support an agents/ component at all. It does (per
antigravity.google/docs/cli/plugins) — that was wrong and is corrected
here; no change to defaultComponents was needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every docs.github.com example tree shows plugin.json at the plugin root, which is what the previous commit shipped. Before treating that fix as done, checked it against real published examples rather than trusting the docs' illustrative trees alone — two real, working plugins (github/copilot-advanced-security-plugin, microsoft/skills-for-fabric) both put plugin.json at .github/plugin/plugin.json instead. Root-only would have repeated the same class of bug (a plugin the real tooling doesn't find where pluginpack put it), just relocated. Mirrors it at both locations now, matching how the marketplace manifest already dual-writes to .claude-plugin/ and .github/plugin/ for the same reason. Generalized manifestPath -> manifestPaths on PluginTargetDefinition to support this (cheap now, before more targets implement the single-path version). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two changes across every source file:
- Every exported function, type, and const now has a JSDoc block
(/** ... */) rather than a bare // line comment, matching normal
TypeScript convention for documenting public API surface. Applied
codebase-wide, not just to this session's new files, since a
partial conversion would leave the codebase in a more inconsistent
state than before.
- Removed comments that narrated the investigation itself rather than
documenting a durable fact about the code — citation trails
("confirmed via direct fetch of..."), references to internal
planning artifacts ("Plan 1's fix"), and before/after narration
("an earlier pass claimed X — reversed here"). That content belongs
in commit messages and PR descriptions, which is where it already
was — duplicating it into source comments only means it goes stale
the next time someone re-verifies the underlying fact and doesn't
think to hunt down every comment that mentions it.
No behavior changes; every edit is comment-only. Full test suite
(56/56) passes unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes real, confirmed discrepancies between what pluginpack emits for the Copilot and Antigravity targets and what those hosts actually require — found while researching an unrelated feature, then independently re-verified (including against real published plugins in the wild, not just docs) before landing here. Introduces
PluginTargetDefinition, a per-target registry that replaces scattered per-target logic acrosscomponents.ts/targets.ts/validate.ts(the "touches ~5 places" patternCLAUDE.mdalready flagged) — this is the first two targets migrated onto it; cursor/claude/codex follow in separate PRs.copilotandantigravitytargets will see different output on their nextpluginpack build. This is a correctness fix, not a new feature — the previous output likely wasn't recognized correctly by either host.Copilot
plugin.jsonat all. The real spec (CLI plugin reference) requires one: "All plugins consist of a plugin directory containing, at minimum, a manifest file named plugin.json." Without it, a plugin likely wasn't recognized once Copilot fetched it from the marketplace'ssource.plugin.jsonat the plugin root — but two real, working, GitHub/Microsoft-authored plugins (github/copilot-advanced-security-plugin,microsoft/skills-for-fabric) both put it at.github/plugin/plugin.jsoninstead. Caught by checking real examples rather than trusting the docs' illustrative trees alone. Now mirrored at both locations, matching how the marketplace manifest already dual-writes for the same reason.NAME.agent.md(Copilot derives agent identity from the filename).Antigravity
hooks/hooks.json(a directory, like every other target) but Antigravity's real convention (confirmed on two docs pages and two real published plugins) is a single root-levelhooks.jsonfile. Hooks authored for this target were silently inert.plugin.jsonincluded aversionfield, but Antigravity's actual JSON Schema isadditionalProperties: falsewith onlyname(required) anddescription(optional) — a strict validator rejects the whole manifest for an extra key, not just ignores it.agents/at all — it does; that was wrong and is corrected with a citation rather than silently dropped.Architecture
New
src/targets/registry: each target implements onePluginTargetDefinition(component defaults, manifest/entry/marketplace builders, hooks/MCP config paths, validation, install-snippet builder, dated citations to the docs each fact traces to) instead of the same facts being scattered across three files independently.adapters.tsbridges old and new during migration — a target resolves through the registry if migrated, else falls back to the legacy function, so the build stays green target-by-target. The emit engine now actually consults each target'shooksPath()when placing hooks rather than copying them verbatim from source — that's what makes the Antigravity fix structural rather than a one-off patch.Test plan
versionever reappears in Antigravity's manifest, a test that fails if Copilot's twoplugin.jsoncopies divergenpm run check(format, lint, typecheck, tests, build, docs) green — 56/56 tests passmainpost-feat: add opt-in update-check hook for claude and cursor targets #12 (update-check) with zero conflicts — verified the two efforts don't share any modified source filesFollow-ups (intentionally not in this PR)
adapters.ts/ legacytargetDefaultComponentsonce all 5 targets are migrated.app.json(registered MCP servers) — noted as a known gap, not fixed hereCHANGELOG.mdentry — left for whoever cuts the next release, since this repo's changelog looks hand-batched at release time rather than per-PR🤖 Generated with Claude Code