Skip to content

fix(cursor): launch cursor-agent on Windows through its bundled node.exe - #29

Merged
freema merged 1 commit into
mainfrom
fix/windows-cursor-agent-resolution
Sep 18, 2026
Merged

freema merged 1 commit into
mainfrom
fix/windows-cursor-agent-resolution

Conversation

@freema

@freema freema commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Fixes #27. Thanks @vilnis for the precise two-layer diagnosis — both layers check out.

What was wrong

  1. resolveBin() probed with which, which Windows doesn't have, so it always threw cursor-agent not found on PATH.
  2. Finding the binary wouldn't have been enough. The official Windows installer (cursor.com/install?win32=true) ships no .exe: agent-cli-package.zip puts cursor-agent.cmd / cursor-agent.ps1 on PATH, and the shim runs versions\<version>\node.exe index.js. Since Node's CVE-2024-27980 fix, spawn() rejects .cmd files without a shell (EINVAL), so pointing CURSOR_AGENT_BIN at the shim didn't help either.

Verified against the current package (2026.09.15-d2fe57e): I read its cursor-agent.cmd and cursor-agent.ps1. The .ps1 accepts the YYYY.MM.DD-commit and YYYY.MM.DD-HH-MM-SS-commit directory names and picks the newest by date.

Fix

  • resolveBin() returns { command, args } instead of a string. On macOS and Linux that's { command: <which result>, args: [] }, so behaviour there is unchanged.
  • On win32 the launcher resolves the way cursor-agent.ps1 does: a node.exe next to the shim, otherwise the newest versions\<version>\ holding both node.exe and index.js. A half-extracted newest version is skipped instead of being fatal. Same-day builds are ordered by build time, which the .ps1 doesn't do.
  • The shim is located with %SystemRoot%\System32\where.exe $PATH:cursor-agent (then agent). Both where and spawn() search the current directory before PATH on Windows, so the absolute path plus the $PATH: pattern stops a repository from planting its own where.exe / cursor-agent.exe.
  • Fallback: %LOCALAPPDATA%\cursor-agent, for a Claude Code session whose PATH predates the install. The installer only updates PATH for new shells.
  • CURSOR_AGENT_BIN may point at the .cmd/.ps1 shim.
  • New runAgent(args, opts) prepends the launcher args. It replaces the resolveBin() + run(bin, …) pairs in authStatus, listModels (×2), listConfiguredMcps, listSessions and the --version check in setup.mjs; runHeadless spreads them into its spawn. setup displays the resolved launcher via describeBin().

The shim's CURSOR_INVOKED_AS isn't replicated on purpose. In the current index.js it only gates a one-time TTY tip ("You can start the Cursor CLI with agent"), which headless runs never show.

Tests

  • tests/windows-bin.test.mjs: version-dir ordering, install resolution (newest, incomplete version skipped, node.exe beside the shim, nothing installed) and shim/.exe path translation. These run on every platform.
  • The same file has a Windows-only end-to-end block against a real node.exe copied into the installer layout. It checks that spawning the .cmd shim fails with EINVAL (the original bug), that resolveBin() finds the shim via where.exe and launches it, the %LOCALAPPDATA% fallback, and CURSOR_AGENT_BIN pointing at the shim.
  • CI: a new windows-bin job on windows-latest runs just that file. The rest of the suite assumes POSIX paths (the stub binary is a shebang .mjs), so putting Windows into the main matrix is a separate piece of work.

Test plan

  • npm test: 180 passed, 4 skipped (the Windows-only block on macOS)
  • npm run lint, npm run typecheck
  • node scripts/setup.mjs -- --doctor on macOS against a real cursor-agent: binary, version and auth all ✓
  • windows-bin CI job green
  • @vilnis: /cursor:setup on Windows 11 with the native installer

🤖 Generated with Claude Code

…exe (#27)

resolveBin() probed with `which`, which Windows does not have, so
/cursor:setup and every command failed with 'cursor-agent not found on
PATH'. Finding the binary would not have been enough: the official
installer ships no .exe, only cursor-agent.cmd/.ps1 shims, and since the
CVE-2024-27980 fix Node refuses to spawn .cmd files without a shell.

On Windows the launcher is now resolved the way cursor-agent.ps1 does it:
the newest %LOCALAPPDATA%\cursor-agent\versions\<version>\node.exe
running index.js. The shim is located with where.exe (absolute path,
PATH-only pattern, so a repository cannot plant a binary in the working
directory), with the default install dir as a fallback for a PATH that
predates the install. CURSOR_AGENT_BIN may point at the shim.

resolveBin() now returns { command, args }; runAgent() prepends the
launcher args at every call site. macOS and Linux behaviour is unchanged.

A windows-latest CI job runs the new launch tests against a real
node.exe in the installer layout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@freema freema left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeForge Review

Verdict: approve | Score: 9/10

Solid, well-tested fix for launching cursor-agent on Windows. resolveBin() now returns {command, args} to route through node.exe + index.js (avoiding the .cmd/.ps1 spawn EINVAL), all call sites and the setup doctor output were updated consistently, docs were updated, and a new Windows CI job exercises the real launch path end-to-end. Verified locally: full vitest suite (minus one pre-existing, unrelated flaky process-group test), tsc, and eslint all pass.


Reviewed by CodeForge

const names = entries.filter((e) => e.isDirectory()).map((e) => e.name);
for (const name of sortVersionDirs(names)) dirs.push(join(root, 'versions', name));
} catch {
// No versions directory — only a node.exe beside the shim can work.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MINOR] resolveWindowsInstall() swallows every readdirSync error (not just ENOENT for a missing versions directory) with a bare catch {}, so a permissions error or other unexpected filesystem failure is silently treated the same as "no versions directory" and only the shim-adjacent node.exe is tried.

Suggestion: Narrow the catch to err.code === 'ENOENT' and rethrow (or log) other errors so unexpected failures aren't masked.

/**
* Since Node's CVE-2024-27980 fix, spawn() rejects `.cmd`/`.bat` files unless
* it goes through a shell (EINVAL), so a shim path is translated into the
* install it launches. An `.exe` is spawnable as is; anything else is not a

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION] findOnWindows() hardcodes where.exe's location as ${SystemRoot}\System32\where.exe; on an unusual environment where SystemRoot is unset and not C:\Windows (rare but possible in some containerized Windows setups), the lookup would fail before ever reaching the LOCALAPPDATA fallback.

Suggestion: Not worth changing for this fix — just noting as a low-probability edge case; the LOCALAPPDATA fallback still covers most real installs.

@vilnis

vilnis commented Sep 18, 2026

Copy link
Copy Markdown

Tested on Windows 11 (x64), Node v24.13.0, cursor-agent from the native installer.

Ran node plugins/cursor/scripts/setup.mjs -- --doctor from the PR branch (2eb8bc5):

  • ✓ cursor-agent binary — %LOCALAPPDATA%\cursor-agent\versions\2026.08.11-e8db854\node.exe + index.js
  • ✓ cursor-agent version — 2026.08.11-e8db854
  • ✓ cursor-agent auth — logged in
  • All checks passed.

@freema
freema merged commit 134eacc into main Sep 18, 2026
7 checks passed
@freema
freema deleted the fix/windows-cursor-agent-resolution branch September 18, 2026 17:01
freema added a commit that referenced this pull request Sep 18, 2026
Cut the 0.6.2 changelog section covering #29 (closes #27) and #28 and
bump the version in package.json, package-lock.json, and plugin.json.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: /cursor:setup fails with 'cursor-agent not found on PATH' — 'which' probe + un-spawnable .cmd shims

2 participants