$npx -y skills add evan043/claude-cli-advanced-starter-pack --skill hook-creatorCreate Claude Code CLI hooks following best practices - proper JSON schema, event types, matchers, error handling, and file organization
| 1 | # Hook Creator Skill |
| 2 | |
| 3 | Expert-level Claude Code hook creation following official Anthropic best practices. |
| 4 | |
| 5 | ## When to Use This Skill |
| 6 | |
| 7 | This skill is automatically invoked when: |
| 8 | |
| 9 | - Creating new hooks for Claude Code CLI |
| 10 | - Modifying existing hook configurations |
| 11 | - Writing hook scripts (JS, Python, PowerShell) |
| 12 | - Configuring hook matchers and event types |
| 13 | |
| 14 | ## Hook Architecture Overview |
| 15 | |
| 16 | ### Configuration Files (Priority Order) |
| 17 | |
| 18 | 1. `~/.claude/settings.json` - Global user settings (all projects) |
| 19 | 2. `.claude/settings.json` - Project settings (checked into git) |
| 20 | 3. `.claude/settings.local.json` - Local overrides (gitignored) |
| 21 | |
| 22 | ### Hook Directory Structure |
| 23 | |
| 24 | ``` |
| 25 | .claude/ |
| 26 | settings.local.json # Hook configurations (JSON) |
| 27 | hooks/ |
| 28 | README.md # Hook system documentation |
| 29 | lifecycle/ # SessionStart, SessionEnd, Stop hooks |
| 30 | README.md |
| 31 | session-start.js |
| 32 | tools/ # PreToolUse, PostToolUse hooks |
| 33 | README.md |
| 34 | validate-files.js |
| 35 | notifications/ # Notification hooks |
| 36 | README.md |
| 37 | alert-handler.js |
| 38 | security/ # Security-focused hooks |
| 39 | README.md |
| 40 | block-dangerous-ops.js |
| 41 | ``` |
| 42 | |
| 43 | ## Supported Hook Events |
| 44 | |
| 45 | | Event | Trigger | Can Block | Primary Use Cases | |
| 46 | |-------|---------|-----------|-------------------| |
| 47 | | `SessionStart` | Session begins | No | Setup, notifications, load context | |
| 48 | | `SessionEnd` | Session ends | No | Cleanup, save state | |
| 49 | | `Stop` | Claude stops responding | No | Notifications, state saving | |
| 50 | | `PreToolUse` | Before tool executes | **Yes** | Security gates, input validation | |
| 51 | | `PostToolUse` | After tool completes | No | Logging, validation, sync | |
| 52 | | `Notification` | Attention needed | No | Custom alerts, sounds | |
| 53 | | `UserPromptSubmit` | Before prompt processes | **Yes** | Input validation | |
| 54 | | `SubagentStop` | Subagent completes | No | Aggregate results | |
| 55 | | `PreCompact` | Before transcript compaction | No | Archive, cleanup | |
| 56 | | `PermissionRequest` | Permission dialog shown | No | Custom handling | |
| 57 | |
| 58 | ## JSON Configuration Schema |
| 59 | |
| 60 | ### Basic Hook Configuration |
| 61 | |
| 62 | ```json |
| 63 | { |
| 64 | "hooks": { |
| 65 | "EventType": [ |
| 66 | { |
| 67 | "matcher": "pattern", |
| 68 | "hooks": [ |
| 69 | { |
| 70 | "type": "command", |
| 71 | "command": "your-command-here", |
| 72 | "timeout": 60 |
| 73 | } |
| 74 | ] |
| 75 | } |
| 76 | ] |
| 77 | } |
| 78 | } |
| 79 | ``` |
| 80 | |
| 81 | ### Configuration Fields |
| 82 | |
| 83 | | Field | Required | Type | Description | |
| 84 | |-------|----------|------|-------------| |
| 85 | | `matcher` | No | string (regex) | Filter which tools/events trigger hook | |
| 86 | | `hooks` | Yes | array | List of hook commands to execute | |
| 87 | | `type` | Yes | `"command"` or `"prompt"` | Hook execution type | |
| 88 | | `command` | For type:command | string | Shell command to execute | |
| 89 | | `timeout` | No | number | Seconds before timeout (default: 60) | |
| 90 | |
| 91 | ### Matcher Patterns |
| 92 | |
| 93 | ```json |
| 94 | // Single tool |
| 95 | "matcher": "Write" |
| 96 | |
| 97 | // Multiple tools (regex OR) |
| 98 | "matcher": "Write|Edit|MultiEdit" |
| 99 | |
| 100 | // MCP namespace |
| 101 | "matcher": "mcp__browserbase__.*" |
| 102 | |
| 103 | // Specific MCP tool |
| 104 | "matcher": "mcp__browserbase__browserbase_stagehand_act" |
| 105 | |
| 106 | // No matcher = all tools |
| 107 | // Just omit the matcher field |
| 108 | ``` |
| 109 | |
| 110 | ## Hook Input/Output |
| 111 | |
| 112 | ### Environment Variables (Available to Hooks) |
| 113 | |
| 114 | | Variable | Description | |
| 115 | |----------|-------------| |
| 116 | | `CLAUDE_HOOK_INPUT` | JSON string of tool input | |
| 117 | | `CLAUDE_TOOL_NAME` | Name of tool being called | |
| 118 | | `CLAUDE_SESSION_ID` | Unique session identifier | |
| 119 | | `CLAUDE_PROJECT_PATH` | Path to project root | |
| 120 | | `CLAUDE_PERMISSION_MODE` | Current permission mode | |
| 121 | |
| 122 | ### Hook Output Format |
| 123 | |
| 124 | ```json |
| 125 | { |
| 126 | "decision": "approve", // "approve", "block", or "ask" |
| 127 | "reason": "Optional explanation", |
| 128 | "systemMessage": "Warning to show user", |
| 129 | "updatedInput": {} // Modified tool input |
| 130 | } |
| 131 | ``` |
| 132 | |
| 133 | ### Exit Codes |
| 134 | |
| 135 | | Code | Meaning | Behavior | |
| 136 | |------|---------|----------| |
| 137 | | 0 | Success | Allow action to proceed | |
| 138 | | 2 | Blocking error | Block action, show stderr to Claude | |
| 139 | | Other | Non-blocking error | Show stderr to user, allow action | |
| 140 | | Timeout | >60s (default) | Kill process, use default behavior | |
| 141 | |
| 142 | ## Hook Templates |
| 143 | |
| 144 | ### Template: JavaScript Tool Hook |
| 145 | |
| 146 | ```javascript |
| 147 | #!/usr/bin/env node |
| 148 | |
| 149 | /** |
| 150 | * Hook Name: [Description] |
| 151 | * Event: PreToolUse | PostToolUse |
| 152 | * Trigger: When [condition] |
| 153 | * Purpose: [What this hook accomplishes] |
| 154 | */ |
| 155 | |
| 156 | const fs = require('fs'); |
| 157 | const path = require('path'); |
| 158 | |
| 159 | // Parse hook input from environment |
| 160 | const hookInput = JSON.parse(process.env.CLAUDE_HOOK_INPUT || '{}'); |
| 161 | const toolName = process.env.CLAUDE_TOOL_NAME || ''; |
| 162 | const projectPath = process.env.CLAUDE_PROJECT_PATH || process.cwd(); |
| 163 | |
| 164 | async function main() { |
| 165 | try { |
| 166 | // Your validation/processing logic here |
| 167 | const shouldBlock = false; // Set based on your logic |
| 168 | |
| 169 | if (shouldBlock) { |
| 170 | console.error('Blocked: [reason]'); |
| 171 | process.exit(2) |