Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Guidance for developing lifecycle hooks for Claude Code plugins from the official Anthropic repository.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/migration.md
1# Migrating from Basic to Advanced Hooks23This guide shows how to migrate from basic command hooks to advanced prompt-based hooks for better maintainability and flexibility.45## Why Migrate?67Prompt-based hooks offer several advantages:89- **Natural language reasoning**: LLM understands context and intent10- **Better edge case handling**: Adapts to unexpected scenarios11- **No bash scripting required**: Simpler to write and maintain12- **More flexible validation**: Can handle complex logic without coding1314## Migration Example: Bash Command Validation1516### Before (Basic Command Hook)1718**Configuration:**19```json20{21"PreToolUse": [22{23"matcher": "Bash",24"hooks": [25{26"type": "command",27"command": "bash validate-bash.sh"28}29]30}31]32}33```3435**Script (validate-bash.sh):**36```bash37#!/bin/bash38input=$(cat)39command=$(echo "$input" | jq -r '.tool_input.command')4041# Hard-coded validation logic42if [[ "$command" == *"rm -rf"* ]]; then43echo "Dangerous command detected" >&244exit 245fi46```4748**Problems:**49- Only checks for exact "rm -rf" pattern50- Doesn't catch variations like `rm -fr` or `rm -r -f`51- Misses other dangerous commands (`dd`, `mkfs`, etc.)52- No context awareness53- Requires bash scripting knowledge5455### After (Advanced Prompt Hook)5657**Configuration:**58```json59{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": 1568}69]70}71]72}73```7475**Benefits:**76- Catches all variations and patterns77- Understands intent, not just literal strings78- No script file needed79- Easy to extend with new criteria80- Context-aware decisions81- Natural language explanation in denial8283## Migration Example: File Write Validation8485### Before (Basic Command Hook)8687**Configuration:**88```json89{90"PreToolUse": [91{92"matcher": "Write",93"hooks": [94{95"type": "command",96"command": "bash validate-write.sh"97}98]99}100]101}102```103104**Script (validate-write.sh):**105```bash106#!/bin/bash107input=$(cat)108file_path=$(echo "$input" | jq -r '.tool_input.file_path')109110# Check for path traversal111if [[ "$file_path" == *".."* ]]; then112echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2113exit 2114fi115116# Check for system paths117if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then118echo '{"decision": "deny", "reason": "System file"}' >&2119exit 2120fi121```122123**Problems:**124- Hard-coded path patterns125- Doesn't understand symlinks126- Missing edge cases (e.g., `/etc` vs `/etc/`)127- No consideration of file content128129### After (Advanced Prompt Hook)130131**Configuration:**132```json133{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```147148**Benefits:**149- Context-aware (considers content too)150- Handles symlinks and edge cases151- Natural understanding of "system directories"152- Can detect secrets in content153- Easy to extend criteria154155## When to Keep Command Hooks156157Command hooks still have their place:158159### 1. Deterministic Performance Checks160161```bash162#!/bin/bash163# Check file size quickly164file_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)166167if [ "$size" -gt 10000000 ]; then168echo '{"decision": "deny", "reason": "File too large"}' >&2169exit 2170fi171```172173**Use command hooks when:** Validation is purely mathematical or deterministic.174175### 2. External Tool Integration176177```bash178#!/bin/bash179# Run security scanner180file_path=$(echo "$input" | jq -r '.tool_input.file_path')181scan_result=$(security-scanner "$file_path")182183if [ "$?" -ne 0 ]; then184echo "Security scan failed: $scan_result" >&2185exit 2186fi187```188189**Use command hooks when:** Integrating with external tools that provide yes/no answers.190191### 3. Very Fast Checks (< 50ms)192193```bash194#!/bin/bash195# Quick regex check196command=$(echo "$input" | jq -r '.tool_input.command')197198if [[ "$command" =~ ^(ls|pwd|echo)$ ]]; then199exit 0 # Safe commands200fi201```202203**Use command hooks when:** Performance is critical and logic is simple.204205## Hybrid Approach206207Combine both for multi-stage validation:208209```json210{211"PreToolUse": [212{213"matcher": "Bash",214"hooks": [215{216"type": "command",217"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh",218"timeout": 5219},220{221"type": "prompt",222"prompt": "Deep analysis of bash command: $TOOL_INPUT",223"timeout": 15224}225]226}227]228}229```230231The command hook does fast deterministic checks, while the prompt hook handles complex reasoning.232233## Migration Checklist234235When migrating hooks:236237- [ ] Identify the validation logic in the command hook238- [ ] Convert hard-coded patterns to natural language criteria239- [ ] Test with edge cases the old hook missed240- [ ] Verify LLM understands the intent241- [ ] Set appropriate timeout (usually 15-30s for prompt hooks)242- [ ] Document the new hook in README243- [ ] Remove or archive old script files244245## Migration Tips2462471. **Start with one hook**: Don't migrate everything at once2482. **Test thoroughly**: Verify prompt hook catches what command hook caught2493. **Look for improvements**: Use migration as opportunity to enhance validation2504. **Keep scripts for reference**: Archive old scripts in case you need to reference the logic2515. **Document reasoning**: Explain why prompt hook is better in README252253## Complete Migration Example254255### Original Plugin Structure256257```258my-plugin/259├── .claude-plugin/plugin.json260├── hooks/hooks.json261└── scripts/262├── validate-bash.sh263├── validate-write.sh264└── check-tests.sh265```266267### After Migration268269```270my-plugin/271├── .claude-plugin/plugin.json272├── hooks/hooks.json # Now uses prompt hooks273└── scripts/ # Archive or delete274└── archive/275├── validate-bash.sh276├── validate-write.sh277└── check-tests.sh278```279280### Updated hooks.json281282```json283{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```317318**Result:** Simpler, more maintainable, more powerful.319320## Common Migration Patterns321322### Pattern: String Contains → Natural Language323324**Before:**325```bash326if [[ "$command" == *"sudo"* ]]; then327echo "Privilege escalation" >&2328exit 2329fi330```331332**After:**333```334"Check for privilege escalation (sudo, su, etc)"335```336337### Pattern: Regex → Intent338339**Before:**340```bash341if [[ "$file" =~ \.(env|secret|key|token)$ ]]; then342echo "Credential file" >&2343exit 2344fi345```346347**After:**348```349"Verify not writing to credential files (.env, secrets, keys, tokens)"350```351352### Pattern: Multiple Conditions → Criteria List353354**Before:**355```bash356if [ condition1 ] || [ condition2 ] || [ condition3 ]; then357echo "Invalid" >&2358exit 2359fi360```361362**After:**363```364"Check: 1) condition1 2) condition2 3) condition3. Deny if any fail."365```366367## Conclusion368369Migrating to prompt-based hooks makes plugins more maintainable, flexible, and powerful. Reserve command hooks for deterministic checks and external tool integration.370