Claude Code Hooks入门:用自动检查减少误操作
面向初学者的Claude Code Hooks实务指南,覆盖危险命令拦截、日志保存、自动格式化与测试。
Claude Code Hooks可以理解为“在Claude开始工作前后自动运行的检查”。你不需要每次都提醒它“不要执行危险命令”“改完代码记得format”“最后跑一下测试”,而是把这些规则放进Claude Code的生命周期里。
对初学者来说,最重要的不是记住所有事件名称,而是分清四个位置:PreToolUse 是刹车,PostToolUse 是维修和验证,UserPromptSubmit 是接待记录,Stop 是离开前的最终确认。Hooks不会替代人工review、权限设置或CI,但它能把容易忘记的重复检查固定下来。
本文基于 Claude Code Hooks官方参考 和 Claude Code settings官方文档 编写。项目上下文的整理可以继续看 CLAUDE.md最佳实践,权限整体设计可以配合 Claude Code权限指南 阅读。
先理解4个最常用事件
Claude Code的Hook事件很多,但实际落地时,先掌握下面四个就够了。
| 事件 | 适合做什么 | 例子 |
|---|---|---|
UserPromptSubmit | 用户提示词交给Claude之前 | 保存请求、检查是否误贴密钥、补充轻量上下文 |
PreToolUse | 工具执行之前 | 阻止危险Bash命令、本番环境操作、删除命令 |
PostToolUse | 工具执行成功之后 | 自动format、lint、运行相关测试 |
Stop | Claude准备结束回复时 | 检查Git冲突、保存总结、提醒缺少验证 |
如果一个动作“绝对不能发生”,放在 PreToolUse。如果动作已经发生,需要整理或验证,放在 PostToolUse。如果你想分析用户请求质量,用 UserPromptSubmit。如果你想避免会话带着明显问题结束,用 Stop。
共享规则通常写在项目的 .claude/settings.json。个人实验可以放在 .claude/settings.local.json,避免提交到仓库。团队使用时要在README里写清楚:这些Hook由谁维护、哪些会阻塞、哪些只记录日志。
可复制的最小配置
下面的配置包含四件事:保存提示词日志、阻止危险Bash命令、编辑后执行质量检查、结束时保存简要状态。先从小配置开始,比一口气自动化所有流程更安全。
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/log-prompt.mjs"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/block-dangerous-command.mjs"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/run-quality-checks.mjs",
"timeout": 120
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/stop-summary.mjs"
}
]
}
]
}
}
注意这里的结构是“事件、matcher组、handler数组”。也就是说,真正运行的命令在内部的 hooks 数组里,并且要写 type: "command"。网上还能看到一些把 command 直接放在 matcher 旁边的旧例子,初学者最好不要照抄。
Use case 1:用PreToolUse阻止危险命令
第一个实际场景是安全刹车。当Claude Code快速修改项目时,最危险的不是命令执行完之后,而是命令即将执行之前。PreToolUse 可以读取Bash输入,在明显危险时拒绝执行。
保存为 .claude/hooks/block-dangerous-command.mjs。
import fs from "node:fs";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const command = String(input.tool_input?.command || "");
const denyPatterns = [
/rm\s+-rf\s+(\/|\*|\.|\$HOME)/i,
/cat\s+\.env(\.|$|\s)/i,
/printenv/i,
/aws\s+.*\s+delete-/i,
/gcloud\s+.*\s+delete/i,
/kubectl\s+delete\s+(namespace|deployment|secret)/i,
/DROP\s+DATABASE/i
];
const matched = denyPatterns.find((pattern) => pattern.test(command));
if (matched) {
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `Blocked by project hook: ${matched}`
}
}));
process.exit(0);
}
这个脚本不是完整的安全产品。正则表达式无法覆盖所有绕过方式。但是它能拦住最常见、最吓人的错误:误删大目录、读取 .env、删除云资源、删除Kubernetes资源、执行破坏性SQL。真正的安全设计还需要结合Claude Code权限、Git保护分支、CI和人工审批。
Use case 2:用UserPromptSubmit保存请求日志
第二个场景是记录提示词。很多Claude Code失败并不是模型能力不够,而是一开始的请求太模糊,例如“修一下”“整理一下”“改得好一点”。如果没有日志,团队很难复盘是哪种请求导致结果变差。
保存为 .claude/hooks/log-prompt.mjs。
import fs from "node:fs";
import path from "node:path";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const dir = path.join(process.cwd(), ".claude", "hook-logs");
fs.mkdirSync(dir, { recursive: true });
const record = {
time: new Date().toISOString(),
event: input.hook_event_name,
cwd: input.cwd,
prompt: input.prompt || input.user_prompt || ""
};
fs.appendFileSync(
path.join(dir, "prompts.jsonl"),
JSON.stringify(record) + "\n",
"utf8"
);
日志先保存在本地,不要急着发送到外部服务。提示词里可能包含客户名、内部URL、API Key、业务信息。把 .claude/hook-logs/ 加到 .gitignore,并且规定保存期限和访问权限,是团队采用日志前必须做的事情。
Use case 3:编辑后自动format和test
第三个场景是日常质量检查。Claude改完文件后,自动运行formatter和小范围测试。这样你不用每次在提示词最后补一句“顺便format并跑测试”。
保存为 .claude/hooks/run-quality-checks.mjs。
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const filePath = String(input.tool_input?.file_path || "");
const isSourceFile = /\.(js|jsx|ts|tsx|css|md|mdx|json)$/.test(filePath);
if (!isSourceFile) process.exit(0);
const run = (cmd, args) => {
try {
return execFileSync(cmd, args, {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"]
});
} catch (error) {
return String(error.stdout || "") + String(error.stderr || "");
}
};
const messages = [];
messages.push(run("npx", ["prettier", "--write", filePath]));
if (/\.(js|jsx|ts|tsx)$/.test(filePath)) {
messages.push(run("npm", ["test", "--", "--runInBand"]));
}
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: messages.join("\n").slice(-4000)
}
}));
大仓库不要每次都跑全量测试。那会让Hooks变成负担。先只做format,再逐步增加相关测试,必要时用 async: true 让重任务在后台运行。返回给Claude的输出也要截断,否则日志会挤占上下文。
Use case 4:Stop阶段检查是否可以结束
Stop 在Claude准备结束回复时触发。这里适合保存总结,或者只阻止非常明确的未完成状态,比如Git冲突还没解决。
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
if (input.stop_hook_active) {
process.exit(0);
}
let status = "";
try {
status = execFileSync("git", ["status", "--short"], {
cwd: process.cwd(),
encoding: "utf8"
});
} catch {
process.exit(0);
}
if (status.includes("UU ")) {
console.error("Git conflict remains. Resolve conflicts before finishing.");
process.exit(2);
}
fs.mkdirSync(".claude/hook-logs", { recursive: true });
fs.appendFileSync(
".claude/hook-logs/stop.jsonl",
JSON.stringify({
time: new Date().toISOString(),
lastAssistantMessage: input.last_assistant_message || "",
gitStatus: status.slice(0, 2000)
}) + "\n"
);
Stop hook最容易写得太严格。如果条件不清楚,Claude会反复尝试结束又被拦住。实际运用时要检查 stop_hook_active,只阻止明确可以修复的问题,其余情况以记录日志为主。
pitfall:常见失败和回避方法
第一个pitfall是把Hooks当成权限系统。Command hook会以你的系统用户权限运行,写错脚本同样可以删除文件或读取敏感信息。输入必须校验,路径必须处理,.env、.git、密钥文件默认不要碰。
第二个pitfall是同步执行太多任务。编辑一个文件后格式化可以接受,但每次都build、全量test、浏览器测试、部署,就会拖慢Claude。快检查放在 PostToolUse,慢检查交给异步Hook或CI。
第三个pitfall是没有日志策略。日志能帮助复盘,但也可能保存秘密信息。决定保存位置、保存期限、谁能读取、是否进Git,比写脚本本身更重要。
第四个pitfall是误解matcher。matcher: "Bash" 只是让Bash事件进入handler,并不代表所有Bash都安全。真正的判断仍然要在脚本里完成。
Masa式验证笔记
实际试下来,效果最明显的是把format和test结果通过 additionalContext 还给Claude。Claude下一步就知道该修什么,不需要人手动复制日志。危险命令拦截也很有价值,因为它在最关键的执行前一刻拦住明显事故。
如果是第一次在团队里导入,我会按三步走:先记录一周提示词,再开启编辑后format,最后增加少量禁止Bash规则。这样有数据、有反馈,也不会一开始就让自动化变得烦人。
需要把权限、CLAUDE.md、Hooks、review流程一起整理时,可以看 Claude Code培训页面。个人开发者也可以把这套workflow整理成Gumroad清单,作为每个新项目的启动模板。
总结
Claude Code Hooks不是炫技,而是让AI开发更稳定的护栏。用 UserPromptSubmit 理解请求,用 PreToolUse 阻止危险动作,用 PostToolUse 验证修改,用 Stop 避免带着明显问题结束。
先小规模开始,再根据日志增加规则。一个简单、稳定、大家愿意保留的Hook,比复杂但两天后被禁用的自动化更有价值。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code Permission Receipt Pattern:记录权限、证据和回滚方式
Claude Code 权限 receipt:记录允许动作、需要批准的边界、验证命令、回滚说明,以及 Gumroad 和咨询 CTA 检查。
Claude Code/Codex 安全 Agent Harness 实战:权限、验证与回滚
用权限策略、执行计划、验证脚本和回滚日志,为 Claude Code 与 Codex 搭建更安全的 AI Agent 工作流。
Claude Code 子代理实战指南:安全委派并行文章与代码工作
用 Claude Code 子代理安全拆分文章和代码工作:委派规则、提示词模板、失败模式与检查清单。