GitNexus × Cursor 集成指南:用 postToolUse Hook 为编码 Agent 注入知识图谱上下文
【免费下载链接】GitNexusGitNexus: The Zero-Server Code Intelligence Engine - GitNexus is a client-side knowledge graph creator that runs entirely in your browser. Drop in a git repository (Github, Gitlab, Azure, Local) or ZIP file, and get an interactive knowledge graph with a built in Graph RAG Agent. Perfect for code exploration项目地址: https://gitcode.com/GitHub_Trending/gi/GitNexus
GitNexus 是一个把代码仓库离线索引为可查询知识图谱的代码智能引擎。本文讲解官方提供的gitnexus-cursor-integration集成包(gitnexus-cursor-integration/README.md),说明如何在 Cursor 中通过「MCP + Skills + postToolUse Hook」三层机制获得与 Claude Code 完全一致的图谱上下文增强,并深入剖析 Hook 的 stdin/stdout 契约、各工具的模式提取逻辑、并发锁与底层augment引擎实现。读完本文你可以亲手把任意已索引项目接入 Cursor,让 Agent 在 Read/Grep/Shell 之后自动看到相关符号的调用方、被调用方与流程参与信息。
集成总览:三层能力与自动化边界
Cursor 集成由三层组成,前两层由npx gitnexus setup一键自动化,第三层(即本 README 的核心)需要手动拷贝,因为 Cursor 把 Hook 作用域限定在单个项目根目录:
| 层面 | 作用 | 安装方式 |
|---|---|---|
| MCP | gitnexusMCP 服务器,提供 17 个工具(query、context、impact、detect_changes、rename等) | npx gitnexus setup自动写入~/.cursor/mcp.json |
| Skills | 随包附带的全部 Markdown 技能(/gitnexus-exploring、/gitnexus-debugging、/gitnexus-impact-analysis、/gitnexus-refactoring、/gitnexus-guide、/gitnexus-cli、/gitnexus-review、/gitnexus-plan、/gitnexus-work、/gitnexus-lfg、/gitnexus-pdq-query、/gitnexus-taint-analysis) | npx gitnexus setup拷贝至全局技能目录 |
| Hooks(本文主角) | postToolUseHook,为Shell/Read/Grep工具调用附加图谱上下文 —— 与 Claude Code 获得的增强完全一致 | 手动——把下述文件拷贝进目标项目的.cursor/ |
Hook 依赖 Cursor 2.4+。更早版本不暴露
postToolUse事件,Hook 会静默失效(no-op)。
关于「自动 vs 手动」的边界,仓库根 README 的编辑器支持矩阵也给出了佐证:Cursor 一行为「Full」,其中 Hooks 一列明确标注manual install,并链接到本 README 的 Hook 安装小节(见 gitnexus/README.md 编辑器支持表)。
手动与自动的分工可以总结为下表:
| 步骤 | gitnexus setup是否自动化? |
|---|---|
~/.cursor/mcp.json | ✅ |
~/.cursor/skills/*(Cursor 技能) | ✅ |
<project>/.cursor/hooks.json+<project>/hooks/gitnexus-hook.cjs+<project>/hooks/hook-lock.cjs | ❌ —— 需手动拷贝(见下节) |
MCP 与 Skills 是全局配置,Hook 是按项目配置(Cursor 将 Hook 限定在项目根目录内)。
环境与前置:先索引仓库
无论装哪一层,前提都是仓库已经被 GitNexus 索引:
# 在仓库根目录执行 npx gitnexus analyzenpm 11.x 兼容性注意:npx在安装期可能崩溃(Cannot destructure property 'package' of 'node.target')。此时改用 pnpm 形式:
pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyze索引完成后,gitnexus setup会检测到已安装的 Cursor,自动写入~/.cursor/mcp.json(keyPath 为mcpServers.gitnexus,见 gitnexus/src/cli/editor-targets.ts),并把随包技能安装到~/.cursor/skills/(每个gitnexus-*技能对应一个目录,见 gitnexus/src/cli/setup.ts 的installCursorSkills)。之后本文的 Hook 手动安装才进入场景。
Hook 安装:三个文件拷进项目根目录
Cursor 2.4+ 从项目根目录读取.cursor/hooks.json,并以项目根目录作为工作目录执行 Hook 命令。从本仓库的gitnexus-cursor-integration/hooks/拷贝以下文件到你的项目根目录:
<your-project>/ ├── .cursor/ │ └── hooks.json ← 来自 gitnexus-cursor-integration/hooks/hooks.json └── hooks/ ├── gitnexus-hook.cjs ← 来自 gitnexus-cursor-integration/hooks/gitnexus-hook.cjs └── hook-lock.cjs ← 来自 gitnexus-cursor-integration/hooks/hook-lock.cjs等效的 Shell 命令(在项目根目录执行,$GITNEXUS_REPO指向本仓库的克隆):
mkdir -p .cursor hooks cp "$GITNEXUS_REPO/gitnexus-cursor-integration/hooks/hooks.json" .cursor/hooks.json cp "$GITNEXUS_REPO/gitnexus-cursor-integration/hooks/gitnexus-hook.cjs" hooks/gitnexus-hook.cjs cp "$GITNEXUS_REPO/gitnexus-cursor-integration/hooks/hook-lock.cjs" hooks/hook-lock.cjs如果你已有.cursor/hooks.json,请合并hooks.postToolUse数组而不是整体覆盖。
实际的配置文件内容(gitnexus-cursor-integration/hooks/hooks.json)非常精简:
{ "version": 1, "hooks": { "postToolUse": [ { "matcher": "Shell|Read|Grep", "command": "node ./hooks/gitnexus-hook.cjs", "timeout": 10 } ] } }值得注意的两点:matcher声明了三个目标工具(用|分隔),timeout为 10 秒 —— 若底层augment调用超预算,Cursor 会终止 Hook 进程,但不会中断原始工具结果。Hook 脚本被设计为可随时静默失败,这是它不阻塞 Agent 的关键。
验证安装
- 索引项目:
npx gitnexus analyze(npm 11.x 上npx可能在安装期崩溃,改用上面给出的 pnpm 形式)。 - 重载 Cursor 窗口,使其加载新的 Hook 配置。
- 向 Agent 提问以触发
Read/Grep/Shell rg。你应当看到工具结果末尾追加了以[GitNexus]开头的上下文块。 - 诊断静默失效:在 shell 环境中设置
GITNEXUS_DEBUG=1—— Hook 会把 Cursor 的原始事件负载写到 stderr,便于核对字段名是否匹配。
Hook 契约:stdin 进、stdout 出
Hook 在 stdin 上收到符合 Cursor 2.4postToolUse形态的 JSON 事件:
{ "tool_name": "Grep" | "Read" | "Shell", "tool_input": { /* 工具相关字段 */ }, "tool_output": { /* 可选 */ }, "cwd": "/absolute/path/to/project" }它把增强上下文写到 stdout,格式为:
{ "additional_context": "[GitNexus] …" }stdout 为空 = 「不增强、照常继续」—— Hook 永远不会阻塞工具本身。这一设计在源码中被反复强化:主流程被 try/catch 包裹(gitnexus-cursor-integration/hooks/gitnexus-hook.cjs),任何异常在非 DEBUG 模式下都不向外抛出。
stdout 与 stderr 的分工(容易踩坑的细节)
注意 stdout 是留给 Cursor 消费的 JSON 响应通道。真正承载图谱上下文的其实是子进程的 stderr:gitnexus augment命令明确把结果写到 stderr 而不是 stdout,原因是 LadybugDB 的原生模块在初始化时会从 OS 层面捕获 stdout 文件描述符,导致子进程环境下 stdout 永久损坏,而 stderr 从不被捕获(见 gitnexus/src/cli/augment.ts 注释)。Cursor Hook 侧则读取child.stderr来取结果。这是「跨进程输出通道必须用 stderr」的经典案例。
各工具的模式提取规则
Hook 会根据工具类型,从tool_input中推导一个搜索 pattern,再交给gitnexus augment <pattern>。由于 Cursor 文档只定义了工具matcher,并未正式规定每个工具的tool_input字段名,Hook 对每个工具探测了一组宽松的 MCP 风格别名:
| 工具 | 模式来源 | 说明 |
|---|---|---|
Grep | tool_input.query(也接受pattern、regex、q、search、searchQuery) | 最后兜底方案:取tool_input中最长的字符串值(长度 ≥ 3)。 |
Read | tool_input.target_file的文件名(也接受file_path、filePath、path、file),裁剪为标识符字符 | auth/handler.ts→handler。 |
Shell | tool_input.command中rg/grep之后的第一个位置参数 | 尽力而为的 tokenizer;带引号的多词 pattern(如rg "User Service")只取第一个词。 |
源码中的别名与回退逻辑(gitnexus-cursor-integration/hooks/gitnexus-hook.cjs)值得展开:
- Grep 兜底策略
pickLongestStringValue:遍历tool_input的所有值,返回第一个长度 ≥ 3 且最长的字符串。这是为「Cursor 改了字段名」准备的最后防线。 - Read 的标识符裁剪:先用
path.basename(filePath, ext)去掉路径与扩展名,再以[^a-zA-Z0-9_]正则会话字符,不足 3 个字符返回null。多词路径退化为核心词。 - Shell 的 tokenizer
parseRgGrepPattern:按空白拆分命令,先扫描到rg/grep出现为止,之后跳过带值标志(-e/-f/-m/-A/-B/-C/-g/--glob/-t/--type/--include/--exclude)的参数,遇到第一个普通 token 时剥掉引号,长度 ≥ 3 才采用。多词 pattern 有意不重建 —— 注释说明 BM25 本身对分词宽容,rg "validateUser"这种单 token 带引号写法完全正常。
pattern 长度 < 3 时直接返回(不增强),这对应augmentCLI 同样pattern.length < 3 → exit(0)的保护(见 gitnexus/src/cli/augment.ts)。
后台实现剖析:目录定位、CLI 解析与并发闸门
Hook 的主流程可以概括为五步,全部能在 gitnexus-cursor-integration/hooks/gitnexus-hook.cjs 中一一对应:
- 读取 stdin 解析 JSON(失败则视为空对象,静默退出)。
- 定位
.gitnexus目录(findGitNexusDir):从事件里的cwd向上最多走 5 层找.gitnexus,同时排除「全局注册表目录」——判断依据是目录内是否同时含有registry.json或repos却没有gitnexus.json/meta.json(isGlobalRegistryDir)。若 cwd 下找不到,再用git rev-parse --path-format=absolute --git-common-dir求规范仓库根(兼容 worktree/子目录场景,findCanonicalRepoRoot),从那里再找一次。找不到就静默退出。 - 提取搜索 pattern(见上一节)。
- 获取并发槽位(
acquireHookSlot,来自 gitnexus-cursor-integration/hooks/hook-lock.cjs),拿不到就跳过。 - 解析 CLI 路径并执行
gitnexus augment -- <pattern>(7 秒超时),成功则把子进程 stderr 内容包装为{ "additional_context": … }输出。
CLI 路径解析:本地优先、npx 兜底
resolveCliPath先尝试require.resolve('gitnexus/dist/cli/index.js')——即在项目内(或祖先 node_modules)可解析的本地安装;解析失败则回退到npx -y gitnexus(Windows 上使用npx.cmd)。之所以默认本地安装,是为了避免 npx 冷启动拉包的开销。README 也建议全局安装npm i -g gitnexus以彻底跳过 npx 冷启动。
并发闸门:为什么需要 hook-lock.cjs
多个并发会话可能同时触发 Hook,导致对同一个 LadybugDB 图谱索引的并发读放大。hook-lock.cjs在每个仓库的.gitnexus/.hook-locks/目录下维护至多 3 个槽位(HOOK_LOCK_MAX_INFLIGHT = 3):
- 通过
fs.writeFileSync(slotPath, pid, { flag: 'wx' })原子抢占;失败说明槽位被占。 - PID 存活检测:读槽位文件里的 owner PID,用
process.kill(owner, 0)判断持有者是否还活着(ESRCH = 已死可回收;EPERM = 跨用户仍视为存活)。process.on('exit', release)保证异常退出也能释放槽位;释放前核对文件内容仍是自己的 PID,避免误删他人接管后的锁(防止 TOCTOU 与 #1486 号 fan-out 问题回归)。 - 陈旧兜底:对超过 30 秒(
HOOK_LOCK_STALE_MS)的槽位,用「年龄」做最终裁决以防御 PID 复用——30 秒远超 augment 的 7 秒超时,健康运行永远不会触达该阈值。
与 Claude/Antigravity 适配器不同的是,Cursor 集成不安装hook-db-lock-probe.cjs,因此其 augment 子进程暂未被该探针守护包装(源码注释将其列入 #2163 后续清单)。
augment 引擎:BM25 + 关系图谱的快速路径
Hook 调用的augment命令走的是专门的轻量快路径(gitnexus/src/cli/augment.ts),目标是 <500ms 冷启动,不启动 Web 服务、不做完整 DB 预热。底层引擎(gitnexus/src/core/augmentation/engine.ts)的逻辑是:
- 定位仓库:遍历
listRegisteredRepos,取「cwd 位于仓库路径内」且**路径最长(最具体)**的匹配,在路径分隔符边界上做比较以避免/projects/gitnexusv2误匹配/projects/gitnexus。 - BM25 全文搜索:只做 BM25(不引入 embedding/语义检索,保证速度),取 top 10 文件结果;FTS 索引不可用(只读库或首次运行)时退化为
name CONTAINS的 Cypher 查询。 - 映射符号:对前 5 个文件结果按
name CONTAINS <pattern首词>找符号。 - 批量取邻居:对每个符号分别取
Called by(入边调用者)与Calls(出边被调者),各限 3 条(NEIGHBOUR_CAP);批量查询STEP_IN_PROCESS参与流程(Flows: label (step x/y))与MEMBER_OF社团 cohesion 值。源码注释特别说明:邻居窗口按符号独立限流而非共享预算,否则排序会成为单桶前缀,导致一个热门符号独占全部配额。 - 内部排序与输出:按 cohesion 排序(仅供内部排名,不出现在输出中),拼接为形如
[GitNexus] N related symbols found:的结构化文本块,每行含符号名、文件路径、Called by、Calls、Flows。
整个引擎任何异常都返回空字符串——「优雅失败,永不破坏原始工具」是贯穿 Hook 与引擎两个层级的铁律。
技能参考镜像与 Cursor 技能形态
集成包内的gitnexus-cursor-integration/skills/目录保留了面向 Cursor 的技能参考镜像,例如gitnexus-exploring(见 gitnexus-cursor-integration/skills/gitnexus-exploring/SKILL.md),其 YAML frontmatter 定义了技能名称与触发描述,正文给出了「先list_repos发现索引仓库 → 读gitnexus://repo/{name}/context检查新鲜度 →query找流程 →context深挖符号 → 读 process 追踪执行流」的工作流模板,并提醒「若提示 Index is stale,运行node .gitnexus/run.cjs analyze」。随包完整技能树见 gitnexus/skills/,gitnexus setup的installSkillsTo会把扁平*.md或{name}/SKILL.md两种布局都转换为{targetDir}/{skillName}/SKILL.md的 Agent Skills 标准形态(支持带references/、scripts/的目录型技能递归拷贝),并能在升级时提示重命名遗留目录而不会删除用户自定义内容。
Troubleshooting 排障手册
| 症状 | 排查路径 |
|---|---|
| 什么都没发生 | 确认 Cursor 在 2.4+,且项目根目录存在.cursor/hooks.json与hooks/gitnexus-hook.cjs、hooks/hook-lock.cjs两个文件;随后npx gitnexus list确认项目已被索引。 |
gitnexusnot found | Hook 优先解析本地可用的gitnexus/dist/cli/index.js,失败才回退到npx -y gitnexus。可用npm i -g gitnexus全局安装以跳过 npx 冷启动延迟。 |
| 提取到的 pattern 不对 | 设置GITNEXUS_DEBUG=1后运行一次工具调用,Hook 会把原始 stdin 负载(截断 500 字符)打到 stderr;对照上文的别名表核对 Cursor 实际的tool_input字段名。若字段不一致,携带捕获的负载内容提交 issue。 |
DEBUG 机制的两个细节值得记住:debug 日志一律走stderr(stdout 是 Cursor 消费的契约通道,绝不能污染);Hook 顶层的 catch 在 DEBUG 下会把错误消息截断 200 字符打印,生产模式下则完全吞掉,保证任何异常都不会波及 Agent 会话。
与同类集成的定位差异
GitNexus 面向多个编辑器提供集成(Claude Code、Antigravity、Codex、OpenCode、CodeBuddy、Qoder 等,见 gitnexus/README.md 的编辑器支持矩阵),其中 Hook 层因各家的事件契约不同而形态各异:Claude Code/Codex 走~/.claude/settings.json或~/.codex/hooks.json的PreToolUse+PostToolUse并可被setup全自动注册;Antigravity 走 Gemini 的AfterTool.additionalContext;而Cursor 的postToolUseHook 只能落到项目级.cursor/hooks.json,这是它必须手动安装的根本原因——它是 Cursor 集成三件套里唯一「无法全局化」的一块。也正因如此,官方把它单独整理为gitnexus-cursor-integration目录,让开发者按项目独立启用或停用这条增强链路。
综合来看,Cursor 用户的完整落地路径是:gitnexus analyze建立图谱索引 →gitnexus setup自动写入 MCP 与全局技能 → 按本文手动拷贝三个文件完成项目级 Hook 接入 → 重载窗口后即可在每一次代码检索中获得带源码依据的图谱上下文增强。
【免费下载链接】GitNexusGitNexus: The Zero-Server Code Intelligence Engine - GitNexus is a client-side knowledge graph creator that runs entirely in your browser. Drop in a git repository (Github, Gitlab, Azure, Local) or ZIP file, and get an interactive knowledge graph with a built in Graph RAG Agent. Perfect for code exploration项目地址: https://gitcode.com/GitHub_Trending/gi/GitNexus
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考