@stablekernel/opencode-cursor 0.8.0 → 0.9.0-next.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 +47 -0
- package/README.md +110 -11
- package/dist/{chunk-YIEC27VB.js → chunk-HWSH5L4H.js} +24 -5
- package/dist/chunk-HWSH5L4H.js.map +1 -0
- package/dist/plugin/index.js +881 -25
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.js +1 -1
- package/dist/sidecar/agent-host.d.ts +32 -8
- package/dist/sidecar/agent-host.js +16 -1
- package/dist/sidecar/agent-host.js.map +1 -1
- package/dist/sidecar/plugin-tools-mcp.d.ts +189 -0
- package/dist/sidecar/plugin-tools-mcp.js +163 -0
- package/dist/sidecar/plugin-tools-mcp.js.map +1 -0
- package/package.json +7 -7
- package/dist/chunk-YIEC27VB.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.0-next.1] — 2026-08-26 (pre-release)
|
|
8
|
+
|
|
9
|
+
Fixes the Cursor SDK's shell-parser diagnostic leaking into the opencode
|
|
10
|
+
TUI prompt (#111). Still not on `latest`; install with
|
|
11
|
+
`npm install @stablekernel/opencode-cursor@next` to test.
|
|
12
|
+
|
|
13
|
+
- **Fix: `shell-parser: tree-sitter natives are unavailable…` no longer
|
|
14
|
+
appears in the TUI prompt.** `@cursor/sdk`'s bundled shell-parser emits a
|
|
15
|
+
one-shot `console.warn` when its vendored tree-sitter natives fail to load
|
|
16
|
+
(e.g. when opencode runs the plugin under Bun); opencode renders plugin
|
|
17
|
+
stderr into the prompt, so the line surfaced visually even though it is
|
|
18
|
+
benign (shell command analysis degrades to `parsingFailed`, which the SDK
|
|
19
|
+
handles). The existing `console.log` interceptor for the SDK's rules/skills
|
|
20
|
+
load diagnostics now covers `console.warn` on both transports: in-process,
|
|
21
|
+
known SDK warnings route through `pluginLog("warn")` to opencode's
|
|
22
|
+
`app.log` instead of stderr; in the Node sidecar, matched lines forward as
|
|
23
|
+
structured `{ev:"log", level:"warn"}` events over the JSONL protocol.
|
|
24
|
+
Unrelated `console.warn` output passes through unchanged, and the message
|
|
25
|
+
remains visible in opencode logs (service `opencode-cursor`).
|
|
26
|
+
|
|
27
|
+
## [0.9.0-next.0] — 2026-08-26 (pre-release)
|
|
28
|
+
|
|
29
|
+
The Cursor agent can now use installed opencode plugins (#104), plus
|
|
30
|
+
consolidated opencode-ai dependency bumps (#107). Not yet on `latest`;
|
|
31
|
+
install with `npm install @stablekernel/opencode-cursor@next` to test.
|
|
32
|
+
|
|
33
|
+
- **Plugin tools bridge: other plugins' custom tools are now exposed to the
|
|
34
|
+
Cursor agent.** Custom tools from installed opencode plugins (e.g.
|
|
35
|
+
`opencode-pty`'s `pty_spawn`, `context-mode`'s `ctx_*`) are bridged to
|
|
36
|
+
Cursor via an `opencode-plugin-tools` MCP server (hand-rolled stdio
|
|
37
|
+
JSON-RPC, no MCP SDK dependency, running under Bun). Permission handling
|
|
38
|
+
mirrors opencode's `Permission.evaluate` — last matching rule wins per ask
|
|
39
|
+
pattern; a rule resolving to `ask` (which can't prompt from Cursor) or no
|
|
40
|
+
match fails closed. Controlled by `forwardPluginTools` and
|
|
41
|
+
`pluginTools.include/exclude`.
|
|
42
|
+
- **Plugin-bundled skills are mirrored into `.cursor/skills/` too.** New
|
|
43
|
+
lowest-priority scan roots in `discoverSkills`: the opencode plugin cache
|
|
44
|
+
(`~/.cache/opencode/packages/`) and `skills/`/`skill/` dirs alongside
|
|
45
|
+
file-based plugins, handling npm and git plugin specs. The per-turn re-sync
|
|
46
|
+
also merges opencode's live skills inventory at the same priority. No new
|
|
47
|
+
config surface — folds into `forwardSkills`; `skills.include/exclude` and
|
|
48
|
+
permission filtering apply unchanged. Project/global skills always win on
|
|
49
|
+
duplicate ids; user-owned skills are never overwritten.
|
|
50
|
+
- **Dependency bumps:** `@opencode-ai/plugin` ^1.18.4 → ^1.18.21 (deps),
|
|
51
|
+
`@opencode-ai/sdk` ^1.18.18 → ^1.18.21 (dev). Consolidates dependabot
|
|
52
|
+
PRs #105 and #106.
|
|
53
|
+
|
|
7
54
|
## [0.8.0] — 2026-08-21
|
|
8
55
|
|
|
9
56
|
Live Cursor subagent activity: the `task` card behaves like a native opencode
|
package/README.md
CHANGED
|
@@ -158,6 +158,7 @@ The plugin also registers two **delegation tools**:
|
|
|
158
158
|
> `edit: deny`, `bash: ask`) do **not** apply to them.
|
|
159
159
|
>
|
|
160
160
|
> Options if you need a permission boundary:
|
|
161
|
+
>
|
|
161
162
|
> - Set `sandbox: true` in `provider.cursor.options` to run Cursor's tools in Cursor's sandbox.
|
|
162
163
|
> - Use `cursor_delegate` instead of the provider path — it is gated by opencode's `permission`
|
|
163
164
|
> config.
|
|
@@ -302,17 +303,34 @@ The mirror includes:
|
|
|
302
303
|
- **Global skills** from `~/.config/opencode/skills/` and `~/.config/opencode/skill/`,
|
|
303
304
|
`~/.claude/skills/`, `~/.agents/skills/`, `~/.opencode/skills/` and `~/.opencode/skill/`.
|
|
304
305
|
- **Configured paths** from `config.skills.paths` in your `opencode.json` — additional
|
|
305
|
-
directories scanned at
|
|
306
|
+
directories scanned at low priority (project and standard global locations
|
|
306
307
|
win on duplicate ids). `~/` prefixes are expanded to your home directory; relative
|
|
307
308
|
paths are resolved against the project directory.
|
|
309
|
+
- **Plugin-bundled skills** — skills that ship inside installed opencode plugins,
|
|
310
|
+
scanned from the opencode plugin cache (`~/.cache/opencode/packages/` on
|
|
311
|
+
macOS/Linux; `%LocalAppData%\opencode\cache\packages\` on Windows) and from
|
|
312
|
+
`skills/`/`skill/` dirs alongside file-based plugins
|
|
313
|
+
(`~/.config/opencode/plugin/`, `~/.config/opencode/plugins/`, and the project's
|
|
314
|
+
`.opencode/plugin/`). Handles npm specs (`pkg@latest`, `@scope/pkg@latest`) and
|
|
315
|
+
git specs (`pkg@git+https:...`). Plugin-bundled skills are the **lowest**
|
|
316
|
+
priority: a project, global, or `skills.paths` skill with the same id always
|
|
317
|
+
wins, so you can shadow a plugin's skill by defining your own.
|
|
318
|
+
- **opencode's live skill inventory** — on every turn the mirror also consults
|
|
319
|
+
opencode's `app.skills` endpoint (when reachable) and merges any skill it
|
|
320
|
+
knows about that the filesystem scan missed, at the same lowest priority.
|
|
321
|
+
- **opencode's built-in skills** — skills opencode registers in code rather
|
|
322
|
+
than on disk (currently `customize-opencode`, its own config-authoring
|
|
323
|
+
guide). They only exist in the live inventory, so the mirror materialises
|
|
324
|
+
them from the endpoint's content into `.cursor/skills/` like any other
|
|
325
|
+
skill; the materialised copy updates whenever opencode's version changes.
|
|
308
326
|
- **Supporting files** alongside each `SKILL.md` (preserving relative paths).
|
|
309
327
|
- An `<available_skills>` catalogue appended to the generated system rule,
|
|
310
328
|
listing each skill's id and description so the Cursor agent can load them on
|
|
311
329
|
demand.
|
|
312
330
|
|
|
313
|
-
> **Note:** `config.skills.urls`
|
|
314
|
-
>
|
|
315
|
-
>
|
|
331
|
+
> **Note:** URL-sourced skills (`config.skills.urls`) that are also present in
|
|
332
|
+
> opencode's live inventory reach the mirror through that route; the mirror
|
|
333
|
+
> does not fetch `skills.urls` catalogs on its own.
|
|
316
334
|
|
|
317
335
|
### Permission filtering
|
|
318
336
|
|
|
@@ -346,13 +364,12 @@ user explicitly asked for them). `exclude` always drops the listed skills.
|
|
|
346
364
|
|
|
347
365
|
### Limitations
|
|
348
366
|
|
|
349
|
-
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
mirror one, add its directory to `config.skills.paths`.
|
|
367
|
+
- Skills served via `skills.urls` that opencode itself hasn't loaded (the
|
|
368
|
+
endpoint is reachable but the catalog wasn't pulled this session) won't
|
|
369
|
+
appear until opencode sees them.
|
|
370
|
+
- Built-in skills require the live `app.skills` endpoint (i.e. a running
|
|
371
|
+
opencode server reachable by this plugin); the filesystem scan can't see
|
|
372
|
+
them on its own.
|
|
356
373
|
- A user-owned `.cursor/skills/<id>/SKILL.md` (without the `generated:
|
|
357
374
|
opencode-cursor` sentinel) is never overwritten or deleted.
|
|
358
375
|
- Individual files larger than 1 MB are skipped (the rest of the skill is still
|
|
@@ -362,6 +379,88 @@ user explicitly asked for them). `exclude` always drops the listed skills.
|
|
|
362
379
|
any pre-existing `.cursor/skills/` there still loads).
|
|
363
380
|
- `cursor_cloud_agent` targets a remote repo and does not inherit skills.
|
|
364
381
|
|
|
382
|
+
## Plugin tools
|
|
383
|
+
|
|
384
|
+
Other opencode plugins can register custom tools (e.g. `opencode-pty`'s
|
|
385
|
+
`pty_spawn`, `context-mode`'s `ctx_execute`). With `forwardPluginTools: true`
|
|
386
|
+
(default), this plugin **mirrors those tools to the Cursor agent** via a local
|
|
387
|
+
stdio MCP server (`opencode-plugin-tools`) that is added to the forwarded
|
|
388
|
+
`mcpServers`. When the Cursor agent calls one, the call runs through the
|
|
389
|
+
plugin's *real* implementation inside opencode's runtime — same code path
|
|
390
|
+
opencode itself uses.
|
|
391
|
+
|
|
392
|
+
How it works:
|
|
393
|
+
|
|
394
|
+
1. At startup (and re-checked each turn) the plugin reads your `plugin: []`
|
|
395
|
+
list, re-imports each plugin from the opencode package cache, and reads its
|
|
396
|
+
`tool` map. Plugins that can't be imported are skipped and logged once.
|
|
397
|
+
2. A loopback-only HTTP control channel (random port, per-session bearer
|
|
398
|
+
token) connects the MCP child process to the host plugin, which owns the
|
|
399
|
+
tool closures. Nothing is reachable from outside the machine.
|
|
400
|
+
3. The Cursor agent sees the tools through its normal MCP surface and calls
|
|
401
|
+
them like any other MCP tool.
|
|
402
|
+
|
|
403
|
+
### Permissions
|
|
404
|
+
|
|
405
|
+
Mirrored calls are evaluated against your opencode `permission` config,
|
|
406
|
+
keyed by the tool id — with the same semantics opencode itself uses:
|
|
407
|
+
permission keys are wildcard-matched, and every pattern a tool's `ask`
|
|
408
|
+
requests is matched against the rule's pattern; the **last** matching rule
|
|
409
|
+
wins. `~`/`$HOME` prefixes in patterns expand against your home directory.
|
|
410
|
+
|
|
411
|
+
- **`allow`** → runs without prompting (every requested pattern must allow).
|
|
412
|
+
- **`deny`** → rejected.
|
|
413
|
+
- **`ask`** (or no rule) → rejected with a clear message. The interactive
|
|
414
|
+
prompt is anchored to opencode's session/TUI and can't be surfaced to the
|
|
415
|
+
Cursor agent, so ask-permissioned tools are withheld rather than run
|
|
416
|
+
unattended. Set the tool to `allow` to use it from Cursor:
|
|
417
|
+
|
|
418
|
+
```json
|
|
419
|
+
{ "permission": { "pty_spawn": "allow", "ctx_*": "allow" } }
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Pattern-scoped rules work too — e.g. allow spawning ptys only under `/tmp`
|
|
423
|
+
(specific patterns must come **after** the wildcard they narrow):
|
|
424
|
+
|
|
425
|
+
```json
|
|
426
|
+
{ "permission": { "pty_spawn": { "*": "ask", "/tmp/*": "allow" } } }
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
If a tool's execution calls `ask` internally and no gate is available, the
|
|
430
|
+
call fails closed.
|
|
431
|
+
|
|
432
|
+
### Filtering
|
|
433
|
+
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"provider": {
|
|
437
|
+
"cursor": {
|
|
438
|
+
"options": {
|
|
439
|
+
"forwardPluginTools": true,
|
|
440
|
+
"pluginTools": {
|
|
441
|
+
"include": ["pty_*"],
|
|
442
|
+
"exclude": ["ctx_execute"]
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
`include` keeps only matching tool ids (wildcards supported); `exclude` always
|
|
451
|
+
drops. `forwardPluginTools: false` disables the bridge entirely.
|
|
452
|
+
|
|
453
|
+
### Limitations
|
|
454
|
+
|
|
455
|
+
- Plugins that fail to re-import under the bridge (e.g. native modules that
|
|
456
|
+
only load under Bun, or plugins that throw when initialised twice) are
|
|
457
|
+
skipped and reported in the opencode log; their tools stay unavailable to
|
|
458
|
+
Cursor. Skills and MCP servers from those plugins are unaffected.
|
|
459
|
+
- Tool definitions are snapshotted at startup and re-checked per turn; a
|
|
460
|
+
plugin installed mid-session is picked up on the next turn.
|
|
461
|
+
- `cursor_cloud_agent` targets a remote repo and does not inherit plugin
|
|
462
|
+
tools.
|
|
463
|
+
|
|
365
464
|
## Delegation tools
|
|
366
465
|
|
|
367
466
|
Both tools resolve the API key from your `opencode auth login` session (or `CURSOR_API_KEY`) and
|
|
@@ -86,6 +86,12 @@ function stripAnsi(input) {
|
|
|
86
86
|
return input.replace(ANSI_PATTERN, "");
|
|
87
87
|
}
|
|
88
88
|
var RULE_LOAD_PATTERN = /^\d{2}:\d{2}:\d{2}\.\d{3}\s+INFO\s+(LocalCursorRulesService|AgentSkillsCursorRulesService|CursorPluginsAgentSkillsService) load completed(?:\s+ctx=\S+)?\s+meta=\{([^}]*)\}\s*$/;
|
|
89
|
+
var SDK_WARNING_PREFIXES = [
|
|
90
|
+
"shell-parser: tree-sitter natives are unavailable in this artifact"
|
|
91
|
+
];
|
|
92
|
+
function matchesKnownSdkWarning(line) {
|
|
93
|
+
return SDK_WARNING_PREFIXES.some((prefix) => line.startsWith(prefix));
|
|
94
|
+
}
|
|
89
95
|
function parseCursorLogMeta(raw) {
|
|
90
96
|
const out = {};
|
|
91
97
|
for (const part of raw.split(",")) {
|
|
@@ -104,11 +110,12 @@ function parseCursorRuleLoadLine(line) {
|
|
|
104
110
|
return { service, meta: parseCursorLogMeta(meta ?? "") };
|
|
105
111
|
}
|
|
106
112
|
var installed = false;
|
|
107
|
-
var
|
|
113
|
+
var originalLog;
|
|
114
|
+
var originalWarn;
|
|
108
115
|
function installCursorLogInterceptor() {
|
|
109
116
|
if (installed) return;
|
|
110
|
-
|
|
111
|
-
const
|
|
117
|
+
originalLog = console.log.bind(console);
|
|
118
|
+
const logPassthrough = originalLog;
|
|
112
119
|
console.log = (...args) => {
|
|
113
120
|
if (args.length === 1 && typeof args[0] === "string") {
|
|
114
121
|
const parsed = parseCursorRuleLoadLine(args[0]);
|
|
@@ -117,7 +124,19 @@ function installCursorLogInterceptor() {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
|
-
|
|
127
|
+
logPassthrough(...args);
|
|
128
|
+
};
|
|
129
|
+
originalWarn = console.warn.bind(console);
|
|
130
|
+
const warnPassthrough = originalWarn;
|
|
131
|
+
console.warn = (...args) => {
|
|
132
|
+
if (args.length === 1 && typeof args[0] === "string") {
|
|
133
|
+
const line = stripAnsi(args[0]);
|
|
134
|
+
if (matchesKnownSdkWarning(line)) {
|
|
135
|
+
pluginLog("warn", line);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
warnPassthrough(...args);
|
|
121
140
|
};
|
|
122
141
|
installed = true;
|
|
123
142
|
}
|
|
@@ -1577,4 +1596,4 @@ export {
|
|
|
1577
1596
|
withSessionLock,
|
|
1578
1597
|
acquireAgent
|
|
1579
1598
|
};
|
|
1580
|
-
//# sourceMappingURL=chunk-
|
|
1599
|
+
//# sourceMappingURL=chunk-HWSH5L4H.js.map
|