Claude Code Hooks Automation Guide: Automate Every Step of Your Workflow

Complete guide to Claude Code hooks — automate testing, linting, deployments, and notifications. With real-world hook recipes for pre-command, post-command, and pipeline integration.

·11 min read

What Are Claude Code Hooks?

Claude Code hooks are scripts that run automatically at specific points in your Claude Code session. They let you:

- Run tests before Claude edits files (pre-edit validation) - Auto-save session state (prevent data loss) - Send notifications when Claude finishes a task - Integrate with CI/CD (auto-deploy after changes) - Enforce code quality (lint on every write)

Think of them as git hooks for your AI coding session — they run automatically at key moments to enforce rules, save state, and trigger actions.

---

Hook Types and When They Fire

Claude Code supports these hooks, configured in .claude/settings.json:

{
  "hooks": {
    "preMessage": "pre-message-script.sh",
    "postMessage": "post-message-script.sh",
    "preToolUse": {
      "Read": "pre-read-hook.sh",
      "Edit": "pre-edit-hook.sh",
      "Bash": "pre-bash-hook.sh"
    },
    "postToolUse": {
      "Read": "post-read-hook.sh",
      "Edit": "post-edit-hook.sh",
      "Bash": "post-bash-hook.sh"
    }
  }
}

| Hook | When It Fires | Use Case | |------|--------------|----------| | preMessage | Before Claude responds | Log the user's input, set context | | postMessage | After Claude responds | Save session, send notifications | | preToolUse.Read | Before reading a file | Audit file access | | preToolUse.Edit | Before editing a file | Run linter, check permissions | | postToolUse.Edit | After editing a file | Auto-commit, run tests | | preToolUse.Bash | Before running a command | Validate command safety | | postToolUse.Bash | After running a command | Log output, notify |

---

Hook Recipe 1: Auto-Save Session State

Prevent data loss from crashes or context resets:

File: .claude/hooks/post-message.sh

#!/bin/bash
# Auto-save session state after every message
SESSION_LOG=".claude/session-log.md"

# Copy the log if it exists if [ -f "$SESSION_LOG" ]; then cp "$SESSION_LOG" ".claude/backups/$(date +%Y%m%d-%H%M%S).md" fi

Config:

{
  "hooks": {
    "postMessage": ".claude/hooks/post-message.sh"
  }
}

> 💡 Pro tip: Create the backup directory: mkdir -p .claude/backups

---

Hook Recipe 2: Auto-Run Tests After Changes

Ensure Claude doesn't break your code:

File: .claude/hooks/post-edit.sh

#!/bin/bash
# Run tests after every file edit

# Only run if package.json exists (JS/TS project) if [ -f "package.json" ]; then # Run relevant tests — adjust for your test runner if grep -q '"test"' package.json 2>/dev/null; then npm test -- --changedSince=HEAD 2>&1 | tail -20 fi fi

Config:

{
  "hooks": {
    "postToolUse": {
      "Edit": ".claude/hooks/post-edit.sh"
    }
  }
}

> ⚠️ Caution: Running full test suites on every edit can slow Claude significantly. Use --changedSince=HEAD or target specific test files.

---

Hook Recipe 3: Enforce Code Style on Every Edit

Auto-format code before Claude commits changes:

File: .claude/hooks/pre-edit.sh

#!/bin/bash
# Run linter on files before Claude edits them
# Collects pre-edit lint errors for comparison

TARGET_FILE=$(echo "$@" | grep -oP '(?<=")[^"]+(?=")' | head -1)

if [ -n "$TARGET_FILE" ] && [ -f "$TARGET_FILE" ]; then echo "Pre-edit lint check: $TARGET_FILE" npx eslint "$TARGET_FILE" 2>/dev/null || true fi

Config:

{
  "hooks": {
    "preToolUse": {
      "Edit": ".claude/hooks/pre-edit.sh"
    }
  }
}

---

Hook Recipe 4: Slack/Telegram Notifications

Get notified when Claude completes a long task:

File: .claude/hooks/post-message-notify.sh

#!/bin/bash
# Send notification for completed tasks
# Requires a webhook URL in .env

if [ -f ".env" ]; then source .env fi

WEBHOOK_URL="${SLACK_WEBHOOK_URL:-$TELEGRAM_WEBHOOK_URL}"

if [ -n "$WEBHOOK_URL" ]; then curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "{\"text\": \"✅ Claude Code completed work in $(pwd)\"}" & fi

External Links: - Slack Incoming Webhooks - Telegram Bot API

---

Hook Recipe 5: Auto-Commit Changes

Create automatic checkpoints:

File: .claude/hooks/auto-commit.sh

#!/bin/bash
# Auto-commit changes with a descriptive message

