ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

[进阶篇18] 构建OpenCode事件钩子实现工作流自动化

[进阶篇18] 构建OpenCode事件钩子实现工作流自动化

前言

你是不是每次改完代码都要手动跑一遍测试、格式化、lint检查?或者每次PR合并后都要手动去更新文档、发通知、部署?这些重复的手工操作,明明可以让AI自动替你完成。

上篇我们优化了大型项目的上下文管理,AI在处理百万行代码时也能保持清醒了。但现在还有一个“效率黑洞”没解决——大量重复的手工操作依然占据着你的时间。代码提交后的测试、文件修改后的格式化、会话结束后的通知……这些能不能让OpenCode自动完成?

建议先点个关注,收藏这个专栏,这篇我们来用OpenCode的事件钩子系统构建自动化工作流——让AI在文件修改后自动跑测试、在会话结束后自动发通知、在工具调用前自动做安全检查,真正实现“写代码,剩下的交给OpenCode”。

上篇回顾

上篇我们构建了四层上下文管理体系——用/compact做被动压缩、用ACP做智能剪枝、用DCP做自动清理、用Context Manager做预索引,大型项目中的AI上下文终于不再“爆炸”了。

现在AI的“脑子”够用了,但手脚还不够勤快。每次你做完一件事,还得手动触发下一件事——改完代码要手动跑测试、写完文档要手动提交、会话结束要手动通知。本篇就是给OpenCode装上“自动化的手脚”——让它在合适的时机自动执行合适的动作。

环境与前置说明

本篇依赖上篇的产出成果:

  • OpenCode已安装并可用
  • 熟悉插件开发的基本流程(event钩子)
  • 了解TypeScript/JavaScript插件编写

本篇会用到以下插件:

# YAML Hooks插件——声明式自动化(推荐入门)opencode plugin opencode-yaml-hooks-gf# 或者手动安装(如果上述命令不生效)bunaddopencode-yaml-hooks

opencode-yaml-hooks是目前最成熟的声明式钩子方案。它通过hooks.yaml文件配置自动化规则,不需要写TypeScript代码,适合90%的自动化场景。如果你需要更复杂的逻辑,也可以用TypeScript插件直接订阅event钩子。

文章目录

    • 前言
    • 上篇回顾
    • 环境与前置说明
    • 核心内容
      • 第一步:理解“事件钩子”到底是什么
      • 第二步:安装opencode-yaml-hooks——零代码自动化
      • 第三步:配置第一个自动化规则——文件修改后自动格式化
      • 第四步:配置工具执行前后的钩子——安全门禁
      • 第五步:配置会话生命周期钩子——会话开始/结束自动化
      • 第六步:高级钩子配置——scope、runIn与async
      • 第七步:综合实战——构建完整的自动化工作流
    • 异常处理与常见坑
      • 报错1:YAML格式错误导致钩子不生效
      • 报错2:`action: stop`不生效,危险操作仍然执行
      • 报错3:`file.changed`钩子在文件修改后没有触发
    • 本章产出总结
    • 作者互动与资源引导
    • 下篇预告

核心内容

第一步:理解“事件钩子”到底是什么

目标:搞清楚OpenCode的事件钩子系统是什么,以及它能用来做什么自动化。

你可能会问:事件钩子不就是监听事件吗?跟前面学的event钩子有什么区别?

OpenCode的事件钩子系统本质上是同一个东西——插件通过event钩子订阅系统事件。但“事件钩子”这个概念,在OpenCode生态里有两种不同的使用方式:

方式一:声明式YAML钩子(opencode-yaml-hooks)

hooks.yaml文件中定义“当X事件发生时,执行Y动作”。不需要写代码,适合快速配置自动化规则。

方式二:程序式TypeScript钩子

在TypeScript插件中直接订阅event钩子,用代码实现复杂的自动化逻辑。

