Claude Code Hooks: Automate Before & After Every Action (2026 Guide) — Claude Skills 360 Blog
Blog / Advanced / Claude Code Hooks: Automate Before & After Every Action (2026 Guide)
Advanced

Claude Code Hooks: Automate Before & After Every Action (2026 Guide)

Published: April 18, 2026
Read time: 10 min read
By: Claude Skills 360

Most Claude Code users know about skills and slash commands. Fewer know about hooks — and the ones who do use them to build automations that feel like magic.

Hooks let you run any shell command automatically in response to Claude Code events. Before a tool executes. After a tool completes. Every time Claude stops responding. When a subagent finishes. These are programmable triggers that plug directly into Claude Code’s execution lifecycle.

If you’ve ever wanted Claude Code to automatically run your test suite after every code change, log every file it touches, or notify you via Slack when a long task finishes — hooks are how you do it.

What Are Claude Code Hooks?

Hooks are shell commands (or arrays of shell commands) that Claude Code executes at specific lifecycle events. They’re configured in ~/.claude/settings.json under the hooks key.

There are five hook types:

HookFires When
PreToolUseBefore any tool is called
PostToolUseAfter a tool completes
StopWhen Claude finishes responding
SubagentStopWhen a subagent finishes
NotificationWhen Claude Code sends a system notification

Each hook receives context about what just happened — the tool name, inputs, outputs, and event details — so your scripts can make decisions based on what Claude is actually doing.

Setting Up Your First Hook

Hooks live in ~/.claude/settings.json. Open it (or create it) and add a hooks section:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'File written' >> ~/.claude/activity.log"
          }
        ]
      }
    ]
  }
}

This logs every time Claude Code writes a file. The matcher field filters which tool triggers the hook — you can match specific tools like Write, Edit, Bash, or use ".*" to match everything.

The Five Hook Types in Detail

PreToolUse — Run Before Any Tool

Use PreToolUse to validate, log, or block tool execution before it happens.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[$(date -Iseconds)] BASH: $CLAUDE_TOOL_INPUT\" >> ~/.claude/bash-audit.log"
          }
        ]
      }
    ]
  }
}

This creates an audit trail of every Bash command Claude executes. Useful for security reviews and debugging long autonomous runs.

Pro tip: If a PreToolUse hook exits with a non-zero code, Claude Code will see it and can abort or modify its approach.

PostToolUse — React to What Claude Just Did

This is the most versatile hook type. Run tests after an edit, format code after a write, sync files after changes:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd $(git rev-parse --show-toplevel 2>/dev/null || echo '.') && git diff --stat HEAD 2>/dev/null | tail -1 >> ~/.claude/changes.log"
          }
        ]
      }
    ]
  }
}

This logs a git diff summary every time Claude edits or creates a file — a running change log of everything it modified.

Stop — When the Conversation Ends

The Stop hook fires whenever Claude finishes a response. Use it to run comprehensive checks after a task completes:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cd $(git rev-parse --show-toplevel 2>/dev/null || echo '.') && [ -f 'package.json' ] && npm test --silent 2>&1 | tail -5 || true"
          }
        ]
      }
    ]
  }
}

This auto-runs your test suite silently after every Claude session. If tests break, you’ll know immediately — Claude’s output shows the tail of the test results.

SubagentStop — React to Subagent Completions

When Claude spawns subagents (via the Agent tool in Claude Code), each one fires SubagentStop when it finishes. Use this to coordinate between parallel workstreams or collect results:

{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[$(date -Iseconds)] Subagent finished\" >> ~/.claude/subagent-log.txt"
          }
        ]
      }
    ]
  }
}

Notification — Catch System Events

Fires when Claude Code sends desktop notifications. Useful for forwarding to custom notification systems:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs attention\" with title \"Claude Code\"' 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

5 Practical Hook Setups

1. Auto-Format After Every Edit

Never manually run Prettier again. Format every file as soon as Claude writes it:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(echo \"$CLAUDE_TOOL_INPUT\" | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('file_path',''))\" 2>/dev/null); [ -n \"$FILE\" ] && command -v prettier >/dev/null && prettier --write \"$FILE\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

2. Slack Notification on Long Task Completion

Hooks shine brightest during autonomous agent runs — when Claude is completing a 15-minute task unattended, you want to know when it’s done. Get notified on your phone when an autonomous build finishes:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST \"$SLACK_WEBHOOK_URL\" -H 'Content-type: application/json' -d '{\"text\":\"Claude Code task finished on '\"$(hostname)\"'\"}' 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

Set SLACK_WEBHOOK_URL in your shell profile. Claude Code inherits environment variables from your shell.

3. Auto-Commit After Every File Write

Build a granular git history of everything Claude touches:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd $(git rev-parse --show-toplevel 2>/dev/null || echo '.') && git add -A && git diff --cached --quiet || git commit -m 'chore: claude auto-save [skip ci]' --no-verify 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

Every change gets committed automatically. You’ll never lose work from a crashed session again.

4. Run Type-Checking After TypeScript Edits

Catch type errors immediately rather than at the end of a long task:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(echo \"$CLAUDE_TOOL_INPUT\" | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('file_path',''))\" 2>/dev/null); echo \"$FILE\" | grep -q '\\.ts' && npx tsc --noEmit 2>&1 | tail -5 || true"
          }
        ]
      }
    ]
  }
}

