Skip to content

Gemini CLI 的 Hooks 系统让你无需修改源码就能自定义 AI 的行为:在 BeforeTool 事件中拦截危险操作、在 SessionStart 中注入项目上下文、在 AfterTool 中自动运行测试。Hook 脚本通过 stdin/stdout 用 JSON 格式通信,在 settings.json 中配置,支持正则表达式匹配特定工具。

Hooks 系统

Hooks 是 Gemini CLI 在 AI 循环的特定节点执行的脚本,让你能在不修改 CLI 源码的情况下拦截和自定义行为。

可以用 Hooks 做什么

  • 注入上下文:在模型处理请求前自动注入 git 历史、项目状态等信息
  • 验证操作:检查工具参数,阻止危险的 Shell 命令或文件操作
  • 执行策略:实施安全扫描和合规检查
  • 记录日志:追踪工具调用和模型响应用于审计
  • 优化行为:动态过滤可用工具或调整模型参数

Hook 事件类型

事件触发时机主要用途
SessionStart会话启动/恢复/清屏时初始化资源、加载上下文
SessionEnd会话结束时清理工作、保存状态
BeforeAgent用户提交 Prompt 后、AI 规划前验证 Prompt、添加上下文
AfterAgentAI 循环结束时审查输出、强制重试或终止
BeforeModel发送请求给 LLM 前修改 Prompt、切换模型
AfterModel收到 LLM 响应后过滤/编辑响应、记录日志
BeforeToolSelectionLLM 选择工具前过滤可用工具列表
BeforeTool工具执行前验证参数、阻止危险操作
AfterTool工具执行后处理结果、自动运行测试
PreCompress上下文压缩前保存状态、通知用户
Notification系统通知事件转发到桌面通知

核心机制

JSON 通信规则(黄金法则)

Hook 通过 stdin(输入)和 stdout(输出)以 JSON 格式通信:

  1. stdout 只输出 JSON:你的脚本在 stdout 上除了最终 JSON 对象外,不能有任何其他输出。哪怕一个 echo 语句都会破坏解析。
  2. 用 stderr 调试:所有日志和调试信息写到 stderr(如 echo "debug" >&2),Gemini CLI 捕获 stderr 但不尝试解析。

退出码含义

退出码含义行为
0成功解析 stdout 中的 JSON,按 JSON 指令执行
2系统阻断立即终止当前操作,用 stderr 内容作为拒绝原因
其他警告非致命失败,显示警告但继续原始参数执行

Matcher 匹配器

matcher 字段过滤 Hook 触发条件:

  • 工具事件BeforeToolAfterTool):matcher 是正则表达式,如 "write_.*" 匹配所有写入工具
  • 生命周期事件:matcher 是精确字符串,如 "startup"
  • 通配"*""" 匹配所有

配置方式

settings.jsonhooks 对象中配置:

json
{
  "hooks": {
    "BeforeTool": [
      {
        "name": "security-check",
        "command": ["node", ".gemini/hooks/security-check.js"],
        "matcher": "run_shell_command"
      }
    ],
    "SessionStart": [
      {
        "name": "inject-context",
        "command": ["bash", ".gemini/hooks/inject-context.sh"]
      }
    ],
    "AfterTool": [
      {
        "name": "run-tests",
        "command": ["node", ".gemini/hooks/run-tests.js"],
        "matcher": "write_.*"
      }
    ]
  }
}

配置优先级

从高到低:

  1. 项目配置:.gemini/settings.json
  2. 用户配置:~/.gemini/settings.json
  3. 系统配置:/etc/gemini-cli/settings.json
  4. Extensions 提供的 Hooks

Hook 脚本示例

BeforeTool:阻止危险 Shell 命令

javascript
// .gemini/hooks/security-check.js
const input = JSON.parse(await readStdin());
const cmd = input.tool_input?.command || '';

// 阻止 rm -rf
if (cmd.includes('rm -rf')) {
  console.log(JSON.stringify({
    decision: 'deny',
    reason: '检测到危险命令 rm -rf,已拒绝执行。请确认操作必要性。'
  }));
} else {
  console.log(JSON.stringify({ decision: 'allow' }));
}

SessionStart:注入 git 日志上下文

bash
#!/bin/bash
# .gemini/hooks/inject-context.sh

RECENT_COMMITS=$(git log --oneline -5 2>/dev/null || echo "无 git 历史")

cat <<EOF
{
  "systemMessage": "最近 5 条提交记录:\n${RECENT_COMMITS}"
}
EOF

下一步

常见问题

Q: Hook 脚本可以用什么语言编写?

A: 任何可执行程序都可以:Node.js、Python、Bash、Go 的编译二进制文件……只要能从 stdin 读 JSON 并把 JSON 写到 stdout 就行。推荐用 Node.js 或 Bash,在所有平台上都易于分发。

Q: Hook 执行失败会影响 AI 的工作吗?

A: 取决于退出码。退出码 0 正常执行,退出码 2 会立即阻断当前操作(高严重性),其他退出码仅显示警告但继续执行。设计 Hook 时要仔细考虑失败模式。

Q: Hook 脚本的超时怎么设置?

A: 可以在 Hook 配置对象中设置 timeout 字段(毫秒),超时后 Hook 被终止,视为警告级失败(不会阻断 AI)。建议为网络请求类 Hook 设置合理的超时时间。