news 2026/6/10 3:57:05

12-Hooks 上篇:五种事件 + 实用模板 —— 让 AI 自动执行你的脚本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
12-Hooks 上篇:五种事件 + 实用模板 —— 让 AI 自动执行你的脚本

Hooks 上篇:五种事件 + 实用模板 —— 让 AI 自动执行你的脚本

Skills 让 AI 学会了你的工作流,MCP 给 AI 装上了手脚。但还有一个问题没解决:你希望某些事在特定时机自动发生——AI 编辑文件后自动格式化、提交前自动跑测试、启动时自动加载环境变量。这些就是 Hooks 的领域。


Hook 是什么:给 Claude Code 装上触发器

一句话理解

Hook = "当 X 发生时,自动执行 Y 脚本" X = 五种事件之一(PreToolUse、PostToolUse、SessionStart...) Y = 你的 Shell 脚本(任何可执行文件) Claude Code 在关键节点自动触发你的脚本, 脚本的 exit code 决定后续行为(允许/拒绝/跳过)。

Hook 和 Skill、MCP 的区别

Skill: 触发方式:你说的话匹配到 Skill 的 trigger 做什么:把一套提示词注入 AI 上下文 谁决定的:AI 判断是否触发 MCP Tool: 触发方式:AI 判断需要调用某个 Tool 做什么:执行一个外部操作 谁决定的:AI 判断是否调用 Hook: 触发方式:Claude Code 的内部事件自动触发 做什么:执行你的脚本,影响 Claude Code 的行为 谁决定的:你在配置中写死了触发条件,AI 无法跳过

关键差异:Hook 不由 AI 控制。你定义的事件发生时,Hook 必然触发,AI 无法绕过。这就是 Hooks 适合做"安全检查"的原因——你不能指望 AI 自己审查自己。


五种事件逐详解

1. PreToolUse —— 工具调用前拦截

触发时机:AI 即将执行某个 Tool(Edit、Write、Bash 等)之前 参数: - tool_name: 即将被调用的 Tool 名称 - tool_input: 即将传给 Tool 的参数 - session_id: 当前会话 ID Hook 脚本的 exit code 含义: 0 → 允许执行(放行) 1 → 拒绝执行(阻止 Tool 调用) 2 → 跳过本次 Hook 的结果(但允许执行)——较少用 适用场景: - 拦截危险命令(rm -rf、git push --force) - 编辑前自动检查文件是否在白名单中 - 特定 Tool 调用前注入额外逻辑

示例:拦截危险命令

#!/bin/bash# hooks/pre-tool-use.sh# PreToolUse Hook:拦截危险操作TOOL_NAME="$1"TOOL_INPUT="$2"# 只拦截 Bash 命令if["$TOOL_NAME"!="Bash"];thenexit0# 允许执行fi# 黑名单:匹配到就拒绝DANGEROUS_PATTERNS=("rm -rf /""git push --force""git push -f""DROP TABLE""DROP DATABASE""> /dev/sda")forpatternin"${DANGEROUS_PATTERNS[@]}";doifecho"$TOOL_INPUT"|grep-qi"$pattern";thenecho"🛑 Hook 拦截:检测到危险操作 '$pattern'。已阻止执行。"echo" 如果你确定要执行,请手动运行此命令。"exit1# 拒绝执行fidone# 高风险操作但可接受:发出警告RISKY_PATTERNS=("rm -rf""sudo ""chmod 777")forpatternin"${RISKY_PATTERNS[@]}";doifecho"$TOOL_INPUT"|grep-qi"$pattern";thenecho"⚠️ 警告:检测到高风险操作 '$pattern'。请确认命令正确。"fidoneexit0# 允许执行

2. PostToolUse —— 工具调用后处理

