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 定义数组

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

Hook 定义字段

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

Hook 配置字段

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

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

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

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

通用输出字段

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

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

工具类 Hook

BeforeTool

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

Matcher 匹配规则

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

专属输入字段

字段 类型 说明
tool_name string 被调用的工具名
tool_input object 模型生成的原始参数
mcp_context object MCP 工具的上下文元数据(可选)
original_request_name string 尾部工具调用时的原始工具名

专属输出字段

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

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


AfterTool

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

专属输入字段

字段 类型 说明
tool_name string 工具名
tool_input object 原始调用参数
tool_response object 工具执行结果,含 llmContentreturnDisplayerror
mcp_context object MCP 上下文
original_request_name string 尾部调用时的原始工具名

专属输出字段

字段 说明
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_response Agent 生成的最终文本响应
stop_hook_active true 表示此 Hook 正在重试序列中运行

专属输出字段

字段 说明
decision: "deny" 拒绝响应,reason 作为新 Prompt 发送给 Agent,触发自动重试
continue: false 停止会话,不重试
hookSpecificOutput.clearContext true 时清空对话历史(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

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

LLMResponse

{
  "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 会在下次触发时热重载配置,无需重启。