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.bash — run_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 (
codex → codex-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
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.
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 onlyarchcore/. 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— explicitopencodecase (env-only host:ARCHCORE_HOST=opencodefrom 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; passesopencodeas the host arg toarchcore 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.bash—run_with_fixture_envhelper for env-forced hosts.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 underplugins/opencode/;bun testscoped 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/afterhooks; deny =throw new Error(...)(model sees the message);confighook mutates live config (cfg.mcp,cfg.skills.paths); MCP config key isenvironment, notenv; skills ship inside the package and register viacfg.skills.paths.stack-and-tooling.rule.md— the TS +bun testexception is scoped toplugins/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:
plugins/archcore/bin/session-starttool.execute.beforeplugins/archcore/bin/pre-tool-usetool.execute.afterplugins/archcore/bin/post-tool-useConsequences for the bridge:
throw new Error(stderr); stdout → context.bin/cli-gte 0.7.0), so a bridge built against them degrades the same way every other host does.codex→codex-cli). Confirm whetheropencodeneeds such a mapping or is passed through as-is.plugins/archcore/bin/lib/plugin-cache-guard.shruns 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
.archcorecontext, direct writes to.archcore/*.mdare blocked with the redirect-to-MCP message, MCP document tools work, post-tool validation/cascade context is appended.Proposed approach
plugins/opencode/as npm package@archcore/opencode-plugin(Bun runtime).normalize-stdin.shheader): spawn the launcher withARCHCORE_HOST=opencodein env and a Claude-shaped snake_case JSON payload on stdin — exactly the shape intest/fixtures/stdin/opencode/.tool.execute.before→bin/pre-tool-use; exit 2 →throw new Error(stderr); stdout (plain text) → context.tool.execute.after→bin/post-tool-use; append plain stdout verbatim.bin/session-start(plain text, appended verbatim).confighook: register the archcore MCP server (keyenvironment) and add the bundled skills path tocfg.skills.paths.bun testinsideplugins/opencode/only, driven by the shared fixtures intest/fixtures/stdin/opencode/so the TS side and the shell side pin the same contract.test/structure/host-coverage-matrix.bats— its enrollment guard already accepts theopencodefixture dir, but a bridge that ships without a matrix row leaves its mutation-tool coverage unchecked.opencoderow 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).@archcore/opencode-plugin(install viaplugin: []array).Acceptance criteria
plugins/archcore/(enforced byhost-logic-locality.batsand the exact-output pins — all stay green).plugins/opencode/(stack rule enforcement);make test lint check-jsongreen;bun testgreen inplugins/opencode/.Dependencies
needs-cli: the Go CLI must acceptarchcore 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.