DeepVCode已实现完整的Hooks系统,提供11个事件钩子来拦截和修改系统行为。本文档说明如何在项目中使用和集成hooks机制。
Hooks系统由5个核心组件组成:
┌──────────────────────┐
│ HookSystem │ (Coordinator)
│ (Main Entry Point) │
└──────────┬───────────┘
│
┌──────┼──────┬──────┬──────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────┐ ┌──────┐ ┌────┐ ┌──────┐ ┌──────────┐
│Reg │ │Plan │ │Run │ │Aggr │ │EventBus │
│Reg │ │ner │ │ner │ │egator│ │Handler │
└────┘ └──────┘ └────┘ └──────┘ └──────────┘
- HookRegistry - 从配置加载和验证hooks
- HookPlanner - 为事件创建执行计划(匹配、去重)
- HookRunner - 在独立进程中执行hooks
- HookAggregator - 使用事件特定的策略合并结果
- HookEventHandler - 协调事件触发和结果处理
packages/core/src/hooks/
├── types.ts # 类型定义和hook输出类
├── hookTranslator.ts # SDK类型和Hook格式之间的转换
├── hookRegistry.ts # Hook注册表
├── hookPlanner.ts # Hook执行计划
├── hookRunner.ts # Hook执行引擎
├── hookAggregator.ts # 结果聚合
├── hookEventHandler.ts # 事件处理器
├── hookSystem.ts # 系统协调器
└── index.ts # 导出
✅ 已完成 - Config类已支持hooks:
// ConfigParameters中添加了hooks字段
hooks?: { [K in HookEventName]?: HookDefinition[] };
// Config类中添加了getter方法
getHooks(): { [K in HookEventName]?: HookDefinition[] }在应用初始化时创建并初始化HookSystem:
import { HookSystem } from '../hooks/index.js';
import type { Config } from '../config/config.js';
// 创建HookSystem
const hookSystem = new HookSystem(config);
// 初始化(加载并验证hooks)
await hookSystem.initialize();
// 获取事件处理器来触发hooks
const eventHandler = hookSystem.getEventHandler();在工具执行前触发权限检查:
// 在toolExecutionEngine.ts中
import type { HookEventHandler } from '../hooks/index.js';
async executeToolInternal(
toolName: string,
toolInput: Record<string, unknown>,
hookEventHandler?: HookEventHandler,
) {
// 触发BeforeTool hook
if (hookEventHandler) {
const result = await hookEventHandler.fireBeforeToolEvent(
toolName,
toolInput,
);
// 检查是否被阻止
if (result.finalOutput?.isBlockingDecision()) {
throw new Error(`Tool execution blocked: ${result.finalOutput.getEffectiveReason()}`);
}
}
// 继续执行工具
// ...
}在工具执行后处理结果:
// 工具执行完成后
if (hookEventHandler) {
const result = await hookEventHandler.fireAfterToolEvent(
toolName,
toolInput,
toolResponse,
);
// 应用额外上下文
const additionalContext = result.finalOutput?.getAdditionalContext();
if (additionalContext) {
// 添加到响应或上下文中
}
}在LLM请求前修改参数:
// 在generateContent前
if (hookEventHandler) {
const result = await hookEventHandler.fireBeforeModelEvent(request);
// 应用LLM请求修改
if (result.finalOutput) {
request = result.finalOutput.applyLLMRequestModifications(request);
}
// 检查是否有合成响应
const syntheticResponse = (result.finalOutput as BeforeModelHookOutput)?.getSyntheticResponse();
if (syntheticResponse) {
return syntheticResponse;
}
}在发送提示给LLM前增强提示:
// 在geminiChat.ts中
if (hookEventHandler) {
const result = await hookEventHandler.fireBeforeAgentEvent(prompt);
// 获取额外上下文
const additionalContext = result.finalOutput?.getAdditionalContext();
if (additionalContext) {
prompt += '\n\n' + additionalContext;
}
}跟踪会话生命周期:
// 会话开始
await hookEventHandler.fireSessionStartEvent(SessionStartSource.Startup);
// 会话结束
await hookEventHandler.fireSessionEndEvent(SessionEndReason.Exit);在.gemini/settings.json或gemini-extension.json中配置hooks:
{
"tools": {
"enableHooks": true
},
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|delete_file",
"sequential": false,
"hooks": [
{
"type": "command",
"command": "bash ./hooks/security-check.sh",
"timeout": 30000
}
]
}
],
"BeforeAgent": [
{
"hooks": [
{
"type": "command",
"command": "./hooks/enhance-prompt.sh",
"timeout": 5000
}
]
}
]
}
}matcher- 正则表达式或精确值sequential- true为顺序执行,false为并行(默认)- 无matcher或
*- 匹配所有
所有hooks收到JSON格式的输入:
{
"session_id": "abc123",
"transcript_path": "",
"cwd": "/current/dir",
"hook_event_name": "BeforeTool",
"timestamp": "2025-01-15T10:00:00Z",
"tool_name": "write_file",
"tool_input": {
"path": "/tmp/file.txt",
"content": "..."
}
}Hook脚本必须返回JSON格式的输出:
{
"decision": "allow",
"reason": "optional reason",
"continue": true,
"suppressOutput": false,
"systemMessage": "optional message",
"hookSpecificOutput": {
"hookEventName": "BeforeTool",
"additionalContext": "optional context"
}
}0- 成功,解析stdout1- 警告,stderr作为systemMessage2- 阻止,触发deny决策- 其他 - 失败(不阻止)
- BeforeTool - 工具执行前权限检查
- AfterTool - 工具执行后结果处理
- BeforeAgent - 提示工程和增强
- AfterAgent - LLM响应验证
- BeforeModel - LLM请求参数修改
- AfterModel - LLM响应过滤
- BeforeToolSelection - 限制可用工具
- SessionStart - 会话初始化
- SessionEnd - 会话清理
- PreCompress - 压缩前准备
- Notification - 权限请求
#!/bin/bash
read INPUT
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
# 禁止删除操作
if [[ "$TOOL" == "delete_file" ]]; then
echo '{"decision":"deny","reason":"Deletion not allowed"}'
exit 0
fi
echo '{"decision":"allow"}'#!/bin/bash
read INPUT
echo '{
"decision": "allow",
"hookSpecificOutput": {
"hookEventName": "BeforeAgent",
"additionalContext": "Follow security guidelines: [...]"
}
}'#!/bin/bash
read INPUT
USER_ROLE=${USER_ROLE:-"user"}
case "$USER_ROLE" in
"admin")
echo '{"decision":"allow"}'
;;
"viewer")
echo '{
"hookSpecificOutput": {
"toolConfig": {
"mode": "ANY",
"allowedFunctionNames": ["read_file","list_directory"]
}
}
}'
;;
esac- 始终验证输入 - 检查JSON有效性和必需字段
- 优雅处理错误 - Hook失败不应阻止系统执行
- 设置合理的超时 - 避免长时间运行的hooks阻塞
- 使用顺序执行链式逻辑 - 后续hook可见前一个的修改
- 记录所有操作 - 用于审计和调试
- 保持hooks轻量 - 复杂逻辑委托给外部服务
- 测试hooks - 使用测试输入验证行为
import { debugLogger } from '../utils/debugLogger.js';
debugLogger.debug('Hook execution started');const hookStatus = hookSystem.getStatus();
console.log(`Total hooks registered: ${hookStatus.totalHooks}`);
const allHooks = hookSystem.getAllHooks();
for (const hook of allHooks) {
console.log(`Event: ${hook.eventName}, Source: ${hook.source}`);
}hookSystem.setHookEnabled('./hooks/security-check.sh', false);Hook执行会自动记录遥测数据,包括:
- Hook名称和事件类型
- 执行时间
- 输入输出数据
- 错误信息
DeepVCode的hooks实现基于Gemini CLI 0.20.2的设计:
- 相同的输入输出格式
- 相同的事件模型
- 相同的配置结构
- 完全兼容的hooks脚本
迁移from Gemini CLI的hooks脚本时无需修改。
需要在以下文件中添加hook触发器:
-
packages/core/src/core/toolExecutionEngine.ts- BeforeTool / AfterTool -
packages/core/src/core/geminiChat.ts- BeforeAgent / AfterAgent / BeforeModel / AfterModel -
packages/core/src/core/contentGenerator.ts- BeforeToolSelection -
packages/cli/src/ui/commands/- SessionStart / SessionEnd - CLI初始化文件 - HookSystem初始化
详见 gemini-cli-0.20.2/HOOKS_GUIDE.md 获取完整的Hooks系统文档。