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 / migration.md
369 lines · 8.1 KBPreviewCodeRaw
1# Migrating from Basic to Advanced Hooks
2
3This guide shows how to migrate from basic command hooks to advanced prompt-based hooks for better maintainability and flexibility.
4
5## Why Migrate?
6
7Prompt-based hooks offer several advantages:
8
9- **Natural language reasoning**: LLM understands context and intent
10- **Better edge case handling**: Adapts to unexpected scenarios
11- **No bash scripting required**: Simpler to write and maintain
12- **More flexible validation**: Can handle complex logic without coding
13
14## Migration Example: Bash Command Validation
15
16### Before (Basic Command Hook)
17
18**Configuration:**
19```json
20{
21 "PreToolUse": [
22 {
23 "matcher": "Bash",
24 "hooks": [
25 {
26 "type": "command",
27 "command": "bash validate-bash.sh"
28 }
29 ]
30 }
31 ]
32}
33```
34
35**Script (validate-bash.sh):**
36```bash
37#!/bin/bash
38input=$(cat)
39command=$(echo "$input" | jq -r '.tool_input.command')
40
41# Hard-coded validation logic
42if [[ "$command" == *"rm -rf"* ]]; then
43 echo "Dangerous command detected" >&2
44 exit 2
45fi
46```
47
48**Problems:**
49- Only checks for exact "rm -rf" pattern
50- Doesn't catch variations like `rm -fr` or `rm -r -f`
51- Misses other dangerous commands (`dd`, `mkfs`, etc.)
52- No context awareness
53- Requires bash scripting knowledge
54
55### After (Advanced Prompt Hook)
56
57**Configuration:**
58```json
59{
60 "PreToolUse": [
61 {
62 "matcher": "Bash",
63 "hooks": [
64 {
65 "type": "prompt",
66 "prompt": "Command: $TOOL_INPUT.command. Analyze for: 1) Destructive operations (rm -rf, dd, mkfs, etc) 2) Privilege escalation (sudo) 3) Network operations without user consent. Return 'approve' or 'deny' with explanation.",
67 "timeout": 15
68 }
69 ]
70 }
71 ]
72}
73```
74
75**Benefits:**
76- Catches all variations and patterns
77- Understands intent, not just literal strings
78- No script file needed
79- Easy to extend with new criteria
80- Context-aware decisions
81- Natural language explanation in denial
82
83## Migration Example: File Write Validation
84
85### Before (Basic Command Hook)
86
87**Configuration:**
88```json
89{
90 "PreToolUse": [
91 {
92 "matcher": "Write",
93 "hooks": [
94 {
95 "type": "command",
96 "command": "bash validate-write.sh"
97 }
98 ]
99 }
100 ]
101}
102```
103
104**Script (validate-write.sh):**
105```bash
106#!/bin/bash
107input=$(cat)
108file_path=$(echo "$input" | jq -r '.tool_input.file_path')
109
110# Check for path traversal
111if [[ "$file_path" == *".."* ]]; then
112 echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
113 exit 2
114fi
115
116# Check for system paths
117if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then
118 echo '{"decision": "deny", "reason": "System file"}' >&2
119 exit 2
120fi
121```
122
123**Problems:**
124- Hard-coded path patterns
125- Doesn't understand symlinks
126- Missing edge cases (e.g., `/etc` vs `/etc/`)
127- No consideration of file content
128
129### After (Advanced Prompt Hook)
130
131**Configuration:**
132```json
133{
134 "PreToolUse": [
135 {
136 "matcher": "Write|Edit",
137 "hooks": [
138 {
139 "type": "prompt",
140 "prompt": "File path: $TOOL_INPUT.file_path. Content preview: $TOOL_INPUT.content (first 200 chars). Verify: 1) Not system directories (/etc, /sys, /usr) 2) Not credentials (.env, tokens, secrets) 3) No path traversal 4) Content doesn't expose secrets. Return 'approve' or 'deny'."
141 }
142 ]
143 }
144 ]
145}
146```
147
148**Benefits:**
149- Context-aware (considers content too)
150- Handles symlinks and edge cases
151- Natural understanding of "system directories"
152- Can detect secrets in content
153- Easy to extend criteria
154
155## When to Keep Command Hooks
156
157Command hooks still have their place:
158
159### 1. Deterministic Performance Checks
160
161```bash
162#!/bin/bash
163# Check file size quickly
164file_path=$(echo "$input" | jq -r '.tool_input.file_path')
165size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null)
166
167if [ "$size" -gt 10000000 ]; then
168 echo '{"decision": "deny", "reason": "File too large"}' >&2
169 exit 2
170fi
171```
172
173**Use command hooks when:** Validation is purely mathematical or deterministic.
174
175### 2. External Tool Integration
176
177```bash
178#!/bin/bash
179# Run security scanner
180file_path=$(echo "$input" | jq -r '.tool_input.file_path')
181scan_result=$(security-scanner "$file_path")
182
183if [ "$?" -ne 0 ]; then
184 echo "Security scan failed: $scan_result" >&2
185 exit 2
186fi
187```
188
189**Use command hooks when:** Integrating with external tools that provide yes/no answers.
190
191### 3. Very Fast Checks (< 50ms)
192
193```bash
194#!/bin/bash
195# Quick regex check
196command=$(echo "$input" | jq -r '.tool_input.command')
197
198if [[ "$command" =~ ^(ls|pwd|echo)$ ]]; then
199 exit 0 # Safe commands
200fi
201```
202
203**Use command hooks when:** Performance is critical and logic is simple.
204
205## Hybrid Approach
206
207Combine both for multi-stage validation:
208
209```json
210{
211 "PreToolUse": [
212 {
213 "matcher": "Bash",
214 "hooks": [
215 {
216 "type": "command",
217 "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh",
218 "timeout": 5
219 },
220 {
221 "type": "prompt",
222 "prompt": "Deep analysis of bash command: $TOOL_INPUT",
223 "timeout": 15
224 }
225 ]
226 }
227 ]
228}
229```
230
231The command hook does fast deterministic checks, while the prompt hook handles complex reasoning.
232
233## Migration Checklist
234
235When migrating hooks:
236
237- [ ] Identify the validation logic in the command hook
238- [ ] Convert hard-coded patterns to natural language criteria
239- [ ] Test with edge cases the old hook missed
240- [ ] Verify LLM understands the intent
241- [ ] Set appropriate timeout (usually 15-30s for prompt hooks)
242- [ ] Document the new hook in README
243- [ ] Remove or archive old script files
244
245## Migration Tips
246
2471. **Start with one hook**: Don't migrate everything at once
2482. **Test thoroughly**: Verify prompt hook catches what command hook caught
2493. **Look for improvements**: Use migration as opportunity to enhance validation
2504. **Keep scripts for reference**: Archive old scripts in case you need to reference the logic
2515. **Document reasoning**: Explain why prompt hook is better in README
252
253## Complete Migration Example
254
255### Original Plugin Structure
256
257```
258my-plugin/
259├── .claude-plugin/plugin.json
260├── hooks/hooks.json
261└── scripts/
262 ├── validate-bash.sh
263 ├── validate-write.sh
264 └── check-tests.sh
265```
266
267### After Migration
268
269```
270my-plugin/
271├── .claude-plugin/plugin.json
272├── hooks/hooks.json # Now uses prompt hooks
273└── scripts/ # Archive or delete
274 └── archive/
275 ├── validate-bash.sh
276 ├── validate-write.sh
277 └── check-tests.sh
278```
279
280### Updated hooks.json
281
282```json
283{
284 "PreToolUse": [
285 {
286 "matcher": "Bash",
287 "hooks": [
288 {
289 "type": "prompt",
290 "prompt": "Validate bash command safety: destructive ops, privilege escalation, network access"
291 }
292 ]
293 },
294 {
295 "matcher": "Write|Edit",
296 "hooks": [
297 {
298 "type": "prompt",
299 "prompt": "Validate file write safety: system paths, credentials, path traversal, content secrets"
300 }
301 ]
302 }
303 ],
304 "Stop": [
305 {
306 "matcher": "*",
307 "hooks": [
308 {
309 "type": "prompt",
310 "prompt": "Verify tests were run if code was modified"
311 }
312 ]
313 }
314 ]
315}
316```
317
318**Result:** Simpler, more maintainable, more powerful.
319
320## Common Migration Patterns
321
322### Pattern: String Contains → Natural Language
323
324**Before:**
325```bash
326if [[ "$command" == *"sudo"* ]]; then
327 echo "Privilege escalation" >&2
328 exit 2
329fi
330```
331
332**After:**
333```
334"Check for privilege escalation (sudo, su, etc)"
335```
336
337### Pattern: Regex → Intent
338
339**Before:**
340```bash
341if [[ "$file" =~ \.(env|secret|key|token)$ ]]; then
342 echo "Credential file" >&2
343 exit 2
344fi
345```
346
347**After:**
348```
349"Verify not writing to credential files (.env, secrets, keys, tokens)"
350```
351
352### Pattern: Multiple Conditions → Criteria List
353
354**Before:**
355```bash
356if [ condition1 ] || [ condition2 ] || [ condition3 ]; then
357 echo "Invalid" >&2
358 exit 2
359fi
360```
361
362**After:**
363```
364"Check: 1) condition1 2) condition2 3) condition3. Deny if any fail."
365```
366
367## Conclusion
368
369Migrating to prompt-based hooks makes plugins more maintainable, flexible, and powerful. Reserve command hooks for deterministic checks and external tool integration.