触发时机:AI 执行完某个 Tool 之后 参数: - tool_name: 刚执行完的 Tool 名称 - tool_input: Tool 的参数 - tool_output: Tool 的输出结果 - exit_code: Tool 的执行结果(0=成功) 适用场景: - 编辑代码后自动格式化 - 修改文件后自动运行相关测试 - 部署后自动做健康检查 - Git 提交后自动推送到备份仓库

示例:编辑后自动格式化 + 测试提醒

#!/bin/bash# hooks/post-tool-use.sh# PostToolUse Hook:编辑后自动格式化 + 测试提醒TOOL_NAME="$1"TOOL_INPUT="$2"TOOL_EXIT_CODE="$4"# 只在 Edit 或 Write 成功后触发if["$TOOL_NAME"!="Edit"]&&["$TOOL_NAME"!="Write"];thenexit0fiif["$TOOL_EXIT_CODE"!="0"];thenexit0# 编辑失败了,不处理fi# 从 Tool input 中提取被编辑的文件路径FILE_PATH=$(echo"$TOOL_INPUT"|grep-o'"file_path": "[^"]*"'|cut-d'"'-f4)if[-z"$FILE_PATH"];thenexit0fiecho"🔧 Hook: 检测到文件修改:$FILE_PATH"# Python 文件 → 自动格式化ifecho"$FILE_PATH"|grep-q'\.py$';thenifcommand-vblack&>/dev/null;thenblack"$FILE_PATH"2>/dev/nullecho" ✅ Black 格式化完成"fifi# TypeScript/JavaScript 文件 → 自动格式化ifecho"$FILE_PATH"|grep-q'\.\(ts\|tsx\|js\|jsx\)$';thenifcommand-vprettier&>/dev/null;thenprettier--write"$FILE_PATH"2>/dev/nullecho" ✅ Prettier 格式化完成"fifi# 如果文件在 src/ 或 lib/ 目录下,提醒运行测试ifecho"$FILE_PATH"|grep-q'src/\|lib/';thenecho" 💡 提示:源文件已修改,建议运行测试: npm test 或 pytest"fiexit0

3. SessionStart —— 会话启动初始化

触发时机:Claude Code 每次启动新会话时 参数: - session_id: 新会话的 ID - project_dir: 当前项目根目录 适用场景: - 自动加载 .env 环境变量 - 检查项目依赖是否已安装 - 展示项目状态(Git 分支、未提交文件、上次部署时间) - 预热 MCP Server 连接

示例:启动时加载环境变量 + 项目状态摘要

#!/bin/bash# hooks/session-start.sh# SessionStart Hook:加载环境变量 + 展示项目状态PROJECT_DIR="$2"echo"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"echo"🚀 Claude Code 会话已启动"echo"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"# 1. 加载 .env 文件(如果存在)if[-f"$PROJECT_DIR/.env"];then# 安全地加载环境变量(不覆盖已存在的变量)set-asource"$PROJECT_DIR/.env"set+aecho"✅ 已加载环境变量 (.env)"fi# 2. 展示 Git 状态if[-d"$PROJECT_DIR/.git"];thenBRANCH=$(git-C"$PROJECT_DIR"branch --show-current)UNTRACKED=$(git-C"$PROJECT_DIR"status--short|wc-l|tr-d' ')echo"📂 Git 分支:$BRANCH| 未提交文件:$UNTRACKED"fi# 3. 检查 Python 依赖if[-f"$PROJECT_DIR/requirements.txt"];thenif!python-c"import fastapi"2>/dev/null;thenecho"⚠️ Python 依赖未安装,运行: pip install -r requirements.txt"fifi# 4. 检查 Node 依赖if[-f"$PROJECT_DIR/package.json"];thenif[!-d"$PROJECT_DIR/node_modules"];thenecho"⚠️ Node 依赖未安装,运行: npm install"fifiecho""exit0

4. PreCompaction —— 上下文压缩前保存

