Skip to content

feat(cli): add experimental cursor-sdk harness#64

Draft
josemontesdeoca wants to merge 4 commits into
mainfrom
feat/cursor-sdk-harness
Draft

feat(cli): add experimental cursor-sdk harness#64
josemontesdeoca wants to merge 4 commits into
mainfrom
feat/cursor-sdk-harness

Conversation

@josemontesdeoca
Copy link
Copy Markdown
Member

@josemontesdeoca josemontesdeoca commented May 4, 2026

Adds cursor-sdk as a third first-party SDK harness in tools/cli, wrapping
@cursor/sdk in local mode behind the existing Harness contract. The
harness ships explicitly as experimental: the default composer-2 model
is tuned for code edits and tool use rather than general instruction-following,
so multi-stage Prose programs may run plan-only. Production runs should still
prefer codex-sdk or claude-sdk.

The change layers cleanly on the existing harness shape — no edits to
canonicalPrompt(), runForwardedProseCommand(), or the two existing SDK
harnesses — and stays within the bounds of the public Harness interface.

What ships

  • New harness tools/cli/src/harnesses/cursor-sdk.ts:

    • Dynamic await import("@cursor/sdk") with an injectable factory (mirrors
      the Codex pattern; tests never touch the real SDK).
    • CURSOR_API_KEY for auth; CURSOR_MODEL selects the local model
      (default composer-2).
    • Local agent created with local: { cwd, settingSources: ["project", "user"] }
      so Cursor auto-loads the OpenProse skill.
    • Streams assistant text to stdout; routes tool/status errors to stderr.
    • Exit codes (0 / 1 / 143) and SIGINT/SIGTERM semantics match
      codex-sdk and claude-sdk — abort cancels the active run and disposes
      the agent in finally.
  • Registry / typing (tools/cli/src/harnesses/{types,index}.ts):

    • "cursor-sdk" added to HarnessName and HARNESS_NAMES.
    • Cursor SDK types re-exported through types.ts so cursor-sdk.ts is the
      only file importing from @cursor/sdk.
    • createHarness("cursor-sdk") and HarnessSelectionOptions.cursorSdk
      wired through the factory.
  • Skill discovery (tools/cli/src/skills/open-prose.ts):

    • New "cursor" SkillAgent value.
    • projectSkillDirectory refactored into projectSkillDirectories so an
      agent can advertise multiple project skill roots; Cursor checks
      .cursor/skills/ first and .agents/skills/ second for cross-client
      interop.
    • User-scope discovery checks ~/.cursor/skills/, ~/.agents/skills/,
      ~/.claude/skills/, and ~/.codex/skills/ per Cursor's documented
      fallback chain.
    • skillAgentsForHarness("cursor-sdk") → ["cursor"]; auto-installer runs
      with --agent cursor via the existing skills@1.5.3 flow.
  • Docs and metadata:

    • tools/cli/README.md adds the provider table row, an example invocation,
      a single Harness Details paragraph with a tight Known-limits note, and
      CURSOR_API_KEY / CURSOR_MODEL references.
    • tools/cli/POST_RELEASE_PLAYTEST.md adds Cursor playtest scenarios
      (read-and-explain Prose programs, missing-key path).
    • tools/cli/package.json pins @cursor/sdk: ^1.0.12, refreshes the
      description, and adds the cursor keyword.
  • Tests (tools/cli/tests/{harnesses/harnesses,skills/open-prose}.test.ts):

    • Registry: HARNESS_NAMES, resolveHarnessName, and createHarness
      coverage for cursor-sdk.
    • Harness: agent-options shape (apiKey, model.id, local.cwd,
      settingSources), CURSOR_MODEL override, missing-key throws before the
      factory runs, assistant-text → stdout, tool-call/status error → stderr,
      run.wait() error mapping, abort mid-stream returns 143, dispose on
      stream throw.
    • Skill discovery: skillAgentsForHarness mapping, .cursor/skills first
      discovery, four user-scope candidates, ancestor walk across both project
      subdirs, install command targets --agent cursor.

@josemontesdeoca josemontesdeoca requested a review from irl-dan May 4, 2026 23:58
@josemontesdeoca josemontesdeoca force-pushed the feat/cursor-sdk-harness branch 5 times, most recently from 4a4e5d2 to 2f9a1d5 Compare May 5, 2026 17:02
@josemontesdeoca josemontesdeoca marked this pull request as draft May 5, 2026 17:03
Wire Cursor as a third first-party SDK harness alongside codex-sdk and
claude-sdk. The harness wraps @cursor/sdk@^1.0.12 in local mode (cwd
plus settingSources: ["project", "user"]), forwards the canonical
OpenProse prompt, streams assistant text to stdout, surfaces failed
tool_call and status ERROR/EXPIRED events to stderr, and converges on
the same 0/1/143 exit-code semantics as the existing SDK harnesses.

