大家好,我是伦哥。
前面我们学习了Skills的完整知识体系——从知识工程到系统集成,从按需加载到跨平台通用。Skills告诉Claude“怎么做”,Commands告诉Claude“做什么”。但有一个关键问题它们都解决不了——Claude能不能做?
今天我们进入一个全新的领域——Hooks,事件驱动自动化。它是Claude Code三大扩展机制中唯一能拦截和修改Claude行为的机制,也是工程化实践中安全防线的最后一道闸门。
一、为什么需要Hooks?
想象这样一个场景:伦哥用Claude Code快速写完了代码,测试通过,git push,然后关机睡觉。
这种AI辅助下的快速迭代,代价有时候是灾难性的。安全漏洞、敏感信息泄露、代码格式混乱、测试没跑就上线……这些问题的共同点是:它们本可以被自动阻止。
如果有一道看不见的防线,在代码提交前自动检查敏感文件、在文件保存后自动格式化、在Claude完成任务时自动运行测试——那些“忘记”就不再是问题。
这道防线,就是Hooks。
二、Hooks的本质——AI时代的中间件
如果你有Web开发经验,你一定熟悉中间件(Middleware)的概念。
中间件在请求到达最终处理函数之前插入检查和处理,实现横切关注点。这些逻辑不属于任何一个业务功能,但又必须贯穿所有请求——认证要每个接口都检查,日志要每个操作都记录,限流要每个入口都控制。
Claude Code的Hooks机制与此异曲同工,但它针对的不是HTTP请求,而是AI Agent的工具调用。
Hooks是AI助手的中间件——拦截、监控、增强每一次交互。这个类比不仅是形象上的相似。Web中间件解决的核心问题是“业务代码不应该操心安全和日志”,Hooks解决的核心问题也一样——Claude不应该操心格式化和权限检查,它只管写好代码就行。安全防线、质量守卫、审计日志,全部由Hooks在“幕后”自动完成。
三、Commands、Skills、Hooks的定位差异
和Commands、Skills相比,Hooks是三者中唯一能拦截和修改Claude行为的机制。
/command | |||
这三者构成了一个完整的控制谱系:
Commands告诉Claude做什么 —— 用户显式发起 Skills告诉Claude怎么做 —— Claude自主调用 Hooks告诉Claude能不能做 —— 用户请求时
如果把Claude比作一个工程师,Commands是你给他下达的任务指令,Skills是他掌握的领域知识,而Hooks是公司的安全制度和质量规范——不管你做什么任务,用什么知识,这些制度都在背后默默运行。
四、17种Hook事件——完整生命周期覆盖
截至2026年3月,根据Anthropic官方文档,Claude Code支持17种Hook事件,覆盖了从会话启动到结束的完整生命周期:
17个事件乍看数量不少,但设计逻辑非常清晰——按照“能否阻止”这一列来看,整个事件体系分为三大阵营:
🛡️ 控制点——能阻止的事件(PreToolUse、UserPromptSubmit、Stop、SubagentStop):你可以通过它们改变Claude的执行路径——拦截危险操作、拒绝不合理的输入、强制Claude继续修复。它们是Hooks系统的肌肉。
🤖 接管点——替代默认行为的事件(PermissionRequest):它不是简单地阻止,而是接管了原本由用户手动处理的权限弹窗——你的脚本可以自动批准或拒绝权限请求,替代人类的决策。它是Hooks系统的自动驾驶。
👁️ 观察点——不能阻止的事件(SessionStart、PostToolUse等):你只能在这些时刻做记录、做反馈、做后处理,但不能改变已经发生的事情。它们是Hooks系统的眼睛。
这种不对称设计是有意为之的。工具执行前可以拦截,因为操作还没发生,拦截不会造成不一致状态。工具执行后不能拦截,因为操作已经完成——你不能“取消”一个已经写入磁盘的文件。但你可以观察它、记录它、反馈它。
伦哥建议:在实践中,你不需要记住全部17种事件。把PreToolUse(守门员)、PostToolUse(质量守卫)、Stop(质量门控)这三个记牢就够了。它们覆盖了80%以上的自动化需求。
五、Hook配置详解
Hooks可以在多个位置配置,每个位置有不同的作用域和共享策略:
~/.claude/settings.json | |||
.claude/settings.json | |||
.claude/settings.local.json | |||
一个典型的Hook配置长这样:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "./hooks/block-dangerous.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "prettier --write $CLAUDE_FILE_PATH"
}
]
}
]
}
}
这个JSON结构有三层嵌套:
第一层:事件类型(什么时候触发)——PreToolUse还是PostToolUse? 第二层:匹配器(针对哪个工具)——Bash、Write还是所有工具? 第三层:Hook列表(执行什么)——执行脚本、调用LLM还是启动子代理?
三层决策,层层收窄,最终精准地把正确的检查逻辑应用到正确的时机和工具上。
Matcher匹配模式
Matcher支持四种匹配模式:
"Write" | ||
"Edit|Write|MultiEdit" | ||
"*" | ||
"" |
对于Notification、Stop、SubagentStop等生命周期事件,matcher会被忽略——这些事件不针对特定工具。
六、四种Hook执行类型
当一个Hook被触发后,其具体执行方式有四种:
1. Command类型——执行Shell脚本
最常用、最可靠的类型。确定性规则永远比LLM判断更可靠——同样的输入永远产生同样的输出。
{
"type": "command",
"command": "./hooks/check-security.sh",
"timeout": 30000
}
2. Prompt类型——LLM评估
当规则无法用确定性脚本表达时,就需要LLM的判断力。比如“这段代码是否有安全隐患”——这种判断需要理解代码语义。
{
"type": "prompt",
"prompt": "Evaluate if this task was completed correctly."
}
3. Agent类型——子代理评估
最强大也最“重”的评估方式。Agent Hook会启动一个子代理,可以使用Read、Grep、Glob等工具来验证条件——不只是“看一眼就判断”,而是可以“翻代码确认”。
{
"type": "agent",
"prompt": "Verify that all unit tests pass.",
"timeout": 120
}
4. HTTP类型——远程服务调用
把事件数据以POST请求发送到远程HTTP端点,由远程服务返回决策结果。适合团队共享审计服务、集中式安全扫描等场景。
选择策略
能用command的不用prompt,能用prompt的不用agent。确定性规则永远比LLM判断更可靠,LLM判断比子代理执行更快。
七、PreToolUse:工具执行前的守门员
PreToolUse是最强大的Hook事件,因为它能阻止工具执行。它就像机场的安检门——在你登机(工具执行)之前,先过一道检查。
PreToolUse Hook可以做三件事:
allow:放行 deny:拦截 updatedInput:改写输入参数后再执行
第三种能力特别有趣——你不仅能“放行或拦截”,还能“偷偷改参数”。比如用户要执行rm -rf /tmp/test,你可以把它改成rm -rf /tmp/test --dry-run,先看看会删掉什么再说。
Hook脚本的通信协议
每个Hook脚本通过stdin接收一个JSON对象:
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/test"
}
}
Hook脚本通过退出码和stdout JSON告诉Claude下一步做什么:
exit 0:放行exit 2:阻止(只有exit 2才表示“我检查过了,这个操作确实危险”)其他非零退出码:脚本出错但不阻止
实战案例1:阻止危险命令
每个工程团队都有一些“绝对不能执行”的命令。rm -rf /会删除整个文件系统,git push --force origin main会覆盖远程主分支的历史。这些命令的共同特点是:一旦执行就无法挽回。
下面这个脚本用模式匹配来拦截这些灾难性命令:
#!/bin/bash
# block-dangerous.sh
INPUT=$(cat)
COMMAND=$(echo"$INPUT" | jq -r '.tool_input.command // ""')
DANGEROUS_PATTERNS=(
"rm -rf /"
"rm -rf /*"
"git push --force origin main"
"git push --force origin master"
"DROP DATABASE"
"DROP TABLE"
"curl.*| sh"
"wget.*| bash"
)
for pattern in"${DANGEROUS_PATTERNS[@]}"; do
if [[ "$COMMAND" == *"$pattern"* ]]; then
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked dangerous command pattern: $pattern"
}
}
EOF
exit 2
fi
done
echo'{}'
exit 0
当Claude试图执行危险命令时,你会在终端看到类似这样的输出:
Hook blocked tool call: Blocked dangerous command pattern: rm -rf /
This command could cause irreversible damage.
Claude收到拦截信息后会自动调整策略——换一种更安全的方式来完成你的请求。
实战案例2:保护敏感文件
另一个常见需求是保护敏感文件(如.env文件)不被Claude修改或读取:
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo"$INPUT" | jq -r '.tool_input.file_path // ""')
PROTECTED_PATTERNS=(
".env"
".env.*"
"credentials.json"
"secrets.yaml"
"*.pem"
"*.key"
"id_rsa"
"kubeconfig"
)
for pattern in"${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Cannot modify sensitive file: $FILE_PATH"
}
}
EOF
exit 2
fi
done
echo'{}'
exit 0
配置时,这个Hook要同时匹配Write和Edit两个工具:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "./hooks/protect-files.sh"
}
]
}
]
}
}
安全原则:宁可误拦截,不可漏放行。安全场景下,误拦截的代价是用户手动确认一下,漏放行的代价可能是密钥泄露。
八、PostToolUse:工具执行后的质量守卫
PostToolUse在工具成功执行后运行。它不能阻止已经发生的操作,但它可以做三件更重要的事情:
后处理:格式化、清理 反馈:向Claude提供lint结果、警告 记录:写入审计日志
PostToolUse最强大的能力在于通过additionalContext向Claude反馈信息:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "ESLint found 3 errors in the file you just wrote."
}
}
additionalContext的内容会被注入到Claude的上下文中,Claude会看到这条反馈并据此调整行为。这不是简单的日志记录,而是一个闭环反馈机制——Hook观察到问题,反馈给Claude,Claude自动修复。
实战案例1:自动格式化
每次Claude写入或修改文件后,自动运行格式化工具:
#!/bin/bash
# auto-format.sh
INPUT=$(cat)
FILE_PATH=$(echo"$INPUT" | jq -r '.tool_input.file_path // ""')
EXTENSION="${FILE_PATH##*.}"
case"$EXTENSION"in
js|jsx|ts|tsx|json|md|css|scss|html)
ifcommand -v npx &>/dev/null; then
npx prettier --write "$FILE_PATH"
fi
;;
py)
ifcommand -v black &>/dev/null; then
black "$FILE_PATH"
fi
;;
go)
ifcommand -v gofmt &>/dev/null; then
gofmt -w "$FILE_PATH"
fi
;;
esac
echo'{}'
exit 0
这个Hook的美妙之处在于,Claude不需要知道项目用什么格式化工具。无论是Prettier、Black、gofmt还是rustfmt,只要本地安装了,就会自动应用。
实战案例2:自动Lint检查
格式化解决了“代码长什么样”的问题,Lint检查解决的是“代码有没有问题”:
#!/bin/bash
# lint-check.sh
INPUT=$(cat)
FILE_PATH=$(echo"$INPUT" | jq -r '.tool_input.file_path // ""')
if [[ "$FILE_PATH" == *.js || "$FILE_PATH" == *.ts ]]; then
LINT_RESULT=$(npx eslint "$FILE_PATH" 2>&1) || true
LINT_EXIT_CODE=$?
if [ $LINT_EXIT_CODE -ne 0 ]; then
ESCAPED_RESULT=$(echo"$LINT_RESULT" | head -30 | jq -Rs '')
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "ESLint found issues:\n$ESCAPED_RESULT"
}
}
EOF
fi
fi
echo'{}'
exit 0
这创造了一个自动化的质量循环:Claude修改文件 → PostToolUse触发 → Lint检查 → 发现问题 → 反馈给Claude → Claude自动修复 → 再次触发PostToolUse → 再次检查……直到所有Lint错误消除。
实战案例3:审计日志
对于金融、医疗、政府等合规性要求高的场景,记录Claude的所有操作至关重要:
#!/bin/bash
# audit-log.sh
INPUT=$(cat)
LOG_FILE="${CLAUDE_PROJECT_DIR:-.}/.claude/audit.log"
mkdir -p "$(dirname "$LOG_FILE")"
TIMESTAMP=$(date -Isseconds)
TOOL_NAME=$(echo"$INPUT" | jq -r '.tool_name' // "unknown")
TOOL_INPUT=$(echo"$INPUT" | jq -c '.tool_input' // {})
echo"[$TIMESTAMP] $TOOL_NAME: $TOOL_INPUT" >> "$LOG_FILE"
echo'{}'
exit 0
配置时用matcher: "*"匹配所有工具:
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "./hooks/audit-log.sh"
}
]
}
]
}
}
审计日志的价值不在当下,而在未来。当你某天需要回答“上周三Claude到底改了什么导致了这个bug”时,审计日志就是你的时光机。
九、总结
这一讲,我们学习了Hooks的基础概念并给出了最常用的两个事件——PreToolUse和PostToolUse的大量示例。
Hooks的本质是AI Agent的中间件。就像Web开发中的中间件可以拦截HTTP请求一样,Hooks可以在Claude执行工具前后插入自定义逻辑。Claude不需要知道有Hook在运行,它只管专注于完成任务,安全防线、质量守卫、审计日志全部由Hooks在“幕后”自动完成。
Claude Code支持17种Hook事件,覆盖完整生命周期。它们分为控制点(能阻止)、接管点(替代默认行为)和观察点(只能记录)三大阵营。
四种Hook类型:
command:确定性规则,最可靠 prompt:单次LLM评估,需要判断力时使用 agent:多轮验证,需要翻代码才能决策时使用 http:对接外部服务
PreToolUse实战:阻止危险命令(rm -rf /、git push --force origin main)和保护敏感文件(.env、*.pem、credentials.json)。
PostToolUse实战:自动格式化、自动lint检查、审计日志。additionalContext字段创造了一个闭环反馈机制——Hook观察到问题,反馈给Claude,Claude自动修复。
现在回想开头伦哥发生的悲剧,用一个简单的PreToolUse Hook就能避免。自动化不是为了替代人的判断,而是为了在人类最容易出错的时刻——疲劳、赶工、分心——提供一道可靠的防线。
本系列往期文章:
第01章:从被动使用到主动驾驭:Claude Code底层技术全景学习笔记 第02章:告别“失忆症”,用 CLAUDE.md 打造 AI 的长期记忆 第03章:分而治之——Sub-Agents 的核心概念与实战 第04章:从 Sub-Agents 到 Multi-Agent,一套架构方法论搞定90%的AI产品设计 第05章:打造只读型代码审查员,让 AI 不再“好心办坏事” 第06章:噪声隔离实战——让子代理替你过滤测试与日志 第07章:并行探索与流水线编排——让 AI 代理像团队一样协同工作 第08章:Agent Teams——当 AI 智能体学会组队协作 第09章:深入理解 Skills——Agent 生态中的“可操作知识”与触发机制 第10章:任务型 Skills 实战——让重复工作一键直达 第11章:渐进式披露架构设计——让知识在正确的时刻到达正确的地方 第12章:双剑合璧——Skills × SubAgent 协同实战 第13章:纲举目张——Skills 架构定位与高阶能力解析 第14章:燎原之势——Claude Code Skills如何百天定义行业新标准
如果我的文章对你有帮助,请帮忙点赞、在看、转发一下,你的支持是我持续输出高质量文章的最大动力,非常感谢!
夜雨聆风