首页
看点啥
插画图片
首页 科技看点 Claude Code Hooks配置教程:从零实现方式任务完成提醒和代码自动检查实用指南

Claude Code Hooks配置教程:从零实现方式任务完成提醒和代码自动检查实用指南

2026-10-28 0

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Claude Code Hooks配置教程:从零实现方法任务完成提醒和代……”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

在这个场景下,CLAUDE.md 是说明,权限和 Hook 才是门锁。这篇就把"锁"装上——5 个拿来即用的 hook 设置。

Claude Code Hooks配置

一、为什么需 Hooks

实际处理时,假设你正用 Claude Code 维护一个项目。它干活利落,但有些事你每次都要叮嘱:改完记得格式化、别碰 .env、删库命令悠着点、写完做下检查。可它是 AI 不是员工,叮嘱不进考核:漏一次,格式乱了;漏两次,.env 里的密钥可能已经被读走。

在这个场景下,这类"每次都必须发生"的事,靠反复口头强调不靠谱,写进 CLAUDE.md 也只是"说明",模型可能遵循也可能忘。稳的办法是把它变成流程:让 Claude Code 在特定动作前后自动执行你指定的命令。这就是 Hooks——你在关键动作上挂一段命令,到点自动执行,执行结果还能反过来影响流程。从此规矩不靠自觉,靠机制兜底。

为什么需要 Hooks

二、设置写在哪、长什么样

实际处理时,Hooks 不写在 CLAUDE.md 里,而是写在 settings.json 中,共三个位置:

  • 项目级 .claude/settings.json:随项目提交进 git,全组共享
  • 个人级 ~/.claude/settings.json:对你的所有项目生效
  • 本地级 .claude/settings.local.json:不提交,适合先拿来做试验

配置写在哪、长什么样

设置结构是固定的三层嵌套:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{ "type": "command",
        "command": "python .claude/hooks/fmt.py" }]
    }]
  }
}

配置写在哪、长什么样

对应关系记三点:

  1. 落到代码里,事件名决定"什么时候触发"。常用的有:PreToolUse(工具执行前,可拦截)、PostToolUse(工具执行后)、UserPromptSubmit(你提交提示词时)、Stop(一轮回答结束时)、SessionStart(会话启动)、Notification(需你注意时)。事件不止这些,以官方文档为准。
  2. 落到代码里,matcher 决定"对哪些工具生效",写正则,如 Edit|Write、Bash。只有 PreToolUse 和 PostToolUse 需 matcher;Stop 这类事件与具体工具无关,不用写。
  3. 在这个场景下,command 是真正执行的 shell 命令。建议 JSON 里只留一行 python 某脚本.py,逻辑全放脚本文件里——转义省心,审查也便于。

配置写在哪、长什么样

三、工作原理:事件、匹配、执行、退出码

一次工具调用会走完这样一条时间线,hook 在其中两个节点出手:

工作原理:事件、匹配、执行、退出码

从实现思路看,hook 命令启动时,Claude Code 会把一段 JSON 从标准输入喂给它:Bash 的命令在 tool_input.command,Edit/Write 的目标路径在 tool_input.file_path。比如你想拦一条危险命令,脚本实际收到的大致长这样(有删减):

{ "tool_name": "Bash",
  "tool_input": { "command": "git push --force origin main" } }

脚本解析输入、做出判断,再用退出码表态——在这个场景下,0 放行,2 阻断(stderr 的内容会反馈给 Claude),其他非零是非阻断错误,流程照常继续结合项目来看,。更复杂的 JSON 输出控制方式本文不展开,以官方文档为准。记住退出码这个协议,下面的设置就都能看懂了。

工作原理:事件、匹配、执行、退出码

四、5 个能直接抄的设置

先看全景,每一行都对应下文一节:

5 个能直接抄的配置

约定:脚本统一放在项目的 .claude/hooks/ 目录下。Windows 用户注意,路径含空格时 command 里的路径要加引号。安装任何一条都是同样五步:建目录、存脚本、把设置粘进 settings.json、会话里输入 /hooks 确认加载、随手找个该触发的地方试一把。

