Repository navigation
Commit c3081bb
feat: allow the Cursor agent to use opencode plugins (#104)
* feat: let the Cursor agent use opencode plugins
Mirror plugin-bundled skills into .cursor/skills/ and bridge other
plugins' custom tools to the Cursor agent via a local stdio MCP server.
Skills (folds into forwardSkills):
- Scan the opencode plugin cache (~/.cache/opencode/packages/, Windows
fallback) and file-plugin sibling skill dirs as lowest-priority roots
after project/global/skills.paths; first-wins on duplicate ids.
- Merge opencode's live app.skills inventory per turn at the same
lowest priority, covering sources the filesystem scan can't see.
Plugin tools (new forwardPluginTools option, default on):
- Re-import plugin modules from the package cache and read their tool
maps — the same closures opencode executes.
- Mirrored tools run behind a loopback, token-authenticated control
channel; a dependency-free stdio MCP server (opencode-plugin-tools)
is merged into the forwarded mcpServers at startup and re-checked
each turn.
- Permission gate mirrors opencode semantics: wildcard keys, per-ask
pattern evaluation, last match wins, ~/ $HOME pattern expansion,
ask/unconfigured fails closed with a clear message.
- pluginTools.include/exclude filter mirrored tool ids.
* test: isolate XDG env vars in skill-discovery tests
CI's GitHub runners carry real XDG_CONFIG_HOME/XDG_CACHE_HOME; the
discovery code prefers those env vars over the mocked homedir, so the
worker's real config dir leaked into the fakeHome-based expectations.
Point both vars at per-file fake dirs for the worker's lifetime.
* chore: drop implementation plans from tracked docs
* feat: mirror opencode built-in skills from the live inventory
opencode registers skills in code (currently `customize-opencode`,
location `<built-in>`) that have no on-disk SKILL.md, so neither the
filesystem scan nor the live merge reached them — the live entry was
dropped because its location isn't resolvable.
Materialise content-only live skills (name + description + content from
`app.skills`) into a per-process scratch dir with generated frontmatter,
so the mirror stamps and copies them like disk-backed skills. The copy
is only rewritten when the content or description changes, keeping
per-turn mtimes (and the skill-set hash) stable.
Verified against a live `opencode debug skill --pure` dump: 23 skills
parsed, `customize-opencode` lands in `.cursor/skills/` with the
sentinel and appears in the `<available_skills>` catalogue.
* fix: fetch live skills via raw GET /skill when SDK lacks app.skills
The V1 SDK typings this repo builds against (1.18.18) predate the
instance route (OpenApi identifier app.skills, path GET /skill), so
client.app.skills was undefined at runtime and the live-skill merge —
including opencode's <built-in> customize-opencode skill — silently
never ran.
Fall back to the hey-api core client underneath the typed groups
(client._client.get({ url: "/skill" })), verified against SDK 1.18.18
and opencode 1.18.21: returns { data: Skill[] } including the built-in
skill with its full 16 KB content.
Live E2E: temp project, branch build via project plugin entry, real
cursor provider (auto-smart) — customize-opencode landed in
.cursor/skills/ mid-session with the generated sentinel, 45 skills
mirrored total.
* fix: bound sidecar control-channel fetches with timeouts
controlRequest had no abort signal: a stale port (something accepting
but never responding, e.g. a hung opencode holding the port after the
bridge closed) would hang Cursor's MCP discovery on tools/list, and
tools/call could block forever.
tools/list gets 5s (loopback list is instant; discovery degrades to an
empty tool list on timeout) and tools/call gets 5 minutes (plugin tools
can legitimately run long). New test spawns the sidecar against a
never-responding server and asserts tools/list answers [] fast.
* fix(plugin-tools): keep bridge alive on live config refresh
Resolve node/bun for the MCP sidecar when execPath is a compiled host,
add hung-control timeouts, and retain the plugin-tools bridge when live
config.get omits plugin entries so chat.params does not tear it down.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>1 parent 143c189 commit c3081bb
10 files changed
Lines changed: 3145 additions & 82 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
| 161 | + | |
161 | 162 | | |
162 | 163 | | |
163 | 164 | | |
| |||
302 | 303 | | |
303 | 304 | | |
304 | 305 | | |
305 | | - | |
| 306 | + | |
306 | 307 | | |
307 | 308 | | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
308 | 326 | | |
309 | 327 | | |
310 | 328 | | |
311 | 329 | | |
312 | 330 | | |
313 | | - | |
314 | | - | |
315 | | - | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
316 | 334 | | |
317 | 335 | | |
318 | 336 | | |
| |||
346 | 364 | | |
347 | 365 | | |
348 | 366 | | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
356 | 373 | | |
357 | 374 | | |
358 | 375 | | |
| |||
362 | 379 | | |
363 | 380 | | |
364 | 381 | | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
365 | 464 | | |
366 | 465 | | |
367 | 466 | | |
| |||
0 commit comments