
我们大概都遇到过这种情况:
让 Claude 改代码,它改了一半停下来问你”要继续吗”?
让它跑测试,跑完一个文件就停下来等你确认?
让它整理文档,每改一段就问”这样行吗”?
来回说几次“继续”,人会烦,节奏也会断。
这不是 Claude 的错,而是对话式交互的局限——它天生就是”说一句动一下”,不会自己把事做完。
Skill 能把步骤和约束先写进去,但它本身不负责在工具执行后把下一步自动串起来。
Hook 补的正是这一层:在关键节点做检查、阻断,或者补一段上下文,让 Claude 更容易沿着流程继续往下走。
Skill + Hook 配合起来,自动化才真正开始像一条连续的工作流。
Skill 的局限与 Hook 的补充:左侧需要人工继续,右侧可以自动推进1. 为什么 Hook 如此重要
AI “偷懒”的真相
很多人抱怨 Claude “偷懒”、”不干完活”、”每次都要说继续”。
真相是:Claude 不是在偷懒,它是在等你的许可。
对话式 AI 的设计逻辑是:
- 1. 你发指令
- 2. Claude 执行一步
- 3. 它停下来等你确认
- 4. 你发”继续”
- 5. 它再执行下一步
这不是 bug,是特性。 它设计出来就是”助手”,不是”代理”。
从”助手”到”代理”的关键一跃
Hook 的核心价值:不是替我们写完整条工作流,而是在关键节点补一层判断,让 Claude 少掉很多“做一步就停一下”的来回。
一个真实的对比
只用 Skill:
我们:/fix-bug
Claude:分析了问题,需要修改 3 个文件
我们:继续
Claude:修改了第 1 个文件
我们:继续
Claude:修改了第 2 个文件
我们:继续
Claude:修改了第 3 个文件,需要跑测试
我们:继续
Claude:测试通过
Skill + Hook:
我们:/fix-bug
Claude:已经完成修改,并补充了测试结果;如果还要继续整理提交信息,可以接着往下走
差别:Skill 先把事讲清,Hook 再把关键节点连起来。它未必能一口气把所有动作都做完,但确实能少掉一大串手动确认。
Hook 生命周期图:从会话开始到会话结束的关键拦截点2. Hook 是什么
Hook 是 Claude Code 生命周期中的”拦截点”和”触发器”。
Hook 的两大作用
作用 1:安全检查(拦截)
- • 在危险操作前阻止
- • 验证条件是否满足
- • 失败时告警
作用 2:补上下文或阻断结果 ⭐ 核心
- • 工具执行后补一段上下文给 Claude
- • 发现结果有问题时直接拦下
- • 把流程从“做一步停一下”推向“继续往下收”
常用 Hook 事件
关键理解:
- •
PreToolUse= “执行前要做什么”,适合检查、阻止、改输入 - •
PostToolUse= “执行后要补什么”,适合补上下文、校验结果,必要时 block - • 如果想硬拦住 Claude 在一个节点停下,更常见的是配合
Stop/SubagentStop一类 hook
Hook 的两种类型
- 1. Command Hook:执行 shell 脚本
- 2. HTTP Hook:调用外部服务
咱们主要用 Command Hook,简单直接。
实战例子一:改代码后自动跑测试、跑检查,再进入可提交状态3. 实战例子:不间断自动化作业
例子 1:改代码→自动跑测试→自动提交(PostToolUse 链式触发)
场景:Claude 修改代码后,自动跑测试。没过就拦下,过了就补一段上下文,告诉 Claude 这轮修改已经过了检查。
核心思路:用 PostToolUse Hook 在工具执行后补检查结果,而不是把“继续 / 停止”全塞回对话里。
创建文件 .claude/hooks/auto-pipeline.sh:
#!/bin/bash
# 这个 Hook 实现:改代码 -> 跑测试 -> 把结果补回上下文
INPUT=$(cat)
EVENT=$(printf '%s' "$INPUT" | jq -r '.event_name')
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name')
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
# 只在 Edit 工具后触发
if [ "$EVENT" != "PostToolUse" ] || [ "$TOOL" != "Edit" ]; then
exit 0
fi
# 判断是否是 Python 文件
if [[ "$FILE" != *.py ]]; then
exit 0
fi
# 步骤 1:自动跑相关测试
pytest "tests/test_$(basename $FILE)" -v --tb=short
TEST_RESULT=$?
if [ $TEST_RESULT -ne 0 ]; then
jq -n '{
decision: "block",
reason: "测试失败,请先修复再继续"
}'
exit 0
fi
# 步骤 2:自动跑 lint
flake8 "$FILE"
LINT_RESULT=$?
if [ $LINT_RESULT -ne 0 ]; then
jq -n '{
decision: "block",
reason: "代码风格检查没有通过,请先修完再继续"
}'
exit 0
fi
# 步骤 3:把检查结果补回 Claude 的上下文
jq -n '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: "刚刚修改的文件已经通过 pytest 和 flake8 检查,可以继续整理提交信息或做后续收尾。"
}
}'
效果:
- 1. 我们让 Claude 修改代码
- 2. 修改完成后,Hook 自动跑测试和 lint
- 3. 没过就直接拦下,过了就把结果补回上下文
- 4. 后面不用再补一句“继续跑测试吧”
这就是这类 Hook 最实用的地方:把机械确认压掉,把结果直接接回流程里。
实战例子二:PreToolUse 在执行前阻止危险命令例子 2:阻止危险命令(PreToolUse)
场景:阻止 rm -rf 和 git push --force
创建文件 .claude/hooks/block-dangerous.sh:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty')
# 阻止 rm -rf
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
exit 0
fi
# 阻止强制推送
if echo "$COMMAND" | grep -q 'git push.*--force'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Force push blocked"
}
}'
exit 0
fi
exit 0
配置到 settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
效果:Claude 尝试执行危险命令时,Hook 会阻止并提示原因。

