Hooks
Hooks let you run custom code when Claude performs specific actions. Use them to enforce policies, add notifications, or extend functionality.
What Are Hooks?
Section titled “What Are Hooks?”Hooks are scripts that execute at specific points in Claude’s workflow.
Available Hook Events
Section titled “Available Hook Events”| Hook | Trigger | Use Case |
|---|---|---|
PreToolUse | Before a tool executes | Validate inputs, block dangerous operations |
PostToolUse | After a tool completes | Auto-format files, run linters |
UserPromptSubmit | When user submits a prompt | Log prompts, validate input |
Stop | When Claude finishes responding | Git checks, notifications |
SessionStart | When a session begins | Environment setup, logging |
Notification | When Claude sends an alert | Custom notification delivery |
SubagentStop | When a subagent completes | Aggregate results, cleanup |
PreCompact | Before message compaction | Save context, log state |
Environment Variables in Hooks
Section titled “Environment Variables in Hooks”Your hook scripts have access to these environment variables:
| Variable | Description |
|---|---|
CLAUDE_PROJECT_DIR | Absolute path to the project root directory |
CLAUDE_CODE_REMOTE | "true" if running in web environment, empty if local CLI |
Example usage in a hook script:
#!/bin/bashecho "Project directory: $CLAUDE_PROJECT_DIR"
if [ "$CLAUDE_CODE_REMOTE" = "true" ]; then echo "Running in remote/web environment"else echo "Running locally"fiHook Behavior
Section titled “Hook Behavior”Timeout
Section titled “Timeout”Hooks have a 60-second timeout by default. Configure per-command:
{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "my-slow-script.sh", "timeout": 120000 }] }] }}Parallel Execution
Section titled “Parallel Execution”All matching hooks run in parallel. If you need sequential execution, combine commands in a single script.
Deduplication
Section titled “Deduplication”Multiple identical hook commands are automatically deduplicated—the same command won’t run twice for the same event.
Configuring Hooks
Section titled “Configuring Hooks”Recommended: Interactive Menu
Section titled “Recommended: Interactive Menu”Use the /hooks command for the easiest configuration:
/hooksThis opens an interactive menu where you can:
- View all configured hooks
- Add new hooks
- Edit existing hooks
- Remove hooks
- Test hook execution
Alternative: JSON Configuration
Section titled “Alternative: JSON Configuration”Edit .claude/settings.json directly:
{ "hooks": { "PostToolUse": [{ "matcher": "Edit", "hooks": [{ "type": "command", "command": "npx prettier --write $CLAUDE_PROJECT_DIR" }] }] }}Setting Up Hooks
Section titled “Setting Up Hooks”Create a settings.local.json in your project or ~/.claude/:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" } ] } ] }}This plays a sound when Claude finishes a response.
Hook Types
Section titled “Hook Types”Command Hooks
Section titled “Command Hooks”Run a shell command:
{ "type": "command", "command": "notify-send 'Claude finished'"}Script Hooks
Section titled “Script Hooks”Run a script file:
{ "type": "command", "command": "python3 ~/.claude/hooks/on-stop.py"}Security Considerations
Section titled “Security Considerations”Common Hook Patterns
Section titled “Common Hook Patterns”1. Notification on Complete
Section titled “1. Notification on Complete”Get notified when Claude finishes long tasks:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'" } ] } ] }}2. Log All Tool Usage
Section titled “2. Log All Tool Usage”Track what tools Claude uses:
{ "hooks": { "PostToolUse": [ { "hooks": [ { "type": "command", "command": "echo \"$(date): $TOOL_NAME\" >> ~/.claude/tool-log.txt" } ] } ] }}3. Auto-Format on File Write
Section titled “3. Auto-Format on File Write”Format files after Claude writes them:
{ "hooks": { "PostToolUse": [ { "matcher": { "tool": "Write" }, "hooks": [ { "type": "command", "command": "black $FILE_PATH 2>/dev/null || true" } ] } ] }}4. Prevent Dangerous Commands
Section titled “4. Prevent Dangerous Commands”Block certain commands:
#!/usr/bin/env python3import sysimport jsonimport os
BLOCKED = ["rm -rf /", "DROP TABLE", "FORMAT"]
input_data = json.loads(sys.stdin.read())command = input_data.get("command", "")
for blocked in BLOCKED: if blocked.lower() in command.lower(): print(json.dumps({ "decision": "block", "reason": f"Blocked dangerous pattern: {blocked}" })) sys.exit(0)
print(json.dumps({"decision": "allow"})){ "hooks": { "PreToolUse": [ { "matcher": { "tool": "Bash" }, "hooks": [ { "type": "command", "command": "python3 ~/.claude/hooks/check-command.py" } ] } ] }}Hook Matchers
Section titled “Hook Matchers”Filter which events trigger a hook:
{ "matcher": { "tool": "Bash" // Only Bash commands }}{ "matcher": { "tool": "Write", "file_pattern": "*.py" // Only Python files }}Hook Responses
Section titled “Hook Responses”Hooks can return JSON to control behavior:
Allow (default)
Section titled “Allow (default)”{"decision": "allow"}{ "decision": "block", "reason": "This action is not permitted"}Modify
Section titled “Modify”{ "decision": "allow", "modifications": { "command": "safer-version-of-command" }}Directory Structure
Section titled “Directory Structure”Directory~/.claude/
- settings.local.json (hook configuration)
Directoryhooks/
- on-stop.py
- check-command.py
- format-file.sh
Debugging Hooks
Section titled “Debugging Hooks”Check Hook Execution
Section titled “Check Hook Execution”# Add logging to your hookecho "Hook triggered: $TOOL_NAME" >> /tmp/claude-hooks.logTest Hooks Manually
Section titled “Test Hooks Manually”# Test a command hookecho '{"tool": "Bash", "command": "ls"}' | python3 ~/.claude/hooks/check-command.pyAdvanced: Continuous Workflow Hook
Section titled “Advanced: Continuous Workflow Hook”Force Claude to always ask “what’s next?”:
#!/usr/bin/env python3import json
print(json.dumps({ "decision": "block", "reason": "Use the AskUserQuestion tool to ask what to do next before stopping."})){ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "python3 ~/.claude/hooks/continue.py" } ] } ] }}Now Claude will always ask for your next instruction instead of stopping.
Hooks in Skills and Agents
Section titled “Hooks in Skills and Agents”You can add hooks directly in skill or agent frontmatter:
---name: my-skillhooks: PostToolUse: - matcher: "Write(**/*.tsx)" command: "npm run lint:fix"---This keeps hooks bundled with their related functionality.