Skip to content

fix: spawn cursor-agent with shell mode on Windows (model discovery falls back to static list) - #128

Merged
Nomadcxx merged 1 commit into
Nomadcxx:mainfrom
Cyctes-X:fix/windows-cursor-agent-shell
Aug 27, 2026
Merged

fix: spawn cursor-agent with shell mode on Windows (model discovery falls back to static list)#128
Nomadcxx merged 1 commit into
Nomadcxx:mainfrom
Cyctes-X:fix/windows-cursor-agent-shell

Conversation

@Cyctes-X

@Cyctes-X Cyctes-X commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Problem

On Windows, cursor-agent is installed as a .cmd shim
(%LOCALAPPDATA%\cursor-agent\cursor-agent.cmd). Node's execFileSync cannot
execute a bare .cmd file and throws EINVAL:

spawnSync C:\Users\...\AppData\Local\cursor-agent\cursor-agent.cmd EINVAL

Because of this, every model-discovery path silently fell back to the static
20-model list
:

  • open-cursor install / open-cursor sync-models -> "Models synced: 20"
  • plugin startup auto-refresh -> never adds newly released Cursor models
  • open-cursor doctor -> misreports cursor-agent: not found / not logged in

Users on Windows therefore never see models that exist in their Cursor
subscription (e.g. the cursor-grok-4.5-* / cursor-grok-4.6-* families), even
though cursor-agent models lists 200+ models from a normal shell.

Root cause

src/utils/binary.ts documents the existing contract clearly:

callers pair this resolver with shell: process.platform === "win32" at every
Node spawn site

plugin.ts follows it (spawnSync(..., shell: process.platform === "win32")),
but cli/model-discovery.ts and the doctor checks in cli/opencode-cursor.ts
did not.

Fix

  • src/cli/model-discovery.ts:
    • discoverModelsFromCursorAgent() now spawns with shell: platform === "win32"
    • added an injectable deps object (platform / execFileSync /
      resolveBinary) so the spawn behaviour is unit-testable
  • src/cli/opencode-cursor.ts: checkCursorAgent() and
    checkCursorAgentLogin() use the same shell: process.platform === "win32"
    so open-cursor doctor works on Windows

Testing

  • New unit tests in tests/unit/cli/model-discovery.test.ts assert:
    • win32 -> shell: true and the resolved .cmd binary is used
    • non-win32 -> shell: false and killSignal: SIGTERM (unchanged behaviour)
  • bun test tests/unit/cli/model-discovery.test.ts -> 4 pass
  • Verified on Windows x64: open-cursor sync-models --dry-run now
    discovers 204 models (previously 20 via fallback) and writes
    cursor-grok-4.5-high / cursor-grok-4.6-* into opencode.json; latest
    open-cursor doctor reports cursor-agent installed/logged in.

Notes

  • dist/ is git-ignored and rebuilt at publish time, so no build artifacts are
    committed.
  • The only unrelated failure in the Windows test suite is a pre-existing
    symlinkSync EPERM in tests/unit/cli/opencode-cursor.test.ts (Windows
    requires Developer Mode / admin for symlinks) -> unrelated to this change.

On Windows, cursor-agent is a .cmd shim. Node's execFileSync cannot execute a bare .cmd file and fails with EINVAL, so model discovery silently fell back to a static 20-model list (missing newly released Cursor models) and 'open-cursor doctor' misreported cursor-agent as not installed.

- discoverModelsFromCursorAgent(): use shell mode on win32 via an injectable deps object (platform/execFileSync/resolveBinary), matching the existing binary.ts contract that Windows callers pair the resolver with shell mode

- checkCursorAgent()/checkCursorAgentLogin(): same win32 shell mode so doctor checks correctly resolve cursor-agent

- add unit tests asserting shell mode is used on win32 and not used elsewhere
@Nomadcxx

Copy link
Copy Markdown
Owner

Thanks for tracking this down. You found the right root cause and CI is green. Every discovery path routes through discoverModelsFromCursorAgent, so the one fix covers sync-models, install, and startup refresh.

One gap: binary.ts asks callers to pair shell: true with formatShellCommandForPlatform(), and the three new spawn sites only do the first half. On a Windows profile with a space in the username, cmd.exe splits the unquoted .cmd path and discovery drops back to the static 20-model list. Same thing came up in #73, fixed in 34339dc.

I'll merge this as the foundation and wrap the three sites in a maintainer follow-up, so you don't need to touch anything here.

Thanks again.

cheers, RAMA

@Nomadcxx
Nomadcxx merged commit de5ff3c into Nomadcxx:main Aug 27, 2026
2 checks passed
Nomadcxx added a commit that referenced this pull request Aug 27, 2026
#128 added shell mode on win32 to the three cli spawn sites but passed
the resolved binary through unquoted. Under shell mode Node joins file
and args into one cmd.exe string without quoting the file, so a profile
path containing a space splits at the space and discovery falls back to
the static model list, which is the failure #128 set out to fix.

Wrap the path in formatShellCommandForPlatform() at all three sites,
matching the contract in utils/binary.ts and the existing spawn sites in
plugin.ts and client/simple.ts.

The win32 test fixture had no space in it, so it passed with or without
the quoting. Swap in a spaced profile path and assert the quoted command.
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.

2 participants