你的第一个自动化钩子
Mission 关联:作为一个进阶用户,你已经习惯了"手动发指令"的工作模式。Hooks 是你迈向"自动化"的第一步——让 Claude Code 在特定事件发生时自动执行你定义的操作,不再需要每次手动提醒。
Hooks 是 Claude Code 的生命周期事件触发器。它们让你在特定事件发生时自动执行脚本、HTTP 请求、或者注入提示词。 你可以把它们想象成"编程中的事件监听器"——当某件事发生时,自动触发一个函数。
与 CLAUDE.md 中的文字说明不同(那是建议性的,Claude 可能不遵循),Hook 是强制性的——每次事件匹配都会执行,没有商量的余地。
一个 Hook 配置由三层组成:
| 层级 | 说明 | 示例 |
|---|---|---|
| 事件 (Event) | 触发的时机 | PreToolUse(使用工具前)PostToolUse(使用工具后) |
| 匹配器 (Matcher) | 过滤条件 | "Edit|Write"(仅在编辑文件时) |
| 动作 (Action) | 执行的命令 | shell 命令、HTTP 请求、prompt |
| 类型 | 用途 | 推荐场景 |
|---|---|---|
command |
执行 shell 命令 | 运行 linter、发送通知、文件检查 |
http |
发送 HTTP POST | 通知外部系统、触发 CI |
prompt |
注入提示词给 Claude | 在特定事件前注入额外指令 |
agent |
启动子 agent | 复杂验证、多步骤检查 |
mcp_tool |
调用 MCP 工具 | 从 MCP 服务获取数据做检查 |
Hooks 写在 settings.json 中。你可以在以下位置配置:
| 位置 | 作用域 | 特点 |
|---|---|---|
~/.claude/settings.json |
全局(所有项目) | 个人配置,不共享 |
.claude/settings.json |
项目级别 | 可提交到仓库,团队共享 |
.claude/settings.local.json |
项目级别 | 被 gitignore,个人覆盖 |
基本格式如下:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node scripts/lint-check.mjs",
"timeout": 10
}
]
}
]
}
}
对于 command 类型的 hook,退出代码决定了行为:
| 退出码 | 含义 | 行为 |
|---|---|---|
0 |
成功 | 继续执行 —— 通过 |
1 |
非阻塞错误 | Claude 看到报错但继续执行 |
2 |
阻塞/拒绝 | 阻止操作,stderr 反馈给 Claude |
| 事件名 | 触发时机 | 匹配对象 |
|---|---|---|
PreToolUse |
使用工具之前 | 工具名 (Bash, Edit, Write...) |
PostToolUse |
工具执行完后 | 工具名 |
Notification |
Claude 需要输入 | 通知类型 |
UserPromptSubmit |
你提交提示词 | 始终触发 |
SessionStart |
会话开始 | 启动方式 |
Stop |
Claude 停止响应 | 始终触发 |
PostToolUseFailure |
工具失败后 | 工具名 |
场景:每次 Claude 用 Edit 或 Write 工具修改了文件后,自动运行 Prettier 格式化。
步骤:
在你的项目根目录(或全局 ~/.claude/)找到 settings.json。如果不存在,创建一个 .claude/settings.json。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo '文件已编辑,可以在这里运行格式化命令'",
"timeout": 10
}
]
}
]
}
}
把 command 换成项目中实际用的格式化工具,比如:
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
注:jq '.tool_input.file_path' 是从 hook 接收的 stdin JSON 中提取被编辑的文件路径。
在同一个项目中启动 Claude Code,让它帮你编辑一个文件。观察终端输出,看看你的 hook 是否被触发。
这可能是最有用的 hook 之一 —— 在 PreToolUse 阶段拦截危险的 Bash 命令:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node -e \"
const input = require('fs').readFileSync('/dev/stdin','utf8');
const cmd = JSON.parse(input).tool_input?.command || '';
const blocked = ['rm -rf', 'git push --force', 'DROP TABLE'];
if (blocked.some(b => cmd.includes(b))) {
console.error('🚫 检测到高危命令已阻止:', cmd);
process.exit(2);
}
\"",
"timeout": 5
}
]
}
]
}
}
这个 hook 会在每次 Bash 工具执行前检查命令内容,如果包含 rm -rf、git push --force 等危险操作,就返回退出码 2 阻止执行。
有任何不清楚的地方,随时问我!Hook 的 matcher 语法、事件选择、或者调试技巧,都可以继续深聊。