feat(cursor): first-class Cursor Plugin support with the process lock kept on - #1040
Conversation
Ship Cursor Plugin manifests on the Claude package and skip the Claude process lock on Cursor and home-keyed roots so reconnect no longer exits 11. Keep GH #672: do not steal a live owner. Node.js remains >= 24. Co-authored-by: Anton Lykhoyda <lykhoyda@gmail.com>
Rebuild Claude and Codex bundled supervisor/index so marketplace installs skip the home-keyed process lock. Shrink lock-skip comments. Co-authored-by: Anton Lykhoyda <lykhoyda@gmail.com>
Drop the --no-lock argument from the Cursor mcp.json and remove the host-process-lock module that skipped acquisition for Cursor hosts, WORKSPACE_FOLDER_PATHS, and home-keyed roots. Every host spawn acquires the same lock again and a second same-root server still exits 11 (GH #672). CLAUDE_USER_CWD is no longer seeded from the first workspace folder; no home-to-app identity is invented. Make the conflict message host-neutral and stop recommending kill, lock deletion, or --no-lock. When the locked root is the home directory the message says the server was started outside an app checkout. Document the Cursor Plugin limitation honestly: the spawn does not bind the exact app, one server per user is shared across Cursor windows, and a project-scoped .cursor/mcp.json is unverified. Regenerate both host bundles from source. Refs #1038, #872
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
| prints the owning PID, project, and lock path. This is the same exclusion on every | ||
| host and it is not a stale-lock bug: a dead owner is reclaimed automatically on the | ||
| next start. |
There was a problem hiding this comment.
This section says the project-root lock provides the same exclusion on every host, but the Codex launcher explicitly starts the supervisor with --no-lock. Codex sessions therefore do not acquire this lock or produce this exit-11 conflict. Scope this guidance to Claude and Cursor, and correct the same universal claim in AGENTS.md; otherwise users and maintainers may diagnose Codex behavior using a lock that is intentionally disabled there.
There was a problem hiding this comment.
Already fixed on HEAD before this thread was re-checked.
a73746701b99ac73054be2f27b3f0f6d2bde1d72 scoped the troubleshooting lock copy (and the matching AGENTS.md / changeset wording) to Claude and Cursor, and names the Codex launcher --no-lock as the exception. Current head is 9f18bcf21649e4f0c7793a551e34880df3dca52d.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 203ffe78db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const oldPluginVersion = claudePlugin.version; | ||
| claudePlugin.version = newVersion; | ||
| claudePluginManifest.version = newVersion; | ||
| cursorPlugin.version = newVersion; |
There was a problem hiding this comment.
Add Cursor manifests to the version-sync guard
The Cursor versions are updated here, but scripts/sync-versions.sh was not extended to read, compare, or repair either the Cursor plugin manifest or marketplace entry. If either file drifts—for example during a manually prepared or resumed release—the dedicated CI/release “Version sync check” exits successfully, and scripts/sync-versions.sh --fix can report everything synchronized while Cursor still advertises a stale version. Add both Cursor files to that script's mismatch and fix paths.
AGENTS.md reference: AGENTS.md:L437-L439
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Already fixed on HEAD before this remaining-comments pass.
a73746701b99ac73054be2f27b3f0f6d2bde1d72 extended scripts/sync-versions.sh mismatch and --fix to include packages/claude-plugin/.cursor-plugin/plugin.json and .cursor-plugin/marketplace.json. The Version sync check already runs that script, so Cursor version drift now fails the same guard. Current head is 9f18bcf21649e4f0c7793a551e34880df3dca52d.
The process lock is acquired on Claude and Cursor spawns only; Codex still launches with --no-lock. Include Cursor plugin and marketplace manifests in the version-sync guard so that check cannot stay green while those files drift. Co-authored-by: Anton Lykhoyda <lykhoyda@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a73746701b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| Claude SessionStart hooks are not auto-loaded. Skills, commands, and agents | ||
| are the same files as Claude. |
There was a problem hiding this comment.
Adapt plugin-root paths before reusing Claude commands
When these workflows run in Cursor, the host supplies CURSOR_PLUGIN_ROOT (as this section and mcp.json assume), but the reused payload still executes package paths through CLAUDE_PLUGIN_ROOT only—for example, commands/setup.md:28 invokes the first onboarding helper that way, while commands/run-action.md:51 and several skills do the same. In a normal Cursor session where the Claude variable is unset, those paths resolve under /rn-dev-agent-core or /scripts, so setup and other advertised slash commands cannot find their bundled helpers. Add a host-neutral root fallback including CURSOR_PLUGIN_ROOT or ship Cursor-adapted command and skill copies before reusing these files.
AGENTS.md reference: AGENTS.md:L14-L18
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 9f18bcf21649e4f0c7793a551e34880df3dca52d.
Reused Claude commands/skills now resolve package helpers through the existing host-neutral fallback, with CURSOR_PLUGIN_ROOT as the second alternative after CLAUDE_PLUGIN_ROOT (then the Codex roots). That covers commands/setup.md, commands/run-action.md, and the other shared/Claude helper paths that previously expanded only CLAUDE_PLUGIN_ROOT. Recovery/pin copy and the session-doctor docs use the same expansion. Codex adapted workflows stay on <package-root> and do not gain CLAUDE_PLUGIN_ROOT. The process lock is unchanged.
Cursor sessions export CURSOR_PLUGIN_ROOT, not CLAUDE_PLUGIN_ROOT. Reuse the existing host-neutral plugin-root fallback in Claude commands, skills, recovery copy, and pin hints so those helpers resolve. Keep Codex adapted paths and the process lock unchanged. Co-authored-by: Anton Lykhoyda <lykhoyda@gmail.com>
Intent
Captain (2026-09-14): independent review of #1040, then "you can also fix the findings."
The review is done. Packaging is clean (Standards 0). The change skips the process lock for Cursor and home roots. That is a lock-policy change, not a faithful fix of #1038 (Spec 8, worst S1). Do not merge. Keep the Cursor plugin launch contract and rewrite the lock skip. Live draft head at review time: 96a7bfc.
The review report's findings, in substance (the captain's ask by reference): S1 (wrong, lock-sensitive) skipping the process lock for every Cursor host and hard-coding --no-lock on the Cursor Plugin mcp.json spawn undoes live same-root exclusion from GH #672 / PR #681; remove the Cursor-wide skip, drop --no-lock from the Cursor mcp.json, do not skip acquire for isCursorHost. S2 (wrong) WORKSPACE_FOLDER_PATHS alone must not be treated as Cursor; no false-positive host skip. S3 (wrong) the lock must not be skipped because the resolved root is the user home; Claude at home must still acquire. S4 (wrong, source identity) seeding CLAUDE_USER_CWD from the first workspace folder without chdir does not establish intended-app identity; do not invent home-as-app, do not chdir to the plugin cache. S5 (missing) the lock-conflict message must be host-neutral and must drop kill / rm lock / --no-lock as recommended recovery; the troubleshooting docs must not tell Cursor users the plugin spawn skips the lock. S6 (missing) document a project-scoped exact-app Cursor setup, or an honest limitation if Cursor cannot supply an app root. S7 (optional) marketplace ingest proof is not required on this PR. S8 (optional, scope) the first-class Cursor Plugin manifests, marketplace pointer, ${CURSOR_PLUGIN_ROOT} spawn, and empty Cursor hooks may stay once the S1 skip is gone. Keep exit 11 on a same-root conflict. Regenerated host bundles must match source after the lock rewrite. Codex's pre-existing --no-lock launcher is not proof the same bypass is safe for Cursor. No new successful-authority path. Device QA is out of this PR.
What Changed
.cursor-plugin/marketplace.jsonpointing atpackages/claude-plugin, a package-local.cursor-plugin/plugin.jsonwith empty hooks, and anmcp.jsonthat spawns the bundled supervisor via${CURSOR_PLUGIN_ROOT}with no--no-lockflag and no cwd override. The version sync script and the package-sync checker now cover these manifests.kill,rm <lock>, and--no-lockrecovery advice. Exit 11 on a same-root conflict is unchanged, and the unit and GH bug: stale-authority adoption deadlock - adopt_stale rejects its own advertised handle after supervisor death #672 integration tests assert the new wording. Host bundles are regenerated to match.claudeorcodexstarted in the app root.Risk Assessment
✅ Low: The only runtime change is a host-neutral conflict-message string with the lock acquire path, exit 11, and both host bundles verified identical to source; the rest is additive Cursor manifests, version-sync plumbing, sync-check assertions, and documentation that all conform to the stated intent.
Testing
Built the core and ran the lockfile unit tests and the GH-672 same-root takeover integration test, then manually launched the committed Cursor host bundle twice from one root using the exact mcp.json spawn command, once as a Cursor-flavoured project spawn and once from a home-directory root; in both cases the first spawn acquired the lock and the second exited 11 with the host-neutral conflict message and no kill/rm/--no-lock advice, while the owner kept running. Manifest sync checks and a full host-bundle regeneration with a clean git porcelain confirmed the Cursor manifests and both host bundles match source. No screenshot was captured because the change is a CLI/MCP lock-policy change; the docs-site pages were reviewed as diffs rather than rendered.
Evidence: Cursor-hosted spawn from a project root: owner acquires lock, second spawn refused with exit 11
mcp.json: node ${CURSOR_PLUGIN_ROOT}/rn-dev-agent-core/dist/supervisor.js <no extra args> env: CURSOR_TRACE_ID=demo WORKSPACE_FOLDER_PATHS=.../my-rn-app lock file: .../tmp/rn-dev-agent-cdp-501-6755fcd5.lock (pid 31366, projectRoot .../my-rn-app) contender exit code: 11 | Another rn-dev-agent MCP already owns this project root. | PID: 31366 | Project: .../my-rn-app | To resolve: | 1. Use the session that already owns this project root, OR | 2. Quit that session's editor window or MCP client. The lock is released on exit | and reclaimed automatically once the owning process is gone. owner still alive after refusal: yes; lock still present: yesEvidence: Spawn from the user home root: lock still acquired and enforced (no home-root skip)
spawn cwd: .../home/anton HOME=.../home/anton lock file: .../tmp/rn-dev-agent-cdp-501-45f11c30.lock (pid 75159, projectRoot .../home/anton) contender exit code: 11 | Another rn-dev-agent MCP already owns this project root. | Project: .../home/anton owner still alive after refusal: yes; lock still present: yesEvidence: GH-672 same-root lock takeover integration test output
Evidence: Demo script that derives the spawn command from packages/claude-plugin/mcp.json
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
packages/rn-dev-agent-core/src/lifecycle/lockfile.ts:457- Simplification: the home-keyed hint block in formatLockConflictMessage (thehomeKeyedcheck plus the injectedhomeparameter and the three extra message lines) is not required by the intent. S5 only requires a host-neutral message without kill / rm lock / --no-lock advice, and S6 asks for the limitation to be documented, which the Getting Started and Troubleshooting pages already do. The block adds a second definition of the home-root explanation that must be kept in sync with the docs. Recommend removing thehomeparameter and the conditional lines, keeping the message uniform for every root. Leaving it is defensible since it is host-neutral and never suggests bypassing the lock, so this is the author's call.packages/rn-dev-agent-core/src/lifecycle/lockfile.ts:446- The first two commits on the branch (2627372, 96a7bfc) introduce the Cursor-wide and home-root lock skip that the intent forbids, and the third commit (756247e) removes it. The reviewed end state is clean, but a non-squash merge would keep two bisectable commits where the same-root exclusion from GH bug: stale-authority adoption deadlock - adopt_stale rejects its own advertised handle after supervisor death #672 is off. Squash-merge or rebase so the forbidden behavior never lands on main as a reachable commit.🔧 Fix: drop home-keyed hint from lock conflict message
1 info still open:
AGENTS.md:303- The new process-lock bullet opens with "every host spawn acquires it" and then names the Codex launcher's--no-lockas the documented exception.packages/codex-plugin/bin/cdp-supervisor.js:50does pass--no-lock, so the Codex spawn does not acquire the lock and the opening clause is false as written. Reword to "the Claude and Cursor spawns acquire it" (or "every host spawn except the Codex launcher") so the bullet does not contradict itself.✅ **Test** - passed
✅ No issues found.
corepack yarn buildin packages/rn-dev-agent-core, thennode --test test/unit/lockfile.test.js(18 pass, includes the rewritten conflict-message assertions)node --test test/integration/gh-672-same-root-lock-takeover.test.ts(2 pass: live-owner refusal with exit 11 and new message; kill -9 owner reclaimed on restart)Manual:cursor-spawn-lock-demo.sh cursor-projectspawns the committed packages/claude-plugin bundle twice via the mcp.json command with CURSOR_TRACE_ID and WORKSPACE_FOLDER_PATHS set; owner writes the lock, contender exits 11 with the host-neutral message, owner and lock surviveManual:cursor-spawn-lock-demo.sh home-rootwith HOME and cwd set to a fake home; lock is still acquired (Project = home) and the second spawn still exits 11bash scripts/test/check-agent-package-sync.test.sh(ALL PASS) andbash scripts/check-agent-package-sync.sh(in sync): Cursor mcp.json must spawn the package-local supervisor with the lock on and no cwd override, plugin.json pins ./mcp.json and empty hooks, Cursor marketplace points at packages/claude-plugincorepack yarn workspace rn-dev-agent-core build:web+node scripts/build-host-runtimes.tsthengit status --porcelain(empty): Claude and Codex host bundles match source; both bundles carry the new message and neither carries the --no-lock advicegrep of packages/rn-dev-agent-core/src for isCursorHost / WORKSPACE_FOLDER / homedir lock keying: none present✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.
Greptile Summary
This PR adds Cursor Plugin packaging to the Claude host package, synchronizes its manifests through release tooling, documents Cursor’s home-keyed lock limitation, and replaces the lock-conflict text with host-neutral recovery guidance.
${CURSOR_PLUGIN_ROOT}without bypassing the process lock.Confidence Score: 4/5
The PR appears safe to merge after correcting the non-blocking documentation claim that Codex uses the same process lock as Claude and Cursor.
The Cursor manifests keep locking enabled, release tooling covers the new artifacts, and the runtime change only updates conflict wording; the remaining issue is inaccurate host-wide lock documentation because Codex intentionally launches with
--no-lock.Files Needing Attention: apps/docs-site/src/content/docs/troubleshooting.mdx, AGENTS.md
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Cursor marketplace] --> B[packages/claude-plugin] B --> C[.cursor-plugin/plugin.json] C --> D[mcp.json] D --> E[Bundled supervisor.js] E --> F[Resolve CLAUDE_USER_CWD or process.cwd] F --> G[Acquire project-root process lock] G -->|Available| H[Start MCP worker] G -->|Live owner exists| I[Exit 11 with host-neutral conflict message]Reviews (1): Last reviewed commit: "no-mistakes(document): docs: list Cursor..." | Re-trigger Greptile