跳到正文
FunCoding

搜索

搜索文档、Skill 和 MCP

编写与验证第一个 Hook

用 Node.js 读取 stdin 并输出严格 JSON,独立测试后再绑定事件。

先实现一个只读取字段并记录名称的脚本,再加入路径、内容或结果校验。下面使用 Node.js JSON 解析器,避免依赖对 JSON 的文本正则提取。

最小脚本

将以下内容保存为 .gemini/hooks/log-tool.cjs:

const fs = require('node:fs');

try {
  const input = JSON.parse(fs.readFileSync(0, 'utf8'));
  if (typeof input.tool_name !== 'string') {
    throw new Error('Expected tool_name');
  }
  console.error(`Tool: ${input.tool_name}`);
  console.log(JSON.stringify({}));
} catch (error) {
  console.error(error.message);
  process.exit(2);
}

这是依据官方协议整理的示例,不是官方分发脚本。它只记录工具名称,不记录完整内容;exit 2 在工具前置事件中阻止该次执行。

绑定事件

{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "read_file",
        "hooks": [
          {
            "name": "log-tool",
            "type": "command",
            "command": "node .gemini/hooks/log-tool.cjs",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

相对脚本路径需对应 CLI 运行目录。通过 node 调用时不依赖脚本可执行位;直接执行 shell/JS 文件则需按平台配置解释器和权限。

独立测试

printf '%s' '{"tool_name":"read_file"}' | node .gemini/hooks/log-tool.cjs

应在 stdout 仅看到 {},在 stderr 看到工具名称。再测试缺少字段、无效 JSON 和自定义拒绝分支,分别检查退出码及两条输出流。这个最小输入只覆盖示例读取的字段,不代表完整 Hook 输入 schema。

接入后检查

在 /hooks panel 检查是否加载、matcher 是否触发、名称是否被禁用,再观察实际事件输入。如果脚本会写日志或缓存,先建立所需目录并限制内容;不要把官方教程中假设已存在的数据库和目录当作自动创建。

扩展到实际校验

输出统一用 JSON.stringify 或其他 JSON 库,尤其当上下文含换行、引号、Git 提交消息时。官方 Git 历史示例直接插入 heredoc,实际生产脚本应正确编码这些字符串。关键词秘密扫描仅是示例,不能当作覆盖所有提交渠道的完整扫描器。