Loope already drives Claude and Codex as real adapters; OpenCode is listed but is a
stub (opencode run with no real invocation, no event parsing). Loope's positioning is
to orchestrate Claude, Codex, and OpenCode, so this phase makes OpenCode a
first-class adapter that can fill any loop role.
OpenCode (v1.3.0) runs non-interactively as:
opencode run [message..] --format json --dir <workdir> [-m provider/model] [--agent name]
- The prompt is passed as the
messagepositional (not stdin). --format jsonemits a JSONL event stream on stdout; each line is an object with atypeand asessionID.--dirsets the working directory.- OpenCode delegates to a configured provider/model. Its default provider may be
unavailable for a given account — a probe here returned
{"type":"error",...,"statusCode":403,...}("not licensed to use Copilot"). So OpenCode availability depends on the user's provider configuration, and auth/provider errors must be handled as a failed step, never a crash.
- Drive
opencodeas a real adapter in any role (implementer / reviewer / designer) viaopencode run --format json, captured and streamed like Claude and Codex. - Parse OpenCode's JSON event stream into Loope's
LoopEventvocabulary (live feed) and extract the final message. - Degrade gracefully when OpenCode's provider/auth fails: the step fails with the error message and the loop halts at that gate, with nothing left half-rendered.
- Let the user point OpenCode at a working model when its default isn't usable.
- Keep tests hermetic (stub unchanged); no new crates.
- OpenCode's server / web / ACP / MCP / GitHub modes.
- Session continue / resume / fork.
- Enforcing read-only through a CLI flag —
opencode runexposes none; read-only roles request "review only, do not edit" in the prompt (a soft, prompt-level constraint). - Changing the stub or any other adapter's behavior.
For the OpenCode adapter, the subprocess invoker builds:
opencode run --format json --dir <workspace> [-m <model>] "<prompt>"
- Prompt delivery is by argument, not stdin (OpenCode's
runtakes the message as a positional). This overrides the default stdin delivery used by Claude/Codex. --diris the runworkspace/.-m <model>is added only when a model is configured for Loope (see below); otherwise OpenCode uses its own configured default.- The working directory is also set as the process cwd, as for the other adapters.
OpenCode's default provider may be unlicensed for the account. Loope resolves an
optional model string and passes it as -m:
- env
LOOPE_OPENCODE_MODEL(e.g.anthropic/claude-...), or - a
--opencode-model provider/modelrun flag (takes precedence).
When neither is set, Loope omits -m and relies on OpenCode's own configuration.
opencode run --format json emits JSONL events keyed by type. The parser maps them to
LoopEvents for the live feed:
- assistant text events →
Message, - tool / file-edit / command events →
Action { kind, target }(edit/write/read/command mapped from the tool, as for the other adapters), - token-usage events (when present) →
Usage, type:"error"→ not an event; it marks the invocation as failed and its message becomes the step's failure message.
The final message is the last assistant text event (with the raw JSONL kept as the transcript). Exact field names are confirmed against a captured success-path sample during implementation; unrecognized lines are ignored safely.
- A missing
opencodebinary is already handled (failed step, clear message). - A provider/auth error (
type:"error", e.g. 403) makes the invocation fail with that message; the implementer/reviewer gate then blocks and the loop halts, exactly like any other failed step. No special-casing, no panic. - Because the real path depends on a configured provider, it is verified manually, not in the automated suite.
- Implementer / designer: write-capable; OpenCode edits the workspace; change detection (workspace before/after diff) already captures what it changed.
- Reviewer / verifier: read-only is requested in the prompt (no CLI enforcement);
the structured-verdict contract (
VERDICT: PASS|BLOCK) applies unchanged.
loope run --implementer opencode --reviewer codex "..." # OpenCode implements
loope run --reviewers opencode,codex "..." # OpenCode reviews in parallel
loope run --opencode-model anthropic/claude-... "..." # point OpenCode at a modelopencode is already a valid value everywhere adapters are parsed. A new --preset
value opencode-codex (OpenCode implements, Codex reviews) is added for convenience.
loope run --implementer opencode ...drivesopencode run --format jsonin the run workspace; its actions stream to the live feed and its final message is captured in the report.- A provider/auth error surfaces as a failed step carrying OpenCode's error message and halts the loop; Loope does not panic and the terminal is left clean.
--opencode-model/LOOPE_OPENCODE_MODELselects the model passed as-m.loope run --dry-runis unchanged; the whole test suite stays green with no binaries or network;cargo clippyclean; no new crates.- The OpenCode event parser is unit-tested against captured JSON samples (including the
type:"error"case).
- Unit-test
parse_opencode_eventagainst captured JSONL samples: an assistant message, a tool/file action, and thetype:"error"line (asserting it yields no event so the failure path is driven by the invocation result, not a spurious message). - Unit-test model resolution (
--opencode-modeloverLOOPE_OPENCODE_MODELover none). - Integration: dry-run and existing behavior unchanged.
- Manual: with a configured provider, a real
--implementer opencodeloop; and, with the default unlicensed provider, confirm the graceful failed-step behavior.
- [[2026-06-28-loope-agent-integration-spec]] — the adapter/execution model this extends.
- [[2026-06-28-loope-live-visibility-spec]] — the event vocabulary the parser feeds.