OpenCode的事件系统基于中央总线(event bus)运作:

  1. 组件发布事件(如session.createdfile.editedtool.execute.before
  2. 总线将事件分发给所有订阅者
  3. 插件通过event钩子消费事件

你可以在插件中订阅33种以上的系统事件,覆盖会话生命周期、文件变更、消息流、工具执行、LSP诊断等全场景。

注意了:声明式YAML钩子和程序式TypeScript钩子不是互斥的。你可以同时使用两者——用YAML钩子处理简单的文件变更自动化,用TypeScript插件处理复杂的业务逻辑。

运行验证:这一步不需要跑代码。你只需要记住一个核心概念——事件钩子 = “当X发生时,自动做Y”

第二步:安装opencode-yaml-hooks——零代码自动化

目标:安装YAML Hooks插件,让自动化配置像写配置文件一样简单。

声明式YAML钩子是入门自动化的最快方式。你不用写一行TypeScript代码,只需要在一个YAML文件里描述“什么事件触发什么动作”。

安装方式:

# 通过opencode plugin命令安装opencode plugin opencode-yaml-hooks-gf# 或者用bun直接安装(如果上述命令不生效)bunaddopencode-yaml-hooks

然后在opencode.json中注册插件:

{"$schema":"https://opencode.ai/config.json","plugin":["opencode-yaml-hooks"]}

创建钩子配置文件。YAML Hooks支持全局和项目两个位置:

类型路径作用范围
全局~/.config/opencode/hook/hooks.yaml所有项目生效
项目<项目根目录>/.opencode/hook/hooks.yaml仅当前项目生效

注意了:全局钩子先加载,项目钩子后加载,项目钩子可以覆盖或扩展全局钩子。

运行验证:安装完成后,在TUI中输入/hooks(如果插件支持该命令),或者检查插件是否在列表中:

opencode plugin list|grepyaml-hooks

如果能看到opencode-yaml-hooks,说明安装成功。

第三步:配置第一个自动化规则——文件修改后自动格式化

目标:配置一个file.changed钩子,让OpenCode在文件修改后自动运行代码格式化。

这是最常用、也最安全的自动化场景——你改完代码,OpenCode自动帮你格式化。

创建项目级钩子配置文件:

mkdir-p.opencode/hooktouch.opencode/hook/hooks.yaml

.opencode/hook/hooks.yaml中写入:

# .opencode/hook/hooks.yaml# 钩子规则列表hooks:# 规则1:代码文件修改后自动格式化-event:file.changed# 触发事件:文件被修改conditions:# 条件:只匹配代码文件-matchesCodeFiles# 内置条件,匹配 .ts, .js, .py 等actions:# 动作:执行格式化-bash:"npx prettier --write {{ .Paths }}"# 用Prettier格式化修改的文件# 规则2:TypeScript文件修改后自动运行类型检查-event:file.changedconditions:-matchesAnyPath:"**/*.ts"# 匹配所有TypeScript文件-matchesAnyPath:"**/*.tsx"# 匹配所有TSX文件actions:-bash:"npx tsc --noEmit"# 运行TypeScript类型检查

逐行解释一下:

  • event: file.changed:当任何文件被修改时触发
  • conditions:可选的条件过滤,只有满足条件才执行动作
  • matchesCodeFiles:内置条件,匹配常见的代码文件扩展名
  • matchesAnyPath:自定义路径匹配,支持glob模式
  • actions:要执行的动作列表,可以是bash命令、command命令或tool调用
  • {{ .Paths }}:模板变量,会被替换为实际修改的文件路径列表

注意了:file.changed是最干净的文件级钩子,适合linting、格式化、测试选择、索引和原子提交等工作流。它会自动去重——同一个文件在短时间内多次修改,只会触发一次钩子。

运行验证:保存配置文件后,在项目中修改一个代码文件(比如改一行代码然后保存)。观察终端——你应该能看到Prettier自动运行,并且修改的文件被格式化了。

第四步:配置工具执行前后的钩子——安全门禁

目标:在AI调用危险工具(如bashedit)前后插入安全检查或日志记录。

tool.before.*tool.after.*钩子让你在AI执行工具的前后插入自定义逻辑。

.opencode/hook/hooks.yaml中添加:

hooks:# 之前的规则保持不变...# 规则3:禁止读取.env文件-event:tool.before.read# 在read工具执行前触发action:stop# 阻止工具执行conditions:-matchesAnyPath:"**/.env"# 匹配.env文件-matchesAnyPath:"**/.env.*"# 匹配.env.local等actions:-bash:|echo "❌ 安全策略:禁止读取 .env 文件" exit 2 # 退出码2触发action: stop# 规则4:危险命令执行前记录审计日志-event:tool.before.bash# 在bash工具执行前触发actions:-bash:|echo "[AUDIT] $(date): AI 执行命令: {{ .Command }}" echo "[AUDIT] $(date): AI 执行命令: {{ .Command }}" >> .opencode/audit.log# 规则5:文件修改后自动运行测试-event:tool.after.edit# 在edit工具执行后触发conditions:-matchesCodeFilesactions:-bash:"npm test -- --findRelatedTests {{ .Paths }}"

逐行解释一下:

  • tool.before.read:在read工具执行前触发
  • action: stop:配合bash脚本的exit 2,可以阻止工具执行
  • tool.before.bash:在bash工具执行前触发,适合审计和命令过滤
  • tool.after.edit:在edit工具执行后触发,适合运行测试或索引
  • {{ .Command }}:模板变量,在tool.before.bash中表示要执行的命令

注意了:action: stop仅在tool.before.*钩子上有效,且需要bash脚本以exit 2退出。如果脚本以其他状态码退出,钩子会继续执行但不会阻止工具。

运行验证:配置完成后,在TUI中让AI“读取.env文件的内容”。AI应该会收到错误提示,无法读取该文件。再让AI执行一个bash命令(如ls -la),检查.opencode/audit.log中是否出现了审计记录。

第五步:配置会话生命周期钩子——会话开始/结束自动化

目标:在会话创建、删除、空闲时触发自动化动作。

会话级别的钩子让你在会话的整个生命周期中插入自动化逻辑。

.opencode/hook/hooks.yaml中添加:

hooks:# 之前的规则保持不变...# 规则6:会话创建时加载项目上下文-event:session.created# 新会话创建时触发actions:-bash:|echo "📝 新会话已创建: $(date)" echo "📝 新会话已创建: $(date)" >> .opencode/session.log# 规则7:会话空闲时自动生成总结-event:session.idle# 会话变为空闲时触发actions:-bash:|echo "✅ 会话完成: $(date)" # 可以在这里触发通知、提交代码等# 规则8:会话删除时清理临时文件-event:session.deleted# 会话被删除时触发actions:-bash:"rm -rf .opencode/temp/*"# 清理临时文件

逐行解释一下:

  • session.created:新会话创建时触发
  • session.idle:会话变为空闲时触发(注意:session.idle已弃用,建议用session.status检测Agent完成工作)
  • session.deleted:会话被删除时触发

注意了:session.idle钩子不支持async: true,且不能用于阻止会话结束——它只是一个“通知”钩子。

运行验证:配置完成后,启动一个新会话。检查.opencode/session.log中是否出现了“新会话已创建”的记录。完成对话后退出,检查是否出现了“会话完成”的记录。

第六步:高级钩子配置——scope、runIn与async

目标:了解钩子的高级配置选项,精细控制钩子的作用范围和执行方式。

YAML Hooks提供了三个高级配置字段,让你精细控制钩子的行为。

scope:控制钩子触发范围

含义
all(默认)主会话和子会话都可以触发
main只有根会话可以触发
child只有子会话可以触发
-event:file.changedscope:main# 只在根会话中触发actions:-bash:"npm run build"# 构建任务只在根会话中运行

runIn:控制动作执行位置

含义
current(默认)在触发钩子的会话中执行
main在根会话中执行
-event:tool.after.editrunIn:main# 在根会话中执行actions:-bash:"git add . && git commit -m 'auto: format'"

async:异步执行

async: true时,钩子立即返回,动作在后台异步执行。适合不阻塞主流程的任务。

-event:file.changedasync:true# 异步执行,不阻塞actions:-bash:"npm run lint"# linting在后台运行

注意了:async: true不能用于tool.before.*session.idle钩子。异步钩子只能使用bash动作。

运行验证:配置一个带scope: mainasync: true的钩子,在子会话中触发它,观察动作是否在根会话中异步执行。

第七步:综合实战——构建完整的自动化工作流

目标:把前面学到的所有钩子组合起来,构建一个“编码 → 格式化 → 测试 → 审计”的完整自动化流水线。

现在我们把所有技能整合到一个完整的hooks.yaml中:

# .opencode/hook/hooks.yaml# 完整的自动化工作流配置hooks:# ========== 文件变更自动化 ==========# 1. 代码文件修改后自动格式化-id:auto-format# 可选ID,用于后续覆盖event:file.changedconditions:-matchesCodeFilesactions:-bash:"npx prettier --write {{ .Paths }}"# 2. 测试文件修改后自动运行对应测试-event:file.changedconditions:-matchesAnyPath:"**/*.test.ts"-matchesAnyPath:"**/*.spec.ts"actions:-bash:"npm test -- --findRelatedTests {{ .Paths }}"# 3. 文档文件修改后自动更新索引-event:file.changedconditions:-matchesAnyPath:"docs/**/*.md"async:true# 异步执行,不阻塞actions:-bash:"npm run docs:build"# ========== 工具执行安全 ==========# 4. 禁止读取敏感文件-event:tool.before.readaction:stopconditions:-matchesAnyPath:"**/.env"-matchesAnyPath:"**/.env.*"-matchesAnyPath:"**/secrets.json"actions:-bash:|echo "❌ 安全策略:禁止读取敏感文件" exit 2# 5. 危险命令审计-event:tool.before.bashactions:-bash:|echo "[AUDIT] $(date) | 命令: {{ .Command }}" >> .opencode/audit.log# 6. 限制危险命令(仅示例,不实际执行)-event:tool.before.bashaction:stopconditions:-matchesAnyPath:"rm -rf /"# 匹配危险命令actions:-bash:|echo "❌ 安全策略:禁止执行危险命令" exit 2# ========== 会话生命周期 ==========# 7. 会话创建时记录-event:session.createdscope:mainactions:-bash:|echo "[SESSION] 创建: $(date)" >> .opencode/session.log# 8. 会话完成时自动总结和提交-event:session.idlescope:mainrunIn:mainactions:-bash:|echo "[SESSION] 完成: $(date)" >> .opencode/session.log # 如果有未提交的改动,自动提交 if [ -n "$(git status --porcelain)" ]; then git add . git commit -m "auto: OpenCode session completed at $(date)" fi

逐行解释关键配置:

  • id: auto-format:给钩子一个唯一ID,方便后续在另一个配置文件中覆盖或禁用
  • matchesAnyPath:支持glob模式匹配文件路径
  • 多个actions按顺序执行,任意一个失败会中断后续动作
  • scope: main+runIn: main:确保提交操作只在根会话中执行一次

运行验证:完成配置后,在一个真实项目中正常使用OpenCode完成一次编码任务。观察:

  1. 修改代码文件后,Prettier是否自动运行
  2. 如果修改了测试文件,对应的测试是否自动运行
  3. 尝试让AI读取.env文件,是否被阻止
  4. 会话结束后,.opencode/session.log中是否有记录
  5. 如果有未提交的改动,是否被自动提交

异常处理与常见坑

报错1:YAML格式错误导致钩子不生效

(配置了hooks.yaml但没有任何钩子被触发)

原因:YAML文件格式错误——缩进不对、缺少必要字段、或者字段名拼写错误。

解决方案

  1. 用YAML验证工具检查格式(如yamllint hooks.yaml
  2. 确认hooks是数组(以-开头)
  3. 确认每个钩子都有event字段
  4. 确认每个钩子都有非空的actions数组
  5. 重启OpenCode后查看启动日志是否有解析错误

报错2:action: stop不生效,危险操作仍然执行

(配置了tool.before.read + action: stop,但AI仍然读取了敏感文件)

原因action: stop需要bash脚本以exit 2退出才能触发阻止逻辑。

解决方案

  1. 确认bash脚本中使用了exit 2
    actions:-bash:|echo "阻止执行" exit 2 # 必须用 exit 2
  2. 确认钩子是tool.before.*类型(action: stop只支持pre-tool钩子)
  3. 检查是否有其他钩子覆盖了该规则
  4. 查看OpenCode日志确认钩子是否被触发

报错3:file.changed钩子在文件修改后没有触发

(修改了文件,但file.changed钩子没有执行)

原因file.changed只对通过OpenCode工具(如editwrite)修改的文件生效,对你在IDE中手动修改的文件不触发。

解决方案

  1. 确认文件修改是通过OpenCode的editwrite工具完成的
  2. 如果需要在IDE中手动修改后也触发,考虑使用文件系统监听工具(如watchman)配合外部脚本
  3. 检查conditions是否过滤掉了你的文件——用matchesAnyPath: "**/*"测试是否所有文件都能触发
  4. 确认hooks.yaml文件路径正确:<项目>/.opencode/hook/hooks.yaml

本章产出总结

完成本篇后,你获得了以下能力/产出:

序号产出物/能力说明
1理解事件钩子系统知道OpenCode的事件驱动架构和33+种事件类型
2YAML Hooks安装安装了opencode-yaml-hooks插件
3文件变更自动化配置了file.changed钩子自动格式化代码
4安全门禁配置了tool.before.*钩子阻止读取敏感文件
5会话生命周期自动化配置了session.createdsession.idle钩子
6高级钩子配置掌握了scoperunInasync的用法
7完整自动化流水线构建了“编码→格式化→测试→审计”的完整工作流

事件钩子让OpenCode从“你指挥它干活”变成了“它自己找活干”。从今天开始,你的每一次代码修改都会自动触发格式化、测试、审计——你只需要专注于写代码,剩下的重复工作交给OpenCode的钩子系统。

作者互动与资源引导

你在配置自动化钩子的过程中有没有遇到什么特别的需求?或者你写了什么好用的钩子规则想跟大家分享?欢迎在评论区留言,我看到就会回复——自动化工作流的可能性是无限的,每个人的场景都不一样,期待看到你的创意。

如果觉得这个专栏对你有帮助:

  • 关注我,后续每一篇更新你都不会错过
  • 关注后私信我,发送暗号“爱学Python”,我会把Python全栈学习路线图本专栏的源码包发给你

我们还有一个技术交流群,群里的小伙伴们每天都在讨论OpenCode的各种自动化玩法。想进群的朋友在评论区扣个“1”,我拉你进来。

下篇预告

下一篇是[[项目篇19] 初始化OpenCode智能问答机器人项目结构],我们会进入专栏的项目实战篇——从零开始搭建一个基于OpenCode的智能问答机器人,把前面学到的所有知识(插件开发、向量记忆、多模型路由、事件钩子)整合到一个真实项目中。

如果本篇对你有帮助,点赞、收藏、关注走一波,咱们下篇见!

返回列表