You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Claude adapter: plugin.json "hooks" re-registers the auto-loaded hooks/hooks.json, so Claude Code ≥2.1.22 refuses the whole plugin (no MCP tools, no hooks) #463
ScriptedAlchemy/cargo-hauler#48 — Claude Code 2.1.255 refuses the whole plugin (Status: ✘ failed to load; no mcp__cargo-hauler__* tools, no hooks in the session). claude plugin list --json carries:
Hook load failed: Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded file
<plugin>/hooks/hooks.json. The standard hooks/hooks.json is loaded automatically, so manifest.hooks
should only reference additional hook files.
The shipped artifact/claude/.claude-plugin/plugin.json (built with the pkg.pr.new preview of main 886b192) is:
Claude Code has auto-loaded hooks/hooks.json since v2.1.22 and rejects a manifest that names the same file again (same failure reported against other plugins: affaan-m/ECC#103, ayghri/i-have-adhd#61). The current plugins reference lists hooks/hooks.json as the default location and shows the manifest hooks field only for a custom path ("hooks": "./config/hooks.json"). The hook set is dropped, not deduplicated, and the plugin is marked failed, so MCP servers and skills go with it.
Root cause in agent-bundle (main 10a98a0fb)
packages/agent-bundle/src/adapters/claude.ts:3164 — the plugin manifest always gets hooks: \./${hookContract.manifestPath}`` whenever a hook document exists.
packages/agent-bundle/src/adapters/claude.ts:423 — manifestPath: 'hooks/hooks.json', i.e. exactly the auto-loaded default.
packages/agent-bundle/src/adapters/schemas/claude/plugin.schema.json:150 — the vendored manifest schema pins "hooks": { "const": "./hooks/hooks.json" }, so the adapter cannot emit anything else and agent-bundle build validates the broken shape as correct.
packages/agent-bundle/src/adapters/codex.ts:1192 emits the same key for Codex. Codex 0.147.0 documents hooks/hooks.json as the default or a manifest path (capabilities/codex-0.147.0.json:798); it is not known to reject the duplicate, but the same fix (omit the key when the document is at the default path) is worth applying or explicitly proving harmless there.
The install proof in cargo-hauler PR 34 reported doctor --host claude → installed copy: current for this artifact because doctor never reads the plugin's load status (filed separately as #464).
Workaround currently in cargo-hauler
None — the manifest is generated. Users can only hand-edit ~/.claude/plugins/cache/cargo-hauler-marketplace/cargo-hauler/<version>/.claude-plugin/plugin.json after install.
Proposed fix
In claude.ts, omit the hooks manifest key when the emitted hook document lives at hooks/hooks.json (always, today). Keep the key only if a future adapter emits hooks at a non-default path.
Relax schemas/claude/plugin.schema.json accordingly (drop the const, or drop the property) and add a PROVENANCE note citing the Claude Code duplicate-hooks rule.
Same treatment for codex.ts:1192 once verified against a live codex plugin list --json, or a PROVENANCE row proving Codex accepts the duplicate.
Regression: the Claude fixture/contract-matrix test asserts plugin.json has no hooks key when hooks are emitted at the default path.
Acceptance
A fresh agent-bundle build for a project with event routes emits artifact/claude/.claude-plugin/plugin.json without hooks; hooks/hooks.json is still present and passes claude plugin validate.
Installing that pack via marketplace on Claude Code ≥ 2.1.255 shows claude plugin list --json with errors: [] and the plugin enabled; the MCP tools appear in the session tool list and the SessionStart/PreToolUse/Stop hooks fire (note [BUG] Misleading "duplicate hooks" warning for marketplace-installed plugins anthropics/claude-code#18265 claims hooks may not auto-load for marketplace installs — the acceptance run must prove hooks fire, not only that the load error is gone).
docs/diagnostics.md / hosts reference: no change unless a diagnostic is added.
Symptom in cargo-hauler
ScriptedAlchemy/cargo-hauler#48 — Claude Code 2.1.255 refuses the whole plugin (
Status: ✘ failed to load; nomcp__cargo-hauler__*tools, no hooks in the session).claude plugin list --jsoncarries:The shipped
artifact/claude/.claude-plugin/plugin.json(built with the pkg.pr.new preview of main886b192) is:{"author":{"name":"cargo-hauler"},"description":"…","hooks":"./hooks/hooks.json","name":"cargo-hauler","version":"0.4.1"}Claude Code has auto-loaded
hooks/hooks.jsonsince v2.1.22 and rejects a manifest that names the same file again (same failure reported against other plugins: affaan-m/ECC#103, ayghri/i-have-adhd#61). The current plugins reference listshooks/hooks.jsonas the default location and shows the manifesthooksfield only for a custom path ("hooks": "./config/hooks.json"). The hook set is dropped, not deduplicated, and the plugin is marked failed, so MCP servers and skills go with it.Root cause in agent-bundle (main
10a98a0fb)packages/agent-bundle/src/adapters/claude.ts:3164— the plugin manifest always getshooks: \./${hookContract.manifestPath}`` whenever a hook document exists.packages/agent-bundle/src/adapters/claude.ts:423—manifestPath: 'hooks/hooks.json', i.e. exactly the auto-loaded default.packages/agent-bundle/src/adapters/schemas/claude/plugin.schema.json:150— the vendored manifest schema pins"hooks": { "const": "./hooks/hooks.json" }, so the adapter cannot emit anything else andagent-bundle buildvalidates the broken shape as correct.packages/agent-bundle/src/adapters/codex.ts:1192emits the same key for Codex. Codex 0.147.0 documentshooks/hooks.jsonas the default or a manifest path (capabilities/codex-0.147.0.json:798); it is not known to reject the duplicate, but the same fix (omit the key when the document is at the default path) is worth applying or explicitly proving harmless there.The install proof in cargo-hauler PR 34 reported
doctor --host claude → installed copy: currentfor this artifact because doctor never reads the plugin's load status (filed separately as #464).Workaround currently in cargo-hauler
None — the manifest is generated. Users can only hand-edit
~/.claude/plugins/cache/cargo-hauler-marketplace/cargo-hauler/<version>/.claude-plugin/plugin.jsonafter install.Proposed fix
claude.ts, omit thehooksmanifest key when the emitted hook document lives athooks/hooks.json(always, today). Keep the key only if a future adapter emits hooks at a non-default path.schemas/claude/plugin.schema.jsonaccordingly (drop theconst, or drop the property) and add a PROVENANCE note citing the Claude Code duplicate-hooks rule.codex.ts:1192once verified against a livecodex plugin list --json, or a PROVENANCE row proving Codex accepts the duplicate.plugin.jsonhas nohookskey when hooks are emitted at the default path.Acceptance
agent-bundle buildfor a project with event routes emitsartifact/claude/.claude-plugin/plugin.jsonwithouthooks;hooks/hooks.jsonis still present and passesclaude plugin validate.claude plugin list --jsonwitherrors: []and the plugin enabled; the MCP tools appear in the session tool list and the SessionStart/PreToolUse/Stop hooks fire (note [BUG] Misleading "duplicate hooks" warning for marketplace-installed plugins anthropics/claude-code#18265 claims hooks may not auto-load for marketplace installs — the acceptance run must prove hooks fire, not only that the load error is gone).docs/diagnostics.md/ hosts reference: no change unless a diagnostic is added.