mirror of
https://github.com/anthropics/claude-code.git
synced 2026-04-25 15:16:40 +00:00
Stop hooks (and other hook types) were not firing because they were missing the required "matcher" field. According to the hook development documentation, all hooks must have a matcher field - "*" for wildcard matching. Changes: - Add matcher: "*" to all hooks in hookify, ralph-wiggum, explanatory-output-style, and learning-output-style plugins - Update validate-hook-schema.sh to properly handle plugin format (with 'hooks' wrapper) vs settings format (events at root) - Add validate-all-hooks.sh script to validate all hooks.json files Fixes: https://anthropic.slack.com/archives/C08EHE6JF3L/p1765822035850959
Hook Development Utility Scripts
These scripts help validate, test, and lint hook implementations before deployment.
validate-hook-schema.sh
Validates hooks.json configuration files for correct structure and common issues.
Usage:
./validate-hook-schema.sh path/to/hooks.json
Checks:
- Valid JSON syntax
- Required fields present
- Valid hook event names
- Proper hook types (command/prompt)
- Timeout values in valid ranges
- Hardcoded path detection
- Prompt hook event compatibility
Example:
cd my-plugin
./validate-hook-schema.sh hooks/hooks.json
test-hook.sh
Tests individual hook scripts with sample input before deploying to Claude Code.
Usage:
./test-hook.sh [options] <hook-script> <test-input.json>
Options:
-v, --verbose- Show detailed execution information-t, --timeout N- Set timeout in seconds (default: 60)--create-sample <event-type>- Generate sample test input
Example:
# Create sample test input
./test-hook.sh --create-sample PreToolUse > test-input.json
# Test a hook script
./test-hook.sh my-hook.sh test-input.json
# Test with verbose output and custom timeout
./test-hook.sh -v -t 30 my-hook.sh test-input.json
Features:
- Sets up proper environment variables (CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT)
- Measures execution time
- Validates output JSON
- Shows exit codes and their meanings
- Captures environment file output
hook-linter.sh
Checks hook scripts for common issues and best practices violations.
Usage:
./hook-linter.sh <hook-script.sh> [hook-script2.sh ...]
Checks:
- Shebang presence
set -euo pipefailusage- Stdin input reading
- Proper error handling
- Variable quoting (injection prevention)
- Exit code usage
- Hardcoded paths
- Long-running code detection
- Error output to stderr
- Input validation
Example:
# Lint single script
./hook-linter.sh ../examples/validate-write.sh
# Lint multiple scripts
./hook-linter.sh ../examples/*.sh
Typical Workflow
-
Write your hook script
vim my-plugin/scripts/my-hook.sh -
Lint the script
./hook-linter.sh my-plugin/scripts/my-hook.sh -
Create test input
./test-hook.sh --create-sample PreToolUse > test-input.json # Edit test-input.json as needed -
Test the hook
./test-hook.sh -v my-plugin/scripts/my-hook.sh test-input.json -
Add to hooks.json
# Edit my-plugin/hooks/hooks.json -
Validate configuration
./validate-hook-schema.sh my-plugin/hooks/hooks.json -
Test in Claude Code
claude --debug
Tips
- Always test hooks before deploying to avoid breaking user workflows
- Use verbose mode (
-v) to debug hook behavior - Check the linter output for security and best practice issues
- Validate hooks.json after any changes
- Create different test inputs for various scenarios (safe operations, dangerous operations, edge cases)
Common Issues
Hook doesn't execute
Check:
- Script has shebang (
#!/bin/bash) - Script is executable (
chmod +x) - Path in hooks.json is correct (use
${CLAUDE_PLUGIN_ROOT})
Hook times out
- Reduce timeout in hooks.json
- Optimize hook script performance
- Remove long-running operations
Hook fails silently
- Check exit codes (should be 0 or 2)
- Ensure errors go to stderr (
>&2) - Validate JSON output structure
Injection vulnerabilities
- Always quote variables:
"$variable" - Use
set -euo pipefail - Validate all input fields
- Run the linter to catch issues