feat(engine): add opencode CLI review engine
Add a third review harness alongside the in-process majordomo loop and the
claude-code CLI shell-out: the OpenCode CLI (opencode.ai) driving an ollama-cloud
model, selected by an "opencode/<model>" spec. The goal is to benchmark gadfly's
boutique executus harness against a freely-available agentic harness on the SAME
model (e.g. "ollama-cloud/glm-5.2" vs "opencode/glm-5.2").
OpenCode has no --append-system-prompt flag, so the lens system prompt and the
read-only discipline are delivered through a generated config injected via
OPENCODE_CONFIG_CONTENT: a "gadfly" agent whose prompt is the system prompt with
edit/bash denied at both the global and agent level, plus a "gadfly" ollama-cloud
provider. That env var is the highest-precedence config source in the container,
so a reviewed repo's own opencode.json can't re-enable edits on the reviewer.
Spec forms: "opencode/<model>" (wrapped in the generated provider), the
"open-code/" alias, "opencode/<provider>/<model>" pass-through to OpenCode's own
registry, and bare "opencode". Model ids are taken verbatim so colon-bearing
ollama ids (qwen3-coder:480b-cloud) survive. Auth reuses OLLAMA_CLOUD_API_KEY
(mapped to OLLAMA_API_KEY, referenced as {env:OLLAMA_API_KEY} in config, never a
literal secret). Knobs mirror GADFLY_CLAUDE_*: GADFLY_OPENCODE_BIN/MODEL/BASE_URL/
EXTRA_ARGS. openCodeEnv() forwards OLLAMA_API_KEY (the inverse of claudeEnv) but
still withholds the Gitea/findings/Anthropic secrets.
main.go engine selection is now a switch (claude-code / opencode / majordomo), and
the auto-select path uses a type-check instead of a boolean so a shell-out engine
can never hit the *majordomoEngine assertion. auto-select and delegate_investigation
stay majordomo-only and are skipped for opencode (the CLI does its own legwork).
Dockerfile bundles opencode-ai (npm auto-selects its musl build on alpine) with a
best-effort version check + provider pre-warm that never fails the shared image
build. README/examples/CLAUDE.md/scripts updated per the maintenance rules.
Tests: new opencode_test.go mirrors engine_test.go (spec/model/args/config/env-
filter + stub-CLI runtime tests). Verified end-to-end with a fake opencode CLI:
correct argv, injected config, and consolidated markdown output.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -140,6 +140,55 @@ as an example, not wired or tested here.
|
||||
> specialist selection and the `delegate_investigation` worker are majordomo-only and are skipped
|
||||
> with this engine (Claude Code does its own legwork).
|
||||
|
||||
### OpenCode engine (`opencode`)
|
||||
|
||||
The same shell-out idea, but with a **freely-available** harness: Gadfly can review through the
|
||||
**[OpenCode](https://opencode.ai) CLI**, which — like Claude Code — brings its own read tools and
|
||||
verifies findings against the checked-out repo, but drives an **ollama-cloud** model. The point is
|
||||
to benchmark gadfly's boutique executus harness against a good open harness *on the same model*:
|
||||
run `ollama-cloud/glm-5.2` (majordomo loop) and `opencode/glm-5.2` (OpenCode) side by side and
|
||||
compare their findings. This is the wired, no-proxy version of the "alternate backends" comparison
|
||||
described above. The CLI is bundled in the image (Node + `opencode-ai`).
|
||||
|
||||
Select it as a model id:
|
||||
|
||||
| Spec | Meaning |
|
||||
|------|---------|
|
||||
| `opencode/glm-5.2` | serve `glm-5.2` via ollama-cloud through OpenCode |
|
||||
| `open-code/glm-5.2` | accepted alias spelling (`opencode` is canonical) |
|
||||
| `opencode/qwen3-coder:480b-cloud` | model ids are taken **verbatim** — colons are preserved (no `:thinking` suffix here, unlike claude-code) |
|
||||
| `opencode/<provider>/<model>` | escape hatch: pass `<provider>/<model>` straight to OpenCode's own provider registry/auth (e.g. `opencode/anthropic/claude-sonnet-4-6`) |
|
||||
| `opencode` | bare: OpenCode's configured default model |
|
||||
|
||||
```yaml
|
||||
GADFLY_MODELS: "ollama-cloud/glm-5.2,opencode/glm-5.2" # the benchmark pairing
|
||||
```
|
||||
|
||||
Auth reuses **`OLLAMA_CLOUD_API_KEY`** (the same secret the ollama-cloud path uses; it's mapped to
|
||||
`OLLAMA_API_KEY`, which the generated provider references as `{env:OLLAMA_API_KEY}` — never a literal
|
||||
secret in config). Tuning knobs (all optional):
|
||||
|
||||
| Env | Default | Meaning |
|
||||
|-----|---------|---------|
|
||||
| `GADFLY_OPENCODE_MODEL` | *(from the spec suffix)* | overrides the model |
|
||||
| `GADFLY_OPENCODE_BASE_URL` | `https://ollama.com/v1` | ollama-cloud endpoint; point at a local Ollama (`http://localhost:11434/v1`) or any OpenAI-compatible server |
|
||||
| `GADFLY_OPENCODE_EXTRA_ARGS` | *(unset)* | extra `opencode run` args, **whitespace-split**, appended before the positional task |
|
||||
| `GADFLY_OPENCODE_BIN` | `opencode` | CLI binary path |
|
||||
|
||||
> **Read-only is enforced through config, not a flag.** OpenCode has no `--append-system-prompt`, so
|
||||
> Gadfly generates a per-lens config — the lens system prompt as a `gadfly` agent's prompt, with
|
||||
> `edit`/`bash` denied at both the global and agent level — and injects it via `OPENCODE_CONFIG_CONTENT`.
|
||||
> That env var is the highest-precedence config source in the container, so it **outranks any
|
||||
> `opencode.json` a reviewed repo ships** — a repo can't re-enable edits on the reviewer. The
|
||||
> subprocess runs with a **minimal environment** (`OLLAMA_API_KEY` + `PATH`/`HOME`/locale/`OPENCODE_*`/
|
||||
> `GADFLY_OPENCODE_*`), not the runner's full env; the Gitea token, Anthropic/Claude keys, and findings
|
||||
> token aren't handed to the CLI.
|
||||
|
||||
> **Newly wired, lightly tested.** Like the claude-code engine, `auto` specialist selection and the
|
||||
> `delegate_investigation` worker are majordomo-only and are skipped here (OpenCode does its own
|
||||
> legwork). Output capture reads OpenCode's default text output, so treat the engine as new and
|
||||
> sanity-check a run before trusting a benchmark.
|
||||
|
||||
### Endpoint aliases via env vars
|
||||
|
||||
For multiple named backends (e.g. a couple of Ollama boxes on your LAN), register them by
|
||||
@@ -384,6 +433,7 @@ The reviewer binary reads these (the stub/entrypoint set sane defaults):
|
||||
| `GADFLY_BASE_URL` | — | override endpoint (OpenAI/Ollama-compatible servers) |
|
||||
| `GADFLY_API_KEY` | — | provider key; falls back to the provider's standard env |
|
||||
| `claude-code` model id | — | route a model through the bundled Claude Code CLI (`claude-code` / `claude-code/<model>`); see [Claude Code engine](#claude-code-engine-claude-code) for its `GADFLY_CLAUDE_*` knobs |
|
||||
| `opencode` model id | — | route an ollama-cloud model through the bundled OpenCode CLI (`opencode/<model>`); see [OpenCode engine](#opencode-engine-opencode) for its `GADFLY_OPENCODE_*` knobs |
|
||||
| `GADFLY_SPECIALISTS` | default suite | csv of lenses, `all`, or `auto` (dynamic selection) |
|
||||
| `GADFLY_SELECTOR_MODEL` | review model | model that picks lenses in `auto` mode |
|
||||
| `GADFLY_WORKER_MODEL` | — | cheap model for `delegate_investigation`; unset = no delegation |
|
||||
|
||||
Reference in New Issue
Block a user