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/advanced.md
1# Advanced Hook Use Cases23This reference covers advanced hook patterns and techniques for sophisticated automation workflows.45## Multi-Stage Validation67Combine command and prompt hooks for layered validation:89```json10{11"PreToolUse": [12{13"matcher": "Bash",14"hooks": [15{16"type": "command",17"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh",18"timeout": 519},20{21"type": "prompt",22"prompt": "Deep analysis of bash command: $TOOL_INPUT",23"timeout": 1524}25]26}27]28}29```3031**Use case:** Fast deterministic checks followed by intelligent analysis3233**Example quick-check.sh:**34```bash35#!/bin/bash36input=$(cat)37command=$(echo "$input" | jq -r '.tool_input.command')3839# Immediate approval for safe commands40if [[ "$command" =~ ^(ls|pwd|echo|date|whoami)$ ]]; then41exit 042fi4344# Let prompt hook handle complex cases45exit 046```4748The command hook quickly approves obviously safe commands, while the prompt hook analyzes everything else.4950## Conditional Hook Execution5152Execute hooks based on environment or context:5354```bash55#!/bin/bash56# Only run in CI environment57if [ -z "$CI" ]; then58echo '{"continue": true}' # Skip in non-CI59exit 060fi6162# Run validation logic in CI63input=$(cat)64# ... validation code ...65```6667**Use cases:**68- Different behavior in CI vs local development69- Project-specific validation70- User-specific rules7172**Example: Skip certain checks for trusted users:**73```bash74#!/bin/bash75# Skip detailed checks for admin users76if [ "$USER" = "admin" ]; then77exit 078fi7980# Full validation for other users81input=$(cat)82# ... validation code ...83```8485## Hook Chaining via State8687Share state between hooks using temporary files:8889```bash90# Hook 1: Analyze and save state91#!/bin/bash92input=$(cat)93command=$(echo "$input" | jq -r '.tool_input.command')9495# Analyze command96risk_level=$(calculate_risk "$command")97echo "$risk_level" > /tmp/hook-state-$$9899exit 0100```101102```bash103# Hook 2: Use saved state104#!/bin/bash105risk_level=$(cat /tmp/hook-state-$$ 2>/dev/null || echo "unknown")106107if [ "$risk_level" = "high" ]; then108echo "High risk operation detected" >&2109exit 2110fi111```112113**Important:** This only works for sequential hook events (e.g., PreToolUse then PostToolUse), not parallel hooks.114115## Dynamic Hook Configuration116117Modify hook behavior based on project configuration:118119```bash120#!/bin/bash121cd "$CLAUDE_PROJECT_DIR" || exit 1122123# Read project-specific config124if [ -f ".claude-hooks-config.json" ]; then125strict_mode=$(jq -r '.strict_mode' .claude-hooks-config.json)126127if [ "$strict_mode" = "true" ]; then128# Apply strict validation129# ...130else131# Apply lenient validation132# ...133fi134fi135```136137**Example .claude-hooks-config.json:**138```json139{140"strict_mode": true,141"allowed_commands": ["ls", "pwd", "grep"],142"forbidden_paths": ["/etc", "/sys"]143}144```145146## Context-Aware Prompt Hooks147148Use transcript and session context for intelligent decisions:149150```json151{152"Stop": [153{154"matcher": "*",155"hooks": [156{157"type": "prompt",158"prompt": "Review the full transcript at $TRANSCRIPT_PATH. Check: 1) Were tests run after code changes? 2) Did the build succeed? 3) Were all user questions answered? 4) Is there any unfinished work? Return 'approve' only if everything is complete."159}160]161}162]163}164```165166The LLM can read the transcript file and make context-aware decisions.167168## Performance Optimization169170### Caching Validation Results171172```bash173#!/bin/bash174input=$(cat)175file_path=$(echo "$input" | jq -r '.tool_input.file_path')176cache_key=$(echo -n "$file_path" | md5sum | cut -d' ' -f1)177cache_file="/tmp/hook-cache-$cache_key"178179# Check cache180if [ -f "$cache_file" ]; then181cache_age=$(($(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || stat -c%Y "$cache_file")))182if [ "$cache_age" -lt 300 ]; then # 5 minute cache183cat "$cache_file"184exit 0185fi186fi187188# Perform validation189result='{"decision": "approve"}'190191# Cache result192echo "$result" > "$cache_file"193echo "$result"194```195196### Parallel Execution Optimization197198Since hooks run in parallel, design them to be independent:199200```json201{202"PreToolUse": [203{204"matcher": "Write",205"hooks": [206{207"type": "command",208"command": "bash check-size.sh", // Independent209"timeout": 2210},211{212"type": "command",213"command": "bash check-path.sh", // Independent214"timeout": 2215},216{217"type": "prompt",218"prompt": "Check content safety", // Independent219"timeout": 10220}221]222}223]224}225```226227All three hooks run simultaneously, reducing total latency.228229## Cross-Event Workflows230231Coordinate hooks across different events:232233**SessionStart - Set up tracking:**234```bash235#!/bin/bash236# Initialize session tracking237echo "0" > /tmp/test-count-$$238echo "0" > /tmp/build-count-$$239```240241**PostToolUse - Track events:**242```bash243#!/bin/bash244input=$(cat)245tool_name=$(echo "$input" | jq -r '.tool_name')246247if [ "$tool_name" = "Bash" ]; then248command=$(echo "$input" | jq -r '.tool_result')249if [[ "$command" == *"test"* ]]; then250count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0")251echo $((count + 1)) > /tmp/test-count-$$252fi253fi254```255256**Stop - Verify based on tracking:**257```bash258#!/bin/bash259test_count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0")260261if [ "$test_count" -eq 0 ]; then262echo '{"decision": "block", "reason": "No tests were run"}' >&2263exit 2264fi265```266267## Integration with External Systems268269### Slack Notifications270271```bash272#!/bin/bash273input=$(cat)274tool_name=$(echo "$input" | jq -r '.tool_name')275decision="blocked"276277# Send notification to Slack278curl -X POST "$SLACK_WEBHOOK" \279-H 'Content-Type: application/json' \280-d "{\"text\": \"Hook ${decision} ${tool_name} operation\"}" \2812>/dev/null282283echo '{"decision": "deny"}' >&2284exit 2285```286287### Database Logging288289```bash290#!/bin/bash291input=$(cat)292293# Log to database294psql "$DATABASE_URL" -c "INSERT INTO hook_logs (event, data) VALUES ('PreToolUse', '$input')" \2952>/dev/null296297exit 0298```299300### Metrics Collection301302```bash303#!/bin/bash304input=$(cat)305tool_name=$(echo "$input" | jq -r '.tool_name')306307# Send metrics to monitoring system308echo "hook.pretooluse.${tool_name}:1|c" | nc -u -w1 statsd.local 8125309310exit 0311```312313## Security Patterns314315### Rate Limiting316317```bash318#!/bin/bash319input=$(cat)320command=$(echo "$input" | jq -r '.tool_input.command')321322# Track command frequency323rate_file="/tmp/hook-rate-$$"324current_minute=$(date +%Y%m%d%H%M)325326if [ -f "$rate_file" ]; then327last_minute=$(head -1 "$rate_file")328count=$(tail -1 "$rate_file")329330if [ "$current_minute" = "$last_minute" ]; then331if [ "$count" -gt 10 ]; then332echo '{"decision": "deny", "reason": "Rate limit exceeded"}' >&2333exit 2334fi335count=$((count + 1))336else337count=1338fi339else340count=1341fi342343echo "$current_minute" > "$rate_file"344echo "$count" >> "$rate_file"345346exit 0347```348349### Audit Logging350351```bash352#!/bin/bash353input=$(cat)354tool_name=$(echo "$input" | jq -r '.tool_name')355timestamp=$(date -Iseconds)356357# Append to audit log358echo "$timestamp | $USER | $tool_name | $input" >> ~/.claude/audit.log359360exit 0361```362363### Secret Detection364365```bash366#!/bin/bash367input=$(cat)368content=$(echo "$input" | jq -r '.tool_input.content')369370# Check for common secret patterns371if echo "$content" | grep -qE "(api[_-]?key|password|secret|token).{0,20}['\"]?[A-Za-z0-9]{20,}"; then372echo '{"decision": "deny", "reason": "Potential secret detected in content"}' >&2373exit 2374fi375376exit 0377```378379## Testing Advanced Hooks380381### Unit Testing Hook Scripts382383```bash384# test-hook.sh385#!/bin/bash386387# Test 1: Approve safe command388result=$(echo '{"tool_input": {"command": "ls"}}' | bash validate-bash.sh)389if [ $? -eq 0 ]; then390echo "✓ Test 1 passed"391else392echo "✗ Test 1 failed"393fi394395# Test 2: Block dangerous command396result=$(echo '{"tool_input": {"command": "rm -rf /"}}' | bash validate-bash.sh)397if [ $? -eq 2 ]; then398echo "✓ Test 2 passed"399else400echo "✗ Test 2 failed"401fi402```403404### Integration Testing405406Create test scenarios that exercise the full hook workflow:407408```bash409# integration-test.sh410#!/bin/bash411412# Set up test environment413export CLAUDE_PROJECT_DIR="/tmp/test-project"414export CLAUDE_PLUGIN_ROOT="$(pwd)"415mkdir -p "$CLAUDE_PROJECT_DIR"416417# Test SessionStart hook418echo '{}' | bash hooks/session-start.sh419if [ -f "/tmp/session-initialized" ]; then420echo "✓ SessionStart hook works"421else422echo "✗ SessionStart hook failed"423fi424425# Clean up426rm -rf "$CLAUDE_PROJECT_DIR"427```428429## Best Practices for Advanced Hooks4304311. **Keep hooks independent**: Don't rely on execution order4322. **Use timeouts**: Set appropriate limits for each hook type4333. **Handle errors gracefully**: Provide clear error messages4344. **Document complexity**: Explain advanced patterns in README4355. **Test thoroughly**: Cover edge cases and failure modes4366. **Monitor performance**: Track hook execution time4377. **Version configuration**: Use version control for hook configs4388. **Provide escape hatches**: Allow users to bypass hooks when needed439440## Common Pitfalls441442### ❌ Assuming Hook Order443444```bash445# BAD: Assumes hooks run in specific order446# Hook 1 saves state, Hook 2 reads it447# This can fail because hooks run in parallel!448```449450### ❌ Long-Running Hooks451452```bash453# BAD: Hook takes 2 minutes to run454sleep 120455# This will timeout and block the workflow456```457458### ❌ Uncaught Exceptions459460```bash461# BAD: Script crashes on unexpected input462file_path=$(echo "$input" | jq -r '.tool_input.file_path')463cat "$file_path" # Fails if file doesn't exist464```465466### ✅ Proper Error Handling467468```bash469# GOOD: Handles errors gracefully470file_path=$(echo "$input" | jq -r '.tool_input.file_path')471if [ ! -f "$file_path" ]; then472echo '{"continue": true, "systemMessage": "File not found, skipping check"}' >&2473exit 0474fi475```476477## Conclusion478479Advanced hook patterns enable sophisticated automation while maintaining reliability and performance. Use these techniques when basic hooks are insufficient, but always prioritize simplicity and maintainability.480