核心能力验证
能力验证 API分析
深入验证 Claude Code 与 OpenCode 的 Hook API 与实现细节
Claude Code Hook 架构
Hook 类型与生命周期
Claude Code 提供了 17 种 Hook 事件类型,覆盖从会话启动到终止的完整生命周期。这些事件可以分为以下几类:
会话生命周期 Hooks:
| 事件 | 触发时机 | 可否阻断 |
|---|---|---|
SessionStart | 会话开始或恢复时 | 否 |
SessionEnd | 会话终止时 | 否 |
Stop | Claude 完成响应时 | 是 |
PreCompact | 上下文压缩前 | 否 |
用户交互 Hooks:
| 事件 | 触发时机 | 可否阻断 |
|---|---|---|
UserPromptSubmit | 用户提交提示词时 | 是 |
PermissionRequest | 权限对话框出现时 | 是 |
Notification | 发送通知时 | 否 |
工具执行 Hooks:
| 事件 | 触发时机 | 可否阻断 |
|---|---|---|
PreToolUse | 工具调用执行前 | 是 |
PostToolUse | 工具调用成功后 | 否 |
PostToolUseFailure | 工具调用失败后 | 否 |
子代理 Hooks:
| 事件 | 触发时机 | 可否阻断 |
|---|---|---|
SubagentStart | 子代理启动时 | 否 |
SubagentStop | 子代理完成时 | 是 |
TeammateIdle | Agent Team 成员空闲时 | 是 |
任务与配置 Hooks:
| 事件 | 触发时机 | 可否阻断 |
|---|---|---|
TaskCompleted | 任务标记完成时 | 是 |
ConfigChange | 配置文件变更时 | 是 |
WorktreeCreate | 创建工作树时 | 是 |
WorktreeRemove | 移除工作树时 | 否 |
Hook 注册与触发机制
Claude Code 的 Hook 注册采用声明式配置方式,支持多层级配置文件:
配置优先级(从高到低):
1. ~/.claude/settings.json - 用户全局配置
2. .claude/settings.json - 项目配置(可提交到仓库)
3. .claude/settings.local.json - 项目本地配置(gitignored)
4. Plugin hooks/hooks.json - 插件配置
5. Skill/Agent frontmatter - 技能/代理内嵌配置
配置结构示例:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/validate.sh",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "prettier --write $FILE_PATH"
}
]
}
]
}
}
四种 Hook 处理器类型:
- Command Hook (
type: "command"): 执行 Shell 命令,通过 stdin 传入 JSON,stdout 返回结果 - HTTP Hook (
type: "http"): POST 请求到指定 URL,通过响应体返回结果 - Prompt Hook (
type: "prompt"): 发送到 Claude 模型进行单轮评估,返回 yes/no 决策 - Agent Hook (
type: "agent"): 启动子代理执行多轮验证,可使用工具
Hook 能力边界
Hook 能做什么:
- 阻断或允许特定工具调用
- 修改工具参数(通过
hookSpecificOutput) - 向 Claude 上下文注入额外信息
- 触发外部系统通知
- 执行任意 Shell 命令
Hook 不能做什么:
- 直接调用 Claude Code 的工具(只能通过 stdout 间接影响)
- 访问 Claude Code 内部状态(只能访问 Hook 输入数据)
- 异步等待用户输入后继续执行(但支持后台异步执行)
- 修改已发生的工具执行结果(
PostToolUse只能记录,不能撤销)
OpenCode Hook 架构
Hook 类型与生命周期
OpenCode 的 Hook 系统作为 Plugin API 的一部分,提供了更底层的事件订阅能力。事件类型按功能域分组:
Command Events:
command.executed: 命令执行完成
File Events:
file.edited: 文件被编辑file.watcher.updated: 文件监视器更新
LSP Events:
lsp.client.diagnostics: LSP 诊断信息lsp.updated: LSP 状态更新
Message Events:
message.part.removed: 消息部分移除message.part.updated: 消息部分更新message.removed: 消息移除message.updated: 消息更新
Permission Events:
permission.asked: 权限请求permission.replied: 权限回复
Session Events:
session.created: 会话创建session.compacted: 会话压缩session.deleted: 会话删除session.diff: 会话差异session.error: 会话错误session.idle: 会话空闲session.status: 会话状态session.updated: 会话更新
Tool Events:
tool.execute.before: 工具执行前(可阻断)tool.execute.after: 工具执行后
TUI Events:
tui.prompt.append: 追加提示tui.command.execute: 执行命令tui.toast.show: 显示通知
Experimental Events:
experimental.session.compacting: 会话压缩前experimental.chat.messages.transform: 消息转换
Hook 注册与触发机制
OpenCode 的 Hook 通过 Plugin 函数返回值 注册:
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// 工具执行前 Hook
"tool.execute.before": async (input, output) => {
if (input.tool === "bash") {
// 修改输出参数
output.args.command = sanitizeCommand(output.args.command)
}
},
// 工具执行后 Hook
"tool.execute.after": async (input, output) => {
console.log(`Tool ${input.tool} executed`)
},
// 会话空闲 Hook
"session.idle": async ({ event }) => {
await $`osascript -e 'display notification "Session completed!"'`
}
}
}
插件加载顺序:
- 全局配置 (
~/.config/opencode/opencode.json) - 项目配置 (
opencode.json) - 全局插件目录 (
~/.config/opencode/plugins/) - 项目插件目录 (
.opencode/plugins/)
Hook 能力边界
Hook 能做什么:
- 修改工具输入/输出参数
- 抛出异常阻断工具执行
- 调用 OpenCode SDK 与系统交互
- 添加自定义工具
- 注入环境变量
- 自定义会话压缩行为
Hook 不能做什么:
- 脱离插件系统独立配置(必须编写代码)
- 直接持久化状态(需要自行实现存储)
- 跨会话共享状态(每次插件加载是独立的)
能力差距对比
功能对比矩阵
| 功能特性 | Claude Code | OpenCode | 差距分析 |
|---|---|---|---|
| 声明式配置 | ✅ JSON 配置 | ❌ 需编程 | Claude Code 用户友好 |
| 命令行管理 | ✅ /hooks 菜单 | ❌ 无 | Claude Code 易于管理 |
| HTTP Hook | ✅ 原生支持 | ⚠️ 需自行实现 | Claude Code 集成更方便 |
| Prompt Hook | ✅ LLM 评估 | ❌ 无 | Claude Code 支持智能决策 |
| Agent Hook | ✅ 多轮验证 | ✅ 通过子代理 | 两者能力相当 |
| 自定义事件 | ❌ 仅预定义 | ⚠️ 有限支持 | OpenCode 更灵活 |
| 工具注册 | ❌ 仅 Hook | ✅ 可注册工具 | OpenCode 扩展性更强 |
| 运行时修改 | ✅ 修改输出对象 | ✅ 修改输出对象 | 两者能力相当 |
| 阻断执行 | ✅ Exit 2 / JSON | ✅ 抛出异常 | 机制不同,效果相同 |
| 超时控制 | ✅ 可配置 | ⚠️ 无内置 | Claude Code 更安全 |
架构差异分析
┌─────────────────────────────────────────────────────────────────┐
│ Claude Hook 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ JSON ┌───────────┐ stdin/stdout │
│ │ Claude │ ──────────► │ Hook │ ◄──────────────────► │
│ │ Code │ │ Script │ Exit Code + JSON │
│ │ (主进程) │ ◄────────── │ (子进程) │ │
│ └──────────┘ 结果 └───────────┘ │
│ │
│ 特点: 进程隔离、声明式配置、确定性控制 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ OpenCode Hook 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ OpenCode Runtime (Bun) │ │
│ │ ┌──────────┐ 直接调用 ┌─────────────────┐ │ │
│ │ │ Core │ ─────────────► │ Plugin Function │ │ │
│ │ │ System │ ◄───────────── │ (Hook Handler) │ │ │
│ │ └──────────┘ 修改对象 └─────────────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 特点: 同进程、编程接口、灵活扩展 │
└─────────────────────────────────────────────────────────────────┘
关键架构差异:
-
执行隔离 vs 共享运行时
- Claude Code 的 Hook 在独立子进程中执行,崩溃不会影响主进程
- OpenCode 的 Hook 与主系统共享运行时,异常可能导致系统不稳定
-
通信机制
- Claude Code 使用 stdin/stdout + JSON 通信,跨语言兼容
- OpenCode 直接操作 JavaScript 对象,类型安全但绑定语言
-
扩展性
- Claude Code 专注于 Hook 功能,扩展性受限于预定义事件
- OpenCode Hook 是插件系统的一部分,可结合工具注册、MCP 集成等实现更复杂功能
参考资料
- Claude Code Hooks Reference - 完整事件参考
- OpenCode Plugins Documentation - 插件开发指南
- oh-my-opencode 深度分析 - 33 个 Hook 实现分析