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、添加上下文
AfterAgent AI 循环结束时 审查输出、强制重试或终止
BeforeModel 发送请求给 LLM 前 修改 Prompt、切换模型
AfterModel 收到 LLM 响应后 过滤/编辑响应、记录日志
BeforeToolSelection LLM 选择工具前 过滤可用工具列表
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 对象中配置:

{
  "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 命令

// .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 日志上下文

#!/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 设置合理的超时时间。