fix(cursor): write repo-relative shim path in project hooks.json - #397
stefanwalther wants to merge 1 commit into
Conversation
init baked the absolute checkout path into .cursor/hooks.json commands. Every clone and worktree then showed the file as modified after its own init run, and pulling a branch that committed the wiring failed when local untracked copies collided with it. Cursor runs project hooks from the repo root, so a relative path resolves the same everywhere and stays committed clean. Old absolute entries still match isGraftEntry, so the next init upgrades them in place.
🌱 graft blast radius1 area changed → 2 areas can be affected. 3 dependent symbols, depth 2. flowchart TB
A0(("wireTarget<br/>2 symbols"))
A1(("Host Initialization<br/>1 symbol"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0,A1 reached;
Who knows this code — 4 people across 3 areas
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no All 3 dependent symbols, grouped by areawireTarget — 2 symbols in 2 files
Host Initialization — 1 symbol in 1 file
Test signal per changed area — 1 ✓Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
4 test suites also reference this code4 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.
Open the interactive graph → — click an area to see the code that changed, and the line that reaches it. |
Problem
graft initwrites the checkout's absolute path into the committed.cursor/hooks.json:{ "command": "node \"/Users/me/code/proj/.cursor/hooks/graft-hooks.cjs\" cursor-post-tool" }That path is different on every machine and worktree. In a repo with committed wiring, each fresh init rewrites these lines, so
hooks.jsonshows as modified everywhere. It also breaks pulls: anyone who ran init locally before fetching the wired branch holds entries that collide with the incoming commit, and git aborts the merge.Fix
Cursor runs project hooks from the repo root, so the command is now repo-relative:
{ "command": "node .cursor/hooks/graft-hooks.cjs cursor-post-tool" }The stale-entry check matches on
graft-hooks.cjs, so the next init swaps old absolute entries in place. Nothing to clean up by hand.Verification
New test assertion that no checkout path leaks into the commands. Full suite green (1221 tests),
tscclean.