4. Skill + Hook:完整的不间断工作流
核心配合模式
完整例子:代码修复机器人
目标:发现一个 bug → 自动分析 → 自动修复 → 自动测试 → 自动提交
Skill(.claude/skills/auto-fix/SKILL.md):
---
name: auto-fix
description: 自动分析 bug、修复、测试、提交的完整流程
disable-model-invocation: true
---
## 自动修复流程
1. 分析错误日志,定位问题
2. 查看相关代码文件
3. 制定修复方案(等待 Hook 检查)
4. 执行修复(Hook 自动触发测试)
5. 测试通过后提交
## 注意
- 每一步都有 Hook 自动检查
- 测试失败会暂停,修复后自动继续
- 提交前必须人工确认
这里故意加了 disable-model-invocation: true,意思是这个 skill 只在我们手动 /auto-fix 时触发,不让 Claude 自己决定什么时候开修。
Hook 配置(settings.json):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/after-read.sh"
}
]
},
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/auto-test-and-lint.sh"
}
]
}
]
}
}
Hook 脚本 1:读完文件后自动分析(.claude/hooks/after-read.sh)
#!/bin/bash
# 读完相关文件后,自动提示分析下一步
INPUT=$(cat)
EVENT=$(printf '%s' "$INPUT" | jq -r '.event_name')
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name')
if [ "$EVENT" == "PostToolUse" ] && [ "$TOOL" == "Read" ]; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: "相关文件已经读完,可以开始分析刚刚看到的实现和潜在问题。"
}
}'
fi
Hook 脚本 2:修改后自动测试(.claude/hooks/auto-test-and-lint.sh)
#!/bin/bash
# 自动跑测试和 lint,把结果补回上下文
INPUT=$(cat)
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.py ]]; then
# 跑测试
pytest "tests/" -q
if [ $? -ne 0 ]; then
jq -n '{
"decision": "block",
"reason": "测试失败,请先修完再继续"
}'
exit 0
fi
# 跑 lint
flake8 "$FILE"
if [ $? -ne 0 ]; then
jq -n '{
"decision": "block",
"reason": "lint 没过,请先修完再继续"
}'
exit 0
fi
jq -n '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: "这轮修改已经通过测试和 lint,可以继续整理结果或准备提交。"
}
}'
fi
使用流程:
我们:/auto-fix 修复登录失败的问题
Claude:(继续分析并修复;如果测试失败会被拦下,如果通过会继续整理结果)
我们:确认提交
关键点:
- • 不用在每个节点都手动补一句“继续”
- • Hook 会把检查结果重新塞回流程里
- • 测试失败会暂停,修复后自动继续
这就是 7×24 小时不间断作业。
三层防护模型:权限模式、权限规则和 Hook 围住自动化核心4. 权限管理——最后一道防线
自动化不等于无控制。权限是最后一道安全边界。
三层防护模型:
第一层:权限模式(大边界)
权限模式控制 Claude 的整体行为方式:
如何设置权限模式:
方式 1:启动时指定(一次性)
claude --permission-mode acceptEdits
方式 2:配置文件中设置(持久)
{
"defaultMode": "acceptEdits"
}
方式 3:运行时切换
/permissions
Yolo 模式(危险但有用):
claude --dangerously-skip-permissions
⚠️ 警告:
- • 只在隔离环境(devcontainer、虚拟机)使用
- • 可能导致数据丢失、系统损坏
- • 企业管理员可以禁用此模式
第二层:权限规则(细粒度控制)
权限规则让你精确控制每个工具的使用。
配置文件位置(按优先级):
- 1. 用户级:
~/.claude/settings.json—— 所有项目生效 - 2. 项目级:
.claude/settings.json—— 当前项目生效 - 3. 本地级:
.claude/settings.local.json—— 仅你个人生效,不提交到 git
权限规则语法:
{
"permissions": {
"allow": [
"Read",
"Edit",
"Bash(npm run *)",
"Bash(git status)",
"Bash(git commit *)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Read(./.env*)"
]
}
}
规则匹配优先级:deny > ask > allow
常用规则示例:
{
"permissions": {
"allow": [
"Bash(ls *)",
"Bash(cat *)",
"Bash(npm run *)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git status)",
"Bash(python *)",
"Bash(pytest *)"
],
"ask": [
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git checkout *)",
"Bash(pip install *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(rm -f /*)",
"Bash(git push --force)",
"Bash(git reset --hard)",
"Read(./.env*)",
"Read(*.key)",
"Read(*.pem)"
]
}
}
测试当前权限配置:
/permissions
这个命令会显示当前生效的所有权限规则。
第三层:Hook(动态检查)
Hook 在运行时动态判断,是最灵活的一层。
三层配合示例:
权限模式:acceptEdits(自动接受文件修改)
↓
权限规则:允许 Bash(pytest *),但阻止 Bash(rm -rf *)
↓
Hook:修改文件后自动跑测试,测试失败阻止提交
完整配置示例
场景:日常开发,自动跑测试,但保护敏感文件
~/.claude/settings.json(用户级,所有项目生效):
{
"defaultMode": "acceptEdits",
"permissions": {
"allow": [
"Read",
"Edit",
"Bash(ls *)",
"Bash(cat *)",
"Bash(npm run *)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(python *)",
"Bash(pytest *)"
],
"ask": [
"Bash(git commit *)",
"Bash(git push *)"
],
"deny": [
"Bash(rm -rf *)",
"Read(./.env*)",
"Read(*.key)",
"Read(*.pem)"
]
}
}
项目级覆盖(.claude/settings.json):
{
"permissions": {
"allow": [
"Bash(npm run build)"
]
}
}
Hook 配置(.claude/settings.json):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/run-tests.sh"
}
]
}
]
}
}
渐进式自动化路径:从手动到半自动、高度自动,再到完全自动5. 渐进式自动化路径
不要直接跳到 Yolo 模式。推荐按这个路径:
阶段 1:手动模式(建立信任)
- • 使用
default模式 - • 每次操作都确认
- • 观察 Claude 的行为
阶段 2:半自动(提高效率)
- • 切换到
acceptEdits模式 - • 添加
PostToolUseHook 自动跑测试 - • 保留关键操作的确认
阶段 3:高度自动(熟练后)
- • 使用
dontAsk模式 - • 完善的 Hook 检查体系
- • 限制工具范围(
--tools)
阶段 4:完全自动(仅限沙箱)
- • devcontainer 或虚拟机
- •
claude --dangerously-skip-permissions - • 完全无人值守
6. 最佳实践
- 1. Skill 负责”做什么”,Hook 负责”什么时候检查”
- • 不要把检查逻辑写进 Skill
- • Hook 更适合做通用检查
- 2. 永远保留一个”逃生通道”
- • 知道怎么快速停止
- • 保留工作区和 git 的回退手段
- 3. 测试 Hook 本身
- • 确保检查逻辑正确
- • 避免 Hook 误拦截
- 4. 日志留痕
- • Hook 执行记录很重要
- • 方便排查问题
- 5. 渐进式放开
- • 不要直接 Yolo
- • 逐步建立信任
配设置、测试这四步连起来动手试试
今天就写一个自己的 Hook:
步骤 1:创建目录
mkdir -p .claude/hooks
步骤 2:写 Hook 脚本 .claude/hooks/my-first-hook.sh:
#!/bin/bash
# 这个 Hook 在修改 README 时提醒你更新目录
INPUT=$(cat)
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *"README.md" ]]; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: "README 刚被改过,别忘了顺手检查目录、示例或相关说明是否也要更新。"
}
}'
fi
exit 0
步骤 3:配置 settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/my-first-hook.sh"
}
]
}
]
}
}
步骤 4:测试
让 Claude 修改 README.md,看看是否有提示。
小结
AI “偷懒”的真正原因:对话式设计让它天生就是”说一句动一下”。
Hook 的核心价值:不是凭空变出一条全自动流水线,而是在关键节点补检查、补上下文、必要时直接拦下。
Skill + Hook 配合 = 真正的 7×24 小时自动化作业
不是不让 Claude 动,而是让它在规则内自主动。
Skill、Hook 和权限三者配合,形成自动化工作流的闭环附录:Hook 返回值格式参考
根据 Claude Code 官方文档,Hook 脚本可以返回 JSON 输出,但不同事件支持的字段并不一样。
PostToolUse 事件
PostToolUse 事件更适合在工具执行后补上下文,或者在结果明显有问题时 block 当前流程:
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
PreToolUse 事件
PreToolUse 事件用于在工具执行前进行拦截检查,返回值格式如下:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow | deny",
"permissionDecisionReason": "决策原因说明"
}
}
常见错误
以下写法不被支持:
// ❌ PostToolUse 不支持 continue / stop 这一套写法
{
"decision": "continue",
"userMessage": "继续执行"
}
// ❌ 不存在的 suggestedUserPrompt 字段
{
"suggestedUserPrompt": "继续执行的提示"
}
正确的做法是按具体事件去返回对应字段。对 PostToolUse 来说,最常见的是 decision: "block" 和 hookSpecificOutput.additionalContext。
参考资料
- • Claude Code Docs,Hooks 指南:https://code.claude.com/docs/en/hooks-guide
- • Claude Code Docs,权限管理:https://code.claude.com/docs/en/permissions
- • Claude Code Docs,最佳实践:https://code.claude.com/docs/en/best-practices
本文来自转载微信公众号-凌小添 ,不代表发现AI立场,如若转载,请联系原作者;如有侵权,请联系编辑删除。