改完代码自动格式化

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{ "type": "command",
        "command": "python .claude/hooks/format_changed.py" }]
    }]
  }
}
import json, os, shutil, subprocess, sys
PRETTY = ["npx", "prettier", "--write"]
FORMATTERS = {".py": ["python", "-m", "black", "--quiet"], ".ts": PRETTY, ".js": PRETTY,
              ".jsx": PRETTY, ".tsx": PRETTY, ".css": PRETTY, ".html": PRETTY}
def main() -> int:
    data = json.load(sys.stdin)
    path = data.get("tool_input", {}).get("file_path", "")
    cmd = FORMATTERS.get(os.path.splitext(path)[1].lower())
    if not cmd or not os.path.isfile(path):
        return 0  # 非代码文件或路径不存在:安全跳过
    if shutil.which(cmd[0]) is None:
        return 0  # 未安装格式化工具:跳过,不报错
    subprocess.run(cmd + [path])
    return 0
sys.exit(main())

结合项目来看,从标准输入取 file_path,按扩展名挑格式化命令。适用:任何多人协作或在意风格的项目。注意两点:没装的格式化工具会被安全跳过,所以先把 black 或 prettier 装好;格式化失败不得到 2,避免格式问题拦死流程。

拦截危险命令

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command",
        "command": "python .claude/hooks/block_danger.py" }]
    }]
  }
}
import json, re, sys
RULES = [
    (r"\brm\s+(-[a-z]*r[a-z]*f|-[a-z]*f[a-z]*r)", "递归强制删除(rm -rf)"),
    (r"\bgit\s+push\s+.*(--force|-f\b|--force-with-lease)", "强制推送远程分支"),
    (r"\bdrop\s+table\b|\btruncate\s+table\b", "清空数据库表"),
    (r"\bmkfs\b|\bdd\s+if=", "格式化/覆写磁盘"),
]
def main() -> int:
    sys.stderr.reconfigure(encoding="utf-8")  # 防止中文 Windows 下乱码
    raw = json.load(sys.stdin).get("tool_input", {}).get("command", "")
    cmd = re.sub(r"\s+", " ", raw.lower())  # 大小写、多余空格先归一化
    for pat, why in RULES:
        if re.search(pat, cmd):
            print(f"已拦截:{why}", file=sys.stderr)
            return 2  # 退出码 2 = 阻断,stderr 会反馈给 Claude
    return 0
sys.exit(main())

在这个场景下,命中黑名单就 exit 2,Claude 当场收到拒绝原因,会换个安全做法。适用:怕误删数据、怕强推覆盖同事提交的日常开发。注意:这是关键词匹配不是完整解析,绕过写法防不住,名单请按项目自己增删,它挡的是"手滑"不是"故意"。

保护敏感文件

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write|Read",
      "hooks": [{ "type": "command",
        "command": "python .claude/hooks/guard_secret.py" }]
    }]
  }
}
import json, os, sys
BAD_ENDS = (".pem", ".key", ".p12", ".pfx")
BAD_NAMES = ("id_rsa", "id_ed25519", "id_ecdsa")
def is_sensitive(path: str) -> bool:
    name = os.path.basename(path).lower()
    if name == ".env" or name.startswith(".env."):
        return True  # .env、.env.local 等变体都算
    return name in BAD_NAMES or name.startswith("id_rsa.") or name.endswith(BAD_ENDS)
def main() -> int:
    sys.stderr.reconfigure(encoding="utf-8")  # 防止中文 Windows 下乱码
    p = json.load(sys.stdin).get("tool_input", {}).get("file_path", "")
    if is_sensitive(p):
        print(f"已拦截:{p} 疑似密钥或环境变量文件", file=sys.stderr)
        return 2
    return 0
sys.exit(main())

结合项目来看,判断不盯全路径盯文件名,所以 .env.local、config/.env.production 这类变体也逃不掉。适用:仓库里有真实密钥、或想让 AI 连读都不读密钥的场景。注意:env.example 这种示例文件不会被误伤;名单是保守起点,按需增删。

任务完成提醒

{
  "hooks": {
    "Stop": [{
      "hooks": [{ "type": "command",
        "command": "powershell -c "[console]::beep(880,200)"" }]
    }]
  }
}

