Skip to content

feat: mirror opencode skills into .cursor/skills/ for Cursor agent discovery - #78

Open
WayneSimpson wants to merge 1 commit into
stablekernel:mainfrom
WayneSimpson:feat/skills-bridge
Open

feat: mirror opencode skills into .cursor/skills/ for Cursor agent discovery#78
WayneSimpson wants to merge 1 commit into
stablekernel:mainfrom
WayneSimpson:feat/skills-bridge

Conversation

@WayneSimpson

Copy link
Copy Markdown

Summary

This PR adds a skills bridge that mirrors opencode's resolved skills into <cwd>/.cursor/skills/ so the Cursor agent (and Cursor sub-agents) can discover and load them natively via the project settings layer.

What it does

  • Discovers skills from all standard opencode locations: project .opencode/skills/, .claude/skills/, .agents/skills/ (walked up to the git worktree root), global ~/.config/opencode/skills/, plus config.skills.paths directories (lowest priority, first-wins on duplicate ids).
  • Filters through opencode's live permission config (allow/deny/ask). ask-permissioned skills are withheld (the ask prompt can't cross the Cursor boundary). Manual skills: { include, exclude } override is supported with wildcard patterns.
  • Materialises as a git-ignored mirror with a generated: opencode-cursor sentinel in each SKILL.md's frontmatter. User-owned skill directories (no sentinel) are never overwritten. Supporting files are copied alongside, with per-file 1MB and total 10MB caps.
  • Catalogues skills in an <available_skills> section appended to the generated system rule, so the Cursor agent knows what's available and can load skills on demand.
  • Re-syncs each turn via the chat.params hook (hash-gated, includes all file mtimes + sizes). The current catalogue is always forwarded per turn, including an empty string when all skills are removed (clearing the catalogue from the rule).
  • Disposes cleanly — removeSkillMirror and removeSystemRule delete only sentinel-bearing files on plugin dispose.

New files

File Purpose
src/plugin/skill-discovery.ts Filesystem walk, frontmatter parsing, permission filtering, path expansion, skillSetHash
src/provider/skill-mirror.ts Materialisation, sentinel stamping, git-ignore, stale-dir pruning, size caps, dispose, catalogue builder
scripts/verify-skill-mirror.mjs Standalone end-to-end verification script
test/skill-discovery.test.ts 25 tests — discovery, filtering, precedence, wildcards, hash
test/skill-mirror.test.ts 14 tests — materialisation, sentinel, idempotency, pruning, size limits, dispose
test/plugin-skill-mirror.test.ts 10 tests — config hook integration, chat.params re-sync, dispose, forwardSkills opt-out

Updated files

  • src/plugin/index.tsforwardSkills option, config-hook materialisation, chat.params live re-sync, dispose cleanup
  • src/provider/language-model.ts — dynamic + static catalogue resolution (no self-provisioning — respects forwardSkills: false)
  • src/provider/system-rule.tswriteSystemRule/resolveSystemDelivery accept optional skillsCatalogue
  • src/provider/delegate.ts + src/plugin/cursor-tools.tssettingSources: ["project"] for delegate
  • src/provider/index.tsskillsCatalogue in provider options
  • README.md — Skills section, options table, limitations
  • CHANGELOG.md[Unreleased] entry
  • SECURITY.md — skills mirror threat model
  • .gitignore — exclude .cursor/ (plugin-generated runtime artifacts)

Test results

403 tests passed
typecheck: clean
build: clean

Security

No API keys, tokens, or personal identifiers in any committed file. The CURSOR_API_KEY is only referenced by name (existing pattern). The skills mirror threat model is documented in SECURITY.md: skill instructions cross the permission boundary once written to disk, deny/ask skills are excluded, and user-owned files are never overwritten.

Limitations

  • config.skills.urls (HTTP catalogs) are not supported — documented in README and SECURITY.md.
  • cursor_cloud_agent targets a remote repo and does not inherit skills.
  • cursor_delegate with a non-session cwd does not mirror skills (but does pass settingSources: ["project"] so pre-existing .cursor/skills/ there still loads).

Verification

Live tested with opencode run --model cursor/composer-2.5 — the Cursor agent successfully discovered, read, and used the mirrored using-agent-skills skill. Standalone verification (node scripts/verify-skill-mirror.mjs) confirms all 46 skills discovered, mirrored, catalogued, and disposed cleanly.

…scovery

Discover opencode's resolved skills (project + global + config.skills.paths),
filter through permission config, and materialise them as a git-ignored mirror
in <cwd>/.cursor/skills/ with a 'generated: opencode-cursor' sentinel. An
<available_skills> catalogue is appended to the generated system rule so the
Cursor agent can discover and load skills on demand.

- New: src/plugin/skill-discovery.ts — filesystem walk, frontmatter parsing,
  permission filtering (map-form + rule-array), extraPaths, path expansion,
  skillSetHash including all files (mtime + size).
- New: src/provider/skill-mirror.ts — materialisation with sentinel, git-ignore,
  idempotent writes, stale-dir pruning, user-owned protection, per-file 1MB
  skip, total 10MB cap, removeSkillMirror for dispose, buildSkillsCatalogue.
- Updated: src/provider/system-rule.ts — writeSystemRule and
  resolveSystemDelivery accept optional skillsCatalogue appended to rule body.
- Updated: src/plugin/index.ts — forwardSkills option (default true),
  skills.include/exclude override, materialisation in config hook, live
  re-sync in chat.params hook (hash-gated), currentSkillsCatalogue always
  forwarded per turn, removeSkillMirror in dispose.
- Updated: src/provider/language-model.ts — dynamic + static catalogue
  resolution (no self-provisioning — respects forwardSkills:false).
- Updated: src/provider/delegate.ts + src/plugin/cursor-tools.ts —
  settingSources: ['project'] passed to delegate's acquireAgent.
- Updated: src/provider/index.ts — skillsCatalogue in provider options.
- Tests: 403 passing (25 skill-discovery, 14 skill-mirror, 10 plugin-skill-
  mirror, 6 catalogue in language-model-system, plus existing).
- Docs: README.md (Skills section), CHANGELOG.md ([Unreleased]),
  SECURITY.md (skills mirror threat model).
- .gitignore: exclude .cursor/ (plugin-generated runtime artifacts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant