Agent工坊

【Agent工坊】Claude Code Hooks实战:3个钩子让你的AI编程助手安全10倍

CLAUDE.md 是道德劝说,Hooks 才是法律。四个生命周期钩子,让你的 AI 编程助手从不靠谱的实习生变成靠谱的高级工程师。

Claude Code Hooks 四层防线:PreToolUse、PostToolUse、Stop▲ Claude Code Hooks 四层防线:PreToolUse、PostToolUse、Stop、Notification 生命周期事件

前言

如果你在用 Claude Code 写代码,你一定遇到过这些场景:

  • Claude 改完三个文件,你手动跑了三遍 eslint --fix
  • Claude 写了一段代码,你担心它是不是偷偷引入了安全漏洞
  • 你让 Claude "每次改完文件跑一下测试",但它在第 10 轮对话后"忘了"
  • 团队里有人用 Claude Code 改了生产配置,你想知道到底改了什么

这些问题的根源是一样的:你只有口头约定,没有代码强制。

Claude Code 在 2025 年底推出的 Hooks 系统,正是解决这个问题的。它不是在 prompt 里"建议"AI 做什么,而是在引擎层面——到达某个生命周期节点时,强制执行你的脚本。今天这篇文章,我们就把 Hooks 从概念到生产级配置,全部讲透。

什么是 Hooks

Hooks 是 Claude Code 的"生命周期事件监听器"。当某个事件发生时(比如工具调用前、工具调用后、会话结束时),Claude Code 会自动触发你预定义的脚本。

这是 Claude Code 从"AI 编程 CLI 工具"向"Agent 工程平台"演进的关键一步。有了 Hooks,你不再是一个人在用 AI,而是你在编程 AI 的行为。

Hooks 的配置放在 .claude/settings.json 中(项目级)或 ~/.claude.json 中(用户级)。项目级配置优先级更高。

四类 Hook 事件类型

Claude Code 目前支持四种 Hook 事件,覆盖了 Agent 生命周期的关键节点:

Hook 事件触发时机典型用途
PreToolUse工具执行之前安全检查、敏感操作拦截、命令白名单
PostToolUse工具执行之后自动格式化、审计日志、结果校验
Stop会话结束时运行测试套件、检查覆盖率、门禁
Notification关键事件发生时桌面弹窗提醒、耗时通知

下面我们逐一深入,每种 Hook 都配有可复制的配置代码。

1. PreToolUse —— AI 动手前的最后一道防线

PreToolUse 在 Claude 调用任何工具(Bash、Edit、Write 等)之前触发。你的脚本可以检查 Claude 想干什么,如果发现危险操作,直接拒绝并返回原因。

典型场景:

  • 禁止 rm -rf / 或 DROP TABLE
  • 限制 Claude 只能修改特定目录下的文件
  • 拦截对 .env、credentials.json 等敏感文件的读取

{

  "hooks": {

    "PreToolUse": [

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/pre_bash_check.py"

      }

    ]

  }

}

配套的 Python 脚本 pre_bash_check.py 示例:

#!/usr/bin/env python3

import json, sys

# Claude Code 通过 stdin 传入工具调用信息

tool_input = json.loads(sys.stdin.read())

command = tool_input.get("command", "")

# 危险命令黑名单

DANGEROUS_PATTERNS = [

    "rm -rf /",

    "DROP TABLE",

    "> /dev/sda",

    "chmod 777 /",

    "git push --force origin main"

]

for pattern in DANGEROUS_PATTERNS:

    if pattern in command:

        print(json.dumps({

            "decision": "block",

            "reason": f"危险命令被拦截: {pattern}"

        }))

        sys.exit(2) # 非零退出 = 阻止执行

# 允许执行

print(json.dumps({"decision": "allow"}))

踩坑提醒:PreToolUse 脚本的 exit code 决定了是否允许执行。exit 0 = 允许,exit 非零 = 阻止。如果你想让 Claude 看到你的理由,用 stdout 输出 JSON 格式的 reason。

2. PostToolUse —— 事后检查与自动修复

PostToolUse 在工具执行之后触发。这是最常用的 Hook,因为大部分自动化需求都是"改完文件之后做点什么"。

典型场景:

  • 每次 Bash 执行后记录审计日志(谁、什么时候、执行了什么、结果如何)
  • 每次 Edit/Write 文件后自动运行 eslint --fix 或 prettier --write
  • 检测 git diff 中是否包含 API key 等敏感信息

{

  "hooks": {

    "PostToolUse": [

      {

        "matcher": "Edit|Write",

        "command": "python3 .claude/hooks/post_edit_lint.py"

      },

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/audit_log.py"

      }

    ]

  }

}

审计日志脚本 audit_log.py:

#!/usr/bin/env python3

import json, sys, os

from datetime import datetime

tool_result = json.loads(sys.stdin.read())

log_entry = {

    "timestamp": datetime.now().isoformat(),

    "tool": tool_result.get("tool_name", "unknown"),

    "input": tool_result.get("tool_input", {}),

    "exit_code": tool_result.get("exitCode"),

    "output_preview": str(tool_result.get("output", ""))[:500]

}

# 追加写入审计日志

with open(".claude/audit.log", "a") as f:

    f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")

# 检测敏感信息泄露

output_text = str(tool_result.get("output", ""))

sensitive_keys = ["sk-", "api_key", "password", "token", "secret"]

for key in sensitive_keys:

    if key.lower() in output_text.lower():

        print(f"⚠️ 警告: 输出中可能包含敏感信息 ({key}),已记录到审计日志",

              file=sys.stderr)

print(json.dumps({"decision": "allow"}))

3. Stop —— 会话门禁

Stop Hook 在 Claude Code 会话结束时触发。这是你的"质量门禁"——在 Claude 说"搞定"之前,先跑一遍测试。

典型场景:

  • 自动运行 pytest / npm test
  • 检查代码覆盖率是否达标
  • 验证 git status 是否干净

{

  "hooks": {

    "Stop": [

      {

        "command": "bash .claude/hooks/stop_gate.sh"

      }

    ]

  }

}

stop_gate.sh 示例:

#!/bin/bash

echo "=== 会话门禁检查 ==="

# 1. 运行测试

echo "[1/3] 运行测试..."

if ! python3 -m pytest tests/ -x --tb=short 2>&1; then

    echo "❌ 测试失败,请修复后再结束会话"

    exit 1

fi

# 2. 检查是否有未提交的敏感文件

echo "[2/3] 敏感文件检查..."

if git diff --cached --name-only | grep -E '\.env$|credentials|\.pem$'; then

    echo "⚠️ 检测到敏感文件被修改,请确认"

fi

# 3. 检查代码覆盖率

echo "[3/3] 覆盖率检查..."

coverage=$(python3 -m pytest --cov=src --cov-report=term 2>&1 | grep "TOTAL" | awk '{print $NF}' | tr -d '%')

if [ -n "$coverage" ] && [ "$coverage" -lt 70 ]; then

    echo "⚠️ 代码覆盖率 ${coverage}%,低于 70% 阈值"

fi

echo "✅ 门禁检查通过"

.claude/settings.json 配置结构与 hooks/ 目录脚本文件的对应关系▲ .claude/settings.json 配置结构与 hooks/ 目录脚本文件的对应关系

4. Notification —— 桌面弹窗提醒

Notification Hook 让你在 Claude 完成长时间任务后收到桌面通知。这在运行耗时操作(如全量测试、数据库迁移脚本生成)时特别有用。

{

  "hooks": {

    "Notification": [

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/notify_on_long_task.py"

      }

    ]

  }

}

#!/usr/bin/env python3

import json, sys, subprocess, os

