| name | hook-audit |
| description | Comprehensive audit of Claude Code hooks for correctness, safety, and performance. Use when reviewing, validating, or debugging hooks, checking JSON stdin handling, verifying exit codes (0=allow, 2=block), analyzing error handling, fixing hook failures, ensuring safe degradation, optimizing performance, or validating settings.json registration. Also triggers when user asks about hook best practices, wants to create a new hook, or needs help with hook configuration. |
| allowed-tools | Read, Grep, Glob, Bash |
Reference Files
Advanced hook patterns and best practices:
- exit-codes.md - Exit code semantics (0=allow, 2=block) with examples
- json-handling.md - Safe JSON stdin parsing patterns
- error-patterns.md - Error handling and graceful degradation
- performance.md - Timeout guidelines and optimization
- examples.md - Good vs bad hook patterns with before/after examples
Hook Audit
Performs comprehensive security and quality audits of Claude Code hooks, ensuring correct JSON handling, exit code semantics, error handling, and performance.
Quick Start
Audit a single hook:
User: "Audit my validate-config.py hook"
Assistant: [Reads hook file, checks patterns, generates report]
Audit all hooks:
User: "Check all my hooks for best practices"
Assistant: [Finds all hooks, analyzes each, generates consolidated report]
Fix specific issues:
User: "My hook is blocking on errors"
Assistant: [Analyzes error handling, suggests fixes]
Hook Audit Checklist
Use this checklist to audit any Claude Code hook:
Critical Requirements
- Shebang Line: Correct interpreter (
#!/usr/bin/env python3or#!/usr/bin/env bash) - JSON stdin Handling: Safe parsing with try/except and
.get()methods - Exit Codes: Correct semantics (0=allow, 2=block, never 1)
- Error Handling: Exit 0 on hook errors (never block on hook failures)
- Settings.json Registration: Hook is registered with correct matcher and timeout
High Priority
- Timeout: Configured appropriately for hook type (PreToolUse <500ms, PostToolUse <2s)
- File Type Validation: Checks file type before processing
- Error Messages: Clear messages to stderr
- Performance: Executes within reasonable time
Medium Priority
- Naming Convention: Uses kebab-case naming
- Documentation: Header comments explain purpose and usage
- Pattern Consistency: Follows existing hook patterns
- Security: No security vulnerabilities or unsafe operations
Audit Workflow
Step 1: Identify Hook Type
Determine the hook type from settings.json registration:
- PreToolUse: Runs before tool execution, can block operations
- PostToolUse: Runs after successful tool execution
- Notification: Runs on specific events (Idle, etc.)
- SessionStart: Runs once at session start
Hook type determines performance requirements and exit code handling.
Step 2: Read Hook File and Check Syntax
Read the hook file and verify basic syntax:
# For Python hooks
python3 -m py_compile hook-file.py
# For Bash hooks
bash -n hook-file.sh
Check for:
- Correct shebang line
- Executable permissions
- Valid syntax
Step 3: Verify JSON Handling Pattern
For hooks that receive JSON stdin (PreToolUse, PostToolUse), verify safe parsing:
Python Pattern:
try:
data = json.load(sys.stdin)
file_path = data.get("tool_input", {}).get("file_path", "")
content = data.get("tool_input", {}).get("content", "")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(0) # Don't block on parsing errors
Bash Pattern:
# Read stdin to variable
stdin_data=$(cat)
# Parse specific fields with jq
file_path=$(echo "$stdin_data" | jq -r '.tool_input.file_path // empty')
Critical: Use .get() with defaults, never direct key access.
Step 4: Check Exit Code Usage
Verify exit codes follow correct semantics:
- Exit 0: Allow operation (or hook encountered error)
- Exit 2: Block operation (validation failed)
- Never Exit 1: Reserved, don't use
Pattern to Check:
# Good: Block on validation failure
if errors:
print(f"Validation errors:", file=sys.stderr)
sys.exit(2)
# Good: Allow on hook error
except Exception as e:
print(f"Hook error: {e}", file=sys.stderr)
sys.exit(0) # Don't block user
# Bad: Exit 1 or non-zero on errors
sys.exit(1) # ✗ Wrong, use 0 or 2
For details on exit code patterns, see exit-codes.md.
Step 5: Review Error Handling
Check that hooks degrade gracefully:
- Dependency Check: Missing dependencies exit 0 (don't block)
- Try/Except: All operations wrapped in try/except
- Error Exit: Exceptions exit 0, not 1 or other codes
- Clear Messages: Errors printed to stderr with context
Example Pattern:
try:
import yaml
except ImportError:
print("Warning: PyYAML not installed, skipping", file=sys.stderr)
sys.exit(0) # Don't block user
try:
# Hook logic here
...
except Exception as e:
print(f"Error in hook: {e}", file=sys.stderr)
sys.exit(0) # Don't block user
For error handling patterns, see error-patterns.md.
Step 6: Validate settings.json Registration
Check that the hook is properly registered:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"command": "python3 ~/.claude/hooks/validate-config.py",
"timeout": 5000
}
]
}
]
}
}
Verify:
- Hook file exists at specified path
- Matcher pattern is correct for intended triggers
- Timeout is appropriate for hook type
- Hook type (PreToolUse/PostToolUse/etc.) matches purpose
Step 7: Generate Report
Create a structured audit report following the output format below.
Hook Type Reference
PreToolUse Hooks
Purpose: Validate operations before execution Can Block: Yes (exit 2 to block) Performance Target: <500ms (blocks user interaction) JSON stdin: Tool name and input parameters
Example Matchers:
Edit|Write- Validate before file modificationsBash- Validate before bash commands.*- All tools (use cautiously)
Common Uses:
- File content validation (YAML, config files)
- Security checks (prevent writing secrets)
- Policy enforcement
PostToolUse Hooks
Purpose: Process results after successful execution Can Block: No (exit code ignored, operation already completed) Performance Target: <2s (runs after operation) JSON stdin: Tool name, input, and output
Common Uses:
- Auto-formatting (gofmt, prettier)
- Logging operations
- Side effects (notifications, metrics)
Notification Hooks
Purpose: React to events Can Block: No Performance Target: <100ms (quick notifications) JSON stdin: Event-specific data
Common Uses:
- Desktop notifications (Idle event)
- Metrics tracking
- Alert systems
SessionStart Hooks
Purpose: Initialize session state Can Block: No Performance Target: <5s (one-time initialization) JSON stdin: Session metadata
Common Uses:
- Load git repository context
- Set environment variables
- Initialize session state
Common Issues
Missing try/except
Problem: Hook crashes on errors, blocks user
# ✗ Bad: No error handling
data = json.load(sys.stdin)
file_path = data["tool_input"]["file_path"]
Fix: Wrap in try/except, exit 0 on errors
# ✓ Good: Safe error handling
try:
data = json.load(sys.stdin)
file_path = data.get("tool_input", {}).get("file_path", "")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(0)
Wrong Exit Codes
Problem: Using exit 1 instead of 0 or 2
# ✗ Bad: Exit 1
if error:
sys.exit(1)
Fix: Use exit 2 to block, exit 0 to allow
# ✓ Good: Correct exit codes
if validation_failed:
sys.exit(2) # Block operation
if hook_error:
sys.exit(0) # Don't block user
Blocking on Hook Errors
Problem: Hook exits non-zero on internal errors, blocks user
# ✗ Bad: Blocks user on missing dependency
import yaml # Crashes if not installed
Fix: Check dependencies, exit 0 on errors
# ✓ Good: Graceful degradation
try:
import yaml
except ImportError:
print("Warning: PyYAML not installed", file=sys.stderr)
sys.exit(0)
Slow Performance
Problem: PreToolUse hook takes >1s, blocks user
Fix: Optimize or move to PostToolUse
- Cache expensive operations
- Use faster parsing (regex instead of full AST)
- Move non-critical checks to PostToolUse
For performance optimization, see performance.md.
Report Format
Generate audit reports in this standardized structure:
# Hook Audit Report: {hook-name}
**Hook**: {name}
**Type**: PreToolUse | PostToolUse | Notification | SessionStart
**File**: {path}
**Audited**: {YYYY-MM-DD HH:MM}
## Summary
{1-2 sentence overview of hook and audit results}
## Compliance Status
**Overall**: PASS | NEEDS WORK | FAIL
- **Shebang Line**: ✓/✗
- **JSON Handling**: ✓/✗
- **Exit Codes**: ✓/✗
- **Error Handling**: ✓/✗
- **Performance**: ✓/✗
- **Registration**: ✓/✗
## Critical Issues
{List of critical failures that must be fixed}
### {Issue Title}
- **Severity**: CRITICAL
- **Location**: {file}:{line}
- **Issue**: {description}
- **Fix**: {specific remediation}
## High Priority Issues
{List of important improvements}
### {Issue Title}
- **Severity**: HIGH
- **Location**: {file}:{line}
- **Issue**: {description}
- **Fix**: {specific remediation}
## Medium Priority Issues
{List of best practice violations}
### {Issue Title}
- **Severity**: MEDIUM
- **Location**: {file}:{line}
- **Issue**: {description}
- **Fix**: {specific remediation}
## Recommendations
1. **Critical**: {must-fix items for safety/correctness}
2. **Important**: {should-fix items for reliability}
3. **Nice-to-Have**: {polish items for maintainability}
## Next Steps
{Specific actions to improve hook quality and safety}
Integration
With Other Auditors
- claude-code-evaluator: General correctness and clarity
- claude-code-test-runner: Functional testing
- audit-coordinator: Orchestrates multiple auditors
With Hook Validation
The validate-config.py hook validates YAML frontmatter in agents/skills. The hook-audit validates hooks themselves - a meta-validation.
Examples
For detailed examples of good and bad hook patterns, including before/after refactoring, see examples.md.
Quick Examples:
Good Hook (validate-config.py):
- ✓ Safe JSON parsing with try/except
- ✓ Correct exit codes (0 on error, 2 to block)
- ✓ Dependency checking (PyYAML)
- ✓ Clear error messages
- ✓ File type validation
Simple Hook (log-git-commands.sh):
- ✓ Basic bash pattern
- ✓ Safe jq parsing
- ✓ Always exits 0 (informational)
- ✓ Clear purpose
For complete examples and anti-patterns, see the references above.