spacehub

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

claude-code / plugins / plugin-dev / skills / hook-development / references / patterns.md
346 lines · 7.0 KBPreviewCodeRaw
1# Common Hook Patterns
2
3This 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
7Block 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
29Ensure 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
51Load 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
72cd "$CLAUDE_PROJECT_DIR" || exit 1
73
74# Detect project type
75if [ -f "package.json" ]; then
76 echo "📦 Node.js project detected"
77 echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
78elif [ -f "Cargo.toml" ]; then
79 echo "🦀 Rust project detected"
80 echo "export PROJECT_TYPE=rust" >> "$CLAUDE_ENV_FILE"
81fi
82```
83
84**Use for:** Automatically detecting and configuring project-specific settings.
85
86## Pattern 4: Notification Logging
87
88Log 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
110Monitor 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
132Ensure 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
154Ask 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
176Run 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
197input=$(cat)
198file_path=$(echo "$input" | jq -r '.tool_input.file_path')
199
200# Run linter if applicable
201if [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then
202 npx eslint "$file_path" 2>&1 || true
203fi
204```
205
206**Use for:** Automatic code quality enforcement.
207
208## Pattern Combinations
209
210Combine 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
259This provides multi-layered protection and automation.
260
261## Pattern 9: Temporarily Active Hooks
262
263Create hooks that only run when explicitly enabled via flag files:
264
265```bash
266#!/bin/bash
267# Hook only active when flag file exists
268FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-security-scan"
269
270if [ ! -f "$FLAG_FILE" ]; then
271 # Quick exit when disabled
272 exit 0
273fi
274
275# Flag present, run validation
276input=$(cat)
277file_path=$(echo "$input" | jq -r '.tool_input.file_path')
278
279# Run security scan
280security-scanner "$file_path"
281```
282
283**Activation:**
284```bash
285# Enable the hook
286touch .enable-security-scan
287
288# Disable the hook
289rm .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
302Use JSON configuration to control hook behavior:
303
304```bash
305#!/bin/bash
306CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/my-plugin.local.json"
307
308# Read configuration
309if [ -f "$CONFIG_FILE" ]; then
310 strict_mode=$(jq -r '.strictMode // false' "$CONFIG_FILE")
311 max_file_size=$(jq -r '.maxFileSize // 1000000' "$CONFIG_FILE")
312else
313 # Defaults
314 strict_mode=false
315 max_file_size=1000000
316fi
317
318# Skip if not in strict mode
319if [ "$strict_mode" != "true" ]; then
320 exit 0
321fi
322
323# Apply configured limits
324input=$(cat)
325file_size=$(echo "$input" | jq -r '.tool_input.content | length')
326
327if [ "$file_size" -gt "$max_file_size" ]; then
328 echo '{"decision": "deny", "reason": "File exceeds configured size limit"}' >&2
329 exit 2
330fi
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