This was written agentically; verify its assertions:
Why
Agent Kit ships two harness surfaces (Claude Code via .claude-plugin/, Codex via .codex-plugin/). OpenCode is the third harness already in live use on this project's fleet (OpenCode + open-weight Qwen models on the local cluster), and a phase-1 prototype already exists: two OpenCode sessions ("AgentKit OpenCode plugin spec execution", "OpenCode plugin fixes and verification", 2026-08-19, qwen3-coder/qwen3.8-27b) executed docs/superpowers/specs/2026-08-18-agentkit-opencode-plugin-spec.md and produced a working contract-injection plugin. Its branch was deleted but both commits survive unreferenced in the object store — 18106a2 (initial) and 7db1a90 (post-review fixes) — recoverable with git branch feat/opencode-adapter 7db1a90.
Verified architecture (2026-08-20, docs + @opencode-ai/plugin typings)
- Plugin shape (typings):
Plugin = (input: PluginInput, options?) => Promise<Hooks>; PluginInput carries client, project, directory, worktree, $ (Bun shell), serverUrl. PluginModule = { id?, server: Plugin }. A tool() helper is exported for custom tools.
- Loading (docs):
opencode.json "plugin" array (npm, auto-installed via Bun to ~/.cache/opencode/node_modules/) or plugin directories (docs currently say .opencode/plugins/ and ~/.config/opencode/plugins/). Config sources merge with project overriding global.
- Hooks (typings — authoritative; the docs page omits several):
chat.message, chat.params, experimental.chat.system.transform (alters the system prompt array — the injection point the prototype used, confirmed real), experimental.chat.messages.transform, tool.execute.before, tool.execute.after, tool.definition, permission.ask, command.execute.before, shell.env, event, config, tool, auth.
- Skills: native
skill tool; discovery includes Claude-compatible paths — .claude/skills/<name>/SKILL.md, ~/.claude/skills/... — plus .opencode/skills/ and .agents/skills/. Frontmatter: name (lowercase alphanumeric+hyphens, ≤64, must match dir) and description (≤1024) required.
- Agents:
.opencode/agents/*.md (+ ~/.config/opencode/agents/), frontmatter description (required), mode: primary|subagent|all, model: provider/model-id, temperature, permission; sub-agents invoked by @-mention, the task tool, or automatically by description.
- Commands:
.opencode/commands/*.md (+ global), frontmatter template (required), description, agent, model, subtask; $ARGUMENTS/$1..$n placeholders; !`cmd` shell interpolation runs at project root.
- Instructions:
AGENTS.md is NOT auto-loaded; the "instructions" config array references files explicitly.
- Known churn risk: the injection hook is
experimental.*-prefixed, and the docs/typings disagree on the hook list and on plugin/ vs plugins/ directory naming — every slice re-verifies its exact identifiers from the installed typings at implementation time, never from this epic.
Prototype findings worth inheriting (from the session cache)
$ must come from PluginInput — a const { $ } = this; inside a plain function throws outside try/catch and rejects the hook on every message.
- Bun shell:
stdin is a readonly WritableStream property (not a method); there is no .timeout() — feed stdin via file redirection (bash -c 'script < file') and bound time with Promise.race or coreutils timeout.
- Export shape vs
PluginModule ({ id?, server }) needs loader evidence before choosing default-export.
Slices
| # |
Slice |
Blocked by |
| S1 |
Packaging: OpenCode plugin manifest + build surface |
— |
| S2 |
Harness identity: opencode in harness-id, contract, spawn-contract |
— |
| S3 |
Contract-injection plugin (productionize the phase-1 prototype) |
S1, S2 |
| S4 |
Skills + commands surface (native skill tool + /command wrappers) |
S3 |
| S5 |
Guard parity via tool.execute.before/after |
S3 |
| S6 |
Sub-agent dispatch parity (worker agents + task tool) |
S2, S4 |
Sub-issues carry the per-slice spec; this epic closes when all six merge.
🤖 Co-authored by Claude Fable 5.
This was written agentically; verify its assertions:
Why
Agent Kit ships two harness surfaces (Claude Code via
.claude-plugin/, Codex via.codex-plugin/). OpenCode is the third harness already in live use on this project's fleet (OpenCode + open-weight Qwen models on the local cluster), and a phase-1 prototype already exists: two OpenCode sessions ("AgentKit OpenCode plugin spec execution", "OpenCode plugin fixes and verification", 2026-08-19,qwen3-coder/qwen3.8-27b) executeddocs/superpowers/specs/2026-08-18-agentkit-opencode-plugin-spec.mdand produced a working contract-injection plugin. Its branch was deleted but both commits survive unreferenced in the object store —18106a2(initial) and7db1a90(post-review fixes) — recoverable withgit branch feat/opencode-adapter 7db1a90.Verified architecture (2026-08-20, docs +
@opencode-ai/plugintypings)Plugin = (input: PluginInput, options?) => Promise<Hooks>;PluginInputcarriesclient,project,directory,worktree,$(Bun shell),serverUrl.PluginModule = { id?, server: Plugin }. Atool()helper is exported for custom tools.opencode.json"plugin"array (npm, auto-installed via Bun to~/.cache/opencode/node_modules/) or plugin directories (docs currently say.opencode/plugins/and~/.config/opencode/plugins/). Config sources merge with project overriding global.chat.message,chat.params,experimental.chat.system.transform(alters the system prompt array — the injection point the prototype used, confirmed real),experimental.chat.messages.transform,tool.execute.before,tool.execute.after,tool.definition,permission.ask,command.execute.before,shell.env,event,config,tool,auth.skilltool; discovery includes Claude-compatible paths —.claude/skills/<name>/SKILL.md,~/.claude/skills/...— plus.opencode/skills/and.agents/skills/. Frontmatter:name(lowercase alphanumeric+hyphens, ≤64, must match dir) anddescription(≤1024) required..opencode/agents/*.md(+~/.config/opencode/agents/), frontmatterdescription(required),mode: primary|subagent|all,model: provider/model-id,temperature,permission; sub-agents invoked by @-mention, the task tool, or automatically by description..opencode/commands/*.md(+ global), frontmattertemplate(required),description,agent,model,subtask;$ARGUMENTS/$1..$nplaceholders;!`cmd`shell interpolation runs at project root.AGENTS.mdis NOT auto-loaded; the"instructions"config array references files explicitly.experimental.*-prefixed, and the docs/typings disagree on the hook list and onplugin/vsplugins/directory naming — every slice re-verifies its exact identifiers from the installed typings at implementation time, never from this epic.Prototype findings worth inheriting (from the session cache)
$must come fromPluginInput— aconst { $ } = this;inside a plain function throws outside try/catch and rejects the hook on every message.stdinis a readonlyWritableStreamproperty (not a method); there is no.timeout()— feed stdin via file redirection (bash -c 'script < file') and bound time withPromise.raceor coreutilstimeout.PluginModule({ id?, server }) needs loader evidence before choosing default-export.Slices
opencodein harness-id, contract, spawn-contractskilltool +/commandwrappers)tool.execute.before/afterSub-issues carry the per-slice spec; this epic closes when all six merge.
🤖 Co-authored by Claude Fable 5.