触发时机:Claude Code 即将压缩上下文(清理旧信息以释放 Token)之前 参数: - session_id: 当前会话 ID - context_size: 当前上下文大小(Token 数) - project_dir: 项目目录 适用场景: - 在上下文被清理前,自动保存重要信息到 Memory - 归档本次会话的关键决策和发现 - 保存当前任务的进度,防止压缩后"失忆"

示例:压缩前自动归档关键信息

#!/bin/bash# hooks/pre-compaction.sh# PreCompaction Hook:压缩前归档关键决策SESSION_ID="$1"CONTEXT_SIZE="$2"PROJECT_DIR="$3"echo"📦 Hook: 上下文压缩前自动归档..."# 从 Claude Code 的会话日志中提取关键信息# (Claude Code 会在调用此 Hook 时传入最近的决策摘要)DECISIONS_FILE="$PROJECT_DIR/.claude/memory/decisions-$(date+%Y-%m-%d).md"# 如果上下文已经很大(超过 150K Token),额外提醒if["$CONTEXT_SIZE"-gt150000];thenecho"⚠️ 上下文已达${CONTEXT_SIZE}Token,建议 /compact 或 /clear"fiecho"✅ 关键信息已归档。压缩后 AI 不会丢失之前的重要决策。"exit0

5. Notification —— 事件通知推送

触发时机:Claude Code 中的特定通知事件发生时 参数: - event_type: 通知类型(task_complete, error, permission_denied 等) - message: 通知内容 - session_id: 会话 ID 适用场景: - 长时间任务完成后发送桌面通知 - 错误发生时发送 Slack 消息 - AI 被拒绝执行某个操作时记录日志 - 集成外部监控系统

示例:长时间任务完成时发送通知

#!/bin/bash# hooks/notification.sh# Notification Hook:任务完成/出错时通知你EVENT_TYPE="$1"MESSAGE="$2"case"$EVENT_TYPE"intask_complete)# macOS 桌面通知osascript-e"display notification\"$MESSAGE\"with title\"Claude Code 任务完成\""# 也可以用 terminal-notifier:# terminal-notifier -title "Claude Code" -message "$MESSAGE";;error)echo"🔴 错误:$MESSAGE">&2# 发送到 Slack(如果你配置了 Slack webhook)# curl -X POST "$SLACK_WEBHOOK" -d "{\"text\":\"Claude Code Error: $MESSAGE\"}";;permission_denied)echo"🟡 AI 的操作被拒绝:$MESSAGE"# 记录到日志文件echo"[$(date)] Permission Denied:$MESSAGE">>~/.claude/logs/denied.log;;esacexit0

五种事件总结

事件触发时机核心用途exit code 0exit code 1
PreToolUseTool 调用拦截、安全检查放行阻止执行
PostToolUseTool 调用自动格式化、提醒正常标记失败
SessionStart会话启动初始化环境、加载配置正常启动失败
PreCompaction上下文压缩前保存关键信息正常阻止压缩
Notification通知事件桌面通知、告警正常忽略

实战模板库:四个开箱即用的 Hook

模板一:自动格式化(PostToolUse)

上面已有完整代码。核心逻辑:检测到 Edit/Write 成功 → 判断文件类型 → 自动运行对应格式化工具。

模板二:Git 提交前检查(PreToolUse)

#!/bin/bash# PreToolUse Hook:Git 提交前检查TOOL_NAME="$1"TOOL_INPUT="$2"# 只拦截 git commit 命令if["$TOOL_NAME"!="Bash"];thenexit0fiif!echo"$TOOL_INPUT"|grep-q"git commit";thenexit0fi# 检查是否有未解决的合并冲突ifgitdiff--name-only --diff-filter=U|grep-q.;thenecho"🛑 提交被阻止:存在未解决的合并冲突。"gitdiff--name-only --diff-filter=Uexit1fi# 检查 .env 文件是否在暂存区(防止密钥提交)ifgitdiff--cached--name-only|grep-q'\.env$';thenecho"🛑 提交被阻止:.env 文件在暂存区。请从暂存区移除。"echo" 运行: git reset HEAD .env"exit1fi# 检查是否有调试代码(console.log / print 等)ifgitdiff--cached|grep-q'+.*console\.log\|+.*print(';thenecho"⚠️ 警告:暂存的代码中包含调试语句(console.log / print)。"echo" 建议清理后提交。"# 注意:这里只用警告,不阻止提交(exit 0)fiecho"✅ 提交前检查通过。"exit0

