@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 +43 -11
- package/README.md +72 -17
- package/dist/provider/index.d.ts +8 -7
- package/dist/provider/index.js +547 -45
- package/dist/provider/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
8
|
-
npm `next` dist-tag for validation ahead of the stable `0.1.0`.
|
|
7
|
+
## [0.2.0] — 2026-06-11
|
|
9
8
|
|
|
10
|
-
|
|
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:
|
|
25
|
-
|
|
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
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
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
|
|
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` | `"
|
|
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
|
|
160
|
-
|
|
161
|
-
(
|
|
162
|
-
|
|
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
|
-
- **`"
|
|
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.
|
|
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
|
-
|
|
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": "
|
|
364
|
+
"options": { "toolDisplay": "reasoning" }
|
|
322
365
|
}
|
|
323
366
|
}
|
|
324
367
|
}
|
|
325
368
|
```
|
|
326
369
|
|
|
327
|
-
> Why
|
|
328
|
-
>
|
|
329
|
-
>
|
|
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.
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -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
|
-
* - `"
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
* - `"
|
|
62
|
-
*
|
|
63
|
-
*
|
|
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
|
}
|