Authentication uses CURSOR_API_KEY; CURSOR_MODEL overrides the local
model id (default composer-2). Skill discovery is intentionally a
no-op for cursor-sdk here; the "cursor" SkillAgent and skill-path
wiring land in a separate change.
Give the cursor-sdk harness first-class participation in skill
discovery. Add "cursor" as a SkillAgent value, replace the prior
no-op so skillAgentsForHarness("cursor-sdk") now returns ["cursor"],
and refactor project skill resolution to walk every supported
subdirectory at every ancestor — cursor checks both .cursor/skills
and .agents/skills per ancestor, and walks ~/.cursor, ~/.agents,
~/.claude, and ~/.codex as user/provider-user scope.

The auto-installer requires no signature change: passing --agent
cursor to skills@1.5.3 already routes to the right install path. In
practice skills@1.5.3 installs to ~/.agents/skills/open-prose for
cursor (the cross-client interop path), which the new ordering picks
up natively — the harness sees the skill regardless of which path
the installer chose.

Tests cover the harness-to-agent mapping, .cursor/skills + .agents/skills
project discovery at every ancestor, all four user-scope candidates,
~/.codex as a provider-user candidate that's exclusive to cursor (not
claude-code), and the install command's --agent cursor flag.
…ness gap

Tag cursor-sdk as an experimental harness. Cursor positions composer-2
as a coding-tuned model rather than a general-purpose instruction-
follower; in practice the harness grounds correctly in OpenProse
SKILL.md vocabulary but resists strict return-token compliance and
runs plan-only on multi-stage execution programs. Read-and-explain
programs (lint, inspector, status, diagnose) remain reliable.

Surface the limitation in three places:

- Runtime stderr warning in cursor-sdk.ts on every run, suppressible
  via PROSE_SUPPRESS_EXPERIMENTAL_WARNINGS=1.
- README provider table tags the row "experimental"; Harness Details
  carries a Known-limits note plus a CURSOR_MODEL passthrough hint
  for stricter instruction-following.
- POST_RELEASE_PLAYTEST scenarios scoped to read-and-explain programs.

Deliberately do NOT add cursor-sdk to the smoke matrix or the CI
workflow: composer-2 does not pass the smoke harness's instruction-
compliance assertion, and a permanently-red CI signal would mask
legitimate failures on codex-sdk and claude-sdk. The smoke script
gains a comment block explaining the omission so future maintainers
do not "fix" it.

package.json description acknowledges experimental Cursor support.
The cursor-sdk README additions had four overlapping problems: the
experimental framing was repeated across the table row, Harness
Details bullet, Caveat paragraph, and a separate env-var paragraph;
the CURSOR_MODEL=claude-4.6-sonnet-thinking hint conflated harness
contract with model fitness and put speculative recommendations in
shipped docs; the per-run stderr warning was noise any serious user
would silence on second run; and the explicit exit-code / SIGINT
contract was missing.

Changes:

- Collapse the cursor-sdk Harness Details bullet to a single
  paragraph plus a tight Known-limits note. Add an explicit
  statement that exit codes (0/1/143) and SIGINT/SIGTERM semantics
  match the other SDK harnesses, and that Cloud / PR automation /
  models.list are not exposed.
- Trim the Provider Selection table Notes cell to one line.
- Remove the redundant standalone Cursor env-var paragraph; the
  facts now live in one place.
- Drop the CURSOR_MODEL=claude-4.6-sonnet-thinking name-drop and
  the model-passthrough probe scenario from POST_RELEASE_PLAYTEST.
  Speculative recommendations don't belong in user-facing docs.
- Remove the runtime stderr warning in cursor-sdk.ts and the
  PROSE_SUPPRESS_EXPERIMENTAL_WARNINGS env var. The README and the
  experimental tag are sufficient signal; a per-run nag adds noise
  without changing behavior. Tests no longer need the suppression
  env entry.
@josemontesdeoca josemontesdeoca force-pushed the feat/cursor-sdk-harness branch from 2f9a1d5 to 17a60e5 Compare May 6, 2026 01:33
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.

1 participant