Skip to content

Gemini CLI Hooks 通过 stdin 接收 JSON 输入,通过 stdout 返回 JSON 决策。exit 0 解析 stdout 为结构化决策;exit 2 触发紧急阻断(stderr 作为拒绝原因);其他 exit code 产生非致命警告。本文是所有 Hook 事件 I/O Schema 的权威参考。

Hooks 参考文档

本文档是 Gemini CLI Hooks 系统的完整技术规范,适合需要精确字段定义的高级用户。

关于如何编写第一个 Hook 的入门教程,见 Hooks 编写指南;关于 Hook 的概念介绍,见 Hooks 系统

全局机制

  • 通信方式stdin 接收 JSON 输入,stdout 输出 JSON 结果,stderr 输出日志
  • 黄金规则stdout 只能输出最终 JSON,任何额外文本(哪怕一个 echo)都会导致解析失败

退出码

退出码行为
0成功。解析 stdout 为 JSON,所有逻辑(包括 deny)都应使用此码
2系统阻断。目标操作被中止,stderr 内容作为拒绝原因
其他非致命警告。展示警告但继续执行

settings.json 配置结构

每个事件名下是一个 Hook 定义数组

json
{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "write_file",
        "sequential": false,
        "hooks": [
          {
            "type": "command",
            "command": "bash .gemini/hooks/check.sh",
            "name": "security-check",
            "timeout": 30000,
            "description": "安全扫描"
          }
        ]
      }
    ]
  }
}

Hook 定义字段

字段类型必填说明
matcherstring正则表达式(工具类)或精确字符串(生命周期类),匹配时触发
sequentialbooleantrue 时同组 Hook 串行执行,false(默认)并行
hooksarrayHook 配置数组

Hook 配置字段

字段类型必填说明
typestring执行引擎,目前只支持 "command"
commandstring是(type=command 时)要执行的 Shell 命令
namestringHook 名称,用于日志和 /hooks 命令
timeoutnumber超时毫秒数,默认 60000
descriptionstringHook 用途说明

基础输入 Schema(所有 Hook 共用)

每个 Hook 通过 stdin 接收到的公共字段:

typescript
{
  "session_id": string,       // 当前会话的唯一 ID
  "transcript_path": string,  // 会话记录的绝对路径(JSON 文件)
  "cwd": string,              // 当前工作目录
  "hook_event_name": string,  // 触发的事件名,如 "BeforeTool"
  "timestamp": string         // ISO 8601 执行时间
}

通用输出字段

大多数 Hook 支持以下 stdout JSON 字段:

字段类型说明
systemMessagestring立即显示给用户的消息(终端内)
suppressOutputbooleantrue 时从日志/遥测中隐藏 Hook 元数据
continuebooleanfalse 时立即停止整个 Agent 循环
stopReasonstringcontinue: false 时显示给用户的原因
decisionstring"allow""deny"(别名 "block"),具体影响见各事件说明
reasonstringdecision: "deny" 时的反馈/错误信息

工具类 Hook

BeforeTool

工具被调用前触发,用于参数验证、安全检查、参数改写。

Matcher 匹配规则

  • 内置工具:直接用工具名,如 read_filerun_shell_command
  • MCP 工具:mcp_<server_name>_<tool_name>
  • 支持正则:read_.* 匹配所有读取类工具

专属输入字段

字段类型说明
tool_namestring被调用的工具名
tool_inputobject模型生成的原始参数
mcp_contextobjectMCP 工具的上下文元数据(可选)
original_request_namestring尾部工具调用时的原始工具名

专属输出字段

字段说明
decision: "deny"阻止工具执行,reason 文本发送给 Agent 作为工具错误,Agent 可据此重试
hookSpecificOutput.tool_input与模型参数合并,用于改写工具参数
continue: false立即终止整个 Agent 循环

exit 2 行为:阻止工具执行,stderr 作为发给 Agent 的 reason。Turn 继续。


AfterTool

工具执行完成后触发,用于结果审计、上下文附加或对 Agent 隐藏敏感输出。

专属输入字段

字段类型说明
tool_namestring工具名
tool_inputobject原始调用参数
tool_responseobject工具执行结果,含 llmContentreturnDisplayerror
mcp_contextobjectMCP 上下文
original_request_namestring尾部调用时的原始工具名

专属输出字段

字段说明
decision: "deny"向 Agent 隐藏真实工具输出,reason 替换工具结果
hookSpecificOutput.additionalContext追加到工具结果后,附加上下文发给 Agent
hookSpecificOutput.tailToolCallRequest{ name: string, args: object }:立即执行一个"尾调用"工具,其结果替换原工具响应
continue: false立即终止整个 Agent 循环

exit 2 行为:隐藏工具结果,stderr 作为替代内容发给 Agent。Turn 继续。


Agent 类 Hook

BeforeAgent

用户提交 Prompt 后、Agent 开始规划前触发。用于上下文注入或 Prompt 验证。

专属输入字段

字段说明
prompt用户提交的原始文本

专属输出字段

字段说明
hookSpecificOutput.additionalContext追加到本轮 Prompt 的额外上下文
decision: "deny"阻断本轮并丢弃用户消息(不进历史)
continue: false阻断本轮但保留消息到历史
reason拒绝或停止时必填

exit 2 行为:阻断 Turn,从上下文清除 Prompt。等同于 decision: "deny"


AfterAgent

每轮 Agent 生成最终响应后触发,是响应验证与自动重试的主要接入点。

专属输入字段

