@stablekernel/opencode-cursor 0.1.0-rc.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,10 +4,30 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- - `0.1.0-rc.1`first pre-release of the 0.1.0 surface below, published to the
8
- npm `next` dist-tag for validation ahead of the stable `0.1.0`.
7
+ ## [0.2.0]2026-06-11
9
8
 
10
- ## [0.1.0] unreleased
9
+ - **More Cursor tools map onto opencode's native tool renderers (blocks mode).**
10
+ Following the `edit` → diff-viewer mapping, Cursor's `shell`, `read`, `write`,
11
+ `glob`, `grep`, `ls`, `updateTodos`, and `task` tool activity is now surfaced
12
+ under opencode's registered `bash`, `read`, `write`, `glob`, `grep`, `list`,
13
+ `todowrite`, and `task` tools, and Cursor's web search (which runs as an MCP
14
+ tool) maps onto opencode's `websearch` renderer — so opencode renders its
15
+ native UI (shell console, file viewer, todo checklist, subagent card, search
16
+ results, …) instead of generic `cursor_*` blocks. Cursor's arg shape is
17
+ translated to opencode's (e.g. `path` → `filePath`, `globPattern` → `pattern`,
18
+ `fileText` → `content`); calls stay provider-executed (display-only, never
19
+ re-run on disk).
20
+ - **Cleaner fallback blocks for tools without an opencode counterpart.**
21
+ `readLints` and `delete` now render as formatted `cursor_*` blocks (a
22
+ diagnostics list / a one-line confirmation) instead of raw JSON, and every MCP
23
+ tool's `content` array is flattened to readable text. Anything else — or a
24
+ result with an unexpected shape — still falls back to a safe `cursor_*` block
25
+ with the raw payload.
26
+
27
+ ## [0.1.0] — 2026-06-10
28
+
29
+ > Pre-releases: `0.1.0-rc.1` and `0.1.0-rc.2` were published to the npm `next`
30
+ > dist-tag for validation ahead of this stable release.
11
31
 
12
32
  Initial public release. A complete opencode integration for Cursor built on the
13
33
  official `@cursor/sdk`: a streaming chat provider, an auth/config/model plugin,
@@ -21,17 +41,24 @@ and a permission-gated delegation tool surface.
21
41
  tool activity, usage). Implements both `doStream()` and `doGenerate()`.
22
42
  - **Per-request controls** via `providerOptions.cursor` — `mode` (agent/plan),
23
43
  `params`, and `thinking` level; works with opencode's model variant picker.
24
- - **Model variants** auto-generated from `Cursor.models.list` parameters: a
25
- `plan` variant plus one per reasoning level a model advertises.
44
+ - **Model variants** auto-generated from `Cursor.models.list` parameters: one
45
+ per reasoning/effort level a model advertises (boolean params collapse to a
46
+ single on-variant). opencode's plan agent maps to Cursor plan mode.
26
47
  - **Session reuse** (`session: true`) — keeps one Cursor agent per opencode
27
48
  session via `Agent.resume()` across turns, with automatic fallback to a fresh
28
49
  agent. A run wedged by a crashed/duplicate process is recovered by retrying
29
50
  the send once with the SDK's `local.force` escape hatch.
30
- - **`toolDisplay` provider option** (`"reasoning"` default | `"blocks"`):
31
- - `"reasoning"` renders Cursor's internal tool activity (including the real
32
- MCP tool name) as concise `[tool] …` reasoning lines. Always safe tool
33
- calls never cross opencode's tool-execution boundary.
34
- - `"blocks"` emits structured, provider-executed **dynamic** `tool-call` /
51
+ - **Native diff viewer for Cursor edits (blocks mode).** A Cursor `edit` tool
52
+ call is now surfaced under opencode's registered `edit` tool with its real
53
+ unified diff in `metadata.diff`, so opencode renders its built-in diff viewer
54
+ instead of a generic block. The required `oldString`/`newString` (which Cursor
55
+ does not expose) are reconstructed from the diff; the call is provider-executed
56
+ so they are never applied to disk. Any edit without a usable diff (errors,
57
+ unexpected shapes, or a host without a registered `edit` tool) falls back to a
58
+ safe `cursor_edit` block. Other Cursor tools (shell/read/mcp/…) remain
59
+ prefixed `cursor_*` blocks.
60
+ - **`toolDisplay` provider option** (`"blocks"` default | `"reasoning"`):
61
+ - `"blocks"` (default) emits structured, provider-executed **dynamic** `tool-call` /
35
62
  `tool-result` parts so opencode renders native tool blocks. Names are