模板三:环境变量注入(SessionStart)

上面已有完整代码。核心逻辑:启动时 source .env,检查依赖安装状态。

模板四:危险文件保护(PreToolUse)

#!/bin/bash# PreToolUse Hook:保护关键文件不被 AI 修改TOOL_NAME="$1"TOOL_INPUT="$2"# 只拦截 Edit 和 Writeif["$TOOL_NAME"!="Edit"]&&["$TOOL_NAME"!="Write"];thenexit0fi# 受保护的文件列表PROTECTED_FILES=(".env"".env.production""credentials.json""secrets.yml""prod-database.yml")# 从 Tool input 中提取文件路径FILE_PATH=$(echo"$TOOL_INPUT"|grep-o'"file_path": "[^"]*"'|cut-d'"'-f4)forprotectedin"${PROTECTED_FILES[@]}";doifecho"$FILE_PATH"|grep-q"$protected";thenecho"🛑 Hook 拦截:$FILE_PATH是受保护文件,AI 不能直接修改。"echo" 请手动编辑此文件。"exit1fidoneexit0

如何配置 Hook

配置文件位置

// ~/.claude/settings.json 或项目 .claude/settings.json{"hooks":{"PreToolUse":[{"matcher":"Bash",// 可选:只匹配特定 Tool"command":"bash .claude/hooks/pre-tool-use.sh"}],"PostToolUse":[{"matcher":"Edit|Write",// 支持正则"command":"bash .claude/hooks/post-tool-use.sh"}],"SessionStart":[{"command":"bash .claude/hooks/session-start.sh"}],"PreCompaction":[{"command":"bash .claude/hooks/pre-compaction.sh"}],"Notification":[{"matcher":"task_complete|error",// 只监听特定事件"command":"bash .claude/hooks/notification.sh"}]}}

Matcher 字段说明

"matcher": "" → 不写或空:匹配所有 Tool/事件 "matcher": "Edit" → 只匹配 Edit 事件 "matcher": "Edit|Write" → 匹配 Edit 或 Write(正则) "matcher": "Bash" → 只匹配 Bash 命令 对于 PreToolUse / PostToolUse:matcher 匹配的是 tool_name 对于 Notification:matcher 匹配的是 event_type 对于 SessionStart / PreCompaction:通常不需要 matcher

Hook 调试三招

第一招:加日志

#!/bin/bash# 在 Hook 脚本开头加日志LOG_FILE="$HOME/.claude/logs/hook-debug.log"echo"==========$(date)==========">>"$LOG_FILE"echo"Event:$1">>"$LOG_FILE"echo"Input:$2">>"$LOG_FILE"echo"Args:$@">>"$LOG_FILE"# 你的 Hook 逻辑...# 退出码也记录EXIT_CODE=$?echo"Exit:$EXIT_CODE">>"$LOG_FILE"exit$EXIT_CODE

然后tail -f ~/.claude/logs/hook-debug.log实时看 Hook 的执行情况。

第二招:Dry-run 模式

# 在 Hook 脚本里加一个 dry-run 开关DRY_RUN=false# 调试时设为 trueif["$DRY_RUN"=true];thenecho"[DRY RUN] 会执行:$@"exit0fi

第三招:手动模拟

# Hook 脚本本质上是 Shell 脚本,可以直接手动调用测试:bash.claude/hooks/pre-tool-use.sh"Bash"'{"command": "rm -rf /test"}'# 预期输出:🛑 Hook 拦截# 预期 exit code:1

常见问题