字段说明
prompt用户的原始请求
prompt_responseAgent 生成的最终文本响应
stop_hook_activetrue 表示此 Hook 正在重试序列中运行

专属输出字段

字段说明
decision: "deny"拒绝响应,reason 作为新 Prompt 发送给 Agent,触发自动重试
continue: false停止会话,不重试
hookSpecificOutput.clearContexttrue 时清空对话历史(LLM 内存),UI 显示保留

exit 2 行为:拒绝响应,stderr 作为反馈 Prompt 触发自动重试。


模型类 Hook

BeforeModel

向 LLM 发起请求前触发,操作稳定的 SDK 无关请求格式。

专属输入字段

字段说明
llm_request包含 modelmessagesconfig(生成参数)的请求对象

专属输出字段

字段说明
hookSpecificOutput.llm_request覆盖部分请求字段(如切换模型、调整 temperature)
hookSpecificOutput.llm_response合成响应对象:提供后跳过真实 LLM 调用,直接用此响应
decision: "deny"阻止请求,中止本轮

exit 2 行为:中止 Turn,跳过 LLM 调用,stderr 作为错误消息。


BeforeToolSelection

LLM 决定调用哪些工具前触发,用于过滤可用工具集或强制特定工具模式。

专属输入字段

字段说明
llm_request同 BeforeModel 格式

专属输出字段

字段说明
hookSpecificOutput.toolConfig.mode"AUTO" / "ANY" / "NONE""NONE" 禁用所有工具(优先级最高);"ANY" 强制调用至少一个工具
hookSpecificOutput.toolConfig.allowedFunctionNames工具名白名单

多 Hook 合并策略:多个 BeforeToolSelection Hook 的白名单取并集,只有 mode: "NONE" 能覆盖其他 Hook 禁用全部工具。

限制:不支持 decisioncontinuesystemMessage


AfterModel

LLM 响应 chunk 接收后立即触发,用于实时内容脱敏或 PII 过滤。

专属输入字段

字段说明
llm_request原始请求
llm_response模型响应(流式时为当前 chunk)

专属输出字段

字段说明
hookSpecificOutput.llm_response替换当前响应 chunk
decision: "deny"丢弃当前 chunk,阻断本轮
continue: false立即终止整个 Agent 循环

流式注意:AfterModel 在每个流式 chunk 触发一次,修改只影响当前 chunk。

exit 2 行为:中止 Turn,丢弃模型输出,stderr 作为错误消息。


生命周期 Hook

SessionStart

应用启动、恢复会话或 /clear 命令后触发,用于加载初始上下文。

专属输入字段

字段说明
source"startup" / "resume" / "clear"

专属输出字段

字段说明
hookSpecificOutput.additionalContext交互模式:注入为首轮历史;非交互模式:前置到 Prompt
systemMessage会话开始时显示给用户

注意:advisory only,continuedecision 被忽略,启动不可被阻断。


SessionEnd

CLI 退出或会话清除时触发,用于清理或最终遥测。

专属输入字段

字段说明
reason"exit" / "clear" / "logout" / "prompt_input_exit" / "other"

专属输出字段

字段说明
systemMessage关闭时显示给用户

注意:CLI 不等待此 Hook 完成,忽略所有流程控制字段。


Notification

CLI 触发系统提示(如工具权限弹窗)时触发,用于外部日志或跨平台通知。

专属输入字段

字段说明
notification_type当前只有 "ToolPermission"
message提示摘要
details提示相关元数据(工具名、文件路径等)JSON 对象

专属输出字段

字段说明
systemMessage与系统提示一起显示

注意:此 Hook 不能阻止弹窗或自动授权,仅观测用。


PreCompress

CLI 在压缩历史记录以节省 Token 前触发,用于日志记录或状态保存。

专属输入字段

字段说明
trigger"auto""manual"

专属输出字段

字段说明
systemMessage压缩前显示给用户

注意:异步触发,不能阻断或修改压缩过程。


稳定模型 API(Stable Model API)

Gemini CLI 使用以下 SDK 无关格式,确保 SDK 升级后 Hook 脚本不需修改:

LLMRequest

typescript
{
  "model": string,
  "messages": Array<{
    "role": "user" | "model" | "system",
    "content": string   // 非文本内容(图片等)在此处被过滤掉
  }>,
  "config": { "temperature": number, ...生成参数 },
  "toolConfig": { "mode": string, "allowedFunctionNames": string[] }
}

LLMResponse

typescript
{
  "candidates": Array<{
    "content": { "role": "model", "parts": string[] },
    "finishReason": string
  }>,
  "usageMetadata": { "totalTokenCount": number }
}

常见问题

Q: BeforeTooldecision: "deny"continue: false 有什么区别?

A: decision: "deny" 只阻止当前工具调用,reason 以工具错误形式发给 Agent,Agent 可以看到错误并决定是否重试;continue: false 立即终止整个 Agent 循环,当前 Turn 直接结束,不给 Agent 任何响应机会。

Q: Hook 脚本超时了会怎样?

A: 超出 timeout(默认 60000ms)后,CLI 将该 Hook 视为非致命失败(相当于 exit 1),输出警告并用原始参数继续执行。如需更长时间,在 hooks 配置中显式设置 "timeout": 120000

Q: 如何在不改代码的情况下临时禁用一个 Hook?

A: 在 settings.json 中将对应 Hook 的 command 改为 echo '{}',或删除该 Hook 条目并保存文件——Gemini CLI 会在下次触发时热重载配置,无需重启。