surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| 1 | # Common Hook Patterns |
| 2 | |
| 3 | This reference provides common, proven patterns for implementing Claude Code hooks. Use these patterns as starting points for typical hook use cases. |
| 4 | |
| 5 | ## Pattern 1: Security Validation |
| 6 | |
| 7 | Block dangerous file writes using prompt-based hooks: |
| 8 | |
| 9 | ```json |
| 10 | { |
| 11 | "PreToolUse": [ |
| 12 | { |
| 13 | "matcher": "Write|Edit", |
| 14 | "hooks": [ |
| 15 | { |
| 16 | "type": "prompt", |
| 17 | "prompt": "File path: $TOOL_INPUT.file_path. Verify: 1) Not in /etc or system directories 2) Not .env or credentials 3) Path doesn't contain '..' traversal. Return 'approve' or 'deny'." |
| 18 | } |
| 19 | ] |
| 20 | } |
| 21 | ] |
| 22 | } |
| 23 | ``` |
| 24 | |
| 25 | **Use for:** Preventing writes to sensitive files or system directories. |
| 26 | |
| 27 | ## Pattern 2: Test Enforcement |
| 28 | |
| 29 | Ensure tests run before stopping: |
| 30 | |
| 31 | ```json |
| 32 | { |
| 33 | "Stop": [ |
| 34 | { |
| 35 | "matcher": "*", |
| 36 | "hooks": [ |
| 37 | { |
| 38 | "type": "prompt", |
| 39 | "prompt": "Review transcript. If code was modified (Write/Edit tools used), verify tests were executed. If no tests were run, block with reason 'Tests must be run after code changes'." |
| 40 | } |
| 41 | ] |
| 42 | } |
| 43 | ] |
| 44 | } |
| 45 | ``` |
| 46 | |
| 47 | **Use for:** Enforcing quality standards and preventing incomplete work. |
| 48 | |
| 49 | ## Pattern 3: Context Loading |
| 50 | |
| 51 | Load project-specific context at session start: |
| 52 | |
| 53 | ```json |
| 54 | { |
| 55 | "SessionStart": [ |
| 56 | { |
| 57 | "matcher": "*", |
| 58 | "hooks": [ |
| 59 | { |
| 60 | "type": "command", |
| 61 | "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh" |
| 62 | } |
| 63 | ] |
| 64 | } |
| 65 | ] |
| 66 | } |
| 67 | ``` |
| 68 | |
| 69 | **Example script (load-context.sh):** |
| 70 | ```bash |
| 71 | #!/bin/bash |
| 72 | cd "$CLAUDE_PROJECT_DIR" || exit 1 |
| 73 | |
| 74 | # Detect project type |
| 75 | if [ -f "package.json" ]; then |
| 76 | echo "📦 Node.js project detected" |
| 77 | echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE" |
| 78 | elif [ -f "Cargo.toml" ]; then |
| 79 | echo "🦀 Rust project detected" |
| 80 | echo "export PROJECT_TYPE=rust" >> "$CLAUDE_ENV_FILE" |
| 81 | fi |
| 82 | ``` |
| 83 | |
| 84 | **Use for:** Automatically detecting and configuring project-specific settings. |
| 85 | |
| 86 | ## Pattern 4: Notification Logging |
| 87 | |
| 88 | Log all notifications for audit or analysis: |
| 89 | |
| 90 | ```json |
| 91 | { |
| 92 | "Notification": [ |
| 93 | { |
| 94 | "matcher": "*", |
| 95 | "hooks": [ |
| 96 | { |
| 97 | "type": "command", |
| 98 | "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/log-notification.sh" |
| 99 | } |
| 100 | ] |
| 101 | } |
| 102 | ] |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | **Use for:** Tracking user notifications or integration with external logging systems. |
| 107 | |
| 108 | ## Pattern 5: MCP Tool Monitoring |
| 109 | |
| 110 | Monitor and validate MCP tool usage: |
| 111 | |
| 112 | ```json |
| 113 | { |
| 114 | "PreToolUse": [ |
| 115 | { |
| 116 | "matcher": "mcp__.*__delete.*", |
| 117 | "hooks": [ |
| 118 | { |
| 119 | "type": "prompt", |
| 120 | "prompt": "Deletion operation detected. Verify: Is this deletion intentional? Can it be undone? Are there backups? Return 'approve' only if safe." |
| 121 | } |
| 122 | ] |
| 123 | } |
| 124 | ] |
| 125 | } |
| 126 | ``` |
| 127 | |
| 128 | **Use for:** Protecting against destructive MCP operations. |
| 129 | |
| 130 | ## Pattern 6: Build Verification |
| 131 | |
| 132 | Ensure project builds after code changes: |
| 133 | |
| 134 | ```json |
| 135 | { |
| 136 | "Stop": [ |
| 137 | { |
| 138 | "matcher": "*", |
| 139 | "hooks": [ |
| 140 | { |
| 141 | "type": "prompt", |
| 142 | "prompt": "Check if code was modified. If Write/Edit tools were used, verify the project was built (npm run build, cargo build, etc). If not built, block and request build." |
| 143 | } |
| 144 | ] |
| 145 | } |
| 146 | ] |
| 147 | } |
| 148 | ``` |
| 149 | |
| 150 | **Use for:** Catching build errors before committing or stopping work. |
| 151 | |
| 152 | ## Pattern 7: Permission Confirmation |
| 153 | |
| 154 | Ask user before dangerous operations: |
| 155 | |
| 156 | ```json |
| 157 | { |
| 158 | "PreToolUse": [ |
| 159 | { |
| 160 | "matcher": "Bash", |
| 161 | "hooks": [ |
| 162 | { |
| 163 | "type": "prompt", |
| 164 | "prompt": "Command: $TOOL_INPUT.command. If command contains 'rm', 'delete', 'drop', or other destructive operations, return 'ask' to confirm with user. Otherwise 'approve'." |
| 165 | } |
| 166 | ] |
| 167 | } |
| 168 | ] |
| 169 | } |
| 170 | ``` |
| 171 | |
| 172 | **Use for:** User confirmation on potentially destructive commands. |
| 173 | |
| 174 | ## Pattern 8: Code Quality Checks |
| 175 | |
| 176 | Run linters or formatters on file edits: |
| 177 | |
| 178 | ```json |
| 179 | { |
| 180 | "PostToolUse": [ |
| 181 | { |
| 182 | "matcher": "Write|Edit", |
| 183 | "hooks": [ |
| 184 | { |
| 185 | "type": "command", |
| 186 | "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/check-quality.sh" |
| 187 | } |
| 188 | ] |
| 189 | } |
| 190 | ] |
| 191 | } |
| 192 | ``` |
| 193 | |
| 194 | **Example script (check-quality.sh):** |
| 195 | ```bash |
| 196 | #!/bin/bash |
| 197 | input=$(cat) |
| 198 | file_path=$(echo "$input" | jq -r '.tool_input.file_path') |
| 199 | |
| 200 | # Run linter if applicable |
| 201 | if [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then |
| 202 | npx eslint "$file_path" 2>&1 || true |
| 203 | fi |
| 204 | ``` |
| 205 | |
| 206 | **Use for:** Automatic code quality enforcement. |
| 207 | |
| 208 | ## Pattern Combinations |
| 209 | |
| 210 | Combine multiple patterns for comprehensive protection: |
| 211 | |
| 212 | ```json |
| 213 | { |
| 214 | "PreToolUse": [ |
| 215 | { |
| 216 | "matcher": "Write|Edit", |
| 217 | "hooks": [ |
| 218 | { |
| 219 | "type": "prompt", |
| 220 | "prompt": "Validate file write safety" |
| 221 | } |
| 222 | ] |
| 223 | }, |
| 224 | { |
| 225 | "matcher": "Bash", |
| 226 | "hooks": [ |
| 227 | { |
| 228 | "type": "prompt", |
| 229 | "prompt": "Validate bash command safety" |
| 230 | } |
| 231 | ] |
| 232 | } |
| 233 | ], |
| 234 | "Stop": [ |
| 235 | { |
| 236 | "matcher": "*", |
| 237 | "hooks": [ |
| 238 | { |
| 239 | "type": "prompt", |
| 240 | "prompt": "Verify tests run and build succeeded" |
| 241 | } |
| 242 | ] |
| 243 | } |
| 244 | ], |
| 245 | "SessionStart": [ |
| 246 | { |
| 247 | "matcher": "*", |
| 248 | "hooks": [ |
| 249 | { |
| 250 | "type": "command", |
| 251 | "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh" |
| 252 | } |
| 253 | ] |
| 254 | } |
| 255 | ] |
| 256 | } |
| 257 | ``` |
| 258 | |
| 259 | This provides multi-layered protection and automation. |
| 260 | |
| 261 | ## Pattern 9: Temporarily Active Hooks |
| 262 | |
| 263 | Create hooks that only run when explicitly enabled via flag files: |
| 264 | |
| 265 | ```bash |
| 266 | #!/bin/bash |
| 267 | # Hook only active when flag file exists |
| 268 | FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-security-scan" |
| 269 | |
| 270 | if [ ! -f "$FLAG_FILE" ]; then |
| 271 | # Quick exit when disabled |
| 272 | exit 0 |
| 273 | fi |
| 274 | |
| 275 | # Flag present, run validation |
| 276 | input=$(cat) |
| 277 | file_path=$(echo "$input" | jq -r '.tool_input.file_path') |
| 278 | |
| 279 | # Run security scan |
| 280 | security-scanner "$file_path" |
| 281 | ``` |
| 282 | |
| 283 | **Activation:** |
| 284 | ```bash |
| 285 | # Enable the hook |
| 286 | touch .enable-security-scan |
| 287 | |
| 288 | # Disable the hook |
| 289 | rm .enable-security-scan |
| 290 | ``` |
| 291 | |
| 292 | **Use for:** |
| 293 | - Temporary debugging hooks |
| 294 | - Feature flags for development |
| 295 | - Project-specific validation that's opt-in |
| 296 | - Performance-intensive checks only when needed |
| 297 | |
| 298 | **Note:** Must restart Claude Code after creating/removing flag files for hooks to recognize changes. |
| 299 | |
| 300 | ## Pattern 10: Configuration-Driven Hooks |
| 301 | |
| 302 | Use JSON configuration to control hook behavior: |
| 303 | |
| 304 | ```bash |
| 305 | #!/bin/bash |
| 306 | CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/my-plugin.local.json" |
| 307 | |
| 308 | # Read configuration |
| 309 | if [ -f "$CONFIG_FILE" ]; then |
| 310 | strict_mode=$(jq -r '.strictMode // false' "$CONFIG_FILE") |
| 311 | max_file_size=$(jq -r '.maxFileSize // 1000000' "$CONFIG_FILE") |
| 312 | else |
| 313 | # Defaults |
| 314 | strict_mode=false |
| 315 | max_file_size=1000000 |
| 316 | fi |
| 317 | |
| 318 | # Skip if not in strict mode |
| 319 | if [ "$strict_mode" != "true" ]; then |
| 320 | exit 0 |
| 321 | fi |
| 322 | |
| 323 | # Apply configured limits |
| 324 | input=$(cat) |
| 325 | file_size=$(echo "$input" | jq -r '.tool_input.content | length') |
| 326 | |
| 327 | if [ "$file_size" -gt "$max_file_size" ]; then |
| 328 | echo '{"decision": "deny", "reason": "File exceeds configured size limit"}' >&2 |
| 329 | exit 2 |
| 330 | fi |
| 331 | ``` |
| 332 | |
| 333 | **Configuration file (.claude/my-plugin.local.json):** |
| 334 | ```json |
| 335 | { |
| 336 | "strictMode": true, |
| 337 | "maxFileSize": 500000, |
| 338 | "allowedPaths": ["/tmp", "/home/user/projects"] |
| 339 | } |
| 340 | ``` |
| 341 | |
| 342 | **Use for:** |
| 343 | - User-configurable hook behavior |
| 344 | - Per-project settings |
| 345 | - Team-specific rules |
| 346 | - Dynamic validation criteria |