tool_input = json.loads(sys.stdin.read())

command = tool_input.get("command", "")

# 只对长时间运行的命令发通知

LONG_COMMANDS = ["pytest", "npm run build", "docker build", "terraform apply"]

for keyword in LONG_COMMANDS:

    if keyword in command:

        # macOS 通知

        if os.uname().sysname == "Darwin":

            subprocess.run([

                "osascript", "-e",

                f'display notification "命令完成" with title "Claude Code" subtitle "{command[:50]}"'

            ])

        # Linux 通知

        else:

            subprocess.run([

                "notify-send", "Claude Code",

                f"命令完成: {command[:100]}"

            ], capture_output=True)

        break

print(json.dumps({"decision": "allow"}))

完整生产级配置

把以上四类 Hook 组合起来,你得到的是一个自带质量保障体系的 AI 编程环境。以下是可直接复制到 .claude/settings.json 的完整配置:

{

  "hooks": {

    "PreToolUse": [

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/pre_bash_guard.py"

      },

      {

        "matcher": "Write|Edit",

        "command": "python3 .claude/hooks/pre_write_check.py"

      }

    ],

    "PostToolUse": [

      {

        "matcher": "Edit|Write",

        "command": "python3 .claude/hooks/post_edit_format.py"

      },

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/post_bash_audit.py"

      }

    ],

    "Stop": [

      {

        "command": "bash .claude/hooks/stop_quality_gate.sh"

      }

    ],

    "Notification": [

      {

        "matcher": "Bash",

        "command": "python3 .claude/hooks/notify_long_task.py"

      }

    ]

  }

}

对应的 .claude/hooks/ 目录结构:

.claude/hooks/

├── pre_bash_guard.py # 命令安全检查

├── pre_write_check.py # 文件写入白名单

├── post_edit_format.py # 改完自动格式化

├── post_bash_audit.py # 审计日志

├── stop_quality_gate.sh # 会话门禁

└── notify_long_task.py # 桌面通知

三个真实事故与 Hooks 如何救场

以下场景来自社区实战反馈,每个都对应一个 Hook:

事故 1:Claude 误删了生产配置

