@stablekernel/opencode-cursor 0.1.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 +88 -0
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/chunk-YYO6O43T.js +502 -0
- package/dist/chunk-YYO6O43T.js.map +1 -0
- package/dist/plugin/index.d.ts +17 -0
- package/dist/plugin/index.js +572 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/provider/index.d.ts +77 -0
- package/dist/provider/index.js +427 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/sidecar/agent-host.d.ts +128 -0
- package/dist/sidecar/agent-host.js +93 -0
- package/dist/sidecar/agent-host.js.map +1 -0
- package/package.json +68 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
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`.
|
|
9
|
+
|
|
10
|
+
## [0.1.0] — unreleased
|
|
11
|
+
|
|
12
|
+
Initial public release. A complete opencode integration for Cursor built on the
|
|
13
|
+
official `@cursor/sdk`: a streaming chat provider, an auth/config/model plugin,
|
|
14
|
+
and a permission-gated delegation tool surface.
|
|
15
|
+
|
|
16
|
+
### Provider
|
|
17
|
+
|
|
18
|
+
- **Cursor provider** backed by the official `@cursor/sdk` — drives a local
|
|
19
|
+
Cursor agent (`Agent.create` / `agent.send`) and translates its `onDelta`
|
|
20
|
+
callbacks into AI SDK `LanguageModelV3` stream parts (text, reasoning,
|
|
21
|
+
tool activity, usage). Implements both `doStream()` and `doGenerate()`.
|
|
22
|
+
- **Per-request controls** via `providerOptions.cursor` — `mode` (agent/plan),
|
|
23
|
+
`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.
|
|
26
|
+
- **Session reuse** (`session: true`) — keeps one Cursor agent per opencode
|
|
27
|
+
session via `Agent.resume()` across turns, with automatic fallback to a fresh
|
|
28
|
+
agent. A run wedged by a crashed/duplicate process is recovered by retrying
|
|
29
|
+
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` /
|
|
35
|
+
`tool-result` parts so opencode renders native tool blocks. Names are
|
|
36
|
+
`cursor_`-prefixed and sanitized (`shell` → `cursor_shell`,
|
|
37
|
+
`serena/find_symbol` → `cursor_serena_find_symbol`) so they can't collide
|
|
38
|
+
with opencode-registered tools, and carry `providerExecuted: true` +
|
|
39
|
+
`dynamic: true` so ai v6's `parseToolCall` accepts them without
|
|
40
|
+
registered-tool validation. Tool-results use the V3-spec `result` +
|
|
41
|
+
`isError` fields. A tool call whose completion never arrives (run
|
|
42
|
+
errored/cancelled mid-tool) is closed with a synthetic error result so the
|
|
43
|
+
block never dangles as "Tool execution aborted", and a run that ends with
|
|
44
|
+
status `error` surfaces the failure instead of finishing silently.
|
|
45
|
+
|
|
46
|
+
### Node sidecar (Bun compatibility)
|
|
47
|
+
|
|
48
|
+
- **Automatic Node sidecar** — opencode runs on Bun, whose `node:http2` client
|
|
49
|
+
is incompatible with the Cursor SDK's long-lived streaming RPC
|
|
50
|
+
(`NGHTTP2_FRAME_SIZE_ERROR`), causing native tool calls to execute but never
|
|
51
|
+
report completion. When Bun is detected and `node` is on `PATH`, the SDK
|
|
52
|
+
agent is hosted in a Node child process and driven over a JSON-lines stdio
|
|
53
|
+
protocol; the provider is otherwise unchanged. Under Node the SDK runs
|
|
54
|
+
in-process. Override with `OPENCODE_CURSOR_SIDECAR=1` (force on) or
|
|
55
|
+
`OPENCODE_CURSOR_SIDECAR=0` (force in-process / silence the Bun warning).
|
|
56
|
+
|
|
57
|
+
### Plugin
|
|
58
|
+
|
|
59
|
+
- **opencode plugin** (`@stablekernel/opencode-cursor/plugin`): auth hook (API-key login;
|
|
60
|
+
the key is validated on first use rather than at login), config hook
|
|
61
|
+
(auto-injects `provider.cursor`),
|
|
62
|
+
`provider.models()` (live catalog via `Cursor.models.list`), and the
|
|
63
|
+
`cursor_refresh_models` tool. The auth loader warms a key-independent catalog
|
|
64
|
+
cache so the model picker is populated on first authed load (and restart)
|
|
65
|
+
rather than showing only the fallback snapshot.
|
|
66
|
+
- **MCP server forwarding** — opencode's configured `config.mcp` entries are
|
|
67
|
+
translated to Cursor `McpServerConfig` and passed to the local agent so it can
|
|
68
|
+
use the same servers (e.g. Serena). Opt out with `provider.cursor.options.forwardMcp`.
|
|
69
|
+
- **Model discovery** with a 24-hour cache (keyed by key fingerprint) and a
|
|
70
|
+
built-in fallback snapshot (composer-2.5, claude-opus-4-8, claude-sonnet-4-6,
|
|
71
|
+
gpt-5.5) for use without an API key.
|
|
72
|
+
|
|
73
|
+
### Delegation tools
|
|
74
|
+
|
|
75
|
+
- **`cursor_cloud_agent`** — launch a Cursor cloud (background) agent on a
|
|
76
|
+
remote repo via `Agent.create({ cloud: { repos, autoCreatePR } })`; returns
|
|
77
|
+
the agent id, terminal status, result, and PR url. Progress is collected from
|
|
78
|
+
`run.onDidChangeStatus`, `onStep`, and `onDelta`.
|
|
79
|
+
- **`cursor_delegate`** — run a single local Cursor turn as a permission-gated,
|
|
80
|
+
auditable opencode tool call (reuses the provider's `acquireAgent` +
|
|
81
|
+
`streamAgentTurn` plumbing). Both tools honor opencode's `permission` config
|
|
82
|
+
via `ToolContext.ask` and are fail-closed when no permission gate is present.
|
|
83
|
+
|
|
84
|
+
### Tooling
|
|
85
|
+
|
|
86
|
+
- **Provider debug tracing** — opt-in via `OPENCODE_CURSOR_DEBUG=1`.
|
|
87
|
+
- End-to-end CI: unit tests on two Node versions plus a full integration test
|
|
88
|
+
(opencode loads the plugin, lists models, optionally runs a live chat turn).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 justin-carper
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# @stablekernel/opencode-cursor
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@stablekernel/opencode-cursor)
|
|
4
|
+
[](https://github.com/stablekernel/opencode-cursor/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
An [opencode](https://opencode.ai) plugin that adds a **Cursor** provider backed by the
|
|
8
|
+
**official [Cursor SDK](https://cursor.com/docs/sdk/typescript)** (`@cursor/sdk`).
|
|
9
|
+
|
|
10
|
+
It uses your Cursor API key to:
|
|
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 reasoning).
|
|
16
|
+
|
|
17
|
+
This plugin registers Cursor as a **native opencode provider**: its models appear in
|
|
18
|
+
`opencode models` and the model picker, and you talk to a Cursor model *directly* — with live model
|
|
19
|
+
discovery, variants, MCP forwarding, and session reuse. For delegated or background workflows it
|
|
20
|
+
also ships two permission-gated tools (`cursor_delegate`, `cursor_cloud_agent`); see
|
|
21
|
+
[Provider vs. delegation tools](#provider-vs-delegation-tools).
|
|
22
|
+
|
|
23
|
+
> ⚠️ **Security.** When you chat with a `cursor/*` model, Cursor runs its own tools — including
|
|
24
|
+
> `shell`, `write`, `edit`, and `delete` — directly in your working directory, **outside opencode's
|
|
25
|
+
> permission system and unsandboxed by default**. Read [Security](#security) before you use it.
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
- **opencode 1.16+** — the provider targets AI SDK `LanguageModelV3`.
|
|
30
|
+
- **Node.js 22+ on your `PATH`** — opencode runs on [Bun](https://bun.sh); the plugin spawns a
|
|
31
|
+
short-lived Node process to host the Cursor SDK (see
|
|
32
|
+
[Runtime: Bun and the Node sidecar](#runtime-bun-and-the-node-sidecar)).
|
|
33
|
+
- A **Cursor account and API key** (from the Cursor dashboard).
|
|
34
|
+
|
|
35
|
+
## Security
|
|
36
|
+
|
|
37
|
+
> ⚠️ **The provider path is unsandboxed and not gated by opencode permissions.**
|
|
38
|
+
> When you chat with a `cursor/*` model, Cursor runs its **own** agent loop and executes its own
|
|
39
|
+
> tools — including `shell`, `write`, `edit`, and `delete` — directly in your working directory.
|
|
40
|
+
> These run **outside** opencode's `permission` system, and the sandbox is **off by default**, so
|
|
41
|
+
> your opencode `permission` rules (e.g. `edit: deny`, `bash: ask`) do **not** apply to them. If you
|
|
42
|
+
> need an approval boundary, either set `sandbox: true` in `provider.cursor.options` (runs Cursor's
|
|
43
|
+
> tools in Cursor's sandbox) or use the permission-gated **`cursor_delegate`** tool instead of the
|
|
44
|
+
> provider path. Only the `cursor_delegate` / `cursor_cloud_agent` tools are gated by opencode's
|
|
45
|
+
> `permission` config.
|
|
46
|
+
|
|
47
|
+
See [SECURITY.md](./SECURITY.md) for the full threat model and how to report a vulnerability.
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
opencode loads two things from this one package:
|
|
52
|
+
|
|
53
|
+
| opencode concept | What it loads | Export |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| Plugin (`plugin` config) | auth + provider registration + dynamic model listing + a refresh tool | `@stablekernel/opencode-cursor/plugin` |
|
|
56
|
+
| Provider (`provider.cursor.npm`) | a Vercel AI SDK `LanguageModelV3` that drives a local Cursor agent | `@stablekernel/opencode-cursor` (`createCursor`) |
|
|
57
|
+
|
|
58
|
+
The plugin's `config` hook registers `provider.cursor` (pointing `npm` at this package) and seeds
|
|
59
|
+
it with discovered/fallback models. The `auth` hook stores your API key and feeds it to the
|
|
60
|
+
provider factory; the key is validated on first use (model discovery / the first call), not at
|
|
61
|
+
login. The `provider.models()` hook refreshes the catalog live once you're authenticated.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install @stablekernel/opencode-cursor
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Add the plugin to your `opencode.json` (project or global):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"$schema": "https://opencode.ai/config.json",
|
|
74
|
+
"plugin": ["@stablekernel/opencode-cursor/plugin"]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You do **not** need to hand-write the `provider` block — the plugin injects it. If you prefer to
|
|
79
|
+
configure it explicitly, the equivalent is:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"provider": {
|
|
84
|
+
"cursor": {
|
|
85
|
+
"npm": "@stablekernel/opencode-cursor",
|
|
86
|
+
"name": "Cursor",
|
|
87
|
+
"options": { "apiKey": "{env:CURSOR_API_KEY}" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Authenticate
|
|
94
|
+
|
|
95
|
+
Either run the interactive login:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
opencode auth login # choose "Cursor", paste your key from the Cursor dashboard
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
…or set the environment variable the Cursor SDK reads:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
export CURSOR_API_KEY="key_..."
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The key is never logged or written to disk by this plugin. It is validated on first use (model
|
|
108
|
+
discovery and the first call surface an invalid key clearly), not at login time.
|
|
109
|
+
|
|
110
|
+
## Use
|
|
111
|
+
|
|
112
|
+
- `opencode models` (or the in-app model picker) lists your Cursor models as `cursor/<id>`.
|
|
113
|
+
- Pick a Cursor model and chat. The Cursor **local** agent runs in your project working directory.
|
|
114
|
+
- Run the `cursor_refresh_models` tool to force a live catalog refresh (bypasses the 24h cache).
|
|
115
|
+
|
|
116
|
+
This plugin also registers two **delegation tools** that complement the provider (see
|
|
117
|
+
[Delegation tools](#delegation-tools)):
|
|
118
|
+
|
|
119
|
+
- `cursor_delegate` — hand a discrete subtask to a local Cursor agent as a permission-gated,
|
|
120
|
+
auditable opencode tool call (your primary model stays in control).
|
|
121
|
+
- `cursor_cloud_agent` — launch a Cursor **cloud** (background) agent on a remote repo that can run
|
|
122
|
+
for minutes and optionally open a PR.
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
| Option (`provider.cursor.options`) | Default | Meaning |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `apiKey` | `CURSOR_API_KEY` | Cursor API key |
|
|
129
|
+
| `cwd` | `process.cwd()` | Directory the local agent operates in |
|
|
130
|
+
| `mode` | `"agent"` | Default Cursor conversation mode (`"agent"` or `"plan"`) |
|
|
131
|
+
| `params` | — | Default Cursor model params, `{ <id>: value }` (e.g. `{ thinking: "high" }`) |
|
|
132
|
+
| `settingSources` | — | Cursor settings layers to load from disk: `["project","user","all",...]` — pulls in your Cursor **skills**, rules, and `.cursor/mcp.json` |
|
|
133
|
+
| `sandbox` | — | Run the agent's tools inside Cursor's sandbox (`true`/`false`) |
|
|
134
|
+
| `agents` | — | Cursor subagent definitions (`{ <name>: { description, prompt, model?, mcpServers? } }`) |
|
|
135
|
+
| `session` | `false` | Reuse one Cursor agent per opencode session (resume across turns; see below) |
|
|
136
|
+
| `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
|
|
137
|
+
| `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)) |
|
|
139
|
+
|
|
140
|
+
### Session reuse (`session`)
|
|
141
|
+
|
|
142
|
+
By default each opencode turn spins up a **fresh** Cursor agent and re-sends the full conversation
|
|
143
|
+
transcript — robust, and correct even for opencode's non-chat calls (e.g. title generation). Set
|
|
144
|
+
`session: true` to instead keep **one Cursor agent per opencode session**: the provider names the
|
|
145
|
+
agent after the session, `Agent.resume()`s it on later turns, and sends only the new message so
|
|
146
|
+
Cursor uses its native conversation memory and checkpoints (the agent is visible in Cursor's
|
|
147
|
+
dashboard). The opencode session id reaches the provider via the plugin's `chat.params` hook
|
|
148
|
+
(`providerOptions.cursor.sessionID`); a failed resume falls back to a fresh turn automatically.
|
|
149
|
+
|
|
150
|
+
### Per-request controls (`mode`, thinking level)
|
|
151
|
+
|
|
152
|
+
opencode delivers per-request, provider-specific settings to the model under
|
|
153
|
+
`providerOptions.cursor`. This plugin reads:
|
|
154
|
+
|
|
155
|
+
- `mode` → `"agent"` | `"plan"`
|
|
156
|
+
- `params` → `{ <paramId>: value }` mapped to Cursor `ModelSelection.params`
|
|
157
|
+
- `thinking` → convenience, mapped to the `thinking` param
|
|
158
|
+
|
|
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:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{ "provider": { "cursor": { "models": {
|
|
166
|
+
"composer-2.5": { "options": { "params": { "thinking": "high" } } }
|
|
167
|
+
} } } }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
| Environment variable | Default | Meaning |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| `CURSOR_API_KEY` | — | API key fallback |
|
|
173
|
+
| `OPENCODE_CURSOR_MODEL_CACHE_TTL_MS` | `86400000` | Model-list cache lifetime |
|
|
174
|
+
|
|
175
|
+
To disable MCP forwarding, set `provider.cursor.options.forwardMcp: false` in your opencode config.
|
|
176
|
+
|
|
177
|
+
## MCP servers
|
|
178
|
+
|
|
179
|
+
The Cursor agent can use the **same MCP servers you've configured in opencode**. The plugin's
|
|
180
|
+
`config` hook reads opencode's `config.mcp`, translates each entry into the Cursor SDK's
|
|
181
|
+
`McpServerConfig` shape, and hands them to the agent via `Agent.create({ mcpServers })`:
|
|
182
|
+
|
|
183
|
+
| opencode `config.mcp` | → Cursor |
|
|
184
|
+
| --- | --- |
|
|
185
|
+
| `{ type: "local", command: [cmd, ...args], environment }` | `{ type: "stdio", command: cmd, args, env }` |
|
|
186
|
+
| `{ type: "remote", url, headers }` | `{ type: "http", url, headers }` |
|
|
187
|
+
|
|
188
|
+
So if your `opencode.json` defines Serena, your Cursor agent connects to that same Serena — MCP
|
|
189
|
+
servers are independent processes, so opencode and the agent each connect to them directly.
|
|
190
|
+
Disabled entries (`enabled: false`) are skipped. Turn this off with `forwardMcp: false`.
|
|
191
|
+
|
|
192
|
+
> Scope note: this forwards **MCP servers**. opencode's *loop-internal* features — its own skills
|
|
193
|
+
> and subagents — are not exposed to the Cursor agent (they run inside opencode's agent loop, which
|
|
194
|
+
> this provider bypasses). The Cursor agent's *own* skills/rules can be loaded with the
|
|
195
|
+
> `settingSources` option (e.g. `["project","user"]`), which reads your local Cursor configuration.
|
|
196
|
+
|
|
197
|
+
## Delegation tools
|
|
198
|
+
|
|
199
|
+
Alongside the provider, the plugin registers two tools so it is a **superset** of both the
|
|
200
|
+
provider and delegated-tool designs. Both resolve the Cursor API key from your `opencode auth login`
|
|
201
|
+
session (or `CURSOR_API_KEY`) and are **permission-gated** via opencode's `permission` config — they
|
|
202
|
+
call `context.ask`, so a policy of `allow` runs silently, `ask` prompts, and `deny` blocks:
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{ "permission": { "cursor_delegate": "ask", "cursor_cloud_agent": "ask" } }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### `cursor_delegate` (local)
|
|
209
|
+
|
|
210
|
+
Run a single Cursor turn on a fresh (or explicitly resumed) **local** agent and return its result.
|
|
211
|
+
Use it when your primary opencode model should stay in control and hand off discrete work to Cursor
|
|
212
|
+
as an explicit, auditable tool call.
|
|
213
|
+
|
|
214
|
+
| Arg | Required | Meaning |
|
|
215
|
+
| --- | --- | --- |
|
|
216
|
+
| `prompt` | ✅ | The subtask to delegate |
|
|
217
|
+
| `model` | ✅ | Cursor model id to run on |
|
|
218
|
+
| `mode` | — | `"agent"` or `"plan"` |
|
|
219
|
+
| `thinking` | — | Thinking level (e.g. `"high"`) |
|
|
220
|
+
| `cwd` | — | Working directory (defaults to the session directory) |
|
|
221
|
+
| `sandbox` | — | Run the agent's tools in Cursor's sandbox |
|
|
222
|
+
| `agentId` | — | Resume a specific Cursor agent id instead of starting fresh |
|
|
223
|
+
|
|
224
|
+
### `cursor_cloud_agent` (cloud / background)
|
|
225
|
+
|
|
226
|
+
Launch a Cursor **cloud** agent against a remote repository. It runs autonomously (potentially for
|
|
227
|
+
minutes) and can open a pull request — work that maps poorly onto the synchronous provider path, so
|
|
228
|
+
it is exposed as a tool. Returns the cloud agent id, terminal status, result text, and PR url.
|
|
229
|
+
|
|
230
|
+
| Arg | Required | Meaning |
|
|
231
|
+
| --- | --- | --- |
|
|
232
|
+
| `prompt` | ✅ | The task for the background agent |
|
|
233
|
+
| `repoUrl` | ✅ | Target repository URL (e.g. `https://github.com/owner/repo`) |
|
|
234
|
+
| `startingRef` | — | Branch/ref to start from (defaults to the repo default) |
|
|
235
|
+
| `model` | — | Cursor model id (optional for cloud) |
|
|
236
|
+
| `mode` | — | `"agent"` or `"plan"` |
|
|
237
|
+
| `thinking` | — | Thinking level (e.g. `"high"`) |
|
|
238
|
+
| `autoCreatePR` | — | Open a PR automatically when finished |
|
|
239
|
+
| `workOnCurrentBranch` | — | Operate on the current branch instead of a new one |
|
|
240
|
+
|
|
241
|
+
## Provider vs. delegation tools
|
|
242
|
+
|
|
243
|
+
This package ships two complementary ways to use Cursor inside opencode:
|
|
244
|
+
|
|
245
|
+
- **Provider** (`cursor/*` models) — chat with a Cursor model directly, integrated into opencode's
|
|
246
|
+
normal model/variant UX, with live model discovery, MCP forwarding, and session reuse. Cursor runs
|
|
247
|
+
its own tools internally (surfaced per the [`toolDisplay`](#tool-display) option).
|
|
248
|
+
- **Delegation tools** — `cursor_delegate` hands a discrete subtask to a local Cursor agent as a
|
|
249
|
+
permission-gated, auditable tool call (your primary opencode model stays in control); and
|
|
250
|
+
`cursor_cloud_agent` launches a background agent on a remote repo that can run for minutes and
|
|
251
|
+
optionally open a PR.
|
|
252
|
+
|
|
253
|
+
**When to use which.** Use the **provider** when you want Cursor to *be* a model you select and
|
|
254
|
+
converse with, integrated into opencode's normal model/variant UX. Use **`cursor_delegate`** when
|
|
255
|
+
you want your existing opencode model to stay in control and hand off discrete tasks as explicit,
|
|
256
|
+
permission-gated tool calls. Use **`cursor_cloud_agent`** when you need background work on a remote
|
|
257
|
+
repo with optional PR creation.
|
|
258
|
+
|
|
259
|
+
## Behavior & limitations
|
|
260
|
+
|
|
261
|
+
> The provider path runs Cursor's own unsandboxed tools outside opencode's permission system — see
|
|
262
|
+
> [Security](#security).
|
|
263
|
+
|
|
264
|
+
This plugin runs Cursor as a **local agent** (`Agent.create({ local: { cwd } })`), so:
|
|
265
|
+
|
|
266
|
+
- **Cursor executes its own tools** (read/write/edit/shell/grep/mcp/…) directly in your working
|
|
267
|
+
directory. How that activity is shown is controlled by the [`toolDisplay`](#tool-display) option.
|
|
268
|
+
Either way it is **not** routed through opencode's tool/permission system — Cursor runs the tools
|
|
269
|
+
itself.
|
|
270
|
+
- By default each turn creates a fresh local agent and sends the full conversation transcript, so
|
|
271
|
+
context is always complete. Enable `session: true` to reuse Cursor's native per-agent memory
|
|
272
|
+
across turns (see [Session reuse](#session-reuse-session)).
|
|
273
|
+
- Token usage is reported from Cursor's `turn-ended` event; cost is shown as `0` because Cursor
|
|
274
|
+
bills your account separately.
|
|
275
|
+
- **Provider path is local.** The `cursor/*` models you chat with run as a **local** agent. Cursor's
|
|
276
|
+
**cloud** runtime (background agents on a remote repo with optional PR creation) maps awkwardly
|
|
277
|
+
onto a synchronous provider call, so it is exposed as the `cursor_cloud_agent` **tool** instead of
|
|
278
|
+
the provider path — see [Delegation tools](#delegation-tools).
|
|
279
|
+
|
|
280
|
+
### Runtime: Bun and the Node sidecar
|
|
281
|
+
|
|
282
|
+
opencode runs on [Bun](https://bun.sh). Bun's `node:http2` client is currently incompatible with the
|
|
283
|
+
Cursor SDK's long-lived streaming RPC (it aborts the stream with `NGHTTP2_FRAME_SIZE_ERROR`), which
|
|
284
|
+
makes Cursor's native tool calls execute but never report completion — they appear stuck or show
|
|
285
|
+
"Tool execution aborted".
|
|
286
|
+
|
|
287
|
+
To work around this transparently, when the plugin detects it is running under Bun and finds `node`
|
|
288
|
+
on your `PATH`, it hosts the Cursor SDK agent in a short-lived **Node child process** (a "sidecar")
|
|
289
|
+
and talks to it over stdio. Behavior is otherwise identical. Under Node the SDK runs in-process and
|
|
290
|
+
no sidecar is spawned.
|
|
291
|
+
|
|
292
|
+
- **Requirement:** a Node.js runtime on `PATH` when running under Bun — Node 22+ to match `engines`
|
|
293
|
+
(the plugin checks that `node` is present, not its version). If Bun is detected but `node` is
|
|
294
|
+
missing, the plugin logs a one-time warning and falls back to in-process (native Cursor tools will
|
|
295
|
+
misbehave until Node is available).
|
|
296
|
+
- **Override** with the `OPENCODE_CURSOR_SIDECAR` environment variable:
|
|
297
|
+
- `OPENCODE_CURSOR_SIDECAR=1` — always use the sidecar (requires `node`).
|
|
298
|
+
- `OPENCODE_CURSOR_SIDECAR=0` — never use the sidecar / silence the Bun warning.
|
|
299
|
+
|
|
300
|
+
## Tool display
|
|
301
|
+
|
|
302
|
+
Cursor runs its own agent loop and executes its own tools. The `toolDisplay` option controls how
|
|
303
|
+
that activity appears in opencode:
|
|
304
|
+
|
|
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**
|
|
310
|
+
`tool-call`/`tool-result` parts so opencode renders proper, collapsible tool blocks with inputs
|
|
311
|
+
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
|
|
313
|
+
V3-native opencode host (1.16+).
|
|
314
|
+
|
|
315
|
+
Enable blocks mode in your opencode config:
|
|
316
|
+
|
|
317
|
+
```jsonc
|
|
318
|
+
{
|
|
319
|
+
"provider": {
|
|
320
|
+
"cursor": {
|
|
321
|
+
"options": { "toolDisplay": "blocks" }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
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.
|
|
330
|
+
|
|
331
|
+
## Troubleshooting
|
|
332
|
+
|
|
333
|
+
- **Native Cursor tools hang or show "Tool execution aborted" (`NGHTTP2_FRAME_SIZE_ERROR`).** This
|
|
334
|
+
is the Bun `node:http2` incompatibility. Make sure **Node.js is installed and on your `PATH`** so
|
|
335
|
+
the plugin can use the Node sidecar (see [Runtime](#runtime-bun-and-the-node-sidecar)); force it
|
|
336
|
+
with `OPENCODE_CURSOR_SIDECAR=1`.
|
|
337
|
+
- **"Running under Bun without a usable Node sidecar" warning.** Install Node.js 22+, or set
|
|
338
|
+
`OPENCODE_CURSOR_SIDECAR=0` to accept in-process behavior and silence the warning.
|
|
339
|
+
- **Only the four fallback models appear in the picker.** The live catalog loads after the first
|
|
340
|
+
authenticated use — restart opencode once after logging in, or run `cursor_refresh_models` to
|
|
341
|
+
force a refresh.
|
|
342
|
+
- **Invalid or expired key.** The key is validated on first use (model discovery / first call), not
|
|
343
|
+
at login, so that's where an error surfaces.
|
|
344
|
+
- **Need more detail?** Set `OPENCODE_CURSOR_DEBUG=1` for provider and sidecar trace logging on
|
|
345
|
+
stderr.
|
|
346
|
+
|
|
347
|
+
## Contributing
|
|
348
|
+
|
|
349
|
+
Issues and pull requests are welcome. See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for development
|
|
350
|
+
setup, the test/typecheck/build commands, and the release process. Please report bugs at the
|
|
351
|
+
[issue tracker](https://github.com/stablekernel/opencode-cursor/issues); for security reports
|
|
352
|
+
see **[SECURITY.md](./SECURITY.md)**.
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
MIT
|