用 Claude Code 撸代码的时候,经常会遇到这么个场景:AI 也就是刚上手那会儿老实,用久了就开始"放飞自我":
- 动不动就想执行个
rm -rf,看得人心惊肉跳 - 改完代码从来不记得跑 lint,格式乱七八八
- 甚至有时候想知道它到底背着我调了多少次 API,也没个记录
其实,这些问题用 Hooks 都能治。
Hooks 是什么
说白了,Hooks 就是给 Claude Code 戴的"紧箍咒"。
它允许你在 AI 执行特定操作的前后插入你自己的脚本。这概念跟 Git Hooks(比如 pre-commit)几乎一模一样:在关键节点拦一道,运行个脚本,通过了才放行,不通过就打回。
区别在于,Git Hooks 拦的是代码提交,Claude Hooks 拦的是 AI 的工具调用。
支持哪些钩子
目前支持的事件有几个,但常用的就俩:
| 事件 | 什么时候触发 | 典型场景 |
|---|---|---|
PreToolUse |
工具调用前 | 拦住危险命令、检查参数、权限控制 |
PostToolUse |
工具调用后 | 自动格式化代码、触发测试、更新文档 |
剩下的 Notification(通知时)、Stop(结束时)用得比较少,一般写插件才用得上。
怎么配置
配置这玩意儿有三个地方能放,优先级从高到低:
.claude/settings.local.json(本地独享,不进 Git,推荐).claude/settings.json(项目共享,进 Git)~/.claude/settings.json(全局配置,所有项目生效)
一个最简单的配置长这样:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 scripts/check_cmd.py"
}
]
}
]
}
}
这里的 matcher 有讲究
matcher 决定了这个钩子管哪些闲事。支持这几种写法:
- 精确匹配 :
"Bash"------ 只管 Bash 命令。 - 多选匹配 :
"Bash|Write"------ 管 Bash 和写文件。 - 通配符 :
".*"------ 所有工具全管(慎用,会慢)。 - 反向匹配 :
"^(?!Read).*"------ 除了读文件,其他全管。
工具名称一般有:Bash、Read、Write、Edit、Glob、Grep、WebFetch 等。
核心机制:JSON 协议
Hooks 的核心就是"一问一答"。
当钩子触发时,Claude Code 会通过 标准输入 (stdin) 扔给你一个 JSON:
json
{
"session_id": "会话ID",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /"
}
}
你的脚本处理完,得通过 标准输出 (stdout) 扔回一个 JSON:
json
{
"continue": false,
"decision": "block",
"reason": "哥们,删库跑路可不行啊"
}
返回值说明
continue:true继续,false停下。decision:approve: 允许执行。block: 禁止执行,并报错给 AI。skip: 跳过执行,但不报错(假装执行了)。
reason: 给 AI 看的拒绝理由。
实战案例
光说不练假把式,整几个实用的例子。
场景一:防删库(拦截危险命令)
这个最实用。写个 Python 脚本拦截 rm 命令。
脚本 hooks/safe_bash.py:
python
#!/usr/bin/env python3
import sys
import json
# 读取输入
data = json.loads(sys.stdin.read())
if data.get("tool_name") == "Bash":
cmd = data.get("tool_input", {}).get("command", "")
# 简单的关键词匹配,实际使用可以用正则
if "rm -rf" in cmd and "/" in cmd:
print(json.dumps({
"continue": False,
"decision": "block",
"reason": "危险操作拦截:检测到高风险删除命令,请确认路径安全后再试。"
}))
sys.exit(0)
# 默认放行
print(json.dumps({"continue": True, "decision": "approve"}))
配置:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "python3 hooks/safe_bash.py" }]
}
]
}
}
场景二:强迫症福音(改完文件自动格式化)
AI 写代码有时候格式不讲究,咱可以用 PostToolUse 帮它体面一下。
脚本 hooks/auto_fmt.sh:
bash
#!/bin/bash
# 读取 JSON 输入
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name')
file=$(echo "$input" | jq -r '.tool_input.file_path // empty')
# 只处理写操作
if [[ "$tool" == "Write" || "$tool" == "Edit" ]]; then
if [[ -n "$file" && "$file" == *.ts ]]; then
# 悄悄给它跑个 prettier
npx prettier --write "$file" >/dev/null 2>&1
fi
fi
# Post 钩子不需要返回 decision,空对象就行
echo '{}'
配置:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "bash hooks/auto_fmt.sh" }]
}
]
}
}
这样 AI 改完 .ts 文件,Prettier 马上就跟进,代码永远整整齐齐。
场景三:审计日志
想知道 AI 到底干了啥?把所有操作记下来。
脚本 hooks/logger.py:
python
#!/usr/bin/env python3
import sys
import json
import datetime
data = json.loads(sys.stdin.read())
log_entry = {
"time": datetime.datetime.now().isoformat(),
"tool": data.get("tool_name"),
"input": data.get("tool_input")
}
# 追加到日志文件
with open(".claude/audit.log", "a") as f:
f.write(json.dumps(log_entry) + "\n")
print(json.dumps({"continue": True}))
避坑指南
玩 Hooks 有几个坑得注意,掉进去容易摔跟头:
-
别太慢 Hooks 是同步执行的。脚本卡 1 秒,AI 就愣 1 秒。复杂逻辑(比如上传日志)最好在脚本里丢给后台进程异步做,别让 AI 等太久。
-
别死循环 别在 Hooks 里调用会触发 Hooks 的命令......虽然 Claude Code 的 Hooks 主要是拦截 AI 的工具调用,但如果你在脚本里又调了 CLI 可能会套娃。
-
容错要做好 脚本要是挂了(非 0 退出码),AI 操作也会跟着挂。记得处理好 JSON 解析失败、文件不存在这些边界情况。除非你是故意想阻断操作(退出码 2)。
-
安全第一 Hooks 权限很大,能读写文件、能联网。网上抄来的脚本,先看懂了再跑,别把后门带进来了。
-
怎么调试 脚本逻辑不对很难受。可以在返回 JSON 里带上
outputToStderr字段,把调试信息打印到 stderr,方便排查:pythonprint(json.dumps({ "continue": True, "outputToStderr": f"Debug: current tool is {data.get('tool_name')}" }))
这跟 Git Hooks / CI 有啥不一样?
你可能会问:我有 Git Hooks 还有 CI/CD,为啥还要搞这个?
关键在于时机 和粒度。
| 方式 | 什么时候管 | 管什么 |
|---|---|---|
| Git Hooks | commit / push 时 | 代码变更 |
| CI/CD | 代码推送后 | 构建和部署流程 |
| Claude Hooks | AI 动手的瞬间 | AI 的每一个操作 |
Claude Hooks 是实时介入的。
- 等 Git Hooks 报错,
rm -rf可能已经执行完了。 - 等 CI 挂了,错误代码已经进仓库了。
- 而 Claude Hooks 可以把危险扼杀在摇篮里,或者让 AI 在修改配置前先过一遍你的"家规"。
进阶玩法
如果你想玩得更花一点:
1. 配合 MCP Server
如果你用了 MCP (Model Context Protocol) 扩展 AI 的能力,Hooks 照样能管。matcher 支持 mcp__serverName__toolName 这种格式:
json
{
"matcher": "mcp__myserver__.*",
"hooks": [{ "type": "command", "command": "..." }]
}
这样即使是三方扩展的工具,也能被你安排得明明白白。
2. 动态规则
硬编码脚本不灵活?让脚本去读外部配置,实现不同项目不同规则。 比如从 .claude/rules.json 读取"禁止修改的文件列表":
python
import json
import os
rules_file = ".claude/rules.json"
if os.path.exists(rules_file):
with open(rules_file) as f:
rules = json.load(f)
protected_files = rules.get("protected_files", [])
# ... 在这里写你的拦截逻辑
这样你只需要维护 JSON 配置文件,Hook 脚本逻辑可以保持通用。
3. 团队共享
把 Hooks 配置和脚本放在项目根目录的 .claude/ 下,提交到 Git。 这样团队里每个人用 Claude Code 的时候,都会自动遵守同一套代码规范和安全策略。即使是新来的实习生,AI 也不会在他的机器上乱来。
总结
Claude Code Hooks 给了我们一个介入 AI 决策环的机会。
以前我们是"监督者",看着 AI 跑;有了 Hooks,我们变成了"规则制定者"。把安全红线、代码规范这些死规矩写进 Hooks 里,让 AI 在笼子里跳舞,既用了它的脑子,又防了它的乱子。
如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:
Claude Code Skills (按需加载,意图自动识别,不浪费 token,介绍文章):
- code-review-skill - 代码审查技能,覆盖 React 19、Vue 3、TypeScript、Rust 等约 9000 行规则(详细介绍)
- 5-whys-skill - 5 Whys 根因分析,说"找根因"自动激活
- first-principles-skill - 第一性原理思考,适合架构设计和技术选型
全栈项目(适合学习现代技术栈):
- prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑
- chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB