enhance-hooks
enhance-hooks
Analyze hook definitions and scripts for safety, correctness, and best practices.
Parse Arguments
const args = '$ARGUMENTS'.split(' ').filter(Boolean);
const targetPath = args.find(a => !a.startsWith('--')) || '.';
const fix = args.includes('--fix');
Workflow
- Discover - Find hook files (.md, .sh, .json)
- Classify - Identify hook type and event
- Parse - Extract frontmatter and script content
- Check - Run all pattern checks against knowledge below
- Filter - Apply certainty filtering
- Report - Generate markdown output
- Fix - Apply auto-fixes if --fix flag present
Hook Knowledge Reference
What Are Hooks
Hooks are automated actions triggered at specific points in a Claude Code session. They enable validation, monitoring, and control of Claude's actions through bash commands or LLM-based evaluation.
Hook Lifecycle (Complete Reference)
Hooks fire in this sequence:
| Order | Event | Description | Matcher Required |
|---|---|---|---|
| 1 | SessionStart |
Session begins or resumes | No |
| 2 | UserPromptSubmit |
User submits a prompt | No |
| 3 | PreToolUse |
Before tool execution (can modify/block) | Yes |
| 4 | PermissionRequest |
When permission dialog appears | Yes |
| 5 | PostToolUse |
After tool succeeds | Yes |
| 6 | SubagentStart |
When spawning a subagent | No |
| 7 | SubagentStop |
When subagent finishes | No |
| 8 | Stop |
Claude finishes responding | No |
| 9 | PreCompact |
Before context compaction | No |
| 10 | SessionEnd |
Session terminates | No |
| 11 | Notification |
Claude Code sends notifications | No |
Hook Types
Command Hooks (type: "command"):
- Execute bash commands with full stdin/stdout control
- Available for all events
Prompt Hooks (type: "prompt"):
- Use LLM evaluation for intelligent, context-aware decisions
- Only supported for
StopandSubagentStopevents
Configuration Locations
| File | Location | Scope | Committed |
|---|---|---|---|
| User settings | ~/.claude/settings.json |
All projects | No |
| Project settings | .claude/settings.json |
Current project | Yes |
| Local settings | .claude/settings.local.json |
Current project | No |
Configuration Structure
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/validate-bash.sh",
"timeout": 30
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all requested tasks are complete.",
"timeout": 30
}
]
}
]
}
}
Matcher Syntax
| Pattern | Description |
|---|---|
Write |
Match exact tool name |
Edit|Write |
Match multiple tools (regex OR) |
Notebook.* |
Regex pattern matching |
* or "" |
Match all tools |
| (omitted) | Required for Stop, SubagentStop, UserPromptSubmit |
Input Schema (JSON via stdin)
All hooks receive this JSON structure:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript",
"cwd": "/project/root",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
}
}
Exit Codes
| Exit Code | Behavior |
|---|---|
| 0 | Success - stdout shown to user or added as context |
| 2 | Blocking error - stderr shown, action blocked |
| Other | Non-blocking error - stderr shown in verbose mode |
Output Schemas
PreToolUse Decision Control:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "Reason for decision",
"updatedInput": {
"command": "modified command"
},
"additionalContext": "Context for Claude"
}
}
Stop/SubagentStop Control:
{
"decision": "block",
"reason": "Tasks incomplete: missing test coverage"
}
Environment Variables
| Variable | Description | Available In |
|---|---|---|
CLAUDE_PROJECT_DIR |
Absolute path to project root | All hooks |
CLAUDE_CODE_REMOTE |
"true" if remote session | All hooks |
CLAUDE_ENV_FILE |
Path to persist env vars | SessionStart only |
CLAUDE_FILE_PATHS |
Space-separated file paths | PostToolUse (Write/Edit) |
Practical Hook Examples
Security Firewall (PreToolUse):
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
# Block dangerous patterns
if echo "$cmd" | grep -qE 'rm -rf|git reset --hard|curl.*\|.*sh'; then
echo '{"decision": "block", "reason": "Dangerous command blocked"}' >&2
exit 2
fi
exit 0
Auto-Formatter (PostToolUse):
#!/usr/bin/env bash
set -euo pipefail
files=$(jq -r '.tool_input.file_path // ""')
for file in $files; do
case "$file" in
*.py) black "$file" 2>/dev/null || true ;;
*.js|*.ts) prettier --write "$file" 2>/dev/null || true ;;
esac
done
exit 0
Command Logger (PreToolUse):
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
printf '%s %s\n' "$(date -Is)" "$cmd" >> .claude/bash-commands.log
exit 0
Workflow Orchestration (SubagentStop - prompt type):
{
"hooks": {
"SubagentStop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review the subagent's work. Did it complete all tasks?"
}
]
}
]
}
}
Detection Patterns
1. Frontmatter Validation (HIGH Certainty)
Required:
- YAML frontmatter with
---delimiters namefield in frontmatterdescriptionfield in frontmatter
Recommended:
timeoutfor command hooks (default: 30s)- Hook type specification
Flag:
- Missing frontmatter delimiters
- Missing name or description
2. Script Safety (HIGH Certainty)
Required Safety Patterns:
set -euo pipefailat script start- Error handling for jq/JSON parsing
- Proper quoting of variables
Dangerous Patterns to Flag:
| Pattern | Risk | Certainty |
|---|---|---|
rm -rf |
Destructive without confirmation | HIGH |
git reset --hard |
Data loss risk | HIGH |
curl | sh |
Remote code execution | HIGH |
eval "$input" |
Arbitrary code execution | HIGH |
rm -r |
Recursive delete (may be intentional) | MEDIUM |
git push --force |
Force push (may be intentional) | MEDIUM |
3. Exit Code Handling (HIGH Certainty)
Check: Scripts use correct exit codes
Flag:
- Missing
exit 0for success path - Using exit code 1 for blocking (should be 2)
- No exit code at end of script
4. Hook Type Appropriateness (HIGH Certainty)
Check: Hook type matches event
Flag:
- Prompt hooks used for events other than Stop/SubagentStop
- Missing type specification
5. Lifecycle Event Appropriateness (MEDIUM Certainty)
| Event | Appropriate Use Cases |
|---|---|
PreToolUse |
Security validation, command blocking, input modification |
PostToolUse |
Formatting, logging, notifications |
Stop |
Completion checks, cleanup, summary |
SubagentStop |
Workflow orchestration, result validation |
SessionStart |
Environment setup, initialization |
Flag:
- PostToolUse hooks trying to block actions (too late)
- PreToolUse hooks doing heavy processing (should be fast)
- Prompt hooks on unsupported events
6. Timeout Configuration (MEDIUM Certainty)
Guidelines:
- Default: 30 seconds for command hooks
- Network operations: Always set explicit timeout
- External service calls: Set timeout based on expected latency
Flag:
- No timeout for network operations
- Timeout missing for external service calls
- Unreasonably long timeouts (>60s without justification)
7. Output Format (MEDIUM Certainty)
PreToolUse Output Fields:
permissionDecision: allow, deny, or askpermissionDecisionReason: Explanation for decisionupdatedInput: Modified tool input (optional)additionalContext: Context for Claude (optional)
Flag:
- Invalid permissionDecision values
- Missing reason for deny decisions
- Malformed JSON output
8. Matcher Patterns (MEDIUM Certainty)
Check: Matcher syntax is valid
Flag:
- Invalid regex patterns
- Too broad matchers (
*without justification) - Matcher on events that don't support it (Stop, SubagentStop)
9. Anti-Patterns (LOW Certainty)
- Complex logic in hooks (should be simple and fast)
- Missing documentation/comments
- Hardcoded paths (should use
$CLAUDE_PROJECT_DIR) - Network calls without error handling
- Secrets/credentials in hook scripts
Auto-Fix Implementations
1. Missing safety header
#!/usr/bin/env bash
set -euo pipefail
2. Missing exit code
Add exit 0 at end of script
3. Missing frontmatter fields
---
name: hook-name
description: Hook description
timeout: 30
---
4. Wrong blocking exit code
Replace exit 1 with exit 2 for blocking errors
Output Format
## Hook Analysis: {hook-name}
**File**: {path}
**Type**: {command|prompt|config}
**Event**: {PreToolUse|PostToolUse|Stop|...}
### Summary
- HIGH: {count} issues
- MEDIUM: {count} issues
### Frontmatter Issues ({n})
| Issue | Fix | Certainty |
### Safety Issues ({n})
| Issue | Fix | Certainty |
### Exit Code Issues ({n})
| Issue | Fix | Certainty |
### Lifecycle Issues ({n})
| Issue | Fix | Certainty |
### Output Format Issues ({n})
| Issue | Fix | Certainty |
Pattern Statistics
| Category | Patterns | Auto-Fixable |
|---|---|---|
| Frontmatter | 3 | 2 |
| Safety | 6 | 2 |
| Exit Code | 3 | 2 |
| Hook Type | 2 | 0 |
| Lifecycle | 5 | 0 |
| Timeout | 3 | 0 |
| Output | 3 | 0 |
| Matcher | 3 | 0 |
| Anti-Pattern | 5 | 0 |
| Total | 33 | 6 |
<bad_example>
#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // ""')
Why it's bad: Missing set -euo pipefail means errors may silently pass.
</bad_example>
<good_example>
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
Why it's good: Fails fast on errors, unset variables, and pipe failures. </good_example>
Example: Wrong Exit Code for Blocking
<bad_example>
if [[ "$cmd" == *"rm -rf"* ]]; then
echo "Blocked dangerous command" >&2
exit 1 # Wrong!
fi
Why it's bad: Exit code 1 is non-blocking. Action will still proceed. </bad_example>
<good_example>
if [[ "$cmd" == *"rm -rf"* ]]; then
echo '{"decision": "block", "reason": "Dangerous command"}' >&2
exit 2 # Correct blocking exit code
fi
Why it's good: Exit code 2 blocks the action. JSON output provides context. </good_example>
Example: Prompt Hook on Wrong Event
<bad_example>
{
"hooks": {
"PreToolUse": [
{
"hooks": [{ "type": "prompt", "prompt": "Is this safe?" }]
}
]
}
}
Why it's bad: Prompt hooks only work for Stop and SubagentStop events. </bad_example>
<good_example>
{
"hooks": {
"PreToolUse": [
{
"hooks": [{ "type": "command", "command": "./validate.sh" }]
}
]
}
}
Why it's good: Command hooks work for all events. </good_example>
Example: Dangerous Command Pattern
<bad_example>
if echo "$cmd" | grep -q 'rm'; then
exit 2
fi
Why it's bad: Too broad - blocks legitimate rm file.tmp.
</bad_example>
<good_example>
if echo "$cmd" | grep -qE 'rm\s+(-rf|-fr)\s+/'; then
exit 2
fi
Why it's good: Specific pattern targets actual dangerous commands. </good_example>
Example: Hardcoded Path
<bad_example>
log_file="/home/user/project/.claude/commands.log"
Why it's bad: Hardcoded path breaks on other machines. </bad_example>
<good_example>
log_file="$CLAUDE_PROJECT_DIR/.claude/commands.log"
Why it's good: Uses environment variable for portability. </good_example>
Constraints
- Only apply auto-fixes for HIGH certainty issues
- Be cautious about security patterns - false negatives worse than false positives
- Never remove content, only suggest improvements
- Validate against embedded knowledge reference above
More from agent-sh/agentsys
debate
Structured AI debate templates and synthesis. Use when orchestrating multi-round debates between AI tools, 'debate topic', 'argue about', 'stress test idea', 'devil advocate'.
10discover-tasks
Use when user asks to \"discover tasks\", \"find next task\", \"prioritize issues\", \"what should I work on\", or \"list open issues\". Discovers and ranks tasks from GitHub, GitLab, local files, and custom sources.
9learn
Research any topic online and create learning guides. Use when user asks to 'learn about', 'research topic', 'create learning guide', 'build knowledge base', or 'study subject'.
9perf-benchmarker
Use when running performance benchmarks, establishing baselines, or validating regressions with sequential runs. Enforces 60s minimum runs (30s only for binary search) and no parallel benchmarks.
9web-browse
Browse and interact with web pages headlessly. Use when agent needs to navigate websites, click elements, fill forms, read content, or take screenshots.
9deslop
Use when user wants to clean AI slop from code. Use for cleanup, remove debug statements, find ghost code, repo hygiene.
8