Q:Hook 脚本执行失败会影响 AI 吗?

A:取决于 Hook 的 exit code。exit 0 放行,exit 1 阻止。但如果 Hook 脚本本身崩溃(非零 exit code 但不是 1),Claude Code 通常按 exit 0 处理(放行),同时记录错误。所以不要在 Hook 里写可能崩溃的代码。

Q:多个 Hook 的执行顺序?

A:同一事件的多个 Hook,按 settings.json 中定义的顺序依次执行。如果某个 Hook 返回 exit 1(PreToolUse),后续 Hook 不会再执行(因为 Tool 已经被阻止了)。

Q:Hook 执行太慢怎么办?

A:Hooks 会阻塞 Claude Code 的主流程。SessionStart 可以慢一点无所谓,但 PreToolUse/PostToolUse 需要快(< 2 秒)。不要在 Hook 里做网络请求或大量 IO。


延伸阅读

  • Claude Code Hooks 官方文档
  • Claude Code Settings 文档
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 3:56:58

工商业储能系列: 主动均衡之集中式主动均衡<双向隔离DCDC+开关矩阵>

前言 在锂电池储能系统中&#xff0c;电芯之间存在难以避免的容量和内阻差异&#xff0c;形成“木桶效应”——最差的那节电芯限制了整个模组的可用容量&#xff0c;并加速整体衰减。主动均衡技术正是为了解决这一问题而生。 集中式主动均衡属于主动均衡的一种主流技术路线&a…

作者头像 李华
网站建设 2026/6/10 3:55:35

还在死磕 写空格?这4招让HTML空格代码原地起飞

怎样于网页里呈现空格吗? 这看上去好像是个简易的问题, 然而事实上, 它关联到诸多不一样的方法以及技巧。在这篇文章当中, 我们会介绍几种常见的技巧用以呈现空格, 涵盖实体字符、CSS样式、HTML标签以及代码。使用实体字符首种办法乃是运用实体字符, 其为一种特殊的HTML字符, …

作者头像 李华
网站建设 2026/6/10 3:54:30

【Spring Boot + MyBatis|第3篇】统一返回结果 Result 类设计

前言上一篇我们学习了 Controller 中常见的三种参数接收方式&#xff1a;RequestParam、PathVariable、RequestBody。这一篇继续学习一个项目中非常常见的设计&#xff1a;统一返回结果类 Result。在 Spring Boot 项目中&#xff0c;如果每个接口返回的数据格式都不一样&#x…

作者头像 李华
网站建设 2026/6/10 3:52:10

基于UCIe与3DIC Compiler的高效多芯片设计与IP集成

基于UCIe与3DIC Compiler的高效多芯片设计与IP集成 随着高性能计算、人工智能、汽车电子和移动终端等应用对计算能力与功耗效率要求的不断提升,芯片设计正加速从传统的单片式SoC向多芯片(multi-die)架构演进。通过2.5D/3D先进封装技术,设计者可以将多个异构或同质芯片(又…

作者头像 李华
网站建设 2026/6/10 3:50:01

2026西安代理记账新选择:金税四期下,如何挑选靠谱财税服务商

面对市场上数量众多的财税服务机构&#xff0c;如何结合新政要求、行业特性、本地化服务能力&#xff0c;筛选出稳定、专业、高风控水平的代账合作伙伴&#xff0c;成为西安广大企业主重点关注的问题。本文结合2026年本地财税行业现状&#xff0c;确立全新的服务商评估体系&…

作者头像 李华
网站建设 2026/6/10 3:49:59

小白都能看懂的setState原理,一次吃透!!

react中执行setState更新状态时都做了什么&#xff1f;结论&#xff1a;setState是react中修改组件状态、触发视图更新的一个api。他是一个异步更新的过程&#xff0c;但是不同于setTimeout、promise等异步Api实现&#xff0c;而是react自身的批量更新机制导致的异步&#xff0…

作者头像 李华