fix(sync): stop syncCursor deleting the user's .cursor/ state#36
Merged
Conversation
syncCursor called rmGenerated on the whole .cursor/ tree, while syncClaude, syncCodex, and syncOpenCode each remove only the subdirectories they generate. So `npm run sync` deleted every file a user keeps in .cursor/ -- including .cursor/environment.json, Cursor's own background-agent config. Demonstrated before the fix by planting files and running sync: .claude/settings.local.json SURVIVED .claude/other/user.txt SURVIVED .cursor/environment.json DELETED .cursor/my-own-rule.mdc DELETED The function's own docstring said agents are prefixed `agentry-` "to avoid collisions with the user's own Cursor agents" -- agents the same function had just deleted. The prefix only ever protected the install path. Now wipes .cursor/agents/ and .cursor/rules/ only. .cursor/mcp.json joins .mcp.json and opencode.json under D20: written when MCP sources exist, never deleted, may carry the user's own servers. The guard had drifted from the engine the same way. protect-generated-dirs listed `.cursor/` and `.codex/` wholesale, so it blocked edits to .cursor/environment.json and .codex/config.toml -- two files sync preserves, and the second is the file README names as Codex's per-user state. Both are narrowed to the wiped subdirectories, so the guard and the engine now describe the same set. Verified by planting a file at all 18 harness paths, syncing, and comparing what survived against the hook's verdict: 18/18 agree, every wiped path blocks and every preserved path allows. Generated output is byte-identical, sync is still idempotent across two runs, and deleting a source still removes its stale .cursor/rules/*.mdc. Suite: 239 -> 243 tests. README's per-user-state list gains Cursor, which had none to name while sync was deleting it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
syncCursorcalledrmGeneratedon the whole.cursor/tree, whilesyncClaude,syncCodex, andsyncOpenCodeeach remove only the subdirectories they generate.So
npm run syncdeleted every file a user keeps in.cursor/— including.cursor/environment.json, Cursor's own background-agent config.Demonstrated by planting files and running sync, before the fix:
The README already promised the opposite:
And
syncCursor's own docstring said agents get anagentry-prefix "to avoid collisions with the user's own Cursor agents" — agents the same function had just deleted three lines earlier. The prefix only ever protected the install path.The guard had drifted the same way
protect-generated-dirslisted.cursor/and.codex/wholesale, so it blocked edits to:.cursor/environment.json— which sync (after this fix) preserves.codex/config.toml— which sync always preserved, and which the README names as Codex's per-user stateA guard that refuses an edit the engine would have kept is as wrong as one that permits an edit the engine will destroy.
The fix
syncCursorwipes.cursor/agents/and.cursor/rules/only..cursor/mcp.jsonjoins.mcp.jsonandopencode.jsonunder D20 — written when MCP sources exist, never deleted, may carry the user's own servers.The hook's
GENERATEDlist now names the wiped subdirectories exactly:Verification — the hook and the engine must describe the same set
Planted a file at all 18 harness paths, ran sync, recorded what survived, and compared it against the hook's exit code. Correct pairing is wiped → BLOCK, preserved → allow.
Plus the three properties the change could have broken:
git diff --numstat -- .cursor→0 filesskills/caching/and re-syncing removes.cursor/rules/caching.mdcREADME's per-user-state list gains Cursor — it had none to name while sync was deleting it.