xiaohongshu
xiaohongshu
claude-hook-builder
Interactive hook creator for Claude Code. Triggers when user mentions creating hooks, PreToolUse, PostToolUse, hook validation, hook configuration, settings.json hooks, or wants to automate tool execution workflows.
Full skill instructions
Guide users through creating effective Claude Code hooks for tool validation, automation, and workflow enhancement. Auto-invokes when users want to create or configure hooks.
Auto-invoke when users mention:
.claude/skills/ai/claude-code/docs/code_claude_com/docs_en_hooks.md.claude/skills/ai/claude-code/docs/code_claude_com/docs_en_hooks-guide.md.claude/docs/creating-components.mdAsk the user:
Let me help you create a Claude Code hook! I need some details:
1. **What should this hook do?**
Examples:
- Auto-format code after editing files
- Validate bash commands before execution
- Add context when user submits prompts
- Prevent access to sensitive files
- Run tests after file changes
2. **When should it trigger?**
- PreToolUse (before tool execution)
- PostToolUse (after tool execution)
- UserPromptSubmit (when user sends message)
- Stop (when Claude finishes responding)
- SubagentStop (when subagent finishes)
- SessionStart (when session begins)
- SessionEnd (when session ends)
- Notification (when notification sent)
- PermissionRequest (when permission requested)
3. **Which tools should it match?**
- Specific tool (Write, Edit, Bash, Read, etc.)
- Multiple tools (Write|Edit)
- All tools (*)
- MCP tools (mcp__server__tool)
4. **What should it return?**
- Simple exit code (0 = success, 2 = block)
- JSON with decision control
- Additional context for Claude
- Modified tool inputs
5. **Scope:**
- User-level (`~/.claude/settings.json`)
- Project-level (`.claude/settings.json`)
- Local project (`.claude/settings.local.json`)
Bash Command Hook:
{
"type": "command",
"command": "/path/to/script.sh"
}
Prompt-based Hook:
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS"
}
Runs before tool executes.
Use for:
JSON Output:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow" | "deny" | "ask",
"permissionDecisionReason": "Why this decision",
"updatedInput": {
"field": "new value"
}
}
}
Runs after tool completes.
Use for:
JSON Output:
{
"decision": "block" | undefined,
"reason": "Why blocking",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Extra info for Claude"
}
}
Runs when user submits prompt.
Use for:
JSON Output:
{
"decision": "block" | undefined,
"reason": "Why blocking",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Extra context"
}
}
Runs when Claude/subagent finishes.
Use for:
JSON Output:
{
"decision": "block" | undefined,
"reason": "Why must continue"
}
Runs when session starts.
Use for:
JSON Output:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Initial context"
}
}
Special: Can persist environment variables:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
fi
Runs when session ends.
Use for:
For bash command hooks, create a script:
Template:
#!/usr/bin/env bash
# Read JSON input from stdin
INPUT=$(cat)
# Parse JSON (requires jq)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Your validation logic here
if [[ condition ]]; then
echo "Error message" >&2
exit 2 # Block operation
fi
# Success
exit 0
Python Template:
#!/usr/bin/env python3
import json
import sys
# Read JSON input
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Your logic here
if condition:
# Block with error
print("Error message", file=sys.stderr)
sys.exit(2)
# Or return JSON for control
output = {
"decision": "approve",
"reason": "Auto-approved"
}
print(json.dumps(output))
sys.exit(0)
Add hook configuration:
Basic Hook:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh"
}
]
}
]
}
}
Multiple Hooks:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh",
"timeout": 30
},
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/lint.sh",
"timeout": 60
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py"
}
]
}
]
}
}
No Matcher (events without tools):
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/add-context.sh"
}
]
}
]
}
}
Each event receives JSON on stdin:
Common fields:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/current/working/dir",
"permission_mode": "default",
"hook_event_name": "PostToolUse"
}
PreToolUse/PostToolUse:
{
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": { /* PostToolUse only */
"success": true
}
}
UserPromptSubmit:
{
"prompt": "User's submitted message"
}
Stop/SubagentStop:
{
"stop_hook_active": false
}
0: Success
2: Blocking error
Other: Non-blocking warning
Test script directly:
# Create test input
echo '{
"tool_name": "Write",
"tool_input": {
"file_path": "test.txt",
"content": "hello"
}
}' | .claude/hooks/your-hook.sh
# Check exit code
echo $?
Test in Claude Code:
1. Add hook to settings.json
2. Restart Claude Code
3. Run /hooks to verify it's loaded
4. Trigger the hook (e.g., write a file)
5. Check verbose mode (Ctrl+O) for output
Debug mode:
claude --debug
# Shows hook execution details
Show the complete configuration:
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh",
"timeout": 60
}
]
}
]
}
}
Hook script (.claude/hooks/format-python.sh):
#!/usr/bin/env bash
INPUT=$(cat)
# Get file path
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Only process .py files
if [[ "$FILE_PATH" == *.py ]]; then
# Run black formatter
python -m black "$FILE_PATH" 2>&1
if [[ $? -eq 0 ]]; then
echo "Formatted: $FILE_PATH" >&2
fi
fi
exit 0
Configuration:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-python.sh"
}
]
}
]
}
}
Hook script (.claude/hooks/validate-bash.py):
#!/usr/bin/env python3
import json
import sys
import re
# Dangerous patterns
DANGEROUS = [
(r'\brm\s+-rf\s+/', 'Dangerous: rm -rf on root'),
(r'>\s*/dev/sd[a-z]', 'Dangerous: writing to block device'),
(r'\bcurl\s+.*\|\s*bash', 'Dangerous: piping curl to bash'),
]
try:
data = json.load(sys.stdin)
except:
sys.exit(1)
if data.get('tool_name') != 'Bash':
sys.exit(0)
command = data.get('tool_input', {}).get('command', '')
# Check for dangerous patterns
for pattern, message in DANGEROUS:
if re.search(pattern, command):
print(f"⚠️ {message}", file=sys.stderr)
print(f"Command: {command}", file=sys.stderr)
sys.exit(2) # Block
sys.exit(0) # Allow
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py"
}
]
}
]
}
}
Hook script (.claude/hooks/add-timestamp.sh):
#!/usr/bin/env bash
# Output current timestamp
echo "Current time: $(date '+%Y-%m-%d %H:%M:%S %Z')"
exit 0
Configuration:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/add-timestamp.sh"
}
]
}
]
}
}
Hook script (.claude/hooks/auto-approve-docs.py):
#!/usr/bin/env python3
import json
import sys
data = json.load(sys.stdin)
if data.get('tool_name') != 'Read':
sys.exit(0)
file_path = data.get('tool_input', {}).get('file_path', '')
# Auto-approve docs
if any(file_path.endswith(ext) for ext in ['.md', '.txt', '.json', '.yaml']):
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Documentation file auto-approved"
},
"suppressOutput": True
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0)
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-approve-docs.py"
}
]
}
]
}
}
Hook script (.claude/hooks/block-secrets.sh):
#!/usr/bin/env bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Block sensitive files
if [[ "$FILE_PATH" =~ \.env ||
"$FILE_PATH" =~ secrets/ ||
"$FILE_PATH" =~ \.aws/ ]]; then
echo "⛔ Access to sensitive file blocked: $FILE_PATH" >&2
exit 2
fi
exit 0
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-secrets.sh"
}
]
}
]
}
}
Configuration:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate whether Claude should stop. Context: $ARGUMENTS\n\nCheck if:\n1. All tasks are complete\n2. Tests are passing\n3. No errors need addressing\n\nRespond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}",
"timeout": 30
}
]
}
]
}
}
Hook script (.claude/hooks/session-setup.sh):
#!/usr/bin/env bash
# Set up environment for session
if [ -n "$CLAUDE_ENV_FILE" ]; then
# Load nvm
source ~/.nvm/nvm.sh
nvm use 20
# Capture environment changes
export -p >> "$CLAUDE_ENV_FILE"
# Add custom variables
echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
# Add context
echo "Development environment initialized"
echo "Node version: $(node --version)"
exit 0
Configuration:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-setup.sh"
}
]
}
]
}
}
Exact match:
"matcher": "Write"
Multiple tools (regex):
"matcher": "Write|Edit|NotebookEdit"
All tools:
"matcher": "*"
Or:
"matcher": ""
MCP tools:
"matcher": "mcp__github__.*"
"matcher": "mcp__.*__write.*"
Event-specific matchers:
Notification:
"matcher": "permission_prompt"
"matcher": "idle_prompt"
PreCompact:
"matcher": "manual"
"matcher": "auto"
SessionStart:
"matcher": "startup"
"matcher": "resume"
"matcher": "clear"
Available in hook scripts:
$CLAUDE_PROJECT_DIR - Absolute path to project root$CLAUDE_CODE_REMOTE - "true" if remote/web, empty if local$CLAUDE_ENV_FILE - (SessionStart only) File to persist env vars✅ Keep hooks fast (<100ms recommended)
✅ Provide clear error messages
✅ Use appropriate exit codes
✅ Quote variables in bash: "$VAR"
✅ Validate inputs before processing
✅ Test thoroughly before deploying
✅ Use $CLAUDE_PROJECT_DIR for portability
✅ Document what your hook does
❌ Run slow operations (full test suites) ❌ Block legitimate operations unnecessarily ❌ Use hooks for everything (be selective) ❌ Forget to handle errors ❌ Skip input validation ❌ Hardcode absolute paths ❌ Leave debug output in production
⚠️ USE AT YOUR OWN RISK
Hooks execute arbitrary commands:
Best practices:
..)Check:
settings.json correctlychmod +x script.sh#!/usr/bin/env bashDebug:
# Run with debug mode
claude --debug
# Check hook execution in output
# Shows: "Executing hooks for PostToolUse:Write"
Check:
echo '{}' | ./script.shecho $?./script.sh | jq .View errors:
claude --debugCheck:
# Make script executable
chmod +x .claude/hooks/script.sh
# Verify permissions
ls -la .claude/hooks/
Validate JSON:
# Test JSON output
echo '{}' | ./script.sh | jq .
# Common issues:
# - Missing quotes
# - Trailing commas
# - Single quotes instead of double
.claude/skills/ai/claude-code/docs/code_claude_com/docs_en_hooks.md.claude/skills/ai/claude-code/docs/code_claude_com/docs_en_hooks-guide.md.claude/skills/ai/claude-code/docs/code_claude_com/docs_en_settings.md.claude/docs/creating-components.mdxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience