@stablekernel/opencode-cursor 0.2.0 → 0.4.0-rc.1
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 +60 -3
- package/README.md +148 -268
- package/dist/{chunk-D4YQ7ZEM.js → chunk-BTI2NHEE.js} +73 -8
- package/dist/chunk-BTI2NHEE.js.map +1 -0
- package/dist/plugin/index.js +134 -23
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +7 -5
- package/dist/provider/index.js +149 -21
- package/dist/provider/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-D4YQ7ZEM.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,99 +4,57 @@
|
|
|
4
4
|
[](https://github.com/stablekernel/opencode-cursor/actions/workflows/ci.yml)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
An [opencode](https://opencode.ai) plugin that adds
|
|
8
|
-
**official [Cursor SDK](https://cursor.com/docs/sdk/typescript)** (`@cursor/sdk`).
|
|
7
|
+
An [opencode](https://opencode.ai) plugin that adds **Cursor** as a native provider. Your Cursor models appear in the model picker; you chat with them the same way you use any other provider.
|
|
9
8
|
|
|
10
|
-
It uses your Cursor
|
|
11
|
-
|
|
12
|
-
- register a `cursor` provider in opencode,
|
|
13
|
-
- **list the models available to your account** (live, via `Cursor.models.list()`), and
|
|
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 structured tool
|
|
16
|
-
blocks by default; see [Tool display](#tool-display)).
|
|
17
|
-
|
|
18
|
-
This plugin registers Cursor as a **native opencode provider**: its models appear in
|
|
19
|
-
`opencode models` and the model picker, and you talk to a Cursor model *directly* — with live model
|
|
20
|
-
discovery, variants, MCP forwarding, and session reuse. For delegated or background workflows it
|
|
21
|
-
also ships two permission-gated tools (`cursor_delegate`, `cursor_cloud_agent`); see
|
|
22
|
-
[Provider vs. delegation tools](#provider-vs-delegation-tools).
|
|
9
|
+
It uses the [official Cursor SDK](https://cursor.com/docs/sdk/typescript) (`@cursor/sdk`) to list your account's models live and run chats through Cursor's local agent runtime. For delegated or background workflows it also ships two permission-gated tools (`cursor_delegate`, `cursor_cloud_agent`) — see [Delegation tools](#delegation-tools).
|
|
23
10
|
|
|
24
11
|
> ⚠️ **Security.** When you chat with a `cursor/*` model, Cursor runs its own tools — including
|
|
25
12
|
> `shell`, `write`, `edit`, and `delete` — directly in your working directory, **outside opencode's
|
|
26
|
-
> permission system
|
|
13
|
+
> permission system**. Read [Security](#security) before you use it.
|
|
27
14
|
|
|
28
15
|
## Requirements
|
|
29
16
|
|
|
30
|
-
- **opencode 1.
|
|
31
|
-
- **Node.js 22+ on your `PATH`** — opencode runs on [Bun](https://bun.sh); the plugin
|
|
32
|
-
|
|
33
|
-
[Runtime: Bun and the Node sidecar](#runtime-bun-and-the-node-sidecar)).
|
|
17
|
+
- **opencode 1.17+**
|
|
18
|
+
- **Node.js 22+ on your `PATH`** — opencode runs on [Bun](https://bun.sh); the plugin needs a
|
|
19
|
+
Node sidecar to host the Cursor SDK (see [Runtime](#runtime-bun-and-the-node-sidecar)).
|
|
34
20
|
- A **Cursor account and API key** (from the Cursor dashboard).
|
|
35
21
|
|
|
36
|
-
## Security
|
|
37
|
-
|
|
38
|
-
> ⚠️ **The provider path is unsandboxed and not gated by opencode permissions.**
|
|
39
|
-
> When you chat with a `cursor/*` model, Cursor runs its **own** agent loop and executes its own
|
|
40
|
-
> tools — including `shell`, `write`, `edit`, and `delete` — directly in your working directory.
|
|
41
|
-
> These run **outside** opencode's `permission` system, and the sandbox is **off by default**, so
|
|
42
|
-
> your opencode `permission` rules (e.g. `edit: deny`, `bash: ask`) do **not** apply to them. If you
|
|
43
|
-
> need an approval boundary, either set `sandbox: true` in `provider.cursor.options` (runs Cursor's
|
|
44
|
-
> tools in Cursor's sandbox) or use the permission-gated **`cursor_delegate`** tool instead of the
|
|
45
|
-
> provider path. Only the `cursor_delegate` / `cursor_cloud_agent` tools are gated by opencode's
|
|
46
|
-
> `permission` config.
|
|
47
|
-
|
|
48
|
-
See [SECURITY.md](./SECURITY.md) for the full threat model and how to report a vulnerability.
|
|
49
|
-
|
|
50
|
-
## How it works
|
|
51
|
-
|
|
52
|
-
opencode loads two things from this one package:
|
|
53
|
-
|
|
54
|
-
| opencode concept | What it loads | Export |
|
|
55
|
-
| --- | --- | --- |
|
|
56
|
-
| Plugin (`plugin` config) | auth + provider registration + dynamic model listing + a refresh tool | `@stablekernel/opencode-cursor` (resolved via the package's `./server` export) |
|
|
57
|
-
| Provider (`provider.cursor.npm`) | a Vercel AI SDK `LanguageModelV3` that drives a local Cursor agent | `@stablekernel/opencode-cursor` (`createCursor`) |
|
|
58
|
-
|
|
59
|
-
The plugin's `config` hook registers `provider.cursor` (pointing `npm` at this package) and seeds
|
|
60
|
-
it with discovered/fallback models. The `auth` hook stores your API key and feeds it to the
|
|
61
|
-
provider factory; the key is validated on first use (model discovery / the first call), not at
|
|
62
|
-
login. The `provider.models()` hook refreshes the catalog live once you're authenticated.
|
|
63
|
-
|
|
64
22
|
## Install
|
|
65
23
|
|
|
66
|
-
###
|
|
24
|
+
### One line
|
|
67
25
|
|
|
68
26
|
```bash
|
|
69
27
|
curl -fsSL https://raw.githubusercontent.com/stablekernel/opencode-cursor/main/install.sh | bash
|
|
70
28
|
```
|
|
71
29
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`CURSOR_API_KEY`. Flags:
|
|
30
|
+
Registers the plugin in your global `opencode.json` (`~/.config/opencode/opencode.json`), checks
|
|
31
|
+
for Node.js 22+, and offers to set `CURSOR_API_KEY`. Flags:
|
|
75
32
|
|
|
76
|
-
- `--project` — write `./opencode.json` in the current directory instead
|
|
77
|
-
- `--yes` / `-y` — non-interactive
|
|
33
|
+
- `--project` — write `./opencode.json` in the current directory instead.
|
|
34
|
+
- `--yes` / `-y` — non-interactive.
|
|
78
35
|
|
|
79
|
-
|
|
80
|
-
available (falling back to Node). You can review it first:
|
|
81
|
-
[`install.sh`](./install.sh).
|
|
36
|
+
[Review the script first.](./install.sh)
|
|
82
37
|
|
|
83
|
-
### Manual
|
|
38
|
+
### Manual
|
|
84
39
|
|
|
85
40
|
```bash
|
|
86
41
|
npm install @stablekernel/opencode-cursor
|
|
87
42
|
```
|
|
88
43
|
|
|
89
|
-
Add
|
|
44
|
+
Add to your `opencode.json` (or `opencode.jsonc` — both are supported):
|
|
90
45
|
|
|
91
46
|
```json
|
|
92
47
|
{
|
|
93
48
|
"$schema": "https://opencode.ai/config.json",
|
|
94
|
-
"plugin": ["@stablekernel/opencode-cursor"]
|
|
49
|
+
"plugin": ["@stablekernel/opencode-cursor@latest"]
|
|
95
50
|
}
|
|
96
51
|
```
|
|
97
52
|
|
|
98
|
-
|
|
99
|
-
|
|
53
|
+
The `@latest` suffix makes opencode re-resolve to the newest release on each
|
|
54
|
+
startup. Drop it (`"@stablekernel/opencode-cursor"`) or pin a version
|
|
55
|
+
(`"@stablekernel/opencode-cursor@1.2.3"`) if you prefer.
|
|
56
|
+
|
|
57
|
+
The plugin injects the `provider` block automatically. If you need explicit control:
|
|
100
58
|
|
|
101
59
|
```json
|
|
102
60
|
{
|
|
@@ -112,34 +70,44 @@ configure it explicitly, the equivalent is:
|
|
|
112
70
|
|
|
113
71
|
## Authenticate
|
|
114
72
|
|
|
115
|
-
Either run the interactive login:
|
|
116
|
-
|
|
117
73
|
```bash
|
|
118
74
|
opencode auth login # choose "Cursor", paste your key from the Cursor dashboard
|
|
119
75
|
```
|
|
120
76
|
|
|
121
|
-
|
|
77
|
+
Or set the environment variable:
|
|
122
78
|
|
|
123
79
|
```bash
|
|
124
80
|
export CURSOR_API_KEY="key_..."
|
|
125
81
|
```
|
|
126
82
|
|
|
127
|
-
The key is
|
|
128
|
-
discovery and the first call surface an invalid key clearly), not at login time.
|
|
83
|
+
The key is validated on first use (model discovery / first call), not at login time.
|
|
129
84
|
|
|
130
85
|
## Use
|
|
131
86
|
|
|
132
|
-
- `opencode models` (or the
|
|
133
|
-
- Pick a
|
|
134
|
-
- Run the `cursor_refresh_models` tool to force a live catalog refresh
|
|
87
|
+
- `opencode models` (or the model picker) lists your Cursor models as `cursor/<id>`.
|
|
88
|
+
- Pick a model and chat — the Cursor local agent runs in your project directory.
|
|
89
|
+
- Run the `cursor_refresh_models` tool to force a live catalog refresh.
|
|
90
|
+
|
|
91
|
+
The plugin also registers two **delegation tools**:
|
|
92
|
+
|
|
93
|
+
- `cursor_delegate` — hand a discrete subtask to a local Cursor agent as a permission-gated tool
|
|
94
|
+
call (your primary model stays in control).
|
|
95
|
+
- `cursor_cloud_agent` — launch a Cursor cloud agent on a remote repo that can run for minutes and
|
|
96
|
+
optionally open a PR.
|
|
135
97
|
|
|
136
|
-
|
|
137
|
-
|
|
98
|
+
## Security
|
|
99
|
+
|
|
100
|
+
> ⚠️ **The provider path is unsandboxed and not gated by opencode permissions.**
|
|
101
|
+
> When you chat with a `cursor/*` model, Cursor runs its own tools — including `shell`, `write`,
|
|
102
|
+
> `edit`, and `delete` — directly in your working directory. opencode's `permission` rules (e.g.
|
|
103
|
+
> `edit: deny`, `bash: ask`) do **not** apply to them.
|
|
104
|
+
>
|
|
105
|
+
> Options if you need a permission boundary:
|
|
106
|
+
> - Set `sandbox: true` in `provider.cursor.options` to run Cursor's tools in Cursor's sandbox.
|
|
107
|
+
> - Use `cursor_delegate` instead of the provider path — it is gated by opencode's `permission`
|
|
108
|
+
> config.
|
|
138
109
|
|
|
139
|
-
|
|
140
|
-
auditable opencode tool call (your primary model stays in control).
|
|
141
|
-
- `cursor_cloud_agent` — launch a Cursor **cloud** (background) agent on a remote repo that can run
|
|
142
|
-
for minutes and optionally open a PR.
|
|
110
|
+
See [SECURITY.md](./SECURITY.md) for the full threat model.
|
|
143
111
|
|
|
144
112
|
## Configuration
|
|
145
113
|
|
|
@@ -147,48 +115,58 @@ This plugin also registers two **delegation tools** that complement the provider
|
|
|
147
115
|
| --- | --- | --- |
|
|
148
116
|
| `apiKey` | `CURSOR_API_KEY` | Cursor API key |
|
|
149
117
|
| `cwd` | `process.cwd()` | Directory the local agent operates in |
|
|
150
|
-
| `mode` | `"agent"` | Default
|
|
151
|
-
| `params` | — | Default
|
|
152
|
-
| `settingSources` | — | Cursor settings layers to load
|
|
153
|
-
| `sandbox` | — | Run the agent's tools
|
|
154
|
-
| `agents` | — | Cursor subagent definitions
|
|
155
|
-
| `session` | `
|
|
118
|
+
| `mode` | `"agent"` | Default conversation mode (`"agent"` or `"plan"`) |
|
|
119
|
+
| `params` | — | Default model params, e.g. `{ thinking: "high" }` |
|
|
120
|
+
| `settingSources` | — | Cursor settings layers to load: `["project","user","all",...]` — pulls in your Cursor skills, rules, and `.cursor/mcp.json` |
|
|
121
|
+
| `sandbox` | — | Run the agent's tools in Cursor's sandbox |
|
|
122
|
+
| `agents` | — | Cursor subagent definitions |
|
|
123
|
+
| `session` | `"auto"` | Session reuse strategy — see [Session reuse](#session-reuse-session) |
|
|
156
124
|
| `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
|
|
157
125
|
| `mcpServers` | — | Extra MCP servers (Cursor `McpServerConfig` shape); merged with forwarded ones |
|
|
158
|
-
| `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown
|
|
126
|
+
| `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown — see [Tool display](#tool-display) |
|
|
127
|
+
|
|
128
|
+
| Environment variable | Default | Meaning |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `CURSOR_API_KEY` | — | API key fallback |
|
|
131
|
+
| `OPENCODE_CURSOR_MODEL_CACHE_TTL_MS` | `86400000` | Model-list cache lifetime (ms) |
|
|
132
|
+
| `OPENCODE_CURSOR_DEBUG` | — | Set to `1` for trace logging on stderr |
|
|
133
|
+
| `OPENCODE_CURSOR_SIDECAR` | — | `1` = always use Node sidecar; `0` = never |
|
|
159
134
|
|
|
160
135
|
### Session reuse (`session`)
|
|
161
136
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Cursor uses its native conversation memory and checkpoints (the agent is visible in Cursor's
|
|
167
|
-
dashboard). The opencode session id reaches the provider via the plugin's `chat.params` hook
|
|
168
|
-
(`providerOptions.cursor.sessionID`); a failed resume falls back to a fresh turn automatically.
|
|
137
|
+
opencode re-sends the full conversation transcript on every turn. `session: "auto"` (the default)
|
|
138
|
+
fingerprints the conversation and resumes the same Cursor agent when nothing has changed, so you
|
|
139
|
+
only pay for the new message. It falls back to a fresh agent + full transcript on edits, reverts,
|
|
140
|
+
or compaction.
|
|
169
141
|
|
|
170
|
-
|
|
142
|
+
| Situation | What happens |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| First turn | Fresh agent, full transcript, pool it |
|
|
145
|
+
| System prompt differs (title gen, other side calls) | Ephemeral fresh agent; pooled agent untouched |
|
|
146
|
+
| Clean continuation (one new user message) | `Agent.resume` — sends only the new message |
|
|
147
|
+
| Forwarded MCP server set changed | Fresh agent + full transcript, re-pooled |
|
|
148
|
+
| Message edited/reverted or conversation compacted | Fresh agent + full transcript, re-pooled |
|
|
149
|
+
|
|
150
|
+
`session: true` is an alias for `"auto"`. `session: false` disables reuse (always fresh agent,
|
|
151
|
+
full transcript every turn).
|
|
171
152
|
|
|
172
|
-
|
|
173
|
-
|
|
153
|
+
Fingerprint records persist to `~/.cache/opencode-cursor/session-pool.json`, so session reuse
|
|
154
|
+
survives opencode restarts.
|
|
155
|
+
|
|
156
|
+
### Per-request controls (`mode`, thinking level)
|
|
174
157
|
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
|
|
158
|
+
The plugin auto-generates model variants for each reasoning/effort level a model advertises,
|
|
159
|
+
plus a `fast` toggle for models that expose Cursor's fast tier. Selecting a variant in the
|
|
160
|
+
model picker sends its settings through `providerOptions.cursor`.
|
|
178
161
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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`.
|
|
162
|
+
`fast` defaults **off** (even though Cursor's own default is `fast: true` for some models, e.g.
|
|
163
|
+
Composer and the codex line) so opencode never silently runs the fast tier — pick the `fast`
|
|
164
|
+
variant to opt in, or set it per model under `options.params.fast` below.
|
|
185
165
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
> plan mode puts the Cursor agent into plan mode too. An explicit `mode` from a selected variant or
|
|
189
|
-
> model option still wins.
|
|
166
|
+
opencode's **plan agent** (`Tab`) maps to Cursor's plan mode automatically — no manual config
|
|
167
|
+
needed.
|
|
190
168
|
|
|
191
|
-
|
|
169
|
+
To set controls statically per model:
|
|
192
170
|
|
|
193
171
|
```json
|
|
194
172
|
{ "provider": { "cursor": { "models": {
|
|
@@ -196,39 +174,30 @@ You can also set controls statically per model:
|
|
|
196
174
|
} } } }
|
|
197
175
|
```
|
|
198
176
|
|
|
199
|
-
| Environment variable | Default | Meaning |
|
|
200
|
-
| --- | --- | --- |
|
|
201
|
-
| `CURSOR_API_KEY` | — | API key fallback |
|
|
202
|
-
| `OPENCODE_CURSOR_MODEL_CACHE_TTL_MS` | `86400000` | Model-list cache lifetime |
|
|
203
|
-
|
|
204
|
-
To disable MCP forwarding, set `provider.cursor.options.forwardMcp: false` in your opencode config.
|
|
205
|
-
|
|
206
177
|
## MCP servers
|
|
207
178
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
179
|
+
With `forwardMcp: true` (default), the Cursor agent uses the same MCP servers configured in
|
|
180
|
+
opencode. The server list is updated live per turn, so enabling or disabling an MCP server takes
|
|
181
|
+
effect on the next message.
|
|
211
182
|
|
|
212
183
|
| opencode `config.mcp` | → Cursor |
|
|
213
184
|
| --- | --- |
|
|
214
|
-
| `{ type: "local", command: [cmd, ...args], environment }` | `{ type: "stdio", command
|
|
185
|
+
| `{ type: "local", command: [cmd, ...args], environment }` | `{ type: "stdio", command, args, env }` |
|
|
215
186
|
| `{ type: "remote", url, headers }` | `{ type: "http", url, headers }` |
|
|
187
|
+
| Remote with registered OAuth `clientId` | `{ type: "http", url, auth: { CLIENT_ID, … } }` |
|
|
216
188
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
189
|
+
Disabled entries (`enabled: false`) are skipped. Remote servers requiring OAuth without a
|
|
190
|
+
shareable `clientId` are also skipped (a one-time toast says which). Disable forwarding with
|
|
191
|
+
`forwardMcp: false`.
|
|
220
192
|
|
|
221
|
-
>
|
|
222
|
-
>
|
|
223
|
-
>
|
|
224
|
-
> `settingSources` option (e.g. `["project","user"]`), which reads your local Cursor configuration.
|
|
193
|
+
> **Note:** This forwards MCP **servers**. opencode's own skills and subagents are not exposed to
|
|
194
|
+
> the Cursor agent. To load your local Cursor skills/rules, use
|
|
195
|
+
> `settingSources: ["project","user"]`.
|
|
225
196
|
|
|
226
197
|
## Delegation tools
|
|
227
198
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
session (or `CURSOR_API_KEY`) and are **permission-gated** via opencode's `permission` config — they
|
|
231
|
-
call `context.ask`, so a policy of `allow` runs silently, `ask` prompts, and `deny` blocks:
|
|
199
|
+
Both tools resolve the API key from your `opencode auth login` session (or `CURSOR_API_KEY`) and
|
|
200
|
+
are gated by opencode's `permission` config:
|
|
232
201
|
|
|
233
202
|
```json
|
|
234
203
|
{ "permission": { "cursor_delegate": "ask", "cursor_cloud_agent": "ask" } }
|
|
@@ -236,175 +205,86 @@ call `context.ask`, so a policy of `allow` runs silently, `ask` prompts, and `de
|
|
|
236
205
|
|
|
237
206
|
### `cursor_delegate` (local)
|
|
238
207
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
as an explicit, auditable tool call.
|
|
208
|
+
Runs one Cursor turn as a permission-gated tool call. Your primary opencode model hands off a
|
|
209
|
+
discrete subtask and gets the result back.
|
|
242
210
|
|
|
243
211
|
| Arg | Required | Meaning |
|
|
244
212
|
| --- | --- | --- |
|
|
245
213
|
| `prompt` | ✅ | The subtask to delegate |
|
|
246
|
-
| `model` | ✅ | Cursor model id
|
|
214
|
+
| `model` | ✅ | Cursor model id |
|
|
247
215
|
| `mode` | — | `"agent"` or `"plan"` |
|
|
248
216
|
| `thinking` | — | Thinking level (e.g. `"high"`) |
|
|
249
|
-
| `cwd` | — | Working directory
|
|
250
|
-
| `sandbox` | — | Run
|
|
251
|
-
| `agentId` | — | Resume a specific Cursor agent
|
|
217
|
+
| `cwd` | — | Working directory |
|
|
218
|
+
| `sandbox` | — | Run in Cursor's sandbox |
|
|
219
|
+
| `agentId` | — | Resume a specific Cursor agent |
|
|
252
220
|
|
|
253
|
-
### `cursor_cloud_agent` (cloud
|
|
221
|
+
### `cursor_cloud_agent` (cloud)
|
|
254
222
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
it is exposed as a tool. Returns the cloud agent id, terminal status, result text, and PR url.
|
|
223
|
+
Launches a background Cursor cloud agent on a remote repo. Can run for minutes and optionally
|
|
224
|
+
open a PR.
|
|
258
225
|
|
|
259
226
|
| Arg | Required | Meaning |
|
|
260
227
|
| --- | --- | --- |
|
|
261
|
-
| `prompt` | ✅ | The task
|
|
228
|
+
| `prompt` | ✅ | The task |
|
|
262
229
|
| `repoUrl` | ✅ | Target repository URL (e.g. `https://github.com/owner/repo`) |
|
|
263
|
-
| `startingRef` | — | Branch/ref to start from
|
|
264
|
-
| `model` | — | Cursor model id
|
|
230
|
+
| `startingRef` | — | Branch/ref to start from |
|
|
231
|
+
| `model` | — | Cursor model id |
|
|
265
232
|
| `mode` | — | `"agent"` or `"plan"` |
|
|
266
|
-
| `thinking` | — | Thinking level
|
|
267
|
-
| `autoCreatePR` | — | Open a PR
|
|
233
|
+
| `thinking` | — | Thinking level |
|
|
234
|
+
| `autoCreatePR` | — | Open a PR when finished |
|
|
268
235
|
| `workOnCurrentBranch` | — | Operate on the current branch instead of a new one |
|
|
269
236
|
|
|
270
|
-
##
|
|
237
|
+
## Tool display
|
|
271
238
|
|
|
272
|
-
|
|
239
|
+
`toolDisplay` controls how Cursor's internal tool activity appears in opencode:
|
|
273
240
|
|
|
274
|
-
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
-
|
|
278
|
-
|
|
279
|
-
`cursor_cloud_agent` launches a background agent on a remote repo that can run for minutes and
|
|
280
|
-
optionally open a PR.
|
|
241
|
+
- **`"blocks"` (default)** — structured, collapsible tool blocks with inputs and outputs. Common
|
|
242
|
+
Cursor tools are mapped to their opencode equivalents (`edit` → diff viewer, `shell` → bash
|
|
243
|
+
console, etc.). Requires opencode 1.17+.
|
|
244
|
+
- **`"reasoning"`** — compact inline lines (`[tool] write {"path":…}`). Works on any host; use
|
|
245
|
+
this on older opencode versions.
|
|
281
246
|
|
|
282
|
-
|
|
283
|
-
converse with, integrated into opencode's normal model/variant UX. Use **`cursor_delegate`** when
|
|
284
|
-
you want your existing opencode model to stay in control and hand off discrete tasks as explicit,
|
|
285
|
-
permission-gated tool calls. Use **`cursor_cloud_agent`** when you need background work on a remote
|
|
286
|
-
repo with optional PR creation.
|
|
287
|
-
|
|
288
|
-
## Behavior & limitations
|
|
289
|
-
|
|
290
|
-
> The provider path runs Cursor's own unsandboxed tools outside opencode's permission system — see
|
|
291
|
-
> [Security](#security).
|
|
292
|
-
|
|
293
|
-
This plugin runs Cursor as a **local agent** (`Agent.create({ local: { cwd } })`), so:
|
|
294
|
-
|
|
295
|
-
- **Cursor executes its own tools** (read/write/edit/shell/grep/mcp/…) directly in your working
|
|
296
|
-
directory. How that activity is shown is controlled by the [`toolDisplay`](#tool-display) option.
|
|
297
|
-
Either way it is **not** routed through opencode's tool/permission system — Cursor runs the tools
|
|
298
|
-
itself.
|
|
299
|
-
- By default each turn creates a fresh local agent and sends the full conversation transcript, so
|
|
300
|
-
context is always complete. Enable `session: true` to reuse Cursor's native per-agent memory
|
|
301
|
-
across turns (see [Session reuse](#session-reuse-session)).
|
|
302
|
-
- Token usage is reported from Cursor's `turn-ended` event; cost is shown as `0` because Cursor
|
|
303
|
-
bills your account separately.
|
|
304
|
-
- **Provider path is local.** The `cursor/*` models you chat with run as a **local** agent. Cursor's
|
|
305
|
-
**cloud** runtime (background agents on a remote repo with optional PR creation) maps awkwardly
|
|
306
|
-
onto a synchronous provider call, so it is exposed as the `cursor_cloud_agent` **tool** instead of
|
|
307
|
-
the provider path — see [Delegation tools](#delegation-tools).
|
|
308
|
-
|
|
309
|
-
### Runtime: Bun and the Node sidecar
|
|
310
|
-
|
|
311
|
-
opencode runs on [Bun](https://bun.sh). Bun's `node:http2` client is currently incompatible with the
|
|
312
|
-
Cursor SDK's long-lived streaming RPC (it aborts the stream with `NGHTTP2_FRAME_SIZE_ERROR`), which
|
|
313
|
-
makes Cursor's native tool calls execute but never report completion — they appear stuck or show
|
|
314
|
-
"Tool execution aborted".
|
|
315
|
-
|
|
316
|
-
To work around this transparently, when the plugin detects it is running under Bun and finds `node`
|
|
317
|
-
on your `PATH`, it hosts the Cursor SDK agent in a short-lived **Node child process** (a "sidecar")
|
|
318
|
-
and talks to it over stdio. Behavior is otherwise identical. Under Node the SDK runs in-process and
|
|
319
|
-
no sidecar is spawned.
|
|
320
|
-
|
|
321
|
-
- **Requirement:** a Node.js runtime on `PATH` when running under Bun — Node 22+ to match `engines`
|
|
322
|
-
(the plugin checks that `node` is present, not its version). If Bun is detected but `node` is
|
|
323
|
-
missing, the plugin logs a one-time warning and falls back to in-process (native Cursor tools will
|
|
324
|
-
misbehave until Node is available).
|
|
325
|
-
- **Override** with the `OPENCODE_CURSOR_SIDECAR` environment variable:
|
|
326
|
-
- `OPENCODE_CURSOR_SIDECAR=1` — always use the sidecar (requires `node`).
|
|
327
|
-
- `OPENCODE_CURSOR_SIDECAR=0` — never use the sidecar / silence the Bun warning.
|
|
247
|
+
To force the fallback:
|
|
328
248
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
Cursor runs its own agent loop and executes its own tools. The `toolDisplay` option controls how
|
|
332
|
-
that activity appears in opencode:
|
|
333
|
-
|
|
334
|
-
- **`"blocks"` (default)** — tool activity is emitted as structured, **provider-executed**
|
|
335
|
-
`tool-call`/`tool-result` parts so opencode renders proper, collapsible tool blocks with inputs
|
|
336
|
-
and outputs. opencode skips execution for provider-executed calls (they're display-only), so
|
|
337
|
-
Cursor's tools (`shell`, `mcp`, …) don't trigger an "unavailable tool" error. Requires a
|
|
338
|
-
V3-native opencode host (1.16+).
|
|
339
|
-
|
|
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):
|
|
359
|
-
|
|
360
|
-
```jsonc
|
|
361
|
-
{
|
|
362
|
-
"provider": {
|
|
363
|
-
"cursor": {
|
|
364
|
-
"options": { "toolDisplay": "reasoning" }
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
249
|
+
```json
|
|
250
|
+
{ "provider": { "cursor": { "options": { "toolDisplay": "reasoning" } } } }
|
|
368
251
|
```
|
|
369
252
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
253
|
+
## Runtime: Bun and the Node sidecar
|
|
254
|
+
|
|
255
|
+
opencode runs on [Bun](https://bun.sh), which has an `node:http2` incompatibility with the Cursor
|
|
256
|
+
SDK's streaming RPC. The plugin transparently hosts the Cursor SDK in a short-lived **Node child
|
|
257
|
+
process** when running under Bun. Under Node it runs in-process.
|
|
258
|
+
|
|
259
|
+
This is why **Node.js 22+ on your `PATH`** is required. If Node isn't found, the plugin warns once
|
|
260
|
+
and falls back to in-process (native Cursor tools will misbehave until Node is available).
|
|
261
|
+
|
|
262
|
+
Override with `OPENCODE_CURSOR_SIDECAR=1` (always sidecar) or `OPENCODE_CURSOR_SIDECAR=0` (never).
|
|
374
263
|
|
|
375
264
|
## Troubleshooting
|
|
376
265
|
|
|
377
|
-
- **Native Cursor tools hang
|
|
378
|
-
|
|
379
|
-
the plugin can use the Node sidecar (see [Runtime](#runtime-bun-and-the-node-sidecar)); force it
|
|
380
|
-
with `OPENCODE_CURSOR_SIDECAR=1`.
|
|
266
|
+
- **Native Cursor tools hang / "Tool execution aborted" (`NGHTTP2_FRAME_SIZE_ERROR`).** Node isn't
|
|
267
|
+
on your `PATH`. Install Node.js 22+, or force the sidecar with `OPENCODE_CURSOR_SIDECAR=1`.
|
|
381
268
|
- **"Running under Bun without a usable Node sidecar" warning.** Install Node.js 22+, or set
|
|
382
269
|
`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`
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
- **
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
- **Only the four fallback models appear in the picker.** The live catalog loads after the first
|
|
395
|
-
authenticated use — restart opencode once after logging in, or run `cursor_refresh_models` to
|
|
396
|
-
force a refresh.
|
|
397
|
-
- **Invalid or expired key.** The key is validated on first use (model discovery / first call), not
|
|
398
|
-
at login, so that's where an error surfaces.
|
|
399
|
-
- **Need more detail?** Set `OPENCODE_CURSOR_DEBUG=1` for provider and sidecar trace logging on
|
|
400
|
-
stderr.
|
|
270
|
+
- **"Could not locate the bindings file" / `node_sqlite3.node` not found.** The `@cursor/sdk`
|
|
271
|
+
native sqlite3 addon was skipped during Bun install. The plugin self-heals on first load (needs
|
|
272
|
+
Node on `PATH`). If that fails, `cd` into the printed sqlite3 directory and run
|
|
273
|
+
`npx prebuild-install -r napi`.
|
|
274
|
+
- **Plugin enabled but no `cursor` provider/models appear.** Stale opencode plugin cache. Pin an
|
|
275
|
+
exact version (`@stablekernel/opencode-cursor@<version>`) or delete
|
|
276
|
+
`~/.cache/opencode/packages/` and restart.
|
|
277
|
+
- **Only the four fallback models appear.** The live catalog loads after the first authenticated
|
|
278
|
+
use. Restart opencode once after login, or run `cursor_refresh_models`.
|
|
279
|
+
- **Invalid or expired key.** Validated on first use — that's where the error surfaces.
|
|
280
|
+
- **Need more detail?** Set `OPENCODE_CURSOR_DEBUG=1`.
|
|
401
281
|
|
|
402
282
|
## Contributing
|
|
403
283
|
|
|
404
|
-
Issues and pull requests are welcome. See
|
|
405
|
-
|
|
284
|
+
Issues and pull requests are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for dev setup,
|
|
285
|
+
test/typecheck/build commands, and the release process. Report bugs at the
|
|
406
286
|
[issue tracker](https://github.com/stablekernel/opencode-cursor/issues); for security reports
|
|
407
|
-
see
|
|
287
|
+
see [SECURITY.md](./SECURITY.md).
|
|
408
288
|
|
|
409
289
|
## License
|
|
410
290
|
|