Appearance
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 格式通信:
- stdout 只输出 JSON:你的脚本在 stdout 上除了最终 JSON 对象外,不能有任何其他输出。哪怕一个
echo语句都会破坏解析。 - 用 stderr 调试:所有日志和调试信息写到
stderr(如echo "debug" >&2),Gemini CLI 捕获 stderr 但不尝试解析。
退出码含义
| 退出码 | 含义 | 行为 |
|---|---|---|
| 0 | 成功 | 解析 stdout 中的 JSON,按 JSON 指令执行 |
| 2 | 系统阻断 | 立即终止当前操作,用 stderr 内容作为拒绝原因 |
| 其他 | 警告 | 非致命失败,显示警告但继续原始参数执行 |
Matcher 匹配器
用 matcher 字段过滤 Hook 触发条件:
- 工具事件(
BeforeTool、AfterTool):matcher 是正则表达式,如"write_.*"匹配所有写入工具 - 生命周期事件:matcher 是精确字符串,如
"startup" - 通配:
"*"或""匹配所有
配置方式
在 settings.json 的 hooks 对象中配置:
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_.*"
}
]
}
}配置优先级
从高到低:
- 项目配置:
.gemini/settings.json - 用户配置:
~/.gemini/settings.json - 系统配置:
/etc/gemini-cli/settings.json - 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下一步
- 查阅 Hooks 的完整 I/O Schema 和所有 JSON 字段:Hooks 技术参考
- 了解 Claude Code 的类似系统:Claude Code Hooks
常见问题
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 设置合理的超时时间。