Skip to content

Commit 280ca13

Browse files
authored
merge: Devin + opencode worker backends (Codex-approved, PR #3)
Devin CLI + opencode CLI as worker backends
2 parents 54840fb + 936a4a2 commit 280ca13

11 files changed

Lines changed: 2401 additions & 40 deletions

commands/v-init.md

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,88 @@ cursor-agent status </dev/null 2>&1 | head -3 # or: [ -n "$CURSOR_API_KEY" ]
124124
> preferred for untrusted / high-stakes work**. See
125125
> [`adapter-cursor.md`](../skills/backend-launcher/adapter-cursor.md).
126126
127+
### 1a-quater. Devin CLI (`devin`) — optional, lower-trust, WORKER-ONLY backend
128+
129+
```bash
130+
command -v devin
131+
```
132+
133+
If absent → Devin is **not available** (record it; routing never offers it).
134+
135+
If present → check **authentication** (auth-free command, verified live):
136+
137+
```bash
138+
devin auth status </dev/null 2>&1 | head -3 # or: [ -n "$COGNITION_API_KEY" ]
139+
```
140+
141+
- Installed **and** authenticated (`devin auth login`, or `COGNITION_API_KEY` set) →
142+
Devin is **usable**; record it and add it to `backends`. The pinned headless invocation
143+
(devin-cli 3000.1.27, verified live for its help/flag surface — task-execution behavior is
144+
DOC-CLAIMED, unverified without an authenticated run):
145+
`cd "$WT" && devin -p "<prompt>" --permission-mode dangerous [--model <M>] --export <path> </dev/null`.
146+
Also probe sandbox availability (auth-free, verified live): `devin sandbox setup` — on macOS
147+
reports *"No sandbox setup is required"*; on Linux it either confirms `bubblewrap`+`socat`
148+
are present or prints install instructions. Record the result — it tells `/v:models` /
149+
the operator whether Devin's `--sandbox` (Research Preview) is even usable on this machine,
150+
though this plugin does **not** rely on it for enforcement in v1 (see below).
151+
- Installed but **not** authenticated → record it as **present but unauthenticated**; treat as
152+
unavailable and tell the user to run `devin auth login` (or set `COGNITION_API_KEY`).
153+
154+
> **Flag it as lower-trust AND worker-only when you record it.** Devin has a real,
155+
> live-confirmed kernel `--sandbox` flag (macOS Seatbelt / Linux bwrap+seccomp) — a
156+
> genuine differentiator from Antigravity/Cursor — but Cognition labels it
157+
> **"[Research Preview]"**, its coverage is scoped to "exec-tool processes" (non-shell
158+
> file-edit tool coverage unverified), and its network-filtering is called "currently
159+
> unstable" in Cognition's own docs. Until those are live-verified, this plugin treats
160+
> Devin as **opt-in, lower-trust — the same tier as Antigravity/Cursor**, NOT Codex: the
161+
> worktree + `git diff` gate is the *real* enforcement (detection, not confirmed
162+
> prevention). **Prefer Codex for untrusted / high-stakes work.** Devin is also
163+
> **model-agnostic** (`--model` accepts a free string spanning Claude/GPT/Gemini/Devin's
164+
> own SWE family) — its resolved model family is data-dependent, so it is **WORKER-ONLY
165+
> for v1, excluded from any cross-model arbiter/review panel** until family-dedup keys on
166+
> the resolved model rather than the backend name. See
167+
> [`adapter-devin.md`](../skills/backend-launcher/adapter-devin.md).
168+
169+
### 1a-quinquies. opencode CLI (`opencode`) — optional, lower-trust, WORKER-ONLY, multi-provider backend
170+
171+
```bash
172+
command -v opencode || npx -y opencode-ai@latest --version # confirms installable even if not on PATH
173+
```
174+
175+
If absent → opencode is **not available** (record it; routing never offers it).
176+
177+
If present → check for at least one usable provider credential (auth-free command,
178+
verified live):
179+
180+
```bash
181+
opencode providers list </dev/null 2>&1 | grep -qv '0 credentials' \
182+
&& echo "opencode has stored credentials" \
183+
|| echo "opencode has NO stored credentials (may still work via ambient provider env vars)"
184+
```
185+
186+
- Installed **and** (stored credentials via `opencode providers login`, **or** a known
187+
provider env var like `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`/`ANTHROPIC_BASE_URL` is
188+
explicitly set for this purpose) → opencode is **usable**; record it and add it to
189+
`backends`. **Load-bearing, live-observed finding:** opencode successfully authenticated
190+
with **zero** stored credentials purely from an inherited `ANTHROPIC_BASE_URL` — record
191+
`auth` as `ambient-env` vs `stored-credentials` so the operator knows which path is live
192+
on this machine, and see the adapter's env-scrub requirement below.
193+
- Installed but no credentials and no relevant env var set → present but unauthenticated;
194+
tell the user to run `opencode providers login`.
195+
196+
> **Flag it as lower-trust, worker-only, AND multi-provider when you record it.** opencode
197+
> has **no kernel write-confinement** at all (no `--sandbox` equivalent), and per its own
198+
> docs defaults to **allowing all operations without explicit approval** — the opposite
199+
> posture from Cursor/Antigravity, which refuse until explicitly unlocked. The worktree +
200+
> `git diff` gate is the only real enforcement (detection, not prevention). **Prefer Codex
201+
> for untrusted / high-stakes work.** opencode addresses models as `provider/model`
202+
> strings (e.g. `anthropic/claude-opus-4-6`), so — like Devin — its resolved model family
203+
> is data-dependent; it is **WORKER-ONLY for v1, excluded from any cross-model
204+
> arbiter/review panel** until family-dedup keys on the resolved model. See
205+
> [`adapter-opencode.md`](../skills/backend-launcher/adapter-opencode.md) for the
206+
> **mandatory env-scrub** (the worker script must NOT blindly inherit the dispatcher's own
207+
> provider env vars into the `opencode run` child process).
208+
127209
### 1b. Context7 MCP (match by namespace)
128210

129211
Context7 is **plugin-namespaced** — match the namespace, not a bare `context7`:
@@ -408,18 +490,27 @@ was, in those two fields).
408490
"claude": { "deep": "opus", "standard": "opus", "light": "sonnet" },
409491
"codex": { "deep": "gpt-5.6-sol", "standard": "gpt-5.6-terra", "light": "gpt-5.6-luna" },
410492
"antigravity": { "deep": "Gemini 3.1 Pro (High)", "standard": "Gemini 3.1 Pro (Low)", "light": "Gemini 3.5 Flash (Low)" },
411-
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" }
493+
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" },
494+
"devin": { "deep": "claude-opus-4.6", "standard": "claude-sonnet-4", "light": "gpt-5.5" },
495+
"opencode": { "deep": "anthropic/claude-opus-4-6", "standard": "openai/gpt-5.6-terra", "light": "opencode/mimo-v2.5-free" }
412496
},
413497
"cost-aware": {
414498
"claude": { "deep": "opus", "standard": "sonnet", "light": "sonnet" },
415499
"codex": { "deep": "gpt-5.6-sol", "standard": "gpt-5.6-terra", "light": "gpt-5.6-luna" },
416500
"antigravity": { "deep": "Gemini 3.1 Pro (High)", "standard": "Gemini 3.1 Pro (Low)", "light": "Gemini 3.5 Flash (Low)" },
417-
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" }
501+
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" },
502+
"devin": { "deep": "claude-opus-4.6", "standard": "claude-sonnet-4", "light": "gpt-5.5" },
503+
"opencode": { "deep": "anthropic/claude-opus-4-6", "standard": "openai/gpt-5.6-terra", "light": "opencode/mimo-v2.5-free" }
418504
}
419505
}
420506
}
421507
```
422508

509+
- `devin` and `opencode` are **worker-only** backends (v1): excluded from any cross-model
510+
arbiter/review panel until family-dedup keys on the *resolved* model rather than the
511+
backend name (both are multi-provider routers, so `backend: devin`/`backend: opencode`
512+
does not fix a single model family — see `adapter-devin.md` / `adapter-opencode.md`).
513+
423514
(`conservative` and `claude-only` mirror `balanced` — seed those two stance blocks
424515
identically to `balanced`. Only `cost-aware.claude.standard` differs: `sonnet`, not
425516
`opus`; `cost-aware.claude.deep` stays `opus`.)
@@ -506,6 +597,20 @@ The user-level cache of what this machine can do, reused across repos:
506597
"codex": { "available": true, "exec_flags_verified": true, "version": "<from `codex --version`>" },
507598
"antigravity": { "available": false, "trust": "lower (no kernel sandbox)", "version": "<from `agy --version`>" },
508599
"cursor": { "available": false, "authenticated": false, "trust": "lower (no kernel sandbox)", "version": "<from `cursor-agent --version`>" },
600+
"devin": {
601+
"available": false,
602+
"authenticated": false,
603+
"trust": "lower (Research-Preview kernel sandbox; exec-tool-only coverage unverified; network filtering unstable per vendor); worker-only",
604+
"version": "<from `devin --version`>",
605+
"sandbox_setup_required": null
606+
},
607+
"opencode": {
608+
"available": false,
609+
"auth": "none",
610+
"trust": "lower (no kernel sandbox; docs claim default-allow permissions); worker-only, multi-provider",
611+
"version": "<from `opencode --version`>",
612+
"providers_configured": []
613+
},
509614
"context7": { "available": true },
510615
"workflows": { "available": false },
511616
"deep_research": true,
@@ -518,6 +623,17 @@ The user-level cache of what this machine can do, reused across repos:
518623
- `antigravity.available` reflects the Step 1a-bis `command -v agy` probe; record the
519624
`version` from `agy --version`. When present, Step 1a-bis also seeds a real model map
520625
via `agy models </dev/null` (headless — no TTY needed).
626+
- `devin.available` reflects the Step 1a-quater `command -v devin` probe;
627+
`devin.authenticated` reflects `devin auth status`. `sandbox_setup_required` records
628+
whether `devin sandbox setup` reported readiness (macOS: always ready; Linux: depends
629+
on `bubblewrap`/`socat`) — informational only, not relied on for enforcement in v1.
630+
- `opencode.available` reflects the Step 1a-quinquies `command -v opencode` probe;
631+
`opencode.auth` records **how** it is authenticating (`ambient-env` /
632+
`stored-credentials` / `none`) — this machine-local nuance matters more for opencode
633+
than any other backend, given the live-observed ambient-credential finding (see
634+
`adapter-opencode.md`).
635+
- `devin` and `opencode` are never added to any arbiter/review-panel capability block —
636+
they are **worker-only** in v1.
521637
- `deep_research` reflects the Step 1d-bis presence probe (is `deep-research` in the
522638
available-skills listing?) — an **advisory hint only**: Trigger 0 re-checks the live
523639
listing at fire time, because the flag can go stale (`disableBundledSkills` /

commands/v-models.md

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: Refresh the Compound V tier→model map — discover the concrete models each backend (claude, codex, antigravity, cursor) currently offers, show them, let you assign deep/standard/light, and write the result into .claude/compound-v.json so intent-based routing survives model churn without touching any call site.
2+
description: Refresh the Compound V tier→model map — discover the concrete models each backend (claude, codex, antigravity, cursor, devin, opencode) currently offers, show them, let you assign deep/standard/light, and write the result into .claude/compound-v.json so intent-based routing survives model churn without touching any call site.
33
disable-model-invocation: true
44
---
55

@@ -15,7 +15,9 @@ at dispatch time, so refreshing the map here is the *only* thing you ever touch
1515
when models churn.
1616

1717
Argument (optional): `{{args}}` may name a single backend to refresh in isolation
18-
(`claude` | `codex` | `antigravity` | `cursor`); otherwise walk all of them.
18+
(`claude` | `codex` | `antigravity` | `cursor` | `devin` | `opencode`); otherwise walk
19+
all of them. `devin` and `opencode` are **worker-only** backends (v1) — their model
20+
maps drive dispatch only, never any arbiter/review panel seat.
1921

2022
**This is the "skill picks the models and offers you the options" surface.** Do the
2123
discovery, *show* what you found, then let the user choose. Never silently pick a
@@ -150,6 +152,62 @@ command -v cursor-agent && cursor-agent status </dev/null >/dev/null 2>&1 && ech
150152
`sonnet-4-thinking`) — a curated roster like codex (no discovery; whatever the plan accepts
151153
via `cursor-agent --model` is valid). Only offer named models if the user confirms a paid plan.
152154

155+
### 1e. devin — curated list, no discovery command (mirrors codex's pattern)
156+
157+
Devin has **no `devin models` / `--list-models` subcommand** — like codex, discovery is
158+
only via the interactive `/model` picker inside a session, so the map is a small
159+
**curated** roster the user can override by hand (devin accepts whatever string you pass
160+
to `--model`). Present this curated starting roster (DOC-CLAIMED aliases — devin-cli
161+
3000.1.27's own `--help` uses these exact strings as its examples, but no authenticated
162+
run has confirmed they resolve):
163+
164+
- `claude-opus-4.6` — strongest; suggested for `deep`
165+
- `claude-sonnet-4` — balanced; suggested for `standard`
166+
- `gpt-5.5` — fast/cheap; suggested for `light`
167+
168+
Confirm devin is even usable first:
169+
170+
```bash
171+
command -v devin && devin auth status </dev/null 2>&1 | grep -q 'Not logged in' \
172+
&& echo "devin present, unauthenticated" || echo "devin usable (or absent)"
173+
```
174+
175+
If devin is unavailable/unauthenticated, say so, keep the existing devin block
176+
unchanged, and skip its reassignment. **Devin is model-agnostic** (`--model` spans
177+
Claude/GPT/Gemini/Devin's own SWE family) — remind the user that whichever family they
178+
pick here determines Devin's error-correlation with the rest of the panel, which is
179+
exactly why it stays **worker-only, never an arbiter seat**, in this plugin.
180+
181+
### 1f. opencode — real discovery command, but curated + user-confirmed assignment
182+
183+
opencode **does** have a real, live discovery command that works even with **zero**
184+
stored credentials (it falls back to a credential-free `opencode/*` catalog via
185+
models.dev):
186+
187+
```bash
188+
command -v opencode >/dev/null && opencode models </dev/null 2>&1 || echo "opencode unavailable"
189+
```
190+
191+
Unlike antigravity's single-family Gemini catalog, opencode spans **multiple unrelated
192+
providers with no shared naming convention** — so, like cursor, Compound V does **not**
193+
auto-rank it. Show the user the live `opencode models` output, then let them assign each
194+
tier. The one genuinely novel option here: `light` MAY legitimately point at one of the
195+
**credential-free** `opencode/*` models (e.g. `opencode/mimo-v2.5-free`) — a real free
196+
tier no other backend offers. Every cell **MUST** be a full `provider/model` string (the
197+
resolver's selftest asserts every `opencode` tier cell contains `/`); a bare model name
198+
will likely fail opencode's own model resolution even though `--model` accepts the
199+
string syntactically. The built-in fallback map (curated, user-overridable):
200+
201+
- `deep``anthropic/claude-opus-4-6`
202+
- `standard``openai/gpt-5.6-terra`
203+
- `light``opencode/mimo-v2.5-free`
204+
205+
If opencode is unavailable/unauthenticated, say so and keep the existing opencode block
206+
unchanged. **opencode is model-agnostic per-cell** (each tier may use a different
207+
provider) — remind the user this is exactly why it stays **worker-only, never an
208+
arbiter seat**: an opencode ballot's family is determined entirely by which
209+
`provider/model` was resolved, not by the backend name.
210+
153211
---
154212

155213
## Step 2 — Show findings and let the user assign tiers
@@ -164,6 +222,8 @@ fast/cheap option → `light`). Example shape:
164222
| claude | opus, sonnet | opus | opus | sonnet |
165223
| codex | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna | gpt-5.6-sol | gpt-5.6-terra | gpt-5.6-luna |
166224
| antigravity | *(from `agy models </dev/null`)* | Gemini 3.1 Pro (High) | Gemini 3.1 Pro (Low) | Gemini 3.5 Flash (Low) |
225+
| devin | *(curated: claude-opus-4.6, claude-sonnet-4, gpt-5.5)* | claude-opus-4.6 | claude-sonnet-4 | gpt-5.5 |
226+
| opencode | *(from `opencode models </dev/null`)* | anthropic/claude-opus-4-6 | openai/gpt-5.6-terra | opencode/mimo-v2.5-free |
167227

168228
Then **let the user assign** each tier per backend — accept the suggestion as-is, or
169229
override any cell with any model name the discovery surfaced (or, for codex, any
@@ -205,13 +265,17 @@ Resulting shape (only `models` is this command's responsibility) — write the
205265
"claude": { "deep": "opus", "standard": "opus", "light": "sonnet" },
206266
"codex": { "deep": "gpt-5.6-sol", "standard": "gpt-5.6-terra", "light": "gpt-5.6-luna" },
207267
"antigravity": { "deep": "", "standard": "", "light": "" },
208-
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" }
268+
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" },
269+
"devin": { "deep": "claude-opus-4.6", "standard": "claude-sonnet-4", "light": "gpt-5.5" },
270+
"opencode": { "deep": "anthropic/claude-opus-4-6", "standard": "openai/gpt-5.6-terra", "light": "opencode/mimo-v2.5-free" }
209271
},
210272
"cost-aware": {
211273
"claude": { "deep": "opus", "standard": "sonnet", "light": "sonnet" },
212274
"codex": { "deep": "gpt-5.6-sol", "standard": "gpt-5.6-terra", "light": "gpt-5.6-luna" },
213275
"antigravity": { "deep": "", "standard": "", "light": "" },
214-
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" }
276+
"cursor": { "deep": "auto", "standard": "auto", "light": "auto" },
277+
"devin": { "deep": "claude-opus-4.6", "standard": "claude-sonnet-4", "light": "gpt-5.5" },
278+
"opencode": { "deep": "anthropic/claude-opus-4-6", "standard": "openai/gpt-5.6-terra", "light": "opencode/mimo-v2.5-free" }
215279
}
216280
// conservative + claude-only mirror balanced
217281
}
@@ -237,7 +301,7 @@ resolver reads the per-stance block you wrote (omitting it defaults to `balanced
237301

238302
```bash
239303
for s in balanced cost-aware; do
240-
for b in claude codex antigravity; do
304+
for b in claude codex antigravity cursor devin opencode; do
241305
for t in deep standard light; do
242306
python3 scripts/compound-v-resolve-model.py --backend "$b" --tier "$t" \
243307
--stance "$s" --config .claude/compound-v.json
@@ -271,7 +335,10 @@ set here. `xhigh` is valid **iff** `backend: codex`; every other backend rejects
271335
with a clear error naming the rule (use `high` instead).
272336

273337
**Honesty rules:** report only what discovery actually returned. `agy models </dev/null`
274-
runs headlessly and returns the live catalog, so report the discovered models as
275-
discovered. Only if `agy` is **absent** do we fall back to the built-in map — say so
276-
plainly when that happens, rather than passing the fallback off as discovered. Never
277-
print token or cost numbers (anti-ruflo). Never assign `haiku`.
338+
and `opencode models </dev/null` both run headlessly and return live catalogs, so
339+
report the discovered models as discovered. Only if the CLI is **absent** do we fall
340+
back to the built-in map — say so plainly when that happens, rather than passing the
341+
fallback off as discovered. devin has no discovery command at all — its roster is
342+
always curated, say so. Never print token or cost numbers (anti-ruflo). Never assign
343+
`haiku`. Always remind the user that `devin` and `opencode` are worker-only backends —
344+
whatever they assign here never seats either on an arbiter/review panel.

0 commit comments

Comments
 (0)