一个团队让 Claude 优化 Nginx 配置,Claude 在 Bash 中执行了 rm /etc/nginx/sites-enabled/*(想先清理再重建),导致生产服务中断 17 分钟。

修复方案——PreToolUse 拦截:

if "rm" in command and ("/etc/" in command or "/var/" in command):

    return {"decision": "block", "reason": "禁止修改系统目录文件"}

事故 2:API Key 泄露到前端代码

Claude 在帮助调试时,将 .env 中的 OpenAI Key 复制到了 config.js 并提交到了 GitHub。

修复方案——PostToolUse 检测:

# 在 post_edit_format.py 中

import subprocess

diff = subprocess.run(["git", "diff"], capture_output=True, text=True).stdout

if "sk-" in diff or "api_key" in diff.lower():

    print("🚨 提交中检测到疑似 API Key,请立即检查!", file=sys.stderr)

事故 3:未经测试的代码直接上线

Claude 写完一个"重构"后说"完成了",但 3 个测试用例失败,没人发现。

修复方案——Stop 门禁:

# stop_quality_gate.sh

python3 -m pytest tests/ -x --tb=short || exit 1

Hooks vs CLAUDE.md:为什么两者都要用

很多开发者以为在 CLAUDE.md 里写"每次改完文件后运行 prettier"就够了。但这是建议,不是强制。

CLAUDE.mdHooks
性质Prompt 指令引擎级强制执行
Claude 能忽略吗能(长对话后会"忘记")不能(绕过除非你删配置)
适用场景代码风格偏好、项目约定安全检查、质量门禁、审计
团队共享✓✓
执行时机Claude 决定什么时候读按事件自动触发

一句话总结:CLAUDE.md 是道德劝说,Hooks 才是法律。 两者搭配使用:CLAUDE.md 管"风格",Hooks 管"底线"。

踩坑与排障

坑1:Hook 脚本路径问题

Hook 的 command 字段中,脚本路径相对于项目根目录,不是你放 .claude/settings.json 的目录。

如果你把脚本放在 .claude/hooks/ 下,配置里要写完整的 python3 .claude/hooks/xxx.py。

CLAUDE.md vs Hooks:道德劝说与法律的对比——建议 vs 强制▲ CLAUDE.md vs Hooks:道德劝说与法律的对比——建议 vs 强制

坑2:Hook 脚本超时

每个 Hook 脚本默认有 30 秒超时。如果你的 PostToolUse 里跑了 eslint 整个项目,大概率超时。解决方案:

  • 只对修改的文件跑检查:git diff --name-only | grep '\.js$' | xargs eslint --fix
  • 把重任务放 Stop Hook(超时更长)

坑3:PostToolUse 里别做耗时操作

PostToolUse 会在每次工具调用后触发。如果你的脚本里跑了 npm run build,那 Claude 改一个注释你就要等 30 秒。原则是:PostToolUse 只做轻量操作(格式化、审计日志),重型操作放 Stop Hook。

坑4:exit code 的含义

  • exit 0 = 允许继续(PreToolUse)/ 表示成功(PostToolUse)
  • exit 1 = 通用错误
  • exit 2 = 明确阻止(PreToolUse 专用,Claude Code 会告诉用户被拦截的原因)

坑5:版本兼容性

Hooks 功能在 Claude Code 2.1.0 及以上版本可用。如果你的 claude --version 低于此版本,需要先升级:

npm update -g @anthropic-ai/claude-code

进阶:Hooks + Slash Commands 组合技

Hooks 负责"自动触发",Slash Commands 负责"手动控制"。两者结合形成完整的自动化体系:

Slash Command: /deploy

  → PreToolUse 检查环境变量是否完整

  → 正常部署

  → PostToolUse 记录部署日志 + 发通知

  → Stop 确认部署状态码

Slash Command: /fix-lint

  → Claude 修改文件

  → PostToolUse 自动运行 prettier + eslint --fix

  → 人只需要确认最终结果

你可以在 .claude/settings.json 中同时定义 hooks 和 commands:

{

  "hooks": { "PostToolUse": [...] },

  "commands": [

    {

      "name": "deploy",

      "description": "安全部署到生产环境",

      "prompt": "按照 deploy.md 中的流程执行部署,每一步都要确认"

    }

  ]

}

总结

Claude Code Hooks 是把 AI 编程从"个人玩具"升级为"团队工具"的关键基础设施。四个生命周期钩子覆盖了:

  • PreToolUse:在 Claude 动手前拦截危险操作
  • PostToolUse:在每次工具调用后自动执行格式化、审计、校验
  • Stop:在会话结束前强制跑测试和质量门禁
  • Notification:在关键任务完成时弹窗提醒

把本文的完整配置复制到你的项目中,给 .claude/hooks/ 目录配上对应的脚本文件,你就拥有了一个自带安全防线、审计能力、质量保障的 AI 编程环境。

记住那句话:CLAUDE.md 是道德劝说,Hooks 才是法律。


踩坑笔记
⚠️ PostToolUse 只做轻量操作(格式化、审计),重型操作放 Stop Hook
⚠️ Hook 脚本路径相对于项目根目录,不是 .claude/ 目录
⚠️ PreToolUse exit 2 = 明确阻止,exit 0 = 允许
⚠️ 需要 Claude Code 2.1.0+,低版本请升级
⚠️ 30 秒超时限制,大项目请限缩检查范围

#AI创业 #Agent工坊 #ClaudeCode #Hooks #自动化 #AI编程

本文由AI辅助创作,经人工审核编辑发布

更多一人公司案例与工具,微信搜索「AI创业内参」关注我们