Hooks
Hooks(钩子)是码道CLI插件系统的核心机制,允许开发者在工具执行、消息处理、权限控制等关键节点注入自定义逻辑。插件通过返回一个Hooks对象来声明自己关注的生命周期事件,当对应事件触发时,插件的自定义函数将被调用。
插件及依赖文件存放路径
插件是一个JavaScript/TypeScript模块,通过导出插件函数来工作。每个函数接收一个上下文对象,并返回对应的Hook对象。如果插件文件中需要使用外部包,必须在配置目录中创建package.json文件并配置所需的依赖项。华为云码道在启动时会自动安装这些依赖项。
| 插件类型 | 插件文件存放路径 | 插件依赖文件存放路径 | 说明 |
|---|---|---|---|
| 项目级 | 当前项目根目录./.codeartsdoer/plugin或./.codeartsdoer/plugins | 当前项目根目录./.codeartsdoer/ | 仅对当前项目有效。 |
| 个人级 | 本地%USERPROFILE%/.codeartsdoer/plugin或 %USERPROFILE%/.codeartsdoer/plugins | 本地%USERPROFILE%/.codeartsdoer/ | 对当前用户下所有项目均有效。 |
支持的Hook事件
码道CLI支持的Hook事件请参考表2,各Hook事件示例详见文档Hook事件参考。
| 事件分类 | 事件名称 | 触发时机 | 可阻断 | 典型使用场景 | 说明 |
|---|---|---|---|---|---|
| 聊天消息 | chat.message | 用户发送消息后、进入处理流程前 | 是 | 聊天消息处理 | 新消息到达时自动触发,支持对消息内容及Parts进行编辑 |
| 聊天参数 | chat.params | 每次调用LLM前 | 是 | 聊天参数修改 | 支持动态调整大模型推理参数,包含temperature、topP、topK等,按需优化模型输出结果 |
| 聊天请求头 | chat.headers | 每次调用LLM前 | 是 | 聊天请求头修改 | 支持自定义修改HTTP请求头字段,适配网络代理、接口鉴权等复杂场景需求 |
| 聊天响应 | chat.response | LLM返回响应后 | 否 | 聊天响应记录 | 自动捕获模型响应的Tokens、Cost、Duration等核心指标,实现精准计费统计、高效故障排查与完整日志留存 |
| 聊天错误 | chat.error | LLM调用发生错误时 | 否 | 聊天错误记录 | 记录LLM调用失败时的错误日志 |
| 聊天压缩 | chat.compression | 上下文压缩时 | 否 | 聊天压缩记录 | 记录上下文压缩前后的Token数量,辅助存储与性能调优 |
| 聊天结束 | chat.finished | 聊天会话结束时 | 否 | 聊天结束记录 | 记录会话结束事件,包含本次运行的Token消耗和运行信息 |
| 用户审批 | user.approval | 需要用户确认/审批操作时 | 是 | 用户审批记录 | 拦截高风险操作,发起人工确认审批,管控敏感操作执行权限 |
| Turn结束 | turn.end | 每个对话轮次结束时 | 否 | Turn结束记录 | 记录每轮对话结束日志,包含处理耗时、终止原因等关键信息 |
| 命令执行 | command.execute.before | 命令执行前 | 是 | 命令执行前置处理 | 支持对Parts进行自定义配置,实现命令的前置处理与灵活适配 |
| 工具执行 | tool.execute.before | 工具执行前 | 是 | 工具执行前置参数调整 | 支持在工具执行前自定义修改参数args,实现校验、动态改写与业务适配 |
| tool.execute.after | 工具执行后 | 是 | 工具执行后置结果处理 | 支持自定义修改返回结果,包括title、output及metadata字段 | |
| 工具定义 | tool.definition | 工具定义发送给LLM前 | 是 | 工具定义修改 | 支持在工具推送模型前自定义配置描述与参数,确保精准适配业务场景 |
| Shell环境 | shell.env | 获取Shell环境变量时 | 是 | Shell环境变量配置管理 | 获取Shell环境变量时,支持添加或修改环境变量 |
| 权限请求 | permission.ask | 需要权限验证时 | 是 | 权限请求处理 | 处理权限校验请求,支持配置allow(允许)、deny(拒绝)、ask(询问)三种策略 |
| 消息转换 | experimental.chat.messages.transform | 消息列表发送给LLM前 | 是 | 消息转换 | 支持在模型上报前预处理聊天消息列表 |
| 系统消息转换 | experimental.chat.system.transform | 系统提示词发送前 | 是 | 系统消息转换 | 支持在系统提示词发送前优化指令内容 |
| 会话压缩 | experimental.session.compacting | 上下文压缩开始前 | 是 | 会话压缩 | 支持在执行上下文压缩前,自定义压缩提示词 |
| 文本补全 | experimental.text.complete | 文本补全完成时触发 | 是 | 文本补全结果定制 | 文本补全完成后,支持对输出内容进行修改 |
| 配置加载 | config | 应用启动配置加载时 | 否 | 配置加载 | 在加载自定义配置时触发,用于执行配置初始化及动态参数注入 |
| 通用事件 | event | 订阅所有Bus事件时触发 | 否 | 事件处理 | 通用事件处理 |
编写示例
本文以创建个人级别的Hook为例。
以下示例创建一个JsonFormatPlugin插件,在读取.json文件后,自动将挤成一行的压缩JSON,整理成2个空格缩进的格式化形式。
- 进入“~\.codeartsdoer\plugin”,创建并编写文件“JsonFormatPlugin.ts”。
// 导入Plugin类型,用于定义插件 import type { Plugin } from "@opencode-ai/plugin" // 导入文件系统模块,用于读写磁盘文件 import { readFileSync, writeFileSync } from "fs" // 导出插件实现,Plugin是一个异步函数,接收插件输入参数并返回Hooks对象 export const JsonFormatPlugin: Plugin = async ({}) => { return { // 监听工具执行后的钩子,在工具执行完成后触发 "tool.execute.after": async ( // 输入参数:包含工具名称、会话ID、调用ID、参数等信息 input: { tool: string; sessionID: string; callID: string; args: any }, // 输出参数:包含工具执行结果的标题、输出内容、元数据 output: { title: string; output: string; metadata: any }, ) => { if (input.tool !== "read") return // 获取文件路径,只处理.json文件 const filePath = input.args?.filePath || input.args?.[0] || "" if (!filePath.endsWith(".json")) return try { const raw = readFileSync(filePath, "utf-8").trim() const parsed = JSON.parse(raw) const formatted = JSON.stringify(parsed, null, 2) + "\n" if (formatted === raw) return // 将格式化后的内容写回磁盘文件 writeFileSync(filePath, formatted, "utf-8") } catch {} }, } } - 创建并编写测试文件“test-plugin.json”,内容为压缩格式的JSON。
{"plugin":"JsonFormatPlugin","test":{"input":"压缩的单行JSON","output":"格式化的多行JSON"}}图1 格式化前
- 根据不同的开发模式,执行命令:
- 如果是TUI开发模式,在TUI对话框中输入如下指令并回车:
读取 test-plugin.json
- 如果是CLI开发模式,请执行如下命令:
codearts run "读取 test-plugin.json"
图2 CLI开发模式的命令截图
- 如果是TUI开发模式,在TUI对话框中输入如下指令并回车:
- 执行完成后,“test-plugin.json”文件将被自动格式化为多行缩进形式。再次读取同一文件时,由于已是格式化状态,不会重复处理。
如下图所示,该JSON文件已经被格式化。
图3 格式化后的效果图