mirror of
https://github.com/anthropics/claude-code.git
synced 2025-11-28 08:40:27 +08:00
Adds the plugin-dev plugin to public marketplace. A comprehensive toolkit for
developing Claude Code plugins with 7 expert skills, 3 AI-assisted agents, and
extensive documentation covering the complete plugin development lifecycle.
Key features:
- 7 skills: hook-development, mcp-integration, plugin-structure, plugin-settings,
command-development, agent-development, skill-development
- 3 agents: agent-creator (AI-assisted generation), plugin-validator (structure
validation), skill-reviewer (quality review)
- 1 command: /plugin-dev:create-plugin (guided 8-phase workflow)
- 10 utility scripts for validation and testing
- 21 reference docs with deep-dive guidance (~11k words)
- 9 working examples demonstrating best practices
Changes for public release:
- Replaced all references to internal repositories with "Claude Code"
- Updated MCP examples: internal.company.com → api.example.com
- Updated token variables: ${INTERNAL_TOKEN} → ${API_TOKEN}
- Reframed agent-creation-system-prompt as "proven in production"
- Preserved all ${CLAUDE_PLUGIN_ROOT} references (186 total)
- Preserved valuable test blocks in core modules
Validation:
- All 3 agents validated successfully with validate-agent.sh
- All JSON files validated with jq
- Zero internal references remaining
- 59 files migrated, 21,971 lines added
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
479 lines
9.9 KiB
Markdown
479 lines
9.9 KiB
Markdown
# Advanced Hook Use Cases
|
|
|
|
This reference covers advanced hook patterns and techniques for sophisticated automation workflows.
|
|
|
|
## Multi-Stage Validation
|
|
|
|
Combine command and prompt hooks for layered 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
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Use case:** Fast deterministic checks followed by intelligent analysis
|
|
|
|
**Example quick-check.sh:**
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
command=$(echo "$input" | jq -r '.tool_input.command')
|
|
|
|
# Immediate approval for safe commands
|
|
if [[ "$command" =~ ^(ls|pwd|echo|date|whoami)$ ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Let prompt hook handle complex cases
|
|
exit 0
|
|
```
|
|
|
|
The command hook quickly approves obviously safe commands, while the prompt hook analyzes everything else.
|
|
|
|
## Conditional Hook Execution
|
|
|
|
Execute hooks based on environment or context:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Only run in CI environment
|
|
if [ -z "$CI" ]; then
|
|
echo '{"continue": true}' # Skip in non-CI
|
|
exit 0
|
|
fi
|
|
|
|
# Run validation logic in CI
|
|
input=$(cat)
|
|
# ... validation code ...
|
|
```
|
|
|
|
**Use cases:**
|
|
- Different behavior in CI vs local development
|
|
- Project-specific validation
|
|
- User-specific rules
|
|
|
|
**Example: Skip certain checks for trusted users:**
|
|
```bash
|
|
#!/bin/bash
|
|
# Skip detailed checks for admin users
|
|
if [ "$USER" = "admin" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Full validation for other users
|
|
input=$(cat)
|
|
# ... validation code ...
|
|
```
|
|
|
|
## Hook Chaining via State
|
|
|
|
Share state between hooks using temporary files:
|
|
|
|
```bash
|
|
# Hook 1: Analyze and save state
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
command=$(echo "$input" | jq -r '.tool_input.command')
|
|
|
|
# Analyze command
|
|
risk_level=$(calculate_risk "$command")
|
|
echo "$risk_level" > /tmp/hook-state-$$
|
|
|
|
exit 0
|
|
```
|
|
|
|
```bash
|
|
# Hook 2: Use saved state
|
|
#!/bin/bash
|
|
risk_level=$(cat /tmp/hook-state-$$ 2>/dev/null || echo "unknown")
|
|
|
|
if [ "$risk_level" = "high" ]; then
|
|
echo "High risk operation detected" >&2
|
|
exit 2
|
|
fi
|
|
```
|
|
|
|
**Important:** This only works for sequential hook events (e.g., PreToolUse then PostToolUse), not parallel hooks.
|
|
|
|
## Dynamic Hook Configuration
|
|
|
|
Modify hook behavior based on project configuration:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
cd "$CLAUDE_PROJECT_DIR" || exit 1
|
|
|
|
# Read project-specific config
|
|
if [ -f ".claude-hooks-config.json" ]; then
|
|
strict_mode=$(jq -r '.strict_mode' .claude-hooks-config.json)
|
|
|
|
if [ "$strict_mode" = "true" ]; then
|
|
# Apply strict validation
|
|
# ...
|
|
else
|
|
# Apply lenient validation
|
|
# ...
|
|
fi
|
|
fi
|
|
```
|
|
|
|
**Example .claude-hooks-config.json:**
|
|
```json
|
|
{
|
|
"strict_mode": true,
|
|
"allowed_commands": ["ls", "pwd", "grep"],
|
|
"forbidden_paths": ["/etc", "/sys"]
|
|
}
|
|
```
|
|
|
|
## Context-Aware Prompt Hooks
|
|
|
|
Use transcript and session context for intelligent decisions:
|
|
|
|
```json
|
|
{
|
|
"Stop": [
|
|
{
|
|
"matcher": "*",
|
|
"hooks": [
|
|
{
|
|
"type": "prompt",
|
|
"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."
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The LLM can read the transcript file and make context-aware decisions.
|
|
|
|
## Performance Optimization
|
|
|
|
### Caching Validation Results
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
|
cache_key=$(echo -n "$file_path" | md5sum | cut -d' ' -f1)
|
|
cache_file="/tmp/hook-cache-$cache_key"
|
|
|
|
# Check cache
|
|
if [ -f "$cache_file" ]; then
|
|
cache_age=$(($(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || stat -c%Y "$cache_file")))
|
|
if [ "$cache_age" -lt 300 ]; then # 5 minute cache
|
|
cat "$cache_file"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Perform validation
|
|
result='{"decision": "approve"}'
|
|
|
|
# Cache result
|
|
echo "$result" > "$cache_file"
|
|
echo "$result"
|
|
```
|
|
|
|
### Parallel Execution Optimization
|
|
|
|
Since hooks run in parallel, design them to be independent:
|
|
|
|
```json
|
|
{
|
|
"PreToolUse": [
|
|
{
|
|
"matcher": "Write",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "bash check-size.sh", // Independent
|
|
"timeout": 2
|
|
},
|
|
{
|
|
"type": "command",
|
|
"command": "bash check-path.sh", // Independent
|
|
"timeout": 2
|
|
},
|
|
{
|
|
"type": "prompt",
|
|
"prompt": "Check content safety", // Independent
|
|
"timeout": 10
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
All three hooks run simultaneously, reducing total latency.
|
|
|
|
## Cross-Event Workflows
|
|
|
|
Coordinate hooks across different events:
|
|
|
|
**SessionStart - Set up tracking:**
|
|
```bash
|
|
#!/bin/bash
|
|
# Initialize session tracking
|
|
echo "0" > /tmp/test-count-$$
|
|
echo "0" > /tmp/build-count-$$
|
|
```
|
|
|
|
**PostToolUse - Track events:**
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
tool_name=$(echo "$input" | jq -r '.tool_name')
|
|
|
|
if [ "$tool_name" = "Bash" ]; then
|
|
command=$(echo "$input" | jq -r '.tool_result')
|
|
if [[ "$command" == *"test"* ]]; then
|
|
count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0")
|
|
echo $((count + 1)) > /tmp/test-count-$$
|
|
fi
|
|
fi
|
|
```
|
|
|
|
**Stop - Verify based on tracking:**
|
|
```bash
|
|
#!/bin/bash
|
|
test_count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0")
|
|
|
|
if [ "$test_count" -eq 0 ]; then
|
|
echo '{"decision": "block", "reason": "No tests were run"}' >&2
|
|
exit 2
|
|
fi
|
|
```
|
|
|
|
## Integration with External Systems
|
|
|
|
### Slack Notifications
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
tool_name=$(echo "$input" | jq -r '.tool_name')
|
|
decision="blocked"
|
|
|
|
# Send notification to Slack
|
|
curl -X POST "$SLACK_WEBHOOK" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{\"text\": \"Hook ${decision} ${tool_name} operation\"}" \
|
|
2>/dev/null
|
|
|
|
echo '{"decision": "deny"}' >&2
|
|
exit 2
|
|
```
|
|
|
|
### Database Logging
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
|
|
# Log to database
|
|
psql "$DATABASE_URL" -c "INSERT INTO hook_logs (event, data) VALUES ('PreToolUse', '$input')" \
|
|
2>/dev/null
|
|
|
|
exit 0
|
|
```
|
|
|
|
### Metrics Collection
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
tool_name=$(echo "$input" | jq -r '.tool_name')
|
|
|
|
# Send metrics to monitoring system
|
|
echo "hook.pretooluse.${tool_name}:1|c" | nc -u -w1 statsd.local 8125
|
|
|
|
exit 0
|
|
```
|
|
|
|
## Security Patterns
|
|
|
|
### Rate Limiting
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
command=$(echo "$input" | jq -r '.tool_input.command')
|
|
|
|
# Track command frequency
|
|
rate_file="/tmp/hook-rate-$$"
|
|
current_minute=$(date +%Y%m%d%H%M)
|
|
|
|
if [ -f "$rate_file" ]; then
|
|
last_minute=$(head -1 "$rate_file")
|
|
count=$(tail -1 "$rate_file")
|
|
|
|
if [ "$current_minute" = "$last_minute" ]; then
|
|
if [ "$count" -gt 10 ]; then
|
|
echo '{"decision": "deny", "reason": "Rate limit exceeded"}' >&2
|
|
exit 2
|
|
fi
|
|
count=$((count + 1))
|
|
else
|
|
count=1
|
|
fi
|
|
else
|
|
count=1
|
|
fi
|
|
|
|
echo "$current_minute" > "$rate_file"
|
|
echo "$count" >> "$rate_file"
|
|
|
|
exit 0
|
|
```
|
|
|
|
### Audit Logging
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
tool_name=$(echo "$input" | jq -r '.tool_name')
|
|
timestamp=$(date -Iseconds)
|
|
|
|
# Append to audit log
|
|
echo "$timestamp | $USER | $tool_name | $input" >> ~/.claude/audit.log
|
|
|
|
exit 0
|
|
```
|
|
|
|
### Secret Detection
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
input=$(cat)
|
|
content=$(echo "$input" | jq -r '.tool_input.content')
|
|
|
|
# Check for common secret patterns
|
|
if echo "$content" | grep -qE "(api[_-]?key|password|secret|token).{0,20}['\"]?[A-Za-z0-9]{20,}"; then
|
|
echo '{"decision": "deny", "reason": "Potential secret detected in content"}' >&2
|
|
exit 2
|
|
fi
|
|
|
|
exit 0
|
|
```
|
|
|
|
## Testing Advanced Hooks
|
|
|
|
### Unit Testing Hook Scripts
|
|
|
|
```bash
|
|
# test-hook.sh
|
|
#!/bin/bash
|
|
|
|
# Test 1: Approve safe command
|
|
result=$(echo '{"tool_input": {"command": "ls"}}' | bash validate-bash.sh)
|
|
if [ $? -eq 0 ]; then
|
|
echo "✓ Test 1 passed"
|
|
else
|
|
echo "✗ Test 1 failed"
|
|
fi
|
|
|
|
# Test 2: Block dangerous command
|
|
result=$(echo '{"tool_input": {"command": "rm -rf /"}}' | bash validate-bash.sh)
|
|
if [ $? -eq 2 ]; then
|
|
echo "✓ Test 2 passed"
|
|
else
|
|
echo "✗ Test 2 failed"
|
|
fi
|
|
```
|
|
|
|
### Integration Testing
|
|
|
|
Create test scenarios that exercise the full hook workflow:
|
|
|
|
```bash
|
|
# integration-test.sh
|
|
#!/bin/bash
|
|
|
|
# Set up test environment
|
|
export CLAUDE_PROJECT_DIR="/tmp/test-project"
|
|
export CLAUDE_PLUGIN_ROOT="$(pwd)"
|
|
mkdir -p "$CLAUDE_PROJECT_DIR"
|
|
|
|
# Test SessionStart hook
|
|
echo '{}' | bash hooks/session-start.sh
|
|
if [ -f "/tmp/session-initialized" ]; then
|
|
echo "✓ SessionStart hook works"
|
|
else
|
|
echo "✗ SessionStart hook failed"
|
|
fi
|
|
|
|
# Clean up
|
|
rm -rf "$CLAUDE_PROJECT_DIR"
|
|
```
|
|
|
|
## Best Practices for Advanced Hooks
|
|
|
|
1. **Keep hooks independent**: Don't rely on execution order
|
|
2. **Use timeouts**: Set appropriate limits for each hook type
|
|
3. **Handle errors gracefully**: Provide clear error messages
|
|
4. **Document complexity**: Explain advanced patterns in README
|
|
5. **Test thoroughly**: Cover edge cases and failure modes
|
|
6. **Monitor performance**: Track hook execution time
|
|
7. **Version configuration**: Use version control for hook configs
|
|
8. **Provide escape hatches**: Allow users to bypass hooks when needed
|
|
|
|
## Common Pitfalls
|
|
|
|
### ❌ Assuming Hook Order
|
|
|
|
```bash
|
|
# BAD: Assumes hooks run in specific order
|
|
# Hook 1 saves state, Hook 2 reads it
|
|
# This can fail because hooks run in parallel!
|
|
```
|
|
|
|
### ❌ Long-Running Hooks
|
|
|
|
```bash
|
|
# BAD: Hook takes 2 minutes to run
|
|
sleep 120
|
|
# This will timeout and block the workflow
|
|
```
|
|
|
|
### ❌ Uncaught Exceptions
|
|
|
|
```bash
|
|
# BAD: Script crashes on unexpected input
|
|
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
|
cat "$file_path" # Fails if file doesn't exist
|
|
```
|
|
|
|
### ✅ Proper Error Handling
|
|
|
|
```bash
|
|
# GOOD: Handles errors gracefully
|
|
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
|
if [ ! -f "$file_path" ]; then
|
|
echo '{"continue": true, "systemMessage": "File not found, skipping check"}' >&2
|
|
exit 0
|
|
fi
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
Advanced hook patterns enable sophisticated automation while maintaining reliability and performance. Use these techniques when basic hooks are insufficient, but always prioritize simplicity and maintainability.
|