Use Claude Code plugins inside OpenCode.
Claude Code has a thriving plugin ecosystem (172+ plugins in the official marketplace alone — skills, MCP servers, slash commands, hooks). OpenCode doesn't natively read that format. plugger is the bridge: register Claude Code marketplaces, browse plugins, install them, and have everything actually activate inside OpenCode.
/plugin
┌──── Plugger · Claude Code plugins for OpenCode ───────────┐
│ ▸ Discover Browse the official CC marketplace │
│ Installed Plugins cloned here, Update / Uninstall│
│ Marketplaces Add / Refresh / Remove marketplaces │
│ + Add marketplace │
└───────────────────────────────────────────────────────────┘
Without plugger, using a Claude Code plugin in OpenCode means: clone the
repo by hand, figure out where each piece goes (commands/*.md here,
skills/<name>/SKILL.md there, .mcp.json merged into opencode.json,
hooks rewritten as JS plugins), and redo it every project. plugger does all
of that on Install, and undoes it on Uninstall.
It works with:
- Slash commands —
<plugin>/commands/*.mdactivate as OpenCode commands. - Skills — full skill trees (markdown + supporting scripts/files) land in OpenCode's skill loader.
- MCP servers — both stdio (
{command, args, env}) and remote ({type, url, headers}) shapes; merged intoopencode.jsonunder namespaced keys. - Hooks (
PreToolUse/PostToolUse) — translated into a generated ESM shim that runs the bash command viachild_processfrom OpenCode'stool.execute.before/after.
Plugger ships as two npm packages — server module to opencode.json, TUI
module to tui.json. (OpenCode's plugin spec resolver treats the whole
string as a package name, so subpaths like pkg/tui don't work — hence two
sibling packages on npm, released together from the same git tag.)
// ~/.config/opencode/tui.json
{
"plugin": ["@sulesky/opencode-plugger-tui@latest"]
}Restart OpenCode. Bun fetches both packages into ~/.cache/opencode/packages/
on first launch (TUI pulls server in transitively as a dependency).
Project-scoped install works the same — drop the same files at the project root and the spec only applies inside that project.
AI-assistant install: paste INSTALL.md at Claude Code / OpenCode / Cursor and let it run the steps. The file ships idempotent Node one-liners that won't clobber your other plugins, model setting, or MCP servers.
Open the TUI and run /plugin:
The official Claude Code marketplace, sorted by real install counts. Pick a
plugin → choose scope (Global or This project) → confirm. Already-installed
plugins are shown with ✓ and can't be selected again.
Lists both global (~/.opencode/plugins/) and project
(<projectDir>/.plugger/plugins/) installations, labelled by scope. Per row:
- Update — re-fetch from the original source and re-run the translator.
- Uninstall — wipes the clone and every translated artifact in that scope.
Register more marketplaces by owner/repo shorthand or any git URL.
Browse / Refresh / Remove. The official anthropics/claude-plugins-official
is auto-registered on first open.
The server module also exposes everything as tools the agent can call:
marketplace_add, marketplace_list, marketplace_remove,
plugin_marketplace_search, plugin_marketplace_install,
plugin_marketplace_list.
| Clone goes to | Artifacts go to | |
|---|---|---|
| global (default) | ~/.opencode/plugins/<id>/ |
~/.config/opencode/{commands,skills,hook-shims}/<id>/ + ~/.config/opencode/opencode.json |
| project | <projectDir>/.plugger/plugins/<id>/ |
<projectDir>/.opencode/{commands,skills,hook-shims}/<id>/ + <projectDir>/opencode.json |
The same plugin can live in both at once — each install is independent, and uninstalling one scope doesn't touch the other.
Uninstall is deterministic — every cleanup decision is rebuilt from the plugin id and the scope, not from the install record on disk. A corrupted or hand-edited meta file can't make uninstall skip artifacts, and it can't trick uninstall into removing someone else's MCP keys or plugin entries.
Specifically:
<configDir>/commands/<id>/and<configDir>/skills/<id>/— wiped wholesale.<configDir>/hook-shims/<id>.js— removed; the sharedpackage.jsonnext to it stays unless that was the last shim.opencode.json— MCP keys with the<id>--prefix and the plugin's shim entry inplugin[]are filtered out; emptymcp:{}/plugin:[]containers are dropped.- The plugin clone itself.
- CC
agents,outputStyles,lspServers— no OpenCode equivalent, warned and skipped. - CC hook events outside
PreToolUse/PostToolUse(SessionStart,UserPromptSubmit,Stop,Notification) — same. - CC hook
decision:"block"protocol — the generated shim ignores hook stdout. Hooks run for their side effects only.
bun install
npm run build # tsc → dist/ + bun build → dist-tui/
npm test # 75 tests
npm run typecheckRun against your working copy by pointing OpenCode at the dist paths instead of the npm spec:
// opencode.json
{ "plugin": ["file:///abs/path/to/repo/dist/index.js"] }
// tui.json
{ "plugin": ["file:///abs/path/to/repo/dist-tui/index.js"] }Tests use tmpdir for scope and inject a baseDir into MarketplaceManager,
so bun test doesn't touch your real ~/.opencode/ or ~/.config/opencode/.
npm version patch # bump + git tag
git push --follow-tags # tag push triggers .github/workflows/release.ymlWatch Actions. One tag, two npm publishes from the same git ref:
- Server (
@sulesky/opencode-plugger) — straight from the repo'spackage.json. - TUI (
@sulesky/opencode-plugger-tui) — workflow templatespackage.tui.jsonin aspackage.json(filling in the matching version + server dep), publishes, then restores the serverpackage.json.
Tag must match package.json version. Typecheck + build + tests run first;
on failure neither package is published.
One-time setup: npm token create --type=automation, add as repo secret
NPM_TOKEN (Settings → Secrets and variables → Actions).