Skip to content

Add OpenCode host adapter — TypeScript bridge package @archcore/opencode-plugin in plugins/opencode/ #25

Description

@ivklgn

Updated for v0.7.0 — the bridge contract changed shape. The plugin no longer ships per-concern guard scripts; hook policy moved into the CLI (cli-owns-layers-4-5.adr) and plugins/archcore/bin/ now holds three thin launchers. The bridge targets those, not the deleted check-* scripts. Everything else below stands.

Problem

The portable core is prepared for OpenCode, but the TypeScript bridge package that connects OpenCode's plugin API to the core does not exist — plugins/ contains only archcore/. This issue implements the bridge; the goal is that OpenCode works exactly like the existing hosts on the same core — the bridge translates, it never decides.

What already exists (do NOT re-implement or refactor)

Core readiness landed on dev (.archcore/plugin/host-expansion-core-readiness.plan.md):

  • plugins/archcore/bin/lib/normalize-stdin.sh — explicit opencode case (env-only host: ARCHCORE_HOST=opencode from the bridge is never clobbered — pinned by a test that fails on the pre-change code); info helpers emit plain text for opencode; block stays exit 2 + reason on stderr. The full bridge contract is in the file header.
  • plugins/archcore/bin/session-start — plain-text arm covers opencode; passes opencode as the host arg to archcore hooks.
  • test/fixtures/stdin/opencode/ — Claude-shaped snake_case fixtures; these ARE the bridge's stdin contract (write-archcore.json, write-regular.json, mcp-update.json).
  • test/helpers/common.bashrun_with_fixture_env helper for env-forced hosts.
  • Tests: block/allow/info pins for opencode across normalize-stdin.bats, session-start.bats, output-helpers-matrix.bats.

Decisions are final and recorded in .archcore/plugin/ — do not re-litigate:

  • opencode-adapter-packaging.adr.md — TypeScript npm package @archcore/opencode-plugin, in THIS repo under plugins/opencode/; bun test scoped to that directory only; hook bridge is shell-out only — no decision logic in TS.
  • opencode-host-support.rnd.md — verified OpenCode plugin API facts: tool.execute.before/after hooks; deny = throw new Error(...) (model sees the message); config hook mutates live config (cfg.mcp, cfg.skills.paths); MCP config key is environment, not env; skills ship inside the package and register via cfg.skills.paths.
  • stack-and-tooling.rule.md — the TS + bun test exception is scoped to plugins/opencode/ only.
  • host-adapter-contract.spec.md — adapter obligations and the deny-semantics translation table.

What changed since this issue was filed (v0.7.0)

The bridge's shell-out targets are now three launchers, not six guards:

OpenCode hook Spawn Notes
session start plugins/archcore/bin/session-start plain-text output, appended verbatim
tool.execute.before plugins/archcore/bin/pre-tool-use write guard and code-alignment context both live behind it, in the CLI
tool.execute.after plugins/archcore/bin/post-tool-use validation, cascade, precision — all CLI-side; always reports, never blocks

Consequences for the bridge:

  • Exit-code protocol is unchanged: exit 2 + stderr → throw new Error(stderr); stdout → context.
  • The launchers fail open when the CLI is absent or older than 0.7.0 (bin/cli-gte 0.7.0), so a bridge built against them degrades the same way every other host does.
  • The launchers map the shell host id to a CLI agent id where the two differ (codexcodex-cli). Confirm whether opencode needs such a mapping or is passed through as-is.
  • plugins/archcore/bin/lib/plugin-cache-guard.sh runs in both launchers — the bridge must not invoke them from a plugin-cache cwd.

Expected result

An OpenCode session with the plugin installed behaves like the shipped hosts: session-start injects .archcore context, direct writes to .archcore/*.md are blocked with the redirect-to-MCP message, MCP document tools work, post-tool validation/cascade context is appended.

Proposed approach

  • Scaffold plugins/opencode/ as npm package @archcore/opencode-plugin (Bun runtime).
  • Bridge contract (documented in normalize-stdin.sh header): spawn the launcher with ARCHCORE_HOST=opencode in env and a Claude-shaped snake_case JSON payload on stdin — exactly the shape in test/fixtures/stdin/opencode/.
  • tool.execute.beforebin/pre-tool-use; exit 2 → throw new Error(stderr); stdout (plain text) → context.
  • tool.execute.afterbin/post-tool-use; append plain stdout verbatim.
  • Session start → bin/session-start (plain text, appended verbatim).
  • config hook: register the archcore MCP server (key environment) and add the bundled skills path to cfg.skills.paths.
  • bun test inside plugins/opencode/ only, driven by the shared fixtures in test/fixtures/stdin/opencode/ so the TS side and the shell side pin the same contract.
  • Enroll opencode in test/structure/host-coverage-matrix.bats — its enrollment guard already accepts the opencode fixture dir, but a bridge that ships without a matrix row leaves its mutation-tool coverage unchecked.
  • Add an opencode row to the records table in .archcore/plugin/host-probe-protocol.spec.md (deferred: until run — see Run the host probe protocol on all four hosts and replace the deferred:not-yet-run records #21).
  • npm packaging/publish config for @archcore/opencode-plugin (install via plugin: [] array).

Acceptance criteria

  • Live OpenCode session: write blocked with redirect message, MCP tools work, session-start context arrives, skills available.
  • Zero changes under plugins/archcore/ (enforced by host-logic-locality.bats and the exact-output pins — all stay green).
  • No TypeScript outside plugins/opencode/ (stack rule enforcement); make test lint check-json green; bun test green in plugins/opencode/.

Dependencies

  • needs-cli: the Go CLI must accept archcore hooks opencode {session-start,pre-tool-use,post-tool-use}. Today an unknown host degrades silently (guard still works, context load does not) — needs a companion issue in the CLI repo.

References

.archcore/plugin/opencode-adapter-packaging.adr.md, opencode-host-support.rnd.md, host-adapter-contract.spec.md, stack-and-tooling.rule.md, host-expansion-core-readiness.plan.md, cli-owns-layers-4-5.adr.md, hooks-validation-system.spec.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:hooksPreToolUse/PostToolUse/SessionStart hooksneeds-cliDepends on the separate Archcore CLI (Go) repopriority:P2Valuable follow-upsize:LMulti-day / needs a spike

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions