跳转到内容

Gemini CLI 钩子

钩子是 Gemini CLI 在代理循环特定点执行的脚本或程序,使你能够在不修改 CLI 源代码的情况下截获和自定义行为。

有关创建你的第一个钩子以及综合性示例的教程,请参阅 编写钩子指南

有关 I/O 架构的技术规范,请参阅 钩子参考

有关安全性、性能和调试的指导原则,请参阅 最佳实践

使用钩子,你可以:

  • 添加上下文: 在模型处理请求之前注入相关信息
  • 验证操作: 审查并阻止潜在危险的操作
  • 执行策略: 实施安全和合规要求
  • 记录交互: 跟踪工具使用和模型响应
  • 优化行为: 动态调整工具选择或模型参数

钩子作为代理循环的一部分同步运行——当钩子事件触发时,Gemini CLI 会等待所有匹配的钩子完成后再继续。

[!WARNING] 钩子以你的用户权限执行任意代码。

通过配置钩子,你明确允许 Gemini CLI 在你的机器上运行 shell 命令。恶意或配置不当的钩子可能导致:

  • 数据泄露: 读取敏感文件(如 .env,ssh 密钥)并将它们发送到远程服务器。
  • 修改系统: 删除文件,安装恶意软件或更改系统设置。
  • 消耗资源: 运行无限循环或使你的系统崩溃。

项目级钩子(在 .gemini/settings.json 中)和扩展钩子在打开来自不可信作者的第三方项目或扩展时特别危险。当 Gemini CLI 第一次检测到新的项目钩子(通过其名称和命令识别)时,它会警告你,但审查这些钩子(以及任何安装的扩展)并信任它们是你的责任

[!NOTE] 插件钩子需要在进行插件安装或更新时,如果检测到钩子,则必须显示强制性的安全警告和同意流程。您必须明确批准包含钩子的任何插件的安装或更新。

请参阅安全考虑以获取详细的威胁模型和缓解策略。

钩子是由 Gemini CLI 生命周期中的特定事件触发的。下表列出了所有可用的钩子事件:

【翻译后文本】

事件触发时机常见用例
会话开始时会话开始时初始化资源,加载上下文
会话结束时会话结束时清理,保存状态
用户提交提示后,计划前用户提交提示后,计划前添加上下文,验证提示
代理循环结束时代理循环结束时审查输出,强制继续
发送请求到LLM前发送请求到LLM前修改提示,添加指令
接收到LLM响应后接收到LLM响应后过滤响应,记录交互
LLM选择工具前(在BeforeModel之后)LLM选择工具前(在BeforeModel之后)过滤可用工具,优化选择
工具执行前工具执行前验证参数,阻止危险操作
工具执行后工具执行后处理结果,运行测试
上下文压缩前上下文压缩前保存状态,通知用户
发生通知时(例如权限)发生通知时(例如权限)自动批准,记录决策

Gemini CLI目前支持命令钩子,运行shell命令或脚本:

{
"type": "command",
"command": "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh",
"timeout": 30000
}

注意: 计划在未来的版本中支持插件钩子(npm包)。

对于与工具相关的事件(BeforeTool, AfterTool),您可以使用过滤器确定哪些工具触发钩子:

{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|replace",
"hooks": [
/* hooks for write operations */
]
}
]
}
}

匹配器模式:

精确匹配: "read_file" 只匹配 read_file

  • 正则表达式: "write_.*|replace" 匹配 write_file, replace
  • 通配符: "*""" 匹配所有工具

会话事件匹配器:

  • 会话开始: startup, resume, clear
  • 会话结束: exit, clear, logout, prompt_input_exit
  • 预压缩: manual, auto
  • 通知: ToolPermission

钩子通过以下方式通信:

  • 输入: 标准输入上的 JSON
  • 输出: 退出码 + 标准输出/错误输出
  • 0: 成功 - 标准输出显示给用户(或某些事件中作为上下文注入)
  • 2: 阻塞错误 - 错误输出显示给代理/用户,操作可能被阻止
  • 其他: 非阻塞警告 - 记录但执行继续

每个钩子接收以下基础字段:

{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "BeforeTool",
"timestamp": "2025-12-01T10:30:00Z"
// ... event-specific fields
}

输入:

{
"tool_name": "write_file",
"tool_input": {
"file_path": "/path/to/file.ts",
"content": "..."
}
}

输出(标准输出上的 JSON):

{
"decision": "allow|deny|ask|block",
"reason": "Explanation shown to agent",
"systemMessage": "Message shown to user"
}

或简单的退出码:

  • 退出 0 = 允许(标准输出显示给用户)
  • 退出 2 = 拒绝(错误输出显示给代理)

输入:

{
"tool_name": "read_file",
"tool_input": { "file_path": "..." },
"tool_response": "file contents..."
}

输出:

{
"decision": "allow|deny",
"hookSpecificOutput": {
"hookEventName": "AfterTool",
"additionalContext": "Extra context for agent"
}
}

输入:

{
"prompt": "Fix the authentication bug"
}

输出:

{
"decision": "allow|deny",
"hookSpecificOutput": {
"hookEventName": "BeforeAgent",
"additionalContext": "Recent project decisions: ..."
}
}

输入:

{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [{ "role": "user", "content": "Hello" }],
"config": { "temperature": 0.7 },
"toolConfig": {
"functionCallingConfig": {
"mode": "AUTO",
"allowedFunctionNames": ["read_file", "write_file"]
}
}
}
}

输出:

{
"decision": "allow",
"hookSpecificOutput": {
"hookEventName": "BeforeModel",
"llm_request": {
"messages": [
{ "role": "system", "content": "Additional instructions..." },
{ "role": "user", "content": "Hello" }
]
}
}
}

输入:

{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [
/* ... */
],
"config": {
/* ... */
},
"toolConfig": {
/* ... */
}
},
"llm_response": {
"text": "string",
"candidates": [
{
"content": {
"role": "model",
"parts": ["array of content parts"]
},
"finishReason": "STOP"
}
]
}
}

输出:

{
"hookSpecificOutput": {
"hookEventName": "AfterModel",
"llm_response": {
"candidate": {
/* modified response */
}
}
}
}

输入:

{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [
/* ... */
],
"toolConfig": {
"functionCallingConfig": {
"mode": "AUTO",
"allowedFunctionNames": [
/* 100+ tools */
]
}
}
}
}

输出:

{
"hookSpecificOutput": {
"hookEventName": "BeforeToolSelection",
"toolConfig": {
"functionCallingConfig": {
"mode": "ANY",
"allowedFunctionNames": ["read_file", "write_file", "replace"]
}
}
}
}

或简单的输出(逗号分隔的工具名称集将模式设置为 ANY):

Terminal window
echo "read_file,write_file,replace"

输入:

{
"source": "startup|resume|clear"
}

输出:

{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Loaded 5 project memories"
}
}

输入:

{
"reason": "exit|clear|logout|prompt_input_exit|other"
}

不期望有结构的输出(但标准输出/错误输出被记录)。

输入:

{
"trigger": "manual|auto"
}

输出:

{
"systemMessage": "Compression starting..."
}

输入:

{
"notification_type": "ToolPermission",
"message": "string",
"details": {
/* notification details */
}
}

输出:

{
"systemMessage": "Notification logged"
}

输入:

{
"notification_type": "ToolPermission",
"message": "string",
"details": {
/* notification details */
}
}

输出:

{
"systemMessage": "Notification logged"
}

钩子定义在 settings.json 文件中通过 hooks 对象进行配置。配置可以在多个级别上指定,并有定义的优先级规则。

钩子配置按照以下执行顺序应用(数字越小,优先级越高):

  1. 项目设置: 在项目目录中的 .gemini/settings.json (最高优先级)
  2. 用户设置: ~/.gemini/settings.json
  3. 系统设置: /etc/gemini-cli/settings.json
  4. 扩展: 由已安装扩展定义的内部钩子(最低优先级)。有关扩展如何定义和配置钩子的详细信息,请参阅扩展文档

如果在不同的配置层中发现了具有相同 名称命令 的多个钩子,Gemini CLI 会将它们去重。来自更高优先级层(例如,项目层)的钩子将被保留,其他钩子将被忽略。

在每个级别内,钩子按照它们在配置中声明的顺序运行。