# Check if there are changes to commit if [ -n "$(git status --porcelain)" ]; then git add -A git commit -m "🤖 Claude Code checkpoint: $(date '+%Y-%m-%d %H:%M')" \ --no-verify 2>/dev/null || true fi

Config:

{
  "hooks": {
    "postToolUse": {
      "Edit": ".claude/hooks/auto-commit.sh"
    }
  }
}

> ⚠️ Caution: Auto-committing can clutter your git history. Use sparingly — every 5-10 edits, not every single one.

---

Hook Recipe 6: Safety Check for Sensitive Operations

Prevent accidental rm -rf or git pushes to main:

File: .claude/hooks/pre-bash.sh

#!/bin/bash
# Safety checks before dangerous commands

COMMAND="$*"

# Block dangerous commands if echo "$COMMAND" | grep -qE "rm -rf /|:(){ :\|:& };:"; then echo "⛔ Blocked: potentially dangerous command detected" exit 1 fi

# Warn on git push to main if echo "$COMMAND" | grep -qE "git push origin main"; then echo "⚠️ Warning: Pushing to main branch!" echo "Are you sure? (yes/no)" read -r response if [ "$response" != "yes" ]; then echo "Push cancelled" exit 1 fi fi

Config:

{
  "hooks": {
    "preToolUse": {
      "Bash": ".claude/hooks/pre-bash.sh"
    }
  }
}

---

Hook Recipe 7: Custom ESLint Rules File

Generate or update eslint config when project changes:

File: .claude/hooks/generate-eslint.sh

#!/bin/bash
# Generate project-specific eslint rules

# If .eslintrc.json doesn't exist, create a starter if [ ! -f ".eslintrc.json" ] && [ -f "package.json" ]; then if grep -q "typescript" package.json 2>/dev/null; then cat > .eslintrc.json << 'EOF' { "extends": ["next/core-web-vitals", "prettier"], "rules": { "no-unused-vars": "warn", "no-console": "warn", "@typescript-eslint/no-explicit-any": "warn" } } EOF fi fi

---

Hook Recipe 8: Project Context Injection

Auto-inject relevant context before every message:

File: .claude/hooks/pre-message.sh

#!/bin/bash
# Inject project context from a context file

CONTEXT_FILE=".claude/context.md"

if [ -f "$CONTEXT_FILE" ]; then echo "📋 Current context loaded from $CONTEXT_FILE" fi

Create the context file:

# .claude/context.md

Current Sprint

Building payment integration (Sprint 4)

Active Branch

feature/stripe-checkout

- src/app/api/stripe/create-checkout/route.ts - src/components/CheckoutForm.tsx - src/lib/stripe.ts

---

Setting Up Hooks: Step by Step

1. Create the Hooks Directory

mkdir -p .claude/hooks
mkdir -p .claude/backups

2. Create Your First Hook Script

cat > .claude/hooks/post-message.sh << 'EOF'
#!/bin/bash
echo "✅ Message processed at $(date)"
EOF

chmod +x .claude/hooks/post-message.sh

3. Register the Hook

// .claude/settings.json
{
  "hooks": {
    "postMessage": ".claude/hooks/post-message.sh"
  }
}

4. Test It

claude
> Hello
# You should see: ✅ Message processed at 2026-05-11 14:30:00

5. Debug Hooks

Hook output goes to stderr. Check Claude Code logs:

cat ~/.claude/logs/claude-code-*.log | grep -i hook

---

Best Practices

DO:

- ✅ Keep hooks fast — under 500ms if possible. Slow hooks make Claude feel sluggish. - ✅ Make hooks idempotent — running them multiple times should be safe. - ✅ Test hooks independently before registering them: bash .claude/hooks/post-message.sh - ✅ Version control your hooks directory: commit .claude/hooks/ to git. - ✅ Use conditional logic — only run tests if package.json exists.

DON'T:

- ❌ Don't make hooks interactive — Claude can't answer prompts. - ❌ Don't use hooks to modify Claude's output — the hook is just a side-effect script. - ❌ Don't put secrets in hook scripts — use environment variables. - ❌ Don't modify files being edited during a pre-edit hook — this can cause race conditions.

---

Hook Performance Budget

| Hook Type | Max Time | Impact on Claude | |-----------|----------|------------------| | preMessage | <500ms | Delays initial response | | postMessage | <2s | No delay (runs after response) | | preToolUse | <300ms | Delays tool execution | | postToolUse | <2s | No visible delay |

If your hook takes longer, run it in background:

# Background the expensive part:
( sleep 5 && npm test ) &

Related Articles: - Claude Code Memory Systems Explained — hooks can save session state - Claude Code Hooks vs MCP Servers — when to use each - 7 Ways to Speed Up Claude Code — hooks should be fast! - Agent Skills for Claude Code — extend Claude Code beyond hooks

Ad Unit Placeholder

Related Articles