|
Download plugins/plugin-dev/skills/hook-development/references/migration.md from SaylorTwift/claude-code: direct link, hf CLI and curl.
- Browser
- Download file 8.3 kB
-
https://huggingface.co/SaylorTwift/claude-code/resolve/main/plugins/plugin-dev/skills/hook-development/references/migration.md
- Command line
-
hf download hf://SaylorTwift/claude-code/plugins/plugin-dev/skills/hook-development/references/migration.md
-
curl -L -o migration.md https://huggingface.co/SaylorTwift/claude-code/resolve/main/plugins/plugin-dev/skills/hook-development/references/migration.md
8.3 kB
| # Migrating from Basic to Advanced Hooks | |
| This guide shows how to migrate from basic command hooks to advanced prompt-based hooks for better maintainability and flexibility. | |
| ## Why Migrate? | |
| Prompt-based hooks offer several advantages: | |
| - **Natural language reasoning**: LLM understands context and intent | |
| - **Better edge case handling**: Adapts to unexpected scenarios | |
| - **No bash scripting required**: Simpler to write and maintain | |
| - **More flexible validation**: Can handle complex logic without coding | |
| ## Migration Example: Bash Command Validation | |
| ### Before (Basic Command Hook) | |
| **Configuration:** | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Bash", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash validate-bash.sh" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| **Script (validate-bash.sh):** | |
| ```bash | |
| #!/bin/bash | |
| input=$(cat) | |
| command=$(echo "$input" | jq -r '.tool_input.command') | |
| # Hard-coded validation logic | |
| if [[ "$command" == *"rm -rf"* ]]; then | |
| echo "Dangerous command detected" >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **Problems:** | |
| - Only checks for exact "rm -rf" pattern | |
| - Doesn't catch variations like `rm -fr` or `rm -r -f` | |
| - Misses other dangerous commands (`dd`, `mkfs`, etc.) | |
| - No context awareness | |
| - Requires bash scripting knowledge | |
| ### After (Advanced Prompt Hook) | |
| **Configuration:** | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Bash", | |
| "hooks": [ | |
| { | |
| "type": "prompt", | |
| "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.", | |
| "timeout": 15 | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| **Benefits:** | |
| - Catches all variations and patterns | |
| - Understands intent, not just literal strings | |
| - No script file needed | |
| - Easy to extend with new criteria | |
| - Context-aware decisions | |
| - Natural language explanation in denial | |
| ## Migration Example: File Write Validation | |
| ### Before (Basic Command Hook) | |
| **Configuration:** | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Write", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash validate-write.sh" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| **Script (validate-write.sh):** | |
| ```bash | |
| #!/bin/bash | |
| input=$(cat) | |
| file_path=$(echo "$input" | jq -r '.tool_input.file_path') | |
| # Check for path traversal | |
| if [[ "$file_path" == *".."* ]]; then | |
| echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2 | |
| exit 2 | |
| fi | |
| # Check for system paths | |
| if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then | |
| echo '{"decision": "deny", "reason": "System file"}' >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **Problems:** | |
| - Hard-coded path patterns | |
| - Doesn't understand symlinks | |
| - Missing edge cases (e.g., `/etc` vs `/etc/`) | |
| - No consideration of file content | |
| ### After (Advanced Prompt Hook) | |
| **Configuration:** | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Write|Edit", | |
| "hooks": [ | |
| { | |
| "type": "prompt", | |
| "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'." | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| **Benefits:** | |
| - Context-aware (considers content too) | |
| - Handles symlinks and edge cases | |
| - Natural understanding of "system directories" | |
| - Can detect secrets in content | |
| - Easy to extend criteria | |
| ## When to Keep Command Hooks | |
| Command hooks still have their place: | |
| ### 1. Deterministic Performance Checks | |
| ```bash | |
| #!/bin/bash | |
| # Check file size quickly | |
| file_path=$(echo "$input" | jq -r '.tool_input.file_path') | |
| size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null) | |
| if [ "$size" -gt 10000000 ]; then | |
| echo '{"decision": "deny", "reason": "File too large"}' >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **Use command hooks when:** Validation is purely mathematical or deterministic. | |
| ### 2. External Tool Integration | |
| ```bash | |
| #!/bin/bash | |
| # Run security scanner | |
| file_path=$(echo "$input" | jq -r '.tool_input.file_path') | |
| scan_result=$(security-scanner "$file_path") | |
| if [ "$?" -ne 0 ]; then | |
| echo "Security scan failed: $scan_result" >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **Use command hooks when:** Integrating with external tools that provide yes/no answers. | |
| ### 3. Very Fast Checks (< 50ms) | |
| ```bash | |
| #!/bin/bash | |
| # Quick regex check | |
| command=$(echo "$input" | jq -r '.tool_input.command') | |
| if [[ "$command" =~ ^(ls|pwd|echo)$ ]]; then | |
| exit 0 # Safe commands | |
| fi | |
| ``` | |
| **Use command hooks when:** Performance is critical and logic is simple. | |
| ## Hybrid Approach | |
| Combine both for multi-stage validation: | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Bash", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh", | |
| "timeout": 5 | |
| }, | |
| { | |
| "type": "prompt", | |
| "prompt": "Deep analysis of bash command: $TOOL_INPUT", | |
| "timeout": 15 | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| The command hook does fast deterministic checks, while the prompt hook handles complex reasoning. | |
| ## Migration Checklist | |
| When migrating hooks: | |
| - [ ] Identify the validation logic in the command hook | |
| - [ ] Convert hard-coded patterns to natural language criteria | |
| - [ ] Test with edge cases the old hook missed | |
| - [ ] Verify LLM understands the intent | |
| - [ ] Set appropriate timeout (usually 15-30s for prompt hooks) | |
| - [ ] Document the new hook in README | |
| - [ ] Remove or archive old script files | |
| ## Migration Tips | |
| 1. **Start with one hook**: Don't migrate everything at once | |
| 2. **Test thoroughly**: Verify prompt hook catches what command hook caught | |
| 3. **Look for improvements**: Use migration as opportunity to enhance validation | |
| 4. **Keep scripts for reference**: Archive old scripts in case you need to reference the logic | |
| 5. **Document reasoning**: Explain why prompt hook is better in README | |
| ## Complete Migration Example | |
| ### Original Plugin Structure | |
| ``` | |
| my-plugin/ | |
| βββ .claude-plugin/plugin.json | |
| βββ hooks/hooks.json | |
| βββ scripts/ | |
| βββ validate-bash.sh | |
| βββ validate-write.sh | |
| βββ check-tests.sh | |
| ``` | |
| ### After Migration | |
| ``` | |
| my-plugin/ | |
| βββ .claude-plugin/plugin.json | |
| βββ hooks/hooks.json # Now uses prompt hooks | |
| βββ scripts/ # Archive or delete | |
| βββ archive/ | |
| βββ validate-bash.sh | |
| βββ validate-write.sh | |
| βββ check-tests.sh | |
| ``` | |
| ### Updated hooks.json | |
| ```json | |
| { | |
| "PreToolUse": [ | |
| { | |
| "matcher": "Bash", | |
| "hooks": [ | |
| { | |
| "type": "prompt", | |
| "prompt": "Validate bash command safety: destructive ops, privilege escalation, network access" | |
| } | |
| ] | |
| }, | |
| { | |
| "matcher": "Write|Edit", | |
| "hooks": [ | |
| { | |
| "type": "prompt", | |
| "prompt": "Validate file write safety: system paths, credentials, path traversal, content secrets" | |
| } | |
| ] | |
| } | |
| ], | |
| "Stop": [ | |
| { | |
| "matcher": "*", | |
| "hooks": [ | |
| { | |
| "type": "prompt", | |
| "prompt": "Verify tests were run if code was modified" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ``` | |
| **Result:** Simpler, more maintainable, more powerful. | |
| ## Common Migration Patterns | |
| ### Pattern: String Contains β Natural Language | |
| **Before:** | |
| ```bash | |
| if [[ "$command" == *"sudo"* ]]; then | |
| echo "Privilege escalation" >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **After:** | |
| ``` | |
| "Check for privilege escalation (sudo, su, etc)" | |
| ``` | |
| ### Pattern: Regex β Intent | |
| **Before:** | |
| ```bash | |
| if [[ "$file" =~ \.(env|secret|key|token)$ ]]; then | |
| echo "Credential file" >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **After:** | |
| ``` | |
| "Verify not writing to credential files (.env, secrets, keys, tokens)" | |
| ``` | |
| ### Pattern: Multiple Conditions β Criteria List | |
| **Before:** | |
| ```bash | |
| if [ condition1 ] || [ condition2 ] || [ condition3 ]; then | |
| echo "Invalid" >&2 | |
| exit 2 | |
| fi | |
| ``` | |
| **After:** | |
| ``` | |
| "Check: 1) condition1 2) condition2 3) condition3. Deny if any fail." | |
| ``` | |
| ## Conclusion | |
| Migrating to prompt-based hooks makes plugins more maintainable, flexible, and powerful. Reserve command hooks for deterministic checks and external tool integration. | |