Skip to content

fix(opencode): port plugin to the OpenCode v2 plugin API#2004

Open
saiqulhaq wants to merge 1 commit into
obra:devfrom
saiqulhaq:opencode-v2-support
Open

fix(opencode): port plugin to the OpenCode v2 plugin API#2004
saiqulhaq wants to merge 1 commit into
obra:devfrom
saiqulhaq:opencode-v2-support

Conversation

@saiqulhaq

Copy link
Copy Markdown

Who is submitting this PR? (required)

Field Value
Your model + version Claude Opus 4.8 (1M context), model ID claude-opus-4-8[1m]
Harness + version Claude Code 2.1.179 (code authored here)
All plugins installed superpowers (Claude Code plugin) — the code was tested against the plugin being changed; plus unrelated MCP servers (ClickUp, Slack, Gmail, Obsidian) that played no role
Human partner who reviewed this diff @saiqulhaq — reviewed the full diff and ran it locally against OpenCode v2 before submission

What problem are you trying to solve?

The bundled OpenCode plugin (.opencode/plugins/superpowers.js) targets the OpenCode v1 plugin API and does not function under OpenCode v2. OpenCode's own migration guide states plainly: "V1 plugins will not work in V2." (https://v2.opencode.ai/migrate-v1)

Concretely, under v2 the current plugin is loaded but never does anything, because v2 replaced the entire plugin contract:

  • v1 exported a named SuperpowersPlugin({ client, directory }) factory; v2 expects a default { id, setup } export. v2 never calls the v1 factory, so setup never runs.
  • v1 registered the skills directory by mutating config.skills.paths inside a config hook; that hook does not exist in v2.
  • v1 injected the bootstrap via experimental.chat.messages.transform over { info, parts } messages; v2 removed that hook and changed the message shape to { role, content: [{ type, text }] }.

Result on OpenCode v2: no skills registered, no bootstrap injected, so no superpowers skills auto-trigger. Verified on a real opencode2 (next-15718) install: with the old plugin the using-superpowers bootstrap is absent and skills never fire.

What does this PR change?

Rewrites .opencode/plugins/superpowers.js against the OpenCode v2 plugin API ({ id, setup } default export, ctx.skill.transform for skill registration, ctx.session.hook("context", …) for bootstrap injection), and updates the OpenCode tests and docs to match. Behavior is otherwise identical to the v1 plugin (same bootstrap content, same module-level caching from #1202, same double-injection guard).

Is this change appropriate for the core library?

Yes. This is not a new harness or a new skill — it repairs an existing, first-party integration that ships in core (.opencode/, tests/opencode/, docs/README.opencode.md). OpenCode support is already part of core; this keeps it working as OpenCode moves to v2. It adds no third-party dependency (the plugin remains zero-dependency; Plugin.define is an identity wrapper, so no runtime import is required) and does not touch any skill content.

What alternatives did you consider?

  • Import @opencode-ai/plugin/v2 and use Plugin.define. Rejected: define is an identity function, and adding a runtime import risks module-resolution failures across the git-package/npm/local install layouts superpowers ships through. A plain default export is equivalent and dependency-free. Types can still be consulted for development.
  • Keep v1 and v2 side by side (dual export). Rejected: v2 ignores the v1 factory entirely, so it would be dead code, and OpenCode states v1 plugins don't run under v2. A clean v2 rewrite is simpler and matches upstream's direction.
  • Register skills via a config file instead of ctx.skill.transform. Rejected: the transform API is the v2-sanctioned way and keeps the symlink-free, no-manual-config property the v1 plugin had.
  • Hook name "request" (as the v2 docs table shows) vs "context". The shipped @opencode-ai/plugin types and the running opencode2 build both use "context"; verified live that "context" fires and mutates messages. Used "context".

Does this PR contain multiple unrelated changes?

No. Every change is the single concern of making the OpenCode plugin work on v2: the plugin rewrite, its tests, and its docs. The one-line edits to docs/porting-to-a-new-harness.md update that guide's OpenCode references (mechanism, log-check command) to stay consistent with the rewritten plugin.

Existing PRs

Environment tested

Harness (e.g. Claude Code, Cursor) Harness version Model Model version/ID
OpenCode v2 (opencode2) v0.0.0-next-15718 OpenCode default free model build · hy3-free

New harness support

Not a new harness — this is a v1→v2 API migration for the existing OpenCode integration. Including the acceptance evidence anyway, since the change affects whether the bootstrap loads.

Ran the canonical acceptance test on a real opencode2 (next-15718) install with this plugin loaded, in a clean empty directory:

User: Let's make a react todo list

The agent auto-triggered the brainstorming skill before writing any code (from --format json output):

"tool":"skill"
"name":"brainstorming"
"text":"Using brainstorming to shape this before building.\n\nLet me first check the current project state."
"text":"Empty project — a clean slate. Let me ask the first clarifying question."

Also verified directly (via a sidecar probe plugin) that:

  • ctx.skill.list() returns the bundled superpowers skills (brainstorming, systematic-debugging, test-driven-development, using-superpowers, …) — so ctx.skill.transform + reload registered the skills/ directory.
  • The ctx.session.hook("context", …) callback fires, first-user-message parts have the { type, text } shape, and the injected bootstrap reaches the model.

tests/opencode/run-tests.sh (non-integration: plugin-loading + bootstrap-caching) passes with the rewritten v2-driven caching test.

OpenCode v2 replaced the v1 plugin API entirely; per OpenCode's migration
guide, "V1 plugins will not work in V2." The existing plugin used the v1 API
(SuperpowersPlugin named export, config + experimental.chat.messages.transform
hooks, {info,parts} message shape), so it no longer functioned under v2.

Rewrite .opencode/plugins/superpowers.js against the v2 API:

- Default `{ id, setup }` export (Plugin.define is an identity wrapper, so the
  plugin stays zero-dependency with no runtime import).
- ctx.skill.transform() + ctx.skill.reload() register the bundled skills dir as
  a directory skill source (replaces the v1 config.skills.paths mutation).
- ctx.session.hook("context", ...) injects the bootstrap into the first user
  message using the v2 { role, content: [{type,text}] } shape (replaces
  experimental.chat.messages.transform). Module-level bootstrap caching (obra#1202)
  and the double-injection guard are preserved.
- Tool mapping updated to v2 tool names: file mutation is now write/edit
  (v2 removed apply_patch).

Verified live against opencode2 (next-15718): skills register and are listed,
the context hook fires and injects, and the canonical acceptance test
("Let's make a react todo list") auto-triggers the brainstorming skill.

Also update the OpenCode tests and docs for v2:

- tests/opencode/test-bootstrap-caching.mjs drives the v2 setup(ctx)/context
  hook shape instead of the v1 transform export.
- tests/opencode/{test-tools,test-priority}.sh drop the invalid v2
  --print-logs flag.
- docs/README.opencode.md, .opencode/INSTALL.md, docs/porting-to-a-new-harness.md
  use the canonical v2 `plugins` config key, describe the new hook mechanism,
  and reference v2 tool names and log locations.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@obra

obra commented Jul 19, 2026

Copy link
Copy Markdown
Owner

It looks like this would immediately break all opencode v1 users. is that right? If so, it's probably a no-go. Is there a way to add opencode 2 support that won't break v1?

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.

2 participants