{
"hooks": {
"EventName": [
{
"matcher": "pattern",
"hooks": [
{
"name": "hook-identifier",
"type": "command",
"command": "./path/to/script.sh",
"description": "What this hook does",
"timeout": 30000
}
]
}
]
}
}

配置属性:

  • name (字符串,推荐):在 /hooks enable/disable 命令中使用的钩子的唯一标识符。如果省略,则使用 command 路径作为标识符。
  • type (字符串,必填):钩子类型 - 目前只支持 "command"
  • command (字符串,必填):要执行的脚本或命令的路径
  • description (字符串,可选):在 /hooks panel 中显示的人类可读描述
  • timeout (数字,可选):超时时间(毫秒)(默认:60000)
  • matcher (字符串,可选):钩子运行时的事件匹配器筛选模式

钩子可以访问以下内容:

【译文】

  • 项目根目录:GEMINI_PROJECT_DIR
  • 当前会话ID:GEMINI_SESSION_ID
  • Gemini API密钥(如果已配置):GEMINI_API_KEY
  • 父进程中的所有其他环境变量

使用 /hooks panel 命令查看所有已注册的钩子:

Terminal window
/hooks panel

此命令显示:

  • 按事件组织的所有活动钩子
  • 钩子来源(用户、项目、系统)
  • 钩子类型(命令或插件)
  • 执行状态和最近输出

您可以使用以下命令临时启用或禁用单个钩子:

Terminal window
/hooks enable hook-name
/hooks disable hook-name

这些命令允许您在不编辑配置文件的情况下控制钩子执行。钩子名称应与您钩子配置中的 name 字段相匹配。通过这些命令所做的更改将保存到您的全局用户设置(~/.gemini/settings.json)。

要永久禁用钩子,请将它们添加到 hooks.disabled 数组中,在您的 settings.json 中:

{
"hooks": {
"disabled": ["secret-scanner", "auto-test"]
}
}

注意: hooks.disabled 数组使用联合合并策略。来自所有配置级别(用户、项目、系统)的禁用钩子会被合并并去重,意味着在任何级别禁用的钩子都会保持禁用。

如果您已为Claude Code配置了钩子,您可以迁移它们:

Terminal window
gemini hooks migrate --from-claude

此命令:

  • 读取 .claude/settings.json
  • 转换事件名称(PreToolUseBeforeTool 等)
  • 翻译工具名称(Bashrun_shell_commandreplacereplace
  • 更新匹配器模式
  • 写入到 .gemini/settings.json

(以下部分未提供英文原文,故不进行翻译)

Claude CodeGeminiCLI
PreToolUseBeforeTool
PostToolUseAfterTool
UserPromptSubmitBeforeAgent
StopAfterAgent
NotificationNotification
SessionStartSessionStart
SessionEndSessionEnd
PreCompactPreCompress
Claude CodeGeminiCLI
Bashrun_shell_command
Editreplace
Readread_file
Writewrite_file
Globglob
Grepsearch_file_content
LSlist_directory

以下内置工具可以在 BeforeToolAfterTool 钩子匹配器中使用:

  • read_file - 读取单个文件
  • read_many_files - 一次性读取多个文件
  • write_file - 创建或覆盖文件
  • replace - 使用查找/替换编辑文件内容
  • list_directory - 列出目录内容
  • glob - 查找与模式匹配的文件
  • search_file_content - 在文件内容中搜索
  • run_shell_command - 执行Shell命令
  • google_web_search - 带有定位的Google搜索
  • web_fetch - 获取网页内容
  • write_todos - 管理TODO项目
  • save_memory - 将信息保存到内存
  • delegate_to_agent - 将任务委派给子代理
{
"matcher": "write_file|replace" // File editing tools
}
{
"matcher": "read_.*" // All read operations
}
{
"matcher": "run_shell_command" // Only shell commands
}
  • startup - 新的会话开始
  • resume - 恢复上一个会话
  • clear - 会话已清除
  • exit - 正常退出
  • clear - 会话已清除
  • logout - 用户已登出
  • prompt_input_exit - 从提示输入退出
  • other - 其他原因
  • manual - 手动触发的压缩
  • auto - 自动触发的压缩
  • ToolPermission - 工具权限通知