
Gemini CLI Hooks 深入解析11 个生命周期钩子、stdin/stdout JSON 协议与双层安全模型【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli本文基于 Gemini CLI 的 Hooks 文档docs/hooks/index.md并结合packages/core/src/hooks/下的核心实现展开系统讲解如何在 Agent 循环的 11 个生命周期事件中注入外部脚本、通过 stdin/stdout JSON 协议与退出码控制工具调用和模型请求、按四层配置体系装配钩子以及如何理解项目级钩子的指纹信任机制。读完本文你可以独立完成一个可用的钩子脚本配置并能从源码层面解释“污染 stdout 为什么会退化为允许”“多个钩子的决策如何合并”等关键行为。一、Hooks 是什么Hooks 是 Gemini CLI 在 Agent 循环agentic loop特定点位执行的脚本或程序允许你在不修改 CLI 源码的前提下拦截并定制其行为。从执行语义看Hooks 是同步执行的当某个钩子事件触发时Gemini CLI 会等待所有匹配的钩子完成后再继续推进 Agent 循环。这一点在实现上对应HookRunner对子进程的生命周期管理hookRunner.ts 中通过spawn启动子进程、写入 stdin、收集 stdout/stderr 并在超时后强制终止。借助 Hooks你可以做到注入上下文在模型处理请求前注入相关信息例如 git 历史校验动作审查工具参数拦截潜在危险操作强制策略实现安全扫描器与合规检查记录交互跟踪工具使用与模型响应用于审计优化行为动态过滤可用工具或调整模型参数。配套的三篇文档构成完整的知识体系建议按顺序阅读写作指南创建第一个钩子的完整教程与综合示例最佳实践安全、性能与调试准则技术参考I/O 模式与退出码的权威规范。二、钩子事件全览Hooks 由 Gemini CLI 生命周期中的特定事件触发。文档定义的 11 个事件与源码中HookEventName枚举types.ts一一对应事件触发时机影响能力典型用例SessionStart会话开始时startup、resume、clear注入上下文初始化资源、加载上下文SessionEnd会话结束时exit、clear建议性Advisory清理、保存状态BeforeAgent用户提交 prompt 后、规划前阻断轮次 / 注入上下文补充上下文、校验 prompt、拦截轮次AfterAgentAgent 循环结束时重试 / 中止审查输出、强制重试或中止执行BeforeModel向 LLM 发送请求前阻断轮次 / Mock修改 prompt、切换模型、伪造响应AfterModel收到 LLM 响应后阻断轮次 / 脱敏过滤/脱敏响应、记录交互BeforeToolSelectionLLM 选择工具前过滤工具过滤可用工具、优化选择BeforeTool工具执行前阻断工具 / 重写参数校验参数、拦截危险操作AfterTool工具执行后阻断结果 / 注入上下文处理结果、跑测试、隐藏结果PreCompress上下文压缩前建议性Advisory保存状态、通知用户Notification系统通知发生时建议性Advisory转发桌面提醒、记录日志事件输入每个钩子“看到”什么所有事件的输入共享一个基础结构types.ts 中的HookInput字段含义session_id当前会话唯一 IDtranscript_path会话记录路径cwd当前工作目录hook_event_name当前触发的事件名timestamp时间戳在此之上各事件携带特有字段决定了钩子的能力边界工具事件BeforeTool/AfterTool输入包含tool_name、tool_inputMCP 工具还附带mcp_context服务器名、连接方式等非敏感身份信息AfterTool额外包含tool_responseAgent 事件BeforeAgent输入包含用户promptAfterAgent包含prompt、prompt_response和stop_hook_active标志防止钩子自身触发无限重试会话事件SessionStart输入带sourcestartup/resume/clear见 types.tsSessionEnd输入带reasonexit/clear/logout/prompt_input_exit/other模型事件BeforeModel/AfterModel/BeforeToolSelection使用解耦的llm_request及响应结构避免把 SDK 内部对象直接暴露给钩子压缩与通知PreCompress输入带triggermanual/autoNotification输入带notification_type、message与details目前通知类型为工具权限确认ToolPermission。事件输出每个钩子“能做什么”钩子输出的基础字段HookOutput包括continue、stopReason、suppressOutput、systemMessage、decision、reason与事件专属的hookSpecificOutput。不同事件在hookSpecificOutput中的扩展能力差异很大见 types.ts事件专属输出能力BeforeTooltool_input重写工具入参与原始参数合并AfterTooladditionalContext向模型追加上下文tailToolCallRequest请求紧接执行另一个工具其结果将替换原工具响应BeforeModelllm_request修改请求模型、配置、内容llm_response提供合成响应以跳过真实模型调用AfterModelllm_response替换/改写模型响应可用于脱敏BeforeToolSelectiontoolConfig控制工具调用模式与允许的工具名列表AfterAgentclearContext请求清空上下文SessionStart/BeforeAgentadditionalContext注入上下文会做转义防标签注入decision字段的合法取值为ask/block/deny/approve/allowtypes.ts。block与deny在语义上都是阻断决策isBlockingDecision()ask表示请求用户确认continue: false则直接停止执行对应stopReason作为停止原因。三、全局机制stdin/stdout 协议与退出码严格 JSON 要求“黄金法则”Hooks 通过stdin输入与stdout输出与 CLI 通信必须遵守三条规则静默是强制的脚本不得向stdout输出最终 JSON 对象以外的任何纯文本。哪怕在 JSON 之前多一个echo或print都会破坏解析。污染即失败若stdout含非 JSON 文本解析失败时 CLI 默认按“允许”处理并把整段输出当作systemMessage。调试走 stderr所有日志与调试信息应输出到stderr如echo debug 2。Gemini CLI 会捕获stderr但从不将其当作 JSON 解析。这条“污染即失败、退化为允许”的行为在源码中可以直接验证hookRunner.ts进程结束后CLI 先取stdout.trim() || stderr.trim()尝试JSON.parse支持“JSON 字符串再套一层”的情况解析失败时调用convertPlainTextToHookOutput把纯文本降级为结构化输出。退出码Gemini CLI 用退出码决定钩子执行的高层结果退出码标签行为影响0Successstdout被解析为 JSON。这是首选退出码适用于一切逻辑包括有意的阻断例如输出{decision: deny}2System Block严重阻断。目标动作工具、轮次或停止被中止stderr内容作为拒绝原因。高严重级别用于安全停机或脚本失败其他Warning非致命失败。显示一条警告但交互使用原始参数继续从 hookRunner.ts 的convertPlainTextToHookOutput可以确认降级路径的精确行为退出码 0 时纯文本输出被转换为{ decision: allow, systemMessage: text }退出码 1 被专门定义为非阻断错误EXIT_CODE_NON_BLOCKING_ERROR转换为带Warning:前缀的 systemMessage而退出码 2 及其他非零码则转换为{ decision: deny, reason: text }。也就是说“用 JSON 表达决策、用退出码表达严重性”是两套互补的通道精细控制请返回退出码 0 JSON粗粒度安全停机则直接用退出码 2。进程执行的工程细节阅读 hookRunner.ts 可以看到几个保证钩子健壮性的实现点超时强制每个钩子有timeout毫秒默认 60000超时先SIGTERMWindows 用taskkill5 秒后仍未退出则SIGKILLShell 适配通过getShellConfiguration()选择执行 shellPowerShell 下会追加$LASTEXITCODE检查以正确传播退出码变量展开命令字符串中的$GEMINI_PROJECT_DIR、$GEMINI_CWD、$GEMINI_PLANS_DIR、$GEMINI_SESSION_ID、$CLAUDE_PROJECT_DIR会在执行前被替换为转义后的实际值expandCommand并行与串行默认同一事件的多个钩子并行执行executeHooksParallel串行模式下前一个钩子的输出会折叠进下一个钩子的输入如BeforeAgent追加上下文、BeforeModel合并llm_request、BeforeTool合并tool_input。四、Matchers精确控制钩子的触发范围用matcher字段过滤哪些工具或触发器会命中你的钩子工具事件BeforeTool、AfterToolmatcher 是正则表达式例如write_.*生命周期事件matcher 是精确字符串例如startup通配*或空字符串匹配所有。源码实现hookPlanner.ts印证了这套规则并补充了两点容错回退工具名匹配时先尝试new RegExp(matcher)若正则非法则退化为字面量精确比较计划去重与串行开关HookPlanner.createExecutionPlan会按name:command组合键getHookKey对相同钩子去重只要某条钩子定义声明了sequential: true该事件下的全部钩子都会改为串行执行——这是让多个钩子形成“处理链”的官方手段。五、配置四层来源与合并优先级Hooks 配置写在settings.json中Gemini CLI 按以下优先级从高到低合并多个来源项目设置当前目录的.gemini/settings.json用户设置~/.gemini/settings.json系统设置/etc/gemini-cli/settings.json扩展已安装扩展定义的钩子。从 hookRegistry.ts 的getSourcePriority看源码中还存在一个优先级更高的Runtime来源程序注册钩子如扩展/插件在运行时注册排序为 Runtime → Project → User → System → Extensions。注册表还会对配置做校验事件名必须是 11 个合法事件之一type必须为command及运行时类型type: command时必须有command字段否则整条配置被丢弃并记入调试日志。配置模式Schema{ hooks: { BeforeTool: [ { matcher: write_file|replace, hooks: [ { name: security-check, type: command, command: $GEMINI_PROJECT_DIR/.gemini/hooks/security.sh, timeout: 5000 } ] } ] } }钩子配置字段字段类型必填说明typestring是执行引擎目前配置层面仅支持commandcommandstring是*要执行的 shell 命令type为command时必填namestring否友好名称用于在日志和 CLI 命令中识别钩子timeoutnumber否执行超时毫秒默认 60000descriptionstring否简要说明钩子用途补充一个源码中的细节CommandHookConfig还支持可选的env字段types.ts用于为单个钩子注入额外环境变量且会合并到钩子执行环境中。六、环境变量钩子的“身份”钩子在净化的环境sanitized environment中执行通过sanitizeEnvironment过滤宿主环境后CLI 显式注入以下变量hookRunner.ts变量含义GEMINI_PROJECT_DIR项目根的绝对路径GEMINI_PLANS_DIRplans 目录的绝对路径GEMINI_SESSION_ID当前会话唯一 IDGEMINI_CWD当前工作目录CLAUDE_PROJECT_DIR兼容别名为兼容性提供值与GEMINI_PROJECT_DIR相同这意味着钩子脚本可以直接引用$GEMINI_PROJECT_DIR/.gemini/hooks/...这类路径而无需硬编码同时也意味着钩子默认拿不到宿主的敏感环境变量——如确实需要某个变量应通过钩子配置的env字段显式传入。七、多钩子决策如何合并当同一事件命中多个钩子时HookAggregatorhookAggregator.ts按事件类型选择合并策略这是理解“多个安全钩子叠加”行为的关键OR 决策逻辑BeforeTool、AfterTool、BeforeAgent、AfterAgent、SessionStart只要任一钩子给出阻断决策block/deny最终即为阻断ask仅在无阻断时生效若无任何阻断/询问/continue: false最终决策默认allow。多个钩子的reason、systemMessage、additionalContext会按换行拼接suppressOutput采用“任一为真即真”。字段替换BeforeModel、AfterModel后执行的输出覆盖先前的输出适合“后一个钩子对前一个的修改做再加工”。工具选择合并BeforeToolSelection采取并集策略——任一钩子声明NONE模式则整体最严格无工具可用否则任一声明ANY则用ANY默认AUTO。允许的工具名取所有钩子的并集并排序保证缓存一致性。简单合并其余事件如PreCompress、Notification、SessionEnd字段直接叠加。八、安全与风险警告Hooks 以你的用户权限执行任意代码。配置钩子即允许脚本在你的机器上运行 shell 命令。项目级钩子在打开不受信任的项目时风险尤其突出。Gemini CLI 对此建立了两道防线均可在源码中确认指纹信任机制trustedHooks.tsCLI 会为项目钩子建立指纹——以name:command组合键getHookKey存入全局trusted_hooks.json。当钩子的名称或命令发生变化例如通过git pull引入修改时它被视为新的、不受信任的钩子CLI 会发出警告用户确认知情后该指纹被写入信任列表避免重复打扰。信任目录门禁hookRunner.ts 与 hookRegistry.ts项目钩子只在受信任目录trusted folder中加载与执行在非受信目录中项目级钩子会被直接拦截“Security: Blocked execution of project hook in untrusted folder”注册表层面也会整体跳过项目钩子处理。更完整的威胁模型与“安全使用钩子”的准则见 最佳实践文档。九、管理钩子/hooks 命令无需手改 JSONCLI 内置/hooks命令族实现见 hooksCommand.ts命令作用/hooks panel查看所有已注册钩子及其状态/hooks enable-all启用全部钩子/hooks disable-all禁用全部钩子/hooks enable name启用指定名称的钩子/hooks disable name禁用指定名称的钩子禁用状态通过注册表的enabled标志与设置中的禁用列表生效HookRegistry.setHookEnabled按名称匹配并更新。对于需要回归验证钩子行为的场景仓库提供了大规模集成测试hooks-system.test.ts 覆盖了工具阻断block-tool、上下文注入after-tool-context、输入改写input-modification、顺序执行sequential-execution、会话启动/清空session-startup/session-clear等场景每个场景配有对应的.responses回放文件可作为钩子行为的“活规范”参考。小结Gemini CLI 的 Hooks 机制把“拦截点 I/O 协议 退出码 配置分层 安全门禁”组装成一套完整的定制体系11 个事件覆盖了从会话开始到上下文压缩的完整 Agent 生命周期stdin/stdout 的严格 JSON 契约与退出码语义让钩子既能精细改写请求/响应也能一键安全停机项目级指纹信任与受信目录门禁则把“执行任意代码”的风险约束在用户知情同意的边界内。掌握本文内容后你可以从 写作指南 起步编写第一个钩子并以 技术参考 与packages/core/src/hooks/源码为权威依据深入排查任何钩子行为问题。【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考