Skill refresh
This commit is contained in:
404
claude-code/skills/claude-hooks/SKILL.md
Normal file
404
claude-code/skills/claude-hooks/SKILL.md
Normal file
@@ -0,0 +1,404 @@
|
||||
---
|
||||
name: Claude Code Hooks
|
||||
description: Configure event-driven hooks for Claude Code to automate workflows, validate code, inject context, and control tool execution. Use PROACTIVELY when setting up automation, enforcing standards, integrating external tools, or when users mention "automatically run", "on save", "event-driven", "workflow automation", or "enforce rules". NOT for one-time scripts.
|
||||
---
|
||||
|
||||
# Claude Code Hooks
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this skill when:
|
||||
- Automating workflows with event-driven triggers
|
||||
- Enforcing code standards or security policies
|
||||
- Validating changes before/after tool execution
|
||||
- Injecting context at session start
|
||||
- Logging or monitoring tool usage
|
||||
- Setting up team-wide automation
|
||||
|
||||
Do NOT use this skill for:
|
||||
- Creating slash commands (use claude-command-expert skill)
|
||||
- Building full plugins (use claude-plugin skill)
|
||||
- One-time scripts (just run them directly)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Hook Configuration File
|
||||
|
||||
```bash
|
||||
# Project-wide (team shared)
|
||||
.claude/settings.json
|
||||
|
||||
# User-specific (not shared)
|
||||
.claude/settings.local.json
|
||||
|
||||
# Global (all projects)
|
||||
~/.claude/settings.json
|
||||
```
|
||||
|
||||
### 2. Simple Example - Log File Changes
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "echo \"$(date): Modified $TOOL_ARGS\" >> .claude/changes.log"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hook Types
|
||||
|
||||
| Event | When It Runs | Common Use |
|
||||
|-------|--------------|-----------|
|
||||
| SessionStart | Session begins | Inject project context |
|
||||
| SessionEnd | Session ends | Cleanup, backups |
|
||||
| PreToolUse | Before tool execution | Validation, permission checks |
|
||||
| PostToolUse | After tool completes | Formatting, linting |
|
||||
| UserPromptSubmit | User submits prompt | Logging, analytics |
|
||||
| Notification | Claude sends notification | Desktop alerts |
|
||||
| Stop | Agent finishes responding | Post-response processing |
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Matcher Patterns
|
||||
|
||||
```json
|
||||
// Single tool
|
||||
"matcher": "Write"
|
||||
|
||||
// Multiple tools (OR)
|
||||
"matcher": "Write|Edit|Read"
|
||||
|
||||
// All tools
|
||||
"matcher": "*"
|
||||
|
||||
// Git operations
|
||||
"matcher": "Bash(git:*)"
|
||||
```
|
||||
|
||||
### Exit Codes
|
||||
|
||||
```bash
|
||||
# Success - Continue
|
||||
exit 0
|
||||
|
||||
# Blocking Error - Stop operation
|
||||
exit 2
|
||||
|
||||
# Non-Blocking Error - Continue
|
||||
exit 1
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
$CLAUDE_PROJECT_DIR # Current project directory
|
||||
$TOOL_NAME # Name of the tool being used
|
||||
$TOOL_ARGS # Arguments passed to the tool
|
||||
$HOOK_EVENT # Event type (PreToolUse, etc.)
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Auto-Format on Save
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cd $CLAUDE_PROJECT_DIR && npx prettier --write $TOOL_ARGS && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Validation
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "if grep -qiE '(password|api[_-]?key|secret)' $TOOL_ARGS 2>/dev/null; then echo 'Error: Possible secret detected' >&2; exit 2; fi; exit 0",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Before Commit
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cd $CLAUDE_PROJECT_DIR && npm test -- --silent || (echo 'Tests failed' >&2; exit 2)",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For complete examples, see [examples.md](examples.md)
|
||||
|
||||
## Hook Configuration
|
||||
|
||||
### Basic Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "command",
|
||||
"command": "your-command-here",
|
||||
"timeout": 60 // Optional, default 60 seconds
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Hooks
|
||||
|
||||
Run several hooks in sequence:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "npx prettier --write $TOOL_ARGS"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "npx eslint --fix $TOOL_ARGS"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "git add $TOOL_ARGS"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Execution
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "command",
|
||||
"command": "if [[ $TOOL_ARGS == *.js ]]; then npm run format $TOOL_ARGS; fi; exit 0"
|
||||
}
|
||||
```
|
||||
|
||||
## Settings File Hierarchy
|
||||
|
||||
**Load Order** (highest to lowest priority):
|
||||
|
||||
1. `.claude/settings.local.json` - Project, user-only (gitignored)
|
||||
2. `.claude/settings.json` - Project, team-shared (in git)
|
||||
3. `~/.claude/settings.json` - Global, user-only
|
||||
|
||||
**Use Cases**:
|
||||
- **Global**: Personal preferences, universal logging
|
||||
- **Project**: Team standards, project automation
|
||||
- **Local**: Personal overrides, development experiments
|
||||
|
||||
## Testing Hooks
|
||||
|
||||
### Start Simple
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "echo 'Hook triggered for: $TOOL_ARGS' && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Exit Codes
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "echo 'This should block' >&2 && exit 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Try writing a file - should be blocked.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| Hook not triggering | Check matcher pattern, restart Claude Code |
|
||||
| Hook fails silently | Check exit codes (use 0 for success) |
|
||||
| Command not found | Use full path: `/usr/local/bin/prettier` |
|
||||
| Permission denied | `chmod +x .claude/hooks/script.sh` |
|
||||
| Timeout | Increase timeout value or optimize command |
|
||||
|
||||
For detailed troubleshooting, see [troubleshooting.md](troubleshooting.md)
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Exit Explicitly
|
||||
|
||||
```bash
|
||||
# Good
|
||||
command && exit 0
|
||||
|
||||
# Bad
|
||||
command
|
||||
```
|
||||
|
||||
### 2. Use Timeouts
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "npm test",
|
||||
"timeout": 120 // Don't let tests run forever
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Handle Errors Gracefully
|
||||
|
||||
```bash
|
||||
if [ -f "$TOOL_ARGS" ]; then
|
||||
process_file "$TOOL_ARGS"
|
||||
else
|
||||
echo "File not found" >&2
|
||||
fi
|
||||
exit 0 # Don't block on missing file
|
||||
```
|
||||
|
||||
### 4. Use Scripts for Complex Logic
|
||||
|
||||
```json
|
||||
// Bad - complex bash in JSON
|
||||
{
|
||||
"command": "if [ -f $TOOL_ARGS ]; then cat $TOOL_ARGS | grep pattern | wc -l; fi"
|
||||
}
|
||||
|
||||
// Good - external script
|
||||
{
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-file.sh $TOOL_ARGS"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Test Before Team Deployment
|
||||
|
||||
Test in `.claude/settings.local.json` before adding to `.claude/settings.json`.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Validate Input
|
||||
|
||||
```bash
|
||||
# Sanitize TOOL_ARGS
|
||||
if [[ ! $TOOL_ARGS =~ ^[a-zA-Z0-9/_.-]+$ ]]; then
|
||||
echo "Invalid file path" >&2
|
||||
exit 2
|
||||
fi
|
||||
```
|
||||
|
||||
### Limit Permissions
|
||||
|
||||
```json
|
||||
// Specific (good)
|
||||
"matcher": "Write|Edit"
|
||||
|
||||
// Too broad (risky)
|
||||
"matcher": "*"
|
||||
```
|
||||
|
||||
### Avoid Destructive Commands
|
||||
|
||||
```bash
|
||||
# Dangerous
|
||||
rm -rf $TOOL_ARGS
|
||||
|
||||
# Safer
|
||||
if [[ -f "$TOOL_ARGS" ]] && [[ "$TOOL_ARGS" != "/" ]]; then
|
||||
rm "$TOOL_ARGS"
|
||||
fi
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Complete Examples](examples.md) - Working hook configurations
|
||||
- [Advanced Patterns](patterns.md) - Complex workflows
|
||||
- [Troubleshooting Guide](troubleshooting.md) - Problem-solution reference
|
||||
- [Official Documentation](https://docs.claude.com/en/docs/claude-code/hooks)
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Common Patterns
|
||||
|
||||
```json
|
||||
// Format on save
|
||||
"PostToolUse": [{"matcher": "Write|Edit", "hooks": [{"type": "command", "command": "prettier --write $TOOL_ARGS"}]}]
|
||||
|
||||
// Block secrets
|
||||
"PreToolUse": [{"matcher": "Write", "hooks": [{"type": "command", "command": "grep -q 'secret' $TOOL_ARGS && exit 2 || exit 0"}]}]
|
||||
|
||||
// Log all activity
|
||||
"PreToolUse": [{"matcher": "*", "hooks": [{"type": "command", "command": "echo \"$TOOL_NAME: $TOOL_ARGS\" >> .claude/log && exit 0"}]}]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Hooks are powerful automation tools. Start simple, test thoroughly, use exit codes properly to control flow.
|
||||
258
claude-code/skills/claude-hooks/examples.md
Normal file
258
claude-code/skills/claude-hooks/examples.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Complete Hook Examples
|
||||
|
||||
Working hook configurations for common scenarios.
|
||||
|
||||
## 1. Auto-Format on Save
|
||||
|
||||
Format files automatically after writing/editing:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cd $CLAUDE_PROJECT_DIR && npx prettier --write $TOOL_ARGS && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Security Validation
|
||||
|
||||
Block writes containing secrets:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "if grep -qiE '(password|api[_-]?key|secret|sk-[a-zA-Z0-9]{48})' $TOOL_ARGS 2>/dev/null; then echo 'Error: Possible secret detected' >&2; exit 2; fi; exit 0",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Auto-Git on Changes
|
||||
|
||||
Automatically stage and commit changes:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cd $CLAUDE_PROJECT_DIR && git add $TOOL_ARGS && git commit -m 'Auto-commit: Modified $TOOL_ARGS' && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Test Before Commit
|
||||
|
||||
Run tests before allowing file writes:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cd $CLAUDE_PROJECT_DIR && npm test -- --silent || (echo 'Tests failed, cannot save' >&2; exit 2)",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Inject Project Context
|
||||
|
||||
Load project info at session start:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "cat << EOF\nProject: MyApp\nEnvironment: Production\nKey Files: src/config.js, .env.example\nCoding Standards: See CONTRIBUTING.md\nEOF"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Log All Activity
|
||||
|
||||
Track all tool usage:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') - $TOOL_NAME - $TOOL_ARGS\" >> $CLAUDE_PROJECT_DIR/.claude/activity.log && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Multiple Hooks Sequence
|
||||
|
||||
Format, lint, then stage:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "npx prettier --write $TOOL_ARGS"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "npx eslint --fix $TOOL_ARGS"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "git add $TOOL_ARGS"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Script-Based Hooks
|
||||
|
||||
Call external script for complex logic:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-write.sh $TOOL_ARGS",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**validate-write.sh**:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
file=$1
|
||||
|
||||
# Check file size
|
||||
if [ -f "$file" ] && [ $(wc -c < "$file") -gt 1000000 ]; then
|
||||
echo "Error: File too large" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Check for secrets
|
||||
if grep -qiE '(password|api[_-]?key)' "$file" 2>/dev/null; then
|
||||
echo "Error: Possible secret detected" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
exit 0
|
||||
```
|
||||
|
||||
## 9. Conditional by File Type
|
||||
|
||||
Only format JavaScript files:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "if [[ $TOOL_ARGS == *.js ]]; then npm run format $TOOL_ARGS; fi; exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10. Desktop Notifications
|
||||
|
||||
Alert when Claude needs attention:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"Notification": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"' && exit 0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
For advanced patterns, see [patterns.md](patterns.md)
|
||||
Reference in New Issue
Block a user