fix(plugins): use Claude Code hooks schema in plugin hook manifests - #1
Open
rangulvers wants to merge 1 commit into
Open
fix(plugins): use Claude Code hooks schema in plugin hook manifests#1rangulvers wants to merge 1 commit into
rangulvers wants to merge 1 commit into
Conversation
Each plugins/<module>/hooks/hooks.json was using PAI's internal manifest
shape with 'module', 'description', and a flat list of
{event, matcher, command, description} entries. Claude Code's plugin
loader expects hooks.json to follow the same schema as settings.json:
an object keyed by event name, where each rule is
{ matcher?, hooks: [ { type: 'command', command } ] }.
When users installed PAI via Claude Code's plugin marketplace (which
references these files from .claude-plugin/plugin.json and
pai-plugin.json), the loader silently flattened the entries into a
broken shape ({matcher, command} without the inner 'hooks' array),
producing the validation panel:
Settings Error
/Users/<user>/.claude/settings.json
└ hooks
├ PostToolUse
│ └ 0 → hooks: Expected array, but received undefined
...
After conversion every event-keyed list contains valid
{matcher?, hooks: [{type: 'command', command}]} entries that Claude Code
accepts on first start. The 'pai setup' CLI path was already producing
the correct shape via mergeHooks() in settings-manager.ts; this brings
the marketplace path in line.
Touches: plugins/{core,productivity,ui,context-preservation,observability}/hooks/hooks.json
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.
Problem
Installing PAI via Claude Code's plugin marketplace (or any other consumer that follows
.claude-plugin/plugin.json/pai-plugin.json) currently produces a settings validation error on every Claude Code start:with three forced choices: Fix with Claude / Exit and fix manually / Continue without these settings. "Continue without these settings" silently disables every PAI hook, so PAI looks installed but is effectively dead.
Root cause
.claude-plugin/plugin.json(andpai-plugin.json) reference the per-module hook manifests:{ "hooks": "plugins/core/hooks/hooks.json", ... }Those files were written in PAI's internal manifest shape:
{ "module": "core", "description": "Essential lifecycle hooks ...", "hooks": [ { "event": "SessionStart", "command": "${PAI_DIR}/Hooks/load-core-context.mjs", "description": "..." }, { "event": "PreToolUse", "matcher": "Bash", "command": "${PAI_DIR}/Hooks/security-validator.mjs", "description": "..." } ] }Claude Code's plugin loader expects
hooks.jsonto use the same schema as~/.claude/settings.jsonhooks — an object keyed by event name, where each rule is{ matcher?, hooks: [{ type: "command", command }] }. When the loader encounters PAI's shape it partially merges the entries into settings.json without the innerhooksarray, producing the broken state above.The
pai setupCLI path was unaffected becausemergeHooks()insrc/cli/commands/settings-manager.tsalready builds the correct shape — only the marketplace/plugin-manifest path was broken.Fix
Convert all
plugins/*/hooks/hooks.jsonto Claude Code's expected schema. Same hooks, same matchers, same commands — just wrapped correctly:{ "SessionStart": [ { "hooks": [ { "type": "command", "command": "${PAI_DIR}/Hooks/load-core-context.mjs" } ] } ], "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "${PAI_DIR}/Hooks/security-validator.mjs" } ] } ] }5 files touched:
plugins/{core,productivity,ui,context-preservation,observability}/hooks/hooks.json. 26 hook rules total, all validated against the schema (object keyed by event → array of rules → each withhooks: [{type:"command", command:string}]).Validation
{matcher?, hooks: [{type:"command", command:string}]}SessionStart,SessionEnd,PreToolUse,PostToolUse,UserPromptSubmit,Stop,SubagentStop,PreCompact)~/.claude/settings.jsonno longer triggers the validation panel and thatclaudestarts cleanlyNotes
descriptionstrings inside the old format were not preserved — Claude Code's schema has no slot for them. The hook script files are self-documenting and the higher-level mapping is already covered inPLUGIN-ARCHITECTURE.md("Hook Distribution" section). Happy to add them as a sidecar*.meta.jsonor as JSON Schema-aware comments if you prefer.~/.claude/settings.jsonin place, since otherwise existing installations continue to fail until the user nukes and reinstalls.