fix(hooks): conform hooks.json to Claude Code plugin hook schema (fixes plugin failing to load)#1
Conversation
PreToolUse/PostToolUse entries were flattened (type/command/timeout as
siblings of matcher), missing the required per-matcher `hooks: []` array.
This fails schema validation with
"hooks.PreToolUse[0].hooks: expected array" and makes the entire plugin
fail to load (Status: failed to load; Hooks: 0).
Wrapping each matcher's action in a `hooks` array fixes the load failure.
Also corrects two latent runtime defects in the same file:
- timeouts were in milliseconds (5000/3000); Claude Code hook timeouts
are in seconds. Corrected to 5/3 (5000s would block for ~83 min).
- command paths were relative (`sh scripts/...`); hooks execute from the
user's project directory, so the scripts are not found. Use
`${CLAUDE_PLUGIN_ROOT}/scripts/...` as the official plugins do.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
β Approved
Approved β 0 blockers, 4 P3. Confidence: 4/5.
π΅ P3 β Minor / nit
hooks/hooks.json:9β π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax (such as pipefail or arrays) is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" remotion-media KIE_API_KEY",
hooks/hooks.json:19β π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" replicate REPLICATE_API_TOKEN",
hooks/hooks.json:31β π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" pexels-attribution",
hooks/hooks.json:41β π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" captions-tip",
Total findings: 4 business context (4 total)
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" remotion-media KIE_API_KEY", |
There was a problem hiding this comment.
π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax (such as pipefail or arrays) is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" remotion-media KIE_API_KEY", | |
| "command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" remotion-media KIE_API_KEY", |
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" replicate REPLICATE_API_TOKEN", |
There was a problem hiding this comment.
π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" replicate REPLICATE_API_TOKEN", | |
| "command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/check-mcp-server.sh\" replicate REPLICATE_API_TOKEN", |
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" pexels-attribution", |
There was a problem hiding this comment.
π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" pexels-attribution", | |
| "command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" pexels-attribution", |
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" captions-tip", |
There was a problem hiding this comment.
π΅ P3 (minor) β Running the hook with POSIX 'sh' ignores the script's bash shebang ('#!/bin/bash'). If bash-specific syntax is introduced in the script, execution under 'sh' can fail. Running it with 'bash' avoids potential shell compatibility issues.
| "command": "sh \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" captions-tip", | |
| "command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-note.sh\" captions-tip", |
Summary
hooks/hooks.jsonis not valid against the Claude Code plugin hook schema, which causes the entire plugin to fail to load (Status: β failed to load,Hooks: 0). This PR makes the file spec-compliant.The validation error Claude Code reports:
Root cause
Each matcher entry was written in a flat shape, with
type/command/timeoutas siblings ofmatcher. The schema requires each matcher to contain a nestedhooks: []array of actions (see the officialsuperpowers/codexplugins). Three defects, all stemming from not following the hook spec:hooks: []wrappertimeoutin ms (5000/3000)5000would block for ~83 minsh scripts/...pathsFix
hooks: []array.5/3).${CLAUDE_PLUGIN_ROOT}/scripts/..., matching the official plugins.Behaviour (which servers/keys are checked, matchers, notes emitted) is unchanged.
Test plan
claude plugin validate .passeshooks.jsonparses as valid JSONclaude plugin listshowsremotion-superpowersasβ enabledandclaude plugin detailsreportsHooks (2) PreToolUse, PostToolUse(wasHooks (0)+ failed to load)π€ Generated with Claude Code