Skip to content

feat(agents): add native Cursor CLI support - #203

Merged
kunchenguid merged 7 commits into
mainfrom
fm/gnhf-cursor-cli-pr187-validate-s1
Aug 13, 2026
Merged

feat(agents): add native Cursor CLI support#203
kunchenguid merged 7 commits into
mainfrom
fm/gnhf-cursor-cli-pr187-validate-s1

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Adopt the open community contribution #187 (a native Cursor CLI agent, --agent cursor) rather than reimplementing Cursor support ourselves, rebased onto current origin/main, and add the fixes found by validating it against a real signed-in cursor-agent.

Background: a scout validated PR 187 end to end against the live signed-in Cursor CLI (2026.08.11-e8db854) on this machine - the contributor had only demonstrated it with a mock binary. The adapter's wire-protocol handling proved correct against the real CLI (stdin prompt, assistant.message.content[].text, camelCase usage keys, result/success terminal event, real token counts), so the decision was to adopt it, not rewrite it.

Deliberate decisions in this change:

  • The contributor's original commit (Evan Ram, 'feat: add Cursor CLI agent support') is preserved verbatim with its authorship; all maintainer changes sit in a separate follow-up commit on top. Keeping contributor credit is a requirement - do not squash, rewrite, or re-author that commit.
  • Binary resolution now tries 'cursor-agent' first and falls back to the generic 'agent'. The PR hardcoded 'agent'. Cursor's installer currently symlinks both, but 'agent' alone collides with any unrelated tool of that name on PATH, and installs predating the alias expose only 'cursor-agent'. Reproduced live: a PATH carrying only 'cursor-agent' failed with 'spawn agent ENOENT'.
  • Signed-out Cursor exits now raise PermanentAgentError instead of a plain retryable Error, so an expired session aborts on the first iteration rather than burning the whole consecutive-failure budget. Verified live: with --max-iterations 5 it now aborts after 1.
  • The Windows CI failure on the PR was 'spawn EFTYPE' in the linger integration test. That is a test-fixture bug, not a product bug: the test spawned a bare .mjs path, which is not directly executable on Windows. Fixed by spawning through process.execPath. The product path was never affected - real .cmd wrappers and full-path binaries are already handled by shouldUseWindowsShell.
  • The PR's private parseCursorOutput was logic-identical to the shared parseAgentOutput helper that landed in main after the PR's base commit, so it was deleted in favor of the shared helper, per the guidance in AGENTS.md.
  • Doc updates fell out of the binary rename: README Agents row and agentPathOverride example, config.ts and bootstrap-config.golden.yml comments, and skills/gnhf/SKILL.md's process scan (its pgrep pattern used the bare word 'agent', which under pgrep -f matches nearly any process with 'agent' anywhere in its command line). Also corrected the example model id composer-2 to composer-2.5.

Deliberately NOT in scope, please do not flag these as omissions:

  • Orphaned 'cursor-agent worker-server' processes that survive a run. Investigated and confirmed to be upstream Cursor CLI behavior, not caused by gnhf: a bare cursor-agent invocation with no gnhf involved leaks one too, and they are pooled (a 3-iteration run added +1, not +3). Intentionally left alone.
  • The bounded stdout-tail error machinery that claude.ts has. Cursor reports its auth error on stderr with empty stdout, so the simple stderr-based classification is sufficient here; adding claude's stdout-tail extraction would be unjustified machinery.
  • Merging. This must land as a green PR only; do not merge it.

What Changed

  • Add --agent cursor with stream-json parsing, structured output validation, token usage tracking, permission defaults, and graceful process shutdown.
  • Prefer the cursor-agent binary with an agent fallback, and treat signed-out sessions as permanent failures.
  • Document Cursor configuration and cover the integration with unit, cross-platform, and end-to-end tests.

Risk Assessment

✅ Low: The Cursor adapter is well-bounded, preserves contributor history, satisfies the stated resolution and security requirements, and includes behavior-focused coverage for the introduced paths.

Testing

Built the CLI, passed the focused Cursor adapter, lifecycle, factory, configuration, and CLI E2E tests, then verified the end-user flow against the real signed-in Cursor CLI and confirmed signed-out authentication aborts after one invocation. Contributor authorship was also preserved, and generated build output was removed afterward.

Evidence: Live signed-in Cursor CLI end-to-end transcript
Cursor binary: 2026.08.11-e8db854
Generic agent in isolated PATH: NOT FOUND

gnhf end-user result:
  cursor ran for 23s before: max iterations reached (1)
  iterations: 1 total, 1 good, 0 failed
  tokens: 14K in, 1K out
  branch diff: 1 commit, +1, -0

Created file:
  native cursor-agent worked
Latest commit:
  58b3317 gnhf 1: Created live-cursor-proof.txt with the exact required content.
Working tree status: clean
Evidence: Signed-out Cursor abort transcript
Requested max iterations: 5
Cursor process invocations: 1
gnhf exit code: 0

                                                                          ·    
                   00:00:00  ·  0 in  ·  0 out  ·  0 commits                ·  
 ⋆                                                                             
                                                                              °
     ·                            working...                              ·    
 ⋆                                                                             
                                                                               
                                                                               
  ⋆  ⋆                                                                      ·  
⋆                                     🌔                                       
                     [ctrl+c to stop, gnhf again to resume]                     
                                                                                 🌕
  gnhf: Run log: /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/no-mistakes-evidence/01KZYM81DBNR9SHDRVH4CER68W/signed-out-repo/.gnhf/runs/this-should-abort-im-90007a/gnhf.log


╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ × gnhf stopped                                                                                                  │
│   cursor ran for 0s before: cursor exited with code 1: Authentication required. Please run cursor-agent login.
 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

  iterations      1 total       0 good       0 failed
  tokens          0 in          0 out        
  branch diff     0 commits     +0           -0
  files           0 added       0 updated    0 deleted

  notes           /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/no-mistakes-evidence/01KZYM81DBNR9SHDRVH4CER68W/signed-out-repo/.gnhf/runs/this-should-abort-im-90007a/notes.md
  debug log       /private/var/folders/0k/bf8mwt2n5qddzk24r20gfk0c0000gn/T/no-mistakes-evidence/01KZYM81DBNR9SHDRVH4CER68W/signed-out-repo/.gnhf/runs/this-should-abort-im-90007a/gnhf.log

  next steps      git log --oneline 3bb0f866677f..HEAD
                  git diff --stat 3bb0f866677f..HEAD
                  gh pr create

  too much        git push no-mistakes:
  to review?      https://github.com/kunchenguid/no-mistakes

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (3) ✅
  • 🚨 src/core/agents/cursor.ts:26 - The required fallback "tries 'cursor-agent' first and falls back to the generic 'agent'" fails on supported Unix environments without the non-standard which utility: both lookups throw, line 37 selects cursor-agent, and an installation exposing only agent then fails with ENOENT. Resolve candidates directly from PATH at this boundary or retry agent when spawning cursor-agent fails with ENOENT.
  • ⚠️ src/core/config.ts:195 - Cursor supports --api-key, but its reserved-argument list permits both --api-key <secret> and --api-key=<secret>, placing credentials in process argv and persistent config. Reserve both forms and direct users to CURSOR_API_KEY, consistent with the existing Pi protection.

🔧 Fix: Block Cursor API keys in agent arguments
1 error still open:

  • 🚨 src/core/agents/cursor.ts:26 - The required behavior that binary resolution "tries 'cursor-agent' first and falls back to the generic 'agent'" still depends on the non-standard Unix which utility. If which is unavailable, both lookups fail, line 37 selects cursor-agent, and an installation exposing only agent fails with ENOENT. Retry agent when spawning cursor-agent fails with ENOENT, or resolve candidates directly from PATH without an external utility.

🔧 Fix: Resolve Cursor binaries directly from PATH
1 warning still open:

  • ⚠️ src/core/agents/cursor.ts:40 - On Unix, PATH resolution treats any regular cursor-agent file as runnable without checking execute permission. A non-executable cursor-agent in PATH therefore prevents fallback to an executable generic agent, and spawn fails with EACCES. Check executability with accessSync(..., X_OK) at this resolution boundary and cover that sequence.

🔧 Fix: Skip non-executable Cursor binaries during PATH resolution
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • git log --format='%H%x09%an%x09%s' --reverse 64329d1d65d382e34ed6f78caa9bf4bd5371e0dc..c51660d263251409d394ade70d606739e4ed1d1c verified Evan Ram's original commit remains separately authored.
  • pnpm run build && pnpm exec vitest run src/core/agents/cursor.test.ts src/core/agents/cursor.linger.integration.test.ts src/core/agents/factory.test.ts src/core/config.test.ts e2e/e2e-cursor.test.ts
  • cursor-agent status and cursor-agent --version confirmed the live CLI was signed in and running version 2026.08.11-e8db854.
  • Ran built gnhf against the real signed-in cursor-agent with no generic agent on the isolated PATH; it created and committed the requested file and reported real token usage.
  • Ran built gnhf with --max-iterations 5 against a signed-out Cursor fixture; the Cursor process was invoked exactly once before the run aborted.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

ioqr and others added 7 commits August 13, 2026 14:39
Wire `--agent cursor` to the Cursor `agent` binary with stream-json output, schema prompt recovery, force/trust/approve-mcps defaults, and post-success process shutdown for non-interactive runs.
…s test

Follow-ups on top of the Cursor CLI adapter, found by driving it end to end
against a live signed-in cursor-agent rather than a mock binary.

Resolve the binary as `cursor-agent` first and fall back to the generic
`agent` name. Cursor's installer currently symlinks both, but `agent` alone
is ambiguous with any unrelated tool of that name on PATH, and installs
predating the alias expose only `cursor-agent`. Driving gnhf with a PATH
carrying just `cursor-agent` failed with `spawn agent ENOENT`.

Raise PermanentAgentError when the CLI reports it is signed out. That exit
was retryable, so an expired session burned the whole consecutive-failure
budget before the run gave up; it now aborts on the first iteration.

Spawn the linger integration test's script through process.execPath. A bare
`.mjs` path is not directly executable on Windows, which failed CI with
`spawn EFTYPE`. The product path was never affected: real `.cmd` wrappers
and full-path binaries are already handled.

Use the shared parseAgentOutput helper instead of a private copy, per the
guidance in AGENTS.md, and point the docs at the `cursor-agent` name.
@kunchenguid
kunchenguid merged commit 06ae1cf into main Aug 13, 2026
5 checks passed
@kunchenguid
kunchenguid deleted the fm/gnhf-cursor-cli-pr187-validate-s1 branch August 13, 2026 23:10
@kunchenguid

Copy link
Copy Markdown
Owner Author

Merged and released in 0.1.44. Thanks for the Cursor CLI adapter.

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