Initial commit
This commit is contained in:
346
skills/hook-development/references/patterns.md
Normal file
346
skills/hook-development/references/patterns.md
Normal file
@@ -0,0 +1,346 @@
|
||||
# Common Hook Patterns
|
||||
|
||||
This reference provides common, proven patterns for implementing Claude Code hooks. Use these patterns as starting points for typical hook use cases.
|
||||
|
||||
## Pattern 1: Security Validation
|
||||
|
||||
Block dangerous file writes using prompt-based hooks:
|
||||
|
||||
```json
|
||||
{
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "File path: $TOOL_INPUT.file_path. Verify: 1) Not in /etc or system directories 2) Not .env or credentials 3) Path doesn't contain '..' traversal. Return 'approve' or 'deny'."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** Preventing writes to sensitive files or system directories.
|
||||
|
||||
## Pattern 2: Test Enforcement
|
||||
|
||||
Ensure tests run before stopping:
|
||||
|
||||
```json
|
||||
{
|
||||
"Stop": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Review transcript. If code was modified (Write/Edit tools used), verify tests were executed. If no tests were run, block with reason 'Tests must be run after code changes'."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** Enforcing quality standards and preventing incomplete work.
|
||||
|
||||
## Pattern 3: Context Loading
|
||||
|
||||
Load project-specific context at session start:
|
||||
|
||||
```json
|
||||
{
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example script (load-context.sh):**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
cd "$CLAUDE_PROJECT_DIR" || exit 1
|
||||
|
||||
# Detect project type
|
||||
if [ -f "package.json" ]; then
|
||||
echo "📦 Node.js project detected"
|
||||
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
echo "🦀 Rust project detected"
|
||||
echo "export PROJECT_TYPE=rust" >> "$CLAUDE_ENV_FILE"
|
||||
fi
|
||||
```
|
||||
|
||||
**Use for:** Automatically detecting and configuring project-specific settings.
|
||||
|
||||
## Pattern 4: Notification Logging
|
||||
|
||||
Log all notifications for audit or analysis:
|
||||
|
||||
```json
|
||||
{
|
||||
"Notification": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/log-notification.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** Tracking user notifications or integration with external logging systems.
|
||||
|
||||
## Pattern 5: MCP Tool Monitoring
|
||||
|
||||
Monitor and validate MCP tool usage:
|
||||
|
||||
```json
|
||||
{
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "mcp__.*__delete.*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Deletion operation detected. Verify: Is this deletion intentional? Can it be undone? Are there backups? Return 'approve' only if safe."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** Protecting against destructive MCP operations.
|
||||
|
||||
## Pattern 6: Build Verification
|
||||
|
||||
Ensure project builds after code changes:
|
||||
|
||||
```json
|
||||
{
|
||||
"Stop": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Check if code was modified. If Write/Edit tools were used, verify the project was built (npm run build, cargo build, etc). If not built, block and request build."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** Catching build errors before committing or stopping work.
|
||||
|
||||
## Pattern 7: Permission Confirmation
|
||||
|
||||
Ask user before dangerous operations:
|
||||
|
||||
```json
|
||||
{
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Command: $TOOL_INPUT.command. If command contains 'rm', 'delete', 'drop', or other destructive operations, return 'ask' to confirm with user. Otherwise 'approve'."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:** User confirmation on potentially destructive commands.
|
||||
|
||||
## Pattern 8: Code Quality Checks
|
||||
|
||||
Run linters or formatters on file edits:
|
||||
|
||||
```json
|
||||
{
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/check-quality.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example script (check-quality.sh):**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
input=$(cat)
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
||||
|
||||
# Run linter if applicable
|
||||
if [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then
|
||||
npx eslint "$file_path" 2>&1 || true
|
||||
fi
|
||||
```
|
||||
|
||||
**Use for:** Automatic code quality enforcement.
|
||||
|
||||
## Pattern Combinations
|
||||
|
||||
Combine multiple patterns for comprehensive protection:
|
||||
|
||||
```json
|
||||
{
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Validate file write safety"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Validate bash command safety"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Verify tests run and build succeeded"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This provides multi-layered protection and automation.
|
||||
|
||||
## Pattern 9: Temporarily Active Hooks
|
||||
|
||||
Create hooks that only run when explicitly enabled via flag files:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Hook only active when flag file exists
|
||||
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-security-scan"
|
||||
|
||||
if [ ! -f "$FLAG_FILE" ]; then
|
||||
# Quick exit when disabled
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Flag present, run validation
|
||||
input=$(cat)
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
|
||||
|
||||
# Run security scan
|
||||
security-scanner "$file_path"
|
||||
```
|
||||
|
||||
**Activation:**
|
||||
```bash
|
||||
# Enable the hook
|
||||
touch .enable-security-scan
|
||||
|
||||
# Disable the hook
|
||||
rm .enable-security-scan
|
||||
```
|
||||
|
||||
**Use for:**
|
||||
- Temporary debugging hooks
|
||||
- Feature flags for development
|
||||
- Project-specific validation that's opt-in
|
||||
- Performance-intensive checks only when needed
|
||||
|
||||
**Note:** Must restart Claude Code after creating/removing flag files for hooks to recognize changes.
|
||||
|
||||
## Pattern 10: Configuration-Driven Hooks
|
||||
|
||||
Use JSON configuration to control hook behavior:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/my-plugin.local.json"
|
||||
|
||||
# Read configuration
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
strict_mode=$(jq -r '.strictMode // false' "$CONFIG_FILE")
|
||||
max_file_size=$(jq -r '.maxFileSize // 1000000' "$CONFIG_FILE")
|
||||
else
|
||||
# Defaults
|
||||
strict_mode=false
|
||||
max_file_size=1000000
|
||||
fi
|
||||
|
||||
# Skip if not in strict mode
|
||||
if [ "$strict_mode" != "true" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Apply configured limits
|
||||
input=$(cat)
|
||||
file_size=$(echo "$input" | jq -r '.tool_input.content | length')
|
||||
|
||||
if [ "$file_size" -gt "$max_file_size" ]; then
|
||||
echo '{"decision": "deny", "reason": "File exceeds configured size limit"}' >&2
|
||||
exit 2
|
||||
fi
|
||||
```
|
||||
|
||||
**Configuration file (.claude/my-plugin.local.json):**
|
||||
```json
|
||||
{
|
||||
"strictMode": true,
|
||||
"maxFileSize": 500000,
|
||||
"allowedPaths": ["/tmp", "/home/user/projects"]
|
||||
}
|
||||
```
|
||||
|
||||
**Use for:**
|
||||
- User-configurable hook behavior
|
||||
- Per-project settings
|
||||
- Team-specific rules
|
||||
- Dynamic validation criteria
|
||||
Reference in New Issue
Block a user