直接答案
Hooks 在工具生命周期执行程序,适合必须发生的检查;它们不是用自然语言提醒模型。
这篇内容解决什么问题
帮助团队安全地把重复验证和策略执行接入 Claude Code 生命周期。
必须执行的动作使用 Hooks
CLAUDE.md 是提供给模型的指导,适合工作习惯和架构约束;Hook 是在特定生命周期事件触发的程序,适合格式化、通知、审计或阻断。
如果某项要求必须零例外发生,不应只靠“务必”提示。反过来,需要语义判断的架构选择也不应硬塞进脆弱的 shell Hook。
从事件、匹配器和处理器理解配置
Hook 配置先选择事件,例如工具调用前后的 PreToolUse、PostToolUse,或会话和停止相关事件;matcher 再缩小到具体工具;hooks 数组定义实际处理器。
从一个低风险、容易观察的 PostToolUse 开始。确认触发次数、执行目录和耗时后,再增加阻断逻辑。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/lint-gate.sh" }
]
}
]
}
}#!/usr/bin/env bash
set -uo pipefail
output=$(pnpm lint 2>&1)
status=$?
if [ "$status" -ne 0 ]; then
printf "%s\n" "$output" >&2
exit 2
fi从标准输入读取结构化事件
命令 Hook 从标准输入接收 JSON,其中包含会话、事件和工具输入等上下文。脚本应使用 JSON 解析器读取字段,不要依赖字符串切割。
把复杂逻辑放到仓库内可测试脚本,settings.json 只引用脚本路径。脚本应处理缺失字段、超时和非零退出,并把诊断写到 stderr。
#!/usr/bin/env bash
set -euo pipefail
payload=$(cat)
printf '%s' "$payload" | jq -e '.hook_event_name' >/dev/null阻断 Hook 要小、快、可解释
PreToolUse 可以在动作发生前检查输入,适合保护敏感路径或高风险命令。阻断结果应给出具体理由和修复方式,避免代理在不知道原因时重复尝试。
Hook 本身以本机权限执行,恶意或被篡改的脚本可能比普通提示更危险。启用项目 Hook 前审查来源,团队变更通过 PR 审核。
- 匹配器只覆盖真正需要检查的工具。
- 设置合理超时,避免每次编辑都长时间阻塞。
- 不在 Hook 命令中拼接未经验证的用户输入。
- 阻断消息说明违反了哪条策略。
独立测试脚本,再在会话中验证触发
先用固定 JSON fixture 对脚本做单元测试,确认允许、阻断和异常输入路径。随后运行 /hooks 检查配置是否加载,再执行一个无害操作观察结果。
配置未生效时检查 settings 作用域、JSON 语法、脚本路径和 matcher。使用 claude --debug-file /tmp/claude-hooks-debug.log 获取加载与执行诊断。
/hooks
# 退出会话后
claude --debug-file /tmp/claude-hooks-debug.log避免把整个 CI 塞进每次工具调用
在每次编辑后运行完整测试会拖慢会话并产生大量上下文。轻量格式化可放 PostToolUse,模块测试可在阶段结束运行,完整 CI 保留在提交或流水线。
Hook 数量增长后记录所有者、事件、平均耗时和失败处理。已经没有价值的 Hook 应删除,而不是让开发者习惯性忽略报错。
官方来源与核验范围
本文以公开官方文档为事实依据;命令和配置可能随客户端版本变化,执行前请同时核对对应来源。
查看技术核验方法