从实现思路看,这条不需脚本,一行命令即可。Stop 没有 matcher,Claude 每答完一轮就响一声。其他系统换个命令:macOS 用 osascript -e 'display notification "任务完成" with title "Claude Code"',Linux 用 notify-send "Claude Code" "任务完成"——我只在 Windows 实测过响铃,其余以你的环境为准。适用:挂着长任务切去做别的事的人。注意:别用弹窗类命令,太吵的提醒活不过三天。

改完自动跑检查

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{ "type": "command",
        "command": "python .claude/hooks/check_changed.py" }]
    }]
  }
}
import json, os, subprocess, sys
CHECK = ["python", "-m", "py_compile"]  # 换成你项目的快速检查,如 npx tsc --noEmit
def main() -> int:
    sys.stderr.reconfigure(encoding="utf-8")  # 防止中文 Windows 下乱码
    path = json.load(sys.stdin).get("tool_input", {}).get("file_path", "").replace("\", "/")
    if "/src/" not in f"/{path}" or not os.path.isfile(path):
        return 0  # 只看 src 下的改动,其余跳过
    r = subprocess.run(CHECK + [path], capture_output=True, text=True, encoding="utf-8", errors="replace")
    if r.returncode:
        print(r.stderr[-1200:], file=sys.stderr)
        return 2  # 检查不过:让 Claude 直接看到报错
    return 0
sys.exit(main())

理解这一步时,检查失败就 exit 2,报错文本直接进 Claude 的眼睛,它下一轮就自己修,不用你当传话筒。适用:TypeScript、Python 这类有秒级检查手段的项目。注意:必须快结合项目来看,——PostToolUse 是同步的,hook 慢一秒,每次修改都慢一秒;所以只跑单文件检查、且只对 src 目录生效,整包测试留给 CI。

五、调试方法与常用的坑

配了没反应,按顺序查:

  1. 理解这一步时,设置改完不是即插即用,会话里输入 /hooks 确认已加载、挂对了位置
  2. 用 python -m json.tool settings.json 验证 JSON 语法,少个逗号就全盘失效
  3. 手动喂 JSON 测脚本:echo '{"tool_input":{"command":"rm -rf /"}}' | python block_danger.py,看退出码和 stderr 是否符合预期
  4. 实际处理时,Windows 的双重转义最容易翻车:JSON 里的引号要写成 ",命令一复杂就老老实实写成脚本文件
  5. 从实现思路看,路径含空格必须加引号;hook 变慢就检查是不是把重型命令挂在了 PostToolUse

调试方法与常见的坑

还有两个容易忽略的细节。一是触发频率:Stop、Notification 这类事件每一轮对话都会触发,UserPromptSubmit 每次提交都触发,挂上命令前先想想"它一天会被调用几百次吗",提醒类尤其要轻。二是怎么停用:不想要了就把对应设置从 settings.json 里删掉,或者先挪到 settings.local.json 里观察几天;改完同样用 /hooks 复核,确认旧设置已经不在列表里。

六、安全提醒:这是把真钥匙

在这个场景下,Hooks 以你的用户权限执行任意 shell 命令,等于交出账号级别的权限,所以:

  • 理解这一步时,抄设置前逐字读 command。来路不明、看不懂、带下载执行的,一律不抄
  • 实际处理时,拦截类 hook 是兜底不是唯一防线,和权限规则配合用,别因为挂了 hook 就全程免确认
  • 改完设置用 /hooks 复核;脚本里不要写死任何密钥
  • 从标准输入读到的 JSON 是输入不是信任,脚本内先校验再采用

安全提醒:这是把真钥匙

结语

结合项目来看,Hooks 的价值在于把"每次都要叮嘱"变成"每次自动发生":格式化交给,底线交给,体验交给,质量交给。建议从 开始试水,确认机制跑通,再上会拦人的。

落到代码里,总的来说,Claude适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

喜欢(0)

上一篇

Ollama最新版安装包下载和安装步骤(开源,简单,小白5分钟学会)实用指南

Ollama最新版安装包下载和安装步骤(开源,简单,小白5分钟学会)实用指南

下一篇

在C# WinForms中实现方式窗体自适应布局的完整方案实用指南

在C# WinForms中实现方式窗体自适应布局的完整方案实用指南
猜你喜欢