5. Security Audit on Bash Commands

Log every shell command Claude runs for a post-session security review:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(echo \"$CLAUDE_TOOL_INPUT\" | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('command',''))\" 2>/dev/null); echo \"[$(date -Iseconds)] $CMD\" >> ~/.claude/bash-history.log"
          }
        ]
      }
    ]
  }
}

Review ~/.claude/bash-history.log after any autonomous session to see exactly what Claude executed.

Combining Hooks with Claude Code Skills

Hooks become even more powerful when combined with Claude Code skills. A skill defines what Claude does — a hook defines what happens immediately after.

For example, if you have a /deploy skill that pushes changes to staging, a Stop hook can automatically run smoke tests against the staging URL after the deploy completes. The skill focuses on the deployment logic; the hook handles the verification.

Similarly, a /security-audit skill that generates a report can trigger a PostToolUse hook that exports the results to a ticket in Linear or Jira.

This is the pattern that separates a casual Claude Code user from someone running production-grade automations: skills handle the intelligence, hooks handle the integration.

Environment Variables Available to Hooks

When a hook fires, Claude Code sets environment variables your script can read:

VariableContains
CLAUDE_TOOL_NAMEName of the tool being called
CLAUDE_TOOL_INPUTJSON of tool inputs
CLAUDE_TOOL_OUTPUTJSON of tool output (PostToolUse only)
CLAUDE_SESSION_IDCurrent Claude Code session ID

Use $CLAUDE_TOOL_NAME to write a single hook that branches by tool:

#!/bin/bash
case "$CLAUDE_TOOL_NAME" in
  Write|Edit)
    echo "File modified" >> ~/.claude/file-log.txt
    ;;
  Bash)
    echo "Shell command" >> ~/.claude/bash-log.txt
    ;;
esac

Save this as ~/.claude/hook-dispatcher.sh, chmod +x it, and reference it in your hooks config.

Keeping Hooks Fast

Hooks pair well with Claude Code’s memory system — use hooks to log what happened, and memory files to carry context across sessions. A PostToolUse hook can append to a session log that your memory system reads next time.

Hooks block the Claude Code event loop if they take too long. Keep them under 1-2 seconds for smooth operation:

  • Background long-running tasks with & and capture the PID
  • Use || true at the end of every hook command so a hook failure doesn’t kill the session
  • Test hooks interactively before adding them to settings.json
  • Use 2>/dev/null to suppress noisy error output

Getting Started

The quickest way to start is with a simple activity log:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command", 
            "command": "echo \"[$(date -Iseconds)] $CLAUDE_TOOL_NAME\" >> ~/.claude/activity.log"
          }
        ]
      }
    ]
  }
}

Add this to ~/.claude/settings.json, run a Claude Code session, then cat ~/.claude/activity.log to see exactly what Claude did. From there, you can filter by tool, add conditionals, and build up to the more sophisticated setups above. And if you want Claude’s behavior within sessions to be consistent — not just what happens after — that’s where CLAUDE.md and slash commands come in.


Hooks are one of those features that seem minor at first — until you’ve had Claude automatically run your test suite, commit your changes, and notify you on Slack while you were making coffee. At that point they become something you can’t imagine working without.

For even more automation building blocks, the Claude Skills 360 bundle includes 2,350+ pre-built skills, 45 autonomous agents, and 12 multi-agent swarms — all designed for exactly this kind of production-grade automation. The free starter kit comes with 360 skills and installs in 90 seconds.

Keep Reading

Advanced

Claude Code Agents: How to Run Autonomous Tasks Without Staying at Your Keyboard

Learn how Claude Code agents differ from skills, how to configure autonomous agents for background work, and real-world patterns for multi-agent development workflows.

10 min read Apr 22, 2026
Advanced

How to Create Your Own Claude Code Skills (Complete Guide 2026)

Learn to build custom Claude Code skills from scratch — the file format, slash commands, agent patterns, and how to structure skills that actually work in production.

11 min read Apr 20, 2026
AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free