36
63
  `cursor_`-prefixed and sanitized (`shell` → `cursor_shell`,
37
64
  `serena/find_symbol` → `cursor_serena_find_symbol`) so they can't collide
@@ -42,6 +69,10 @@ and a permission-gated delegation tool surface.
42
69
  errored/cancelled mid-tool) is closed with a synthetic error result so the
43
70
  block never dangles as "Tool execution aborted", and a run that ends with
44
71
  status `error` surfaces the failure instead of finishing silently.
72
+ - `"reasoning"` renders Cursor's internal tool activity (including the real
73
+ MCP tool name) as concise `[tool] …` reasoning lines. Always safe — tool
74
+ calls never cross opencode's tool-execution boundary; the fallback for
75
+ older/non-V3 hosts (`provider.cursor.options.toolDisplay: "reasoning"`).
45
76
 
46
77
  ### Node sidecar (Bun compatibility)
47
78
 
@@ -56,7 +87,8 @@ and a permission-gated delegation tool surface.
56
87
 
57
88
  ### Plugin
58
89
 
59
- - **opencode plugin** (`@stablekernel/opencode-cursor/plugin`): auth hook (API-key login;
90
+ - **opencode plugin** (`@stablekernel/opencode-cursor`, resolved via the package's
91
+ `./server` export): auth hook (API-key login;
60
92
  the key is validated on first use rather than at login), config hook
61
93
  (auto-injects `provider.cursor`),
62
94
  `provider.models()` (live catalog via `Cursor.models.list`), and the
package/README.md CHANGED
@@ -12,7 +12,8 @@ It uses your Cursor API key to:
12
12
  - register a `cursor` provider in opencode,
13
13
  - **list the models available to your account** (live, via `Cursor.models.list()`), and
14
14
  - run chats through Cursor's local agent runtime (`Agent.create` / `agent.send`), streaming
15
- text and reasoning back into opencode (Cursor's own tool activity is surfaced as reasoning).
15
+ text and reasoning back into opencode (Cursor's own tool activity is surfaced as structured tool
16
+ blocks by default; see [Tool display](#tool-display)).
16
17
 
17
18
  This plugin registers Cursor as a **native opencode provider**: its models appear in
18
19
  `opencode models` and the model picker, and you talk to a Cursor model *directly* — with live model
@@ -62,6 +63,25 @@ login. The `provider.models()` hook refreshes the catalog live once you're authe
62
63
 
63
64
  ## Install
64
65
 
66
+ ### Quick install (one line)
67
+
68
+ ```bash
69
+ curl -fsSL https://raw.githubusercontent.com/stablekernel/opencode-cursor/main/install.sh | bash
70
+ ```
71
+
72
+ The script registers the plugin in your **global** `opencode.json`
73
+ (`~/.config/opencode/opencode.json`), checks for Node.js 22+ on your `PATH`, and offers to set
74
+ `CURSOR_API_KEY`. Flags:
75
+
76
+ - `--project` — write `./opencode.json` in the current directory instead of the global config.
77
+ - `--yes` / `-y` — non-interactive; skip all prompts.
78
+
79
+ It backs up an existing config before editing, is safe to re-run (idempotent), and uses `jq` when
80
+ available (falling back to Node). You can review it first:
81
+ [`install.sh`](./install.sh).
82
+
83
+ ### Manual install
84
+
65
85
  ```bash
66
86
  npm install @stablekernel/opencode-cursor
67
87
  ```
@@ -135,7 +155,7 @@ This plugin also registers two **delegation tools** that complement the provider
135
155
  | `session` | `false` | Reuse one Cursor agent per opencode session (resume across turns; see below) |
136
156
  | `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
137
157
  | `mcpServers` | — | Extra MCP servers (Cursor `McpServerConfig` shape); merged with forwarded ones |
138
- | `toolDisplay` | `"reasoning"` | How Cursor's internal tool activity is shown: `"reasoning"` (compact lines, works everywhere) or `"blocks"` (structured provider-executed tool blocks; opt-in, see [Tool display](#tool-display)) |
158
+ | `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown: `"blocks"` (structured provider-executed tool blocks; default, requires opencode 1.16+) or `"reasoning"` (compact lines, the fallback for older/non-V3 hosts). See [Tool display](#tool-display) |
139
159
 
140
160
  ### Session reuse (`session`)
141
161
 
@@ -156,10 +176,19 @@ opencode delivers per-request, provider-specific settings to the model under
156
176
  - `params` → `{ <paramId>: value }` mapped to Cursor `ModelSelection.params`
157
177
  - `thinking` → convenience, mapped to the `thinking` param
158
178
 
159
- These are most naturally driven by opencode's **model variant picker**: the plugin auto-generates a
160
- `plan` variant plus one variant per reasoning/thinking value the model advertises
161
- (`Cursor.models.list()` parameters). Selecting a variant sends its settings through
162
- `providerOptions.cursor`. You can also set them statically per model:
179
+ These are most naturally driven by opencode's **model variant picker**: the plugin auto-generates
180
+ one variant per reasoning/effort level a model advertises (`Cursor.models.list()` parameters). A
181
+ boolean parameter (e.g. `thinking: ["false","true"]`) collapses to a single variant named after the
182
+ parameter that switches it on (the off state is the default — no variant selected); enum parameters
183
+ (e.g. `effort`, `reasoning`) produce one variant per value. Selecting a variant sends its settings
184
+ through `providerOptions.cursor`.
185
+
186
+ > **Plan mode is not a variant.** opencode's **plan agent** (toggled with `Tab`) is mapped to
187
+ > Cursor's plan mode automatically by the plugin's `chat.params` hook, so switching opencode into
188
+ > plan mode puts the Cursor agent into plan mode too. An explicit `mode` from a selected variant or
189
+ > model option still wins.
190
+
191
+ You can also set controls statically per model:
163
192
 
164
193
  ```json
165
194
  { "provider": { "cursor": { "models": {
@@ -302,31 +331,46 @@ no sidecar is spawned.
302
331
  Cursor runs its own agent loop and executes its own tools. The `toolDisplay` option controls how
303
332
  that activity appears in opencode:
304
333
 
305
- - **`"reasoning"` (default)** — each tool call is shown as a compact reasoning line
306
- (`[tool] write {"path":…}`; failures as `[tool] x failed`). Robust on every host: no tool-call
307
- parts cross into opencode, so there's no dependency on how the host treats provider-executed
308
- tools.
309
- - **`"blocks"` (opt-in)** — tool activity is emitted as structured, **provider-executed**
334
+ - **`"blocks"` (default)** — tool activity is emitted as structured, **provider-executed**
310
335
  `tool-call`/`tool-result` parts so opencode renders proper, collapsible tool blocks with inputs
311
336
  and outputs. opencode skips execution for provider-executed calls (they're display-only), so
312
- Cursor's tools (`shell`, `mcp`, …) don't trigger an "unavailable tool" error. This requires a
337
+ Cursor's tools (`shell`, `mcp`, …) don't trigger an "unavailable tool" error. Requires a
313
338
  V3-native opencode host (1.16+).
314
339
 
315
- Enable blocks mode in your opencode config:
340
+ Where a Cursor tool has a natural opencode counterpart, it's surfaced under opencode's
341
+ **registered** tool name so its native renderer is used instead of a generic block: `edit` →
342
+ opencode's diff viewer (via `metadata.diff`), `shell` → `bash` console, `task` → the subagent
343
+ card, web search (which Cursor runs as an MCP tool) → the `websearch` renderer, and
344
+ `read`/`write`/`glob`/`grep`/`ls`/`updateTodos` → opencode's
345
+ `read`/`write`/`glob`/`grep`/`list`/`todowrite` renderers. Cursor's arg shape is translated to
346
+ opencode's (e.g. `path` → `filePath`); the call stays provider-executed, so it's display-only and
347
+ never re-run on disk.
348
+
349
+ Tools with no opencode counterpart still get cleaned up: `readLints` and `delete` render as
350
+ formatted `cursor_*` blocks (a diagnostics list / a one-line confirmation) rather than raw JSON,
351
+ and any MCP tool's `content` is flattened to readable text. Anything else — or a result with an
352
+ unexpected shape — falls back to a prefixed `cursor_*` block with the raw payload.
353
+ - **`"reasoning"` (fallback)** — each tool call is shown as a compact reasoning line
354
+ (`[tool] write {"path":…}`; failures as `[tool] x failed`). Robust on every host: no tool-call
355
+ parts cross into opencode, so there's no dependency on how the host treats provider-executed
356
+ tools. Use this on older/non-V3 opencode hosts.
357
+
358
+ The default needs no configuration. To force the reasoning fallback (e.g. on a pre-1.16 host):
316
359
 
317
360
  ```jsonc
318
361
  {
319
362
  "provider": {
320
363
  "cursor": {
321
- "options": { "toolDisplay": "blocks" }
364
+ "options": { "toolDisplay": "reasoning" }
322
365
  }
323
366
  }
324
367
  }
325
368
  ```
326
369
 
327
- > Why opt-in: `"blocks"` depends on V3-native, provider-executed dynamic tool parts and has been
328
- > verified against opencode 1.16+. `"reasoning"` requires nothing from the host and remains the
329
- > always-safe default. If `"blocks"` renders cleanly for you, it's the nicer experience.
370
+ > Why blocks by default: structured tool blocks are the nicer experience and have been verified
371
+ > against opencode 1.16+. `"blocks"` depends on V3-native, provider-executed dynamic tool parts; if
372
+ > your host predates that (or renders them poorly), set `"toolDisplay": "reasoning"` — it requires
373
+ > nothing from the host and works everywhere.
330
374
 
331
375
  ## Troubleshooting
332
376
 
@@ -336,6 +380,17 @@ Enable blocks mode in your opencode config:
336
380
  with `OPENCODE_CURSOR_SIDECAR=1`.
337
381
  - **"Running under Bun without a usable Node sidecar" warning.** Install Node.js 22+, or set
338
382
  `OPENCODE_CURSOR_SIDECAR=0` to accept in-process behavior and silence the warning.
383
+ - **"Could not locate the bindings file" / `node_sqlite3.node` not found.** `@cursor/sdk` depends on
384
+ the native `sqlite3` addon, and opencode installs plugins with Bun, which skips sqlite3's install
385
+ script — so the prebuilt binary may be missing. The plugin detects this and self-heals on first SDK
386
+ load by running sqlite3's own `prebuild-install -r napi` under your system Node (requires Node on
387
+ `PATH`). If it can't (no Node, offline), it logs a one-line manual fix: `cd` into the printed
388
+ sqlite3 directory and run `npx prebuild-install -r napi` (or `npm rebuild sqlite3`). Set
389
+ `OPENCODE_CURSOR_DEBUG=1` to see the repair output.
390
+ - **Plugin looks enabled but no `cursor` provider/models appear.** opencode caches a plugin by its
391
+ install spec under `~/.cache/opencode/packages/`; a stale cache from an older version can persist.
392
+ Pin an exact version (`@stablekernel/opencode-cursor@<version>`) or delete the cached dir and
393
+ restart so opencode reinstalls.
339
394
  - **Only the four fallback models appear in the picker.** The live catalog loads after the first
340
395
  authenticated use — restart opencode once after logging in, or run `cursor_refresh_models` to
341
396
  force a refresh.
@@ -4,10 +4,9 @@ import { AgentModeOption, McpServerConfig, SettingSource, AgentDefinition } from
4
4
  /**
5
5
  * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced to
6
6
  * opencode:
7
- * - `"reasoning"` (default): rendered as compact reasoning lines. Robust on
8
- * every host — no tool-call parts cross the execution boundary.
9
- * - `"blocks"`: emitted as provider-executed AI-SDK `tool-call`/`tool-result`
10
- * parts so opencode renders structured tool blocks. The parts must carry
7
+ * - `"blocks"` (default): emitted as provider-executed AI-SDK
8
+ * `tool-call`/`tool-result` parts so opencode renders structured tool
9
+ * blocks. Requires a V3-native opencode host (1.16+). The parts must carry
11
10
  * BOTH `providerExecuted: true` AND `dynamic: true` — ai's `parseToolCall`
12
11
  * (v6, `doParseToolCall`) only exempts that combination from registered-tool
13
12
  * validation; without `dynamic` an unknown name raises `NoSuchToolError`,
@@ -58,9 +57,11 @@ interface CursorProviderOptions {
58
57
  session?: boolean;
59
58
  /**
60
59
  * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced:
61
- * - `"reasoning"` (default): compact reasoning lines (works on every host).
62
- * - `"blocks"`: structured provider-executed `tool-call`/`tool-result` parts
63
- * so opencode renders proper tool blocks. Opt-in; requires a V3-native host.
60
+ * - `"blocks"` (default): structured provider-executed `tool-call`/
61
+ * `tool-result` parts so opencode renders proper tool blocks. Requires a
62
+ * V3-native opencode host (1.16+).
63
+ * - `"reasoning"`: compact reasoning lines; the fallback for older/non-V3
64
+ * hosts (works everywhere).
64
65
  */
65
66
  toolDisplay?: ToolDisplay;
66
67
  }