xiaohongshu
xiaohongshu
hook-configuration
Use this skill when creating or refining Claude Code hooks. Hooks are shell commands that execute at specific lifecycle events (tool use, prompt submit, notifications, session events). Helps design event handlers for notifications, formatting, logging, feedback, and permission control. Automatica...
Full skill instructions
This skill helps create production-ready Claude Code hooks following Anthropic's official specifications.
Hooks are user-defined shell commands that execute at various points in Claude Code's lifecycle. They provide:
| Feature | Hook | Skill | Command |
|---|---|---|---|
| Trigger | Lifecycle event | Context match | User invocation |
| Type | Shell command | AI capability | Prompt template |
| Use case | Automation, validation | Autonomous features | Reusable prompts |
| Control | Deterministic | LLM-driven | User-initiated |
When to use Hooks: Automatic formatting, logging, notifications, permission control, code validation
Ten lifecycle events trigger hooks:
Executes after Claude creates tool parameters but before processing.
Use cases:
Blocking capability: Yes - exit code 2 blocks with error fed to Claude; JSON output with "permissionDecision": "deny" also blocks
Runs immediately after successful tool completion.
Use cases:
Blocking capability: No - tool already executed
Fires when users submit prompts, before Claude processes them.
Use cases:
Blocking capability: Yes - JSON output with "decision": "block" prevents submission
Activates when permission dialogs appear to users.
Use cases:
Blocking capability: Yes - can allow, deny, or pass through to user
Triggers when Claude Code sends notifications.
Use cases:
Blocking capability: No - notification already generated
Activates when the main agent finishes responding.
Use cases:
Blocking capability: Yes - JSON output with "decision": "block" prevents stop
Runs when subagent tasks complete.
Use cases:
Blocking capability: Yes - JSON output with "decision": "block" prevents stop
Executes before context window compaction operations.
Use cases:
Blocking capability: Yes - can prevent compaction
Triggers at session initialization or resumption.
Use cases:
Blocking capability: No - session already started
Runs when sessions terminate.
Use cases:
Blocking capability: No - session already ending
Hooks are configured in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "echo 'Editing file' >> /tmp/claude-audit.log"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.py$'; then black \"$file_path\"; fi; }"
}
]
}
]
}
}
{
"hooks": {
"EventName": [ // Event type (PreToolUse, PostToolUse, etc.)
{
"matcher": "ToolPattern", // Tool name, regex (pipe-separated), or "*" for all
"hooks": [ // Array of hooks for this matcher
{
"type": "command", // "command" or "prompt"
"command": "shell command", // Shell command to execute
"timeout": 60 // Optional timeout in seconds (default: 60)
}
]
}
]
}
}
~/.claude/settings.json (all projects).claude/settings.json (this project, team-shared).claude/settings.local.json (uncommitted, project-specific overrides)plugin-name/hooks/hooks.json (bundled with plugin)Exact match:
{
"matcher": "Edit"
}
Regex pattern (use pipe for OR):
{
"matcher": "Edit|Write|MultiEdit"
}
Wildcard (match all tools):
{
"matcher": "*"
}
Common tool names: Bash, Read, Write, Edit, MultiEdit, Glob, Grep, Task, WebFetch, WebSearch, TodoRead, TodoWrite, NotebookRead, NotebookEdit
MCP tools: Use mcp__<server>__<tool> pattern (e.g., mcp__github__create_issue)
Hook input arrives via stdin as JSON. Use jq to extract values:
# Access file_path from Edit tool
jq -r '.tool_input.file_path'
# Access bash command
jq -r '.tool_input.command'
# Check if file matches pattern
jq -r '.tool_input.file_path' | { read file_path; if echo "$file_path" | grep -q '\.py$'; then echo "Python file"; fi; }
# Access tool response (PostToolUse only)
jq -r '.tool_response'
Common JSON fields:
.session_id - Current session identifier.transcript_path - Path to conversation transcript.cwd - Current working directory.tool_name - Name of the tool being used.tool_input - Tool parameters (varies by tool).tool_response - Tool output (PostToolUse only).hook_event_name - Name of the eventEnvironment variables:
$CLAUDE_PROJECT_DIR - Project root absolute path$CLAUDE_ENV_FILE - For SessionStart hooks to persist environment variables$CLAUDE_CODE_REMOTE - "true" if remote execution, empty if local${CLAUDE_PLUGIN_ROOT} - Plugin directory path (plugin hooks only)Execute bash scripts with stdin JSON input:
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I {} prettier --write {}"
}
Exit codes:
0 - Success; stdout shown to user (except UserPromptSubmit shows to Claude)2 - Blocking error; stderr fed to Claude as contextSend input to LLM (Haiku) for decisions (PreToolUse, Stop, SubagentStop, UserPromptSubmit):
{
"type": "prompt",
"prompt": "Analyze this tool use and decide if it should be allowed: {{input}}"
}
Returns JSON with decision fields.
For PreToolUse only - exit code 2 blocks execution:
#!/bin/bash
# Block edits to .env files
file_path=$(jq -r '.tool_input.file_path')
if [[ "$file_path" == ".env" ]]; then
echo "ERROR: Cannot edit .env file" >&2
exit 2 # Blocks the edit
fi
exit 0 # Allows the edit
For PreToolUse, UserPromptSubmit, Stop, SubagentStop:
{
"permissionDecision": "deny", // PreToolUse: "allow", "deny", "ask"
"decision": "block", // UserPromptSubmit/Stop/SubagentStop
"reason": "Explanation shown to user",
"systemMessage": "Warning message",
"updatedInput": {...}, // PreToolUse: modify tool parameters
"continue": false, // Stop Claude execution
"stopReason": "User message",
"suppressOutput": true // Hide from transcript
}
⚠️ CRITICAL: Hooks execute arbitrary shell commands automatically with your credentials.
Security checklist:
"$VAR").env, .git, credentials filesHook configuration changes require explicit review via /hooks menu before affecting current sessions.
Log all bash commands to audit file:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \\\"No description\\\")\"' | xargs -I {} sh -c 'echo \"$(date \"+%Y-%m-%d %H:%M:%S\") - {}\" >> ~/.claude-audit.log'"
}
]
}
]
}
}
Auto-format after edits:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -qE '\\.(ts|tsx|js|jsx)$'; then npx prettier --write \"$file_path\"; fi; }"
}
]
}
]
}
}
Format Python files with Black and isort:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.py$'; then black \"$file_path\" && isort \"$file_path\"; fi; }"
}
]
}
]
}
}
Block edits to sensitive files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -qE '\\.(env|env\\..*|git/config|secrets\\.yaml)$'; then echo 'ERROR: Cannot edit protected file' >&2; exit 2; fi; }"
}
]
}
]
}
}
Alert when Claude awaits input:
{
"hooks": {
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude needs your input\" with title \"Claude Code\" sound name \"Glass\"'"
}
]
}
]
}
}
Desktop notification with notify-send:
{
"hooks": {
"Notification": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Awaiting your input' -u normal -t 5000"
}
]
}
]
}
}
Warn before force push:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' | { read cmd; if echo \"$cmd\" | grep -q 'git push --force'; then echo '⚠️ WARNING: Force push detected. Press Enter to continue or Ctrl+C to cancel' >&2; read; fi; }"
}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "if [ -f package.json ]; then npm run test:quick 2>&1 | head -20; fi"
}
]
}
]
}
}
Load project info when session starts:
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "echo \"📋 Project: $(basename $(pwd)) | Branch: $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'N/A') | Files changed: $(git status --short 2>/dev/null | wc -l)\""
}
]
}
]
}
}
Format multiple languages automatically:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; case \"$file_path\" in *.py) black \"$file_path\" && isort \"$file_path\";; *.ts|*.tsx|*.js|*.jsx) npx prettier --write \"$file_path\";; *.go) gofmt -w \"$file_path\";; *.rs) rustfmt \"$file_path\";; esac; }"
}
]
}
]
}
}
Execute only in specific conditions:
#!/bin/bash
# ~/.claude/hooks/conditional-formatter.sh
file_path=$(jq -r '.tool_input.file_path')
# Only format files in frontend/ directory
if [[ "$file_path" == frontend/* ]]; then
case "$file_path" in
*.ts|*.tsx)
prettier --write "$file_path"
;;
*.css)
prettier --write "$file_path"
;;
esac
fi
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/conditional-formatter.sh"
}
]
}
]
}
}
Different behavior per environment:
#!/bin/bash
# Check NODE_ENV before running tests
if [[ "$NODE_ENV" == "production" ]]; then
echo "⚠️ Production environment - running full test suite..."
npm run test:full
else
echo "Development environment - running quick tests"
npm run test:quick
fi
Include git context in logs:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r --arg branch \"$(git rev-parse --abbrev-ref HEAD 2>/dev/null)\" --arg ts \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" '{timestamp: $ts, branch: $branch, tool: .tool_name, command: .tool_input.command}' >> ~/.claude-context.jsonl"
}
]
}
]
}
}
Add to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I {} echo 'Reading: {}' >> /tmp/hook-test.log"
}
]
}
]
}
}
Ask Claude to read a file.
cat /tmp/hook-test.log
# Should show: Reading: <file-path>
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read fp; if [[ \"$fp\" == \"test.txt\" ]]; then echo 'Blocked test.txt' >&2; exit 2; fi; }"
}
]
}
]
}
}
Attempt to edit test.txt - should be blocked.
❌ Incorrect JSON structure:
{
"hooks": [ // Wrong - should be object with event names as keys
{"event": "PreToolUse", ...}
]
}
✅ Correct structure:
{
"hooks": {
"PreToolUse": [ // Event name as key
{"matcher": "Edit", ...}
]
}
}
❌ Using old environment variable syntax:
{
"command": "black \"$file_path\"" // Variables don't work this way
}
✅ Using jq to extract from stdin:
{
"command": "jq -r '.tool_input.file_path' | xargs -I {} black {}"
}
❌ Blocking in non-blocking events:
{
"hooks": {
"PostToolUse": [ // Can't block after execution
{"matcher": "Edit", "hooks": [{"type": "command", "command": "exit 2"}]}
]
}
}
❌ Logging sensitive data:
{
"command": "jq '.' | curl -d @- https://external-log-service.com"
}
✅ Safe local logging:
{
"command": "jq -r '{timestamp: now, tool: .tool_name}' >> ~/.local-audit.jsonl"
}
Reference the examples directory for:
Next Steps: After creating hooks, test in a safe environment, verify they don't leak credentials, check performance impact, and document for team members. Use /hooks menu to review and approve hook configuration changes.
xiaohongshu
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