Loading...
Loading...
Creates and configures Claude Code hooks for lifecycle automation. Covers all 17 hook events, 4 hook types (command, prompt, agent, http), matchers, input/output formats, and exit codes. Follows official Anthropic best practices. USE WHEN: user mentions "hook", "hooks", "auto-format", "pre tool use", "post tool use", "session start", "notification hook", "block command", "validate tool", "lifecycle event", "PostToolUse", "PreToolUse" DO NOT USE FOR: creating skills - use `skill-authoring`; creating agents - use `agent-authoring`; webhook endpoints - different concept
npx skill4agent add claude-dev-suite/claude-dev-suite hook-authoring| Location | Scope | Shareable |
|---|---|---|
| All projects | No |
| Single project | Yes (commit) |
| Single project | No (gitignored) |
| Agent/skill frontmatter | While component active | Yes |
Plugin | When plugin enabled | Yes |
| Type | How it works | Use when |
|---|---|---|
| Runs shell command, reads stdin JSON, uses exit codes | Deterministic validation, formatting, logging |
| Single-turn LLM call, returns | Judgment-based decisions without tool access |
| Multi-turn subagent with tool access | Verification requiring file reads or commands |
| POSTs event data to URL endpoint | External service integration, audit logging |
| Event | Matcher input | Can block? | Common use |
|---|---|---|---|
| startup/resume/clear/compact | No | Re-inject context after compaction |
| (none) | Yes | Validate/transform user input |
| Tool name | Yes | Block commands, validate operations |
| Tool name | Yes | Auto-allow/deny permissions |
| Tool name | No* | Auto-format files, logging |
| Tool name | No | Error handling |
| Notification type | No | Desktop alerts |
| Agent type | No | Setup before agent runs |
| Agent type | No | Cleanup after agent |
| (none) | Yes | Verify completeness |
| Config source | Yes | Audit, block unauthorized changes |
| manual/auto | No | Save context before compaction |
| Exit reason | No | Cleanup |
Stop{"decision": "block"}{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
"timeout": 30
}
]
}
]
}
}{
"session_id": "abc123",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" }
}| Exit code | Effect |
|---|---|
| Allow — action proceeds. Stdout added to context (SessionStart, UserPromptSubmit) |
| Block — action cancelled. Stderr sent to Claude as feedback |
| Other | Allow — stderr logged (visible in verbose mode Ctrl+O) |
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep"
}
}"allow""deny""ask"{
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}]
}#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "package-lock.json" ".git/"; do
if [[ "$FILE" == *"$pattern"* ]]; then
echo "Blocked: matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0{
"SessionStart": [{
"matcher": "compact",
"hooks": [{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run tests before commits.'" }]
}]
}{
"Notification": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"'" }]
}]
}stop_hook_activeINPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Let Claude stop
fi
# ... your logic| Anti-Pattern | Fix |
|---|---|
Shell profile | Wrap in |
| Stop hook without loop guard | Check |
| Using PostToolUse to undo actions | Too late — use PreToolUse to block instead |
| Relying on PermissionRequest in headless mode | Doesn't fire in |
chmod +xjqstop_hook_active$CLAUDE_PROJECT_DIR