Four fixes to the Cursor postToolUse hook's npx fallback. F1 — Homebrew installs `gtimeout`, not `timeout`. The builtin candidate list named `/opt/homebrew/bin/timeout` and `/usr/local/bin/timeout`, which exist on no Homebrew install, so on Apple Silicon the `-s KILL` orphan-containment wrap silently never engaged. Adopt the canonical list: `/usr/bin/timeout`, `/bin/timeout`, `/opt/homebrew/bin/gtimeout`, `/usr/local/bin/gtimeout`. F2 — candidates were adopted on mere existence. `GITNEXUS_HOOK_TIMEOUT_PATH` pointing at an always-exit-0 binary passed `existsSync`, and the wrapped spawn then exited 0 without ever running npx — status 0 and empty stderr satisfy main()'s success check, so the hook emitted no augmentation at all. Port `passesGuardSelfTest` from hook-db-lock-probe.cjs: a candidate is adopted only when `timeout -k 1 1 /bin/sh -c 'exit 42'` exits 42, i.e. it both runs the wrapped command and propagates its status. This also rejects busybox <1.34 / toybox, which lack `-k`. The override is resolved with `path.resolve` because runGitNexusCli spawns it with the tool request's cwd. Resolution is memoized per hook process. F3 — `Math.max(1000, ...)` handed the guard 1000ms of an already-expired host window (3000ms with the spawnSync allowance), so the host killed the hook before the guard returned or the final stdout write landed. Skip the npx spawn when no usable budget remains; return the same shape main() already handles so the hook slot is still released. F4 — `timeout + 5000` capped every npx fallback at 12s (main passes 7000), defeating the 60s hooks.json manifest that exists to accommodate the cold `npx -y gitnexus` install. Size the npx arm from the remaining host budget instead, clamped by a documented max so one slow install cannot hold a per-repo hook slot for the whole window. Tests: pin the Homebrew gtimeout candidates, the always-exit-0 stub rejection, the exhausted-budget skip, and the uncapped budget. The budget arithmetic and the guard resolver are now exported so the tests exercise them directly instead of matching substrings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| hooks | ||
| skills | ||
| README.md | ||
GitNexus — Cursor integration
Static config that adds GitNexus knowledge-graph augmentation and skill files to Cursor.
Hooks require Cursor 2.4+. Earlier versions don't expose
postToolUseand the hook will silently no-op.
What you get
| Layer | What it does | How it's installed |
|---|---|---|
| MCP | gitnexus MCP server with 19 tools (query, context, impact, detect_changes, rename, …) |
npx gitnexus setup writes ~/.cursor/mcp.json automatically. |
| Skills | All bundled markdown skills (/gitnexus-exploring, /gitnexus-debugging, /gitnexus-impact-analysis, /gitnexus-refactoring, /gitnexus-guide, /gitnexus-cli, /gitnexus-review, /gitnexus-plan, /gitnexus-work, /gitnexus-lfg, /gitnexus-pdg-query, /gitnexus-taint-analysis) |
npx gitnexus setup copies them to ~/.cursor/skills/gitnexus/. |
| Hooks (this README) | postToolUse hook that enriches Shell / Read / Grep tool calls with graph context — same augmentation Claude Code gets |
Manual — copy the files described below into your project's .cursor/. |
Hook install
Cursor 2.4+ reads .cursor/hooks.json from the project root and runs hook commands with the project root as the working directory (docs).
From this repo's gitnexus-cursor-integration/hooks/, copy the files below into your project root:
<your-project>/
├── .cursor/
│ └── hooks.json ← from gitnexus-cursor-integration/hooks/hooks.json
└── hooks/
├── gitnexus-hook.cjs ← from gitnexus-cursor-integration/hooks/gitnexus-hook.cjs
├── hook-lock.cjs ← from gitnexus-cursor-integration/hooks/hook-lock.cjs
└── registry-query.cjs ← from gitnexus-cursor-integration/hooks/registry-query.cjs
Equivalent shell commands (run from your project root, with $GITNEXUS_REPO pointing at a clone of this 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
cp "$GITNEXUS_REPO/gitnexus-cursor-integration/hooks/registry-query.cjs" hooks/registry-query.cjs
If you already have a .cursor/hooks.json, merge the hooks.postToolUse array rather than overwriting.
Verify
- Index the project:
npx gitnexus analyze(on npm 11.x,npxcan crash during install — usepnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyzeinstead; see #1939) - Reload the Cursor window so it picks up the new hook config.
- Ask the agent something that triggers
Read/Grep/Shell rg. You should see a[GitNexus]block appended to the tool result. - Diagnose silent no-ops by setting
GITNEXUS_DEBUG=1in your shell environment — the hook will write Cursor's raw event payload to stderr so you can verify field names.
What's installed manually vs. automated
| Step | Automated by gitnexus setup? |
|---|---|
~/.cursor/mcp.json |
✅ |
~/.cursor/skills/gitnexus/* |
✅ |
<project>/.cursor/hooks.json + <project>/hooks/gitnexus-hook.cjs + <project>/hooks/hook-lock.cjs + <project>/hooks/registry-query.cjs |
❌ — copy manually (see above) |
Hook install is per-project (Cursor scopes hooks to a project root); skills and MCP config are global.
Hook contract
The hook receives a JSON event on stdin matching Cursor 2.4's postToolUse shape:
{
"tool_name": "Grep" | "Read" | "Shell",
"tool_input": { /* tool-specific */ },
"tool_output": { /* optional */ },
"cwd": "/absolute/path/to/project"
}
It writes augmentation context to stdout as:
{ "additional_context": "[GitNexus] …" }
Empty stdout means "no augmentation, continue normally" — the hook never blocks the tool.
Pattern extraction per tool
| Tool | Pattern source | Notes |
|---|---|---|
Grep |
tool_input.query (also pattern, regex, q, search, searchQuery) |
Last-resort fallback: longest string value in tool_input (≥ 3 chars). |
Read |
basename of tool_input.target_file (also file_path, filePath, path, file), stripped to identifier characters |
auth/handler.ts → handler. |
Shell |
First positional argument after rg / grep in tool_input.command |
Best-effort tokenizer; quoted multi-word patterns (rg "User Service") extract the first word only. |
Troubleshooting
- Nothing happens — Confirm Cursor is on 2.4+ and the project root has
.cursor/hooks.jsonplus the hook files athooks/gitnexus-hook.cjs,hooks/hook-lock.cjs, andhooks/registry-query.cjs. Thennpx gitnexus listto confirm the project is indexed. gitnexusnot found — The hook prefers a locally-resolvablegitnexus/dist/cli/index.jsand falls back tonpx -y gitnexus. Install globally withnpm i -g gitnexusto skip the npx cold-start latency.- Wrong pattern extracted — Set
GITNEXUS_DEBUG=1and run a tool call. The raw stdin payload is logged to stderr; use it to confirm Cursor's actualtool_inputfield names against the table above. If they differ, file an issue with the captured payload.