Claude Code: Hooks System

Last updated: 2026-08-31

Hooks let you insert custom logic at Claude Code's key points — auto-backup before modifications, auto-test before commits, auto-rollback on errors.

💡 Tip: Hooks are "interceptors" in Claude Code's lifecycle — you execute custom scripts when specific events occur, implementing automated workflows.

📋 Prerequisites: Chapter 17 - Output Styles

1. What You'll Learn


2. Hook Lifecycle

(1) Event Types

Event Trigger Time Common Use
before:prompt Before processing user input Input preprocessing
before:tool:write Before writing file Auto-backup
after:tool:write After writing file Auto-format
before:tool:bash Before executing command Security check
after:tool:bash After executing command Result post-processing
after:response After generating response Send notification

(2) Hook Context

Each hook receives a context object with event name, timestamp, file path, command, content, and session ID.

▶ Example 1: Hook Trigger Flow

TEXT 📖 Display only
> Modify src/auth/jwt.ts

Triggered hooks:
1. [before:tool:write] → Auto-backup jwt.ts
2. [File written]
3. [after:tool:write] → Run ESLint --fix
4. [before:tool:bash] → Check command safety
5. [Execute: npm test]
6. [after:tool:bash] → Parse test results
7. [after:response] → Send Slack notification

3. Hook Configuration

(1) Global Hook Configuration

JSON
// ~/.claude/hooks.json
{
  "hooks": {
    "before:tool:write": [
      {
        "name": "auto-backup",
        "command": "cp ${filePath} ${filePath}.bak",
        "enabled": true
      }
    ],
    "after:tool:write": [
      {
        "name": "auto-format",
        "command": "npx prettier --write ${filePath}",
        "enabled": true
      }
    ]
  }
}

(2) Project-Level Hooks

JSON
// .claude/hooks.json
{
  "hooks": {
    "before:tool:write": [
      {
        "name": "protect-config",
        "condition": "filePath.endsWith('.env')",
        "command": "echo 'Config file modification blocked' && exit 1",
        "enabled": true
      }
    ]
  }
}

4. Practical Scenarios

▶ Example 2: Auto-Backup Hook

JSON
{
  "hooks": {
    "before:tool:write": [
      {
        "name": "git-backup",
        "command": "git stash push -m 'auto-backup-before-claude' -- ${filePath} 2>/dev/null || true",
        "enabled": true
      }
    ]
  }
}

▶ Example 3: Security Audit Hook

JSON
{
  "hooks": {
    "before:tool:bash": [
      {
        "name": "block-dangerous-commands",
        "condition": "command.includes('rm -rf') || command.includes('DROP TABLE')",
        "command": "echo '⚠️ Dangerous command blocked' && exit 1",
        "enabled": true
      }
    ]
  }
}

▶ Example 4: Auto-Test Hook

JSON
{
  "hooks": {
    "after:tool:write": [
      {
        "name": "auto-test",
        "condition": "filePath.includes('src/') && filePath.endsWith('.ts')",
        "command": "npm test 2>&1 | tail -5",
        "enabled": true
      }
    ]
  }
}

5. Hook Debugging

BASH
# Enable hook debugging
export CLAUDE_HOOK_DEBUG=1

# View hook execution logs
cat ~/.claude/hooks.log

# Temporarily disable all hooks
claude --no-hooks

Common Issues

Problem Cause Solution
Hook not triggering enabled: false Check config
Hook errors Command path issues Use absolute paths
Hook slow Script execution time Async or simplify logic
Loop triggering Hook triggers another hook Add conditions to avoid

6. Comprehensive Example: Complete Hook Solution

JSON
{
  "hooks": {
    "before:tool:write": [
      {
        "name": "auto-backup",
        "command": "cp ${filePath} /tmp/claude-backup/$(basename ${filePath}).$(date +%s)",
        "enabled": true
      },
      {
        "name": "protect-env",
        "condition": "filePath.endsWith('.env')",
        "command": "echo '❌ Env file modification blocked' && exit 1",
        "enabled": true
      }
    ],
    "after:tool:write": [
      {
        "name": "format",
        "command": "npx prettier --write ${filePath} 2>/dev/null; npx eslint --fix ${filePath} 2>/dev/null; true",
        "enabled": true,
        "files": ["src/**/*.ts"]
      }
    ],
    "before:tool:bash": [
      {
        "name": "block-dangerous",
        "condition": "command.match(/rm -rf|DROP|npm publish/)",
        "command": "echo '⛔ Dangerous command blocked' && exit 1",
        "enabled": true
      }
    ]
  }
}

❓ FAQ

Q Do hooks slow down Claude Code?
A Yes. Each hook executes a command; slow hooks noticeably impact experience. Keep hook scripts under 1 second.
Q Does hook failure block operations?
A before hooks that fail (exit 1) block operations; after hook failures don't affect completed operations.
Q Can hooks modify Claude Code's output?
A No. Hooks only perform side effects (backup, format, notify), not change returned content.
Q Hook vs Plugin?
A Hooks are lightweight event responses (execute commands); plugins are complete feature extensions (register tools, modify behavior). Simple needs: hooks; complex needs: plugins.

📖 Summary


📝 Exercises

  1. Basic (⭐): Configure an after:tool:write hook that auto-runs Prettier on file modification.
  2. Intermediate (⭐⭐): Configure security hooks to block rm -rf and npm publish.
  3. Advanced (⭐⭐⭐): Design complete hook solution covering backup, format, security check, and notification.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