opencode-foreman
v0.1.1
Published
Let Claude plan and validate coding tasks while OpenCode CLI, driven by a free model, does the implementation. CLI + MCP server.
Maintainers
Readme
opencode-foreman
Claude plans and checks the work. A free model does the typing.
opencode-foreman is a foreman for your codebase: it hands the bricklaying — the actual
file-by-file implementation — to OpenCode CLI running a free model,
while Claude stays on site to write the spec and sign off on the finished work. You get a working
diff without spending Claude's own tokens on the iterative, heavy part of writing code.
It ships as both a CLI you can script and an MCP server Claude can call mid-conversation.
Why
Claude Code / Cowork sessions burn a lot of tokens on the part that matters least for Claude to do
itself: writing and rewriting implementation code. opencode-foreman flips the division of labor —
| | Claude | opencode-foreman | |---|---|---| | Writes the task spec | ✅ | | | Writes the implementation | | ✅ (via a free model) | | Runs your tests / build | | ✅ | | Diffs the result against git, independently | | ✅ | | Judges whether it's actually done | ✅ | |
Claude never has to take the delegated agent's word for it — every result is checked against a
real git diff and a real verify command before Claude ever sees it.
How it works
Claude opencode-foreman OpenCode CLI + free model
────── ──────────────── ─────────────────────────
"fix the bug, ─────▶ 1. git checkpoint
here's the 2. ensure safety guardrails
verify cmd" 3. run ───────────────────────▶ reads, edits, runs bash
4. git diff (ground truth) ◀──── claims it's done
5. run your verify command
6. compact bundle
◀─────────────── (summary + diff + verify result)
"looks good" / "try
again" / "switch
models" / "revert"The agent's own claims are never trusted — changed files and the diff come from git, and
pass/fail comes from actually running your verify command.
Features
- CLI and MCP server, same underlying engine, so you can script it or let Claude call it directly.
- Free-model-first: discovers whatever free models are actually available (OpenCode Zen's
free tier out of the box,
openrouter/*once you setOPENROUTER_API_KEY) and lets you pick one per project, per session, or per call. - Session resume with mid-task model/agent/effort switching — a follow-up
continuecall can swap models without losing OpenCode's session context. - Git-checkpointed, one-command revert — every delegated run is preceded by a snapshot;
revertrestores the directory exactly, including deleting files the agent newly created. - Guardrails like a real foreman — enforce site safety, don't dictate method. Hard limits that
hold even under
--dangerously-skip-permissions: a merged deny-list (rm -rf,git push,npm publish,sudo, ...) andexternal_directory: denyto confine the agent to the target directory. Butdoom_loopis set toallow— the worker is trusted to iterate its own way (retry, backtrack, rethink) rather than being hard-stopped mid-process; runaway iteration is bounded from the outside by the timeout, no-output watchdog, and no-progress/oscillation detection.--dangerously-skip-permissionsitself is unconditional — OpenCode has no non-interactive mode that skips permission granting otherwise, so it's always passed; the deny rules hold regardless. - High reasoning by default, and room to think — delegated runs use high reasoning effort
(
defaultVariant) unless a call dials it down, and the no-output watchdog is deliberately patient (minutes of true silence, not seconds) so a model that goes quiet while reasoning isn't killed mid-thought. - Richer task context, non-invasively —
delegate_tasktakes an optionalcontextfield for supporting background (file paths, snippets, constraints) the worker can draw on, kept separate from the task and explicitly not instructions on how to implement. - Timeout + no-output watchdog, with automatic model fallback — a genuinely hung or rate-limited
model (this happens for real) is killed and the run is transparently retried on the next free model,
bounded by
maxModelFallbacks, before ever reporting failure.opencode/deepseek-v4-flash-freeis the recommended default for general dev/file-editing work and sits first in the fallback chain. - Objective diff stats and token/cost usage in every bundle (
diffStats,fileStats,tokens,cost) — the files-changed/+/- counts come straight fromgit diff --numstat, independent of anything the agent says about its own work.fileStatsbreaks the churn down per file, sorted by size, so a multi-file result can be triaged without pulling the whole diff. - Verify command inference — if you don't pass one, the project's own test command is detected
(npm/pnpm/yarn/bun scripts,
pytest,cargo test,go test, maven, gradle,make test) so a run still gets checked against something real. It returns nothing rather than a wrong guess, and a pass from an inferred command is flagged as such (verify.source) so it's never mistaken for a gate you defined. - Run history and cost accounting — every run is appended to
.opencode-foreman/runs.jsonlwith its model, outcome, diff stats, tokens, cost and duration.historyaggregates totals per model and per stop reason, which surfaces a model that keeps timing out or burning tokens without landing changes. - Per-delegation budget ceilings —
maxCostUsdandmaxTokensPerTaskbound the cumulative spend of a delegation, checked before committing to another attempt. Retries compound (maxAttempts×maxModelFallbacks), andbundle.spendreports the running total. - One delegation per directory at a time — an advisory lock stops two runs interleaving their
git checkpoints, which would make each diff against the other's writes. Locks held by a dead
process are reclaimed automatically;
revertrefuses to run while a delegation is active. doctorpreflight — checks the OpenCode binary, git, model discovery, the chosen model and the verify command up front, with a concrete fix for each, instead of surfacing a missing dependency later as an empty diff or a hung run.- Tooling-tamper warning — flags (doesn't block) if a run's diff touches
package.jsonscripts, lockfiles,.npmrc, or similar before your verify command executes them via shell. - Secret scrubbing — known API key shapes and any explicitly-known secret values are redacted from everything handed back to Claude.
- Built to never run unless you ask, on three independent layers — every MCP tool carries an
invocation gate naming both what counts as an explicit request and the rationalisations that
don't; slash-command prompts make invocation explicit by construction; and an
ask-mode permission rule (see below) forces an approval prompt regardless of what Claude decides. - Cross-platform: Windows, WSL, macOS, Linux.
Prerequisites
- Node.js ≥ 18
- OpenCode CLI installed and on
PATH - Free to use immediately with OpenCode Zen's free-tier models — no API key needed. Set
OPENROUTER_API_KEYin your environment if you wantopenrouter/*models too.
Install
Published on npm — no cloning or building required:
npm install -g opencode-foreman # puts `opencode-foreman` and `opencode-foreman-mcp` on PATHOr skip the global install and let npx fetch it on demand (see MCP server below) —
nothing persists on disk beyond npm's own cache.
CLI
opencode-foreman models # list free models available right now
opencode-foreman agents # list agents (build, plan, ...)
opencode-foreman set-model <id> --dir <dir> [--agent build] [--variant high]
opencode-foreman run "<task>" --dir <dir> [--verify "npm test"] [--context "..."] [--model <id>] [--variant high] [--max-attempts 3]
opencode-foreman continue "<message>" --session <id> --dir <dir> [--model <id>]
opencode-foreman status --session <id> --dir <dir>
opencode-foreman revert --dir <dir> [--session <id> | --checkpoint <commit>]
opencode-foreman doctor --dir <dir> # check everything delegation depends on
opencode-foreman detect --dir <dir> # show the verify command that would be inferred
opencode-foreman history --dir <dir> [-n 10] [--summary]Every command prints a JSON result and exits non-zero on failure — easy to script or pipe.
MCP server
Register it once, globally, so it's available across every project. No install step needed —
npx fetches the package from npm on first use and caches it after that:
claude mcp add opencode-foreman -s user -- npx -y --package opencode-foreman opencode-foreman-mcpOr by hand, in your MCP client config:
{
"mcpServers": {
"opencode-foreman": {
"command": "npx",
"args": ["-y", "--package", "opencode-foreman", "opencode-foreman-mcp"]
}
}
}If you did a global npm install -g opencode-foreman above, you can use the shorter form instead,
which skips npx's version-resolution step:
{
"mcpServers": {
"opencode-foreman": {
"command": "opencode-foreman-mcp"
}
}
}Tools: list_models, list_agents, set_session_defaults, get_session_defaults,
delegate_task, delegate_continue, delegate_status, delegate_revert, fetch_full_diff,
foreman_doctor, detect_verify, delegate_history.
Slash commands. The server also exposes MCP prompts, which appear in Claude Code as
/mcp__opencode-foreman__<name>:
| Command | What it does |
|---|---|
| delegate | hands a task over, with the verify command and the review discipline already in the message |
| review-delegation | audits a finished run as a reviewer rather than a summariser, ending in keep / continue / revert |
| foreman-setup | runs doctor, then picks a default model for the directory |
These are the recommended entry point, and they're the cleanest answer to "how do I make sure this only runs when I want it to": you typed the command, so the explicit request is the invocation — no inference required.
If no model has been chosen yet for a directory, delegate_task returns a prompt listing available
free models instead of running — call set_session_defaults once, and every later call in that
directory reuses it silently. Any call can still override model / agent / variant just for
itself, and pass optional context (supporting background the worker draws on — not method).
Want Claude to only ever touch this on request, never on its own initiative? Add a permission rule so every call requires your explicit approval:
// ~/.claude/settings.json
{
"permissions": {
"ask": ["mcp__opencode-foreman__*"]
}
}Config
Layered: built-in defaults → ~/.opencode-foreman/config.json → <dir>/.opencode-foreman.json →
session defaults → per-call overrides.
| Key | Meaning |
|---|---|
| timeoutMs | hard wall-clock timeout per run (default: 20 min — generous, so a long but productive high-reasoning run isn't cut off) |
| noOutputTimeoutMs | kill the run only after this much true silence (default: 5 min). The model goes quiet while it reasons; this must stay well above a single thinking step or you'll disable the model mid-thought |
| maxAttempts | internal auto-retry loop on verify failure (default: 3 — gives a free model room to iterate, since first-try success is unlikely). Pass 1 per call to force a single Claude-driven run |
| defaultVariant | reasoning effort passed to OpenCode's --variant, applied unless a call/session overrides it (default: high) |
| maxModelFallbacks | how many alternate models to try if one hangs (default: 2 — always on, independent of maxAttempts) |
| preferredModels | ordered list suggested first when choosing a model, and the head of the fallback chain (default: ["opencode/deepseek-v4-flash-free"]) |
| diffTruncateLines | how much diff to inline before truncating (full diff always fetchable) |
| verify | default verification command |
| autoVerify | infer the project's own test command when no verify is given (default: true) |
| maxCostUsd | stop retrying once one delegation has cumulatively cost this much (default: 0 = unlimited) |
| maxTokensPerTask | stop retrying once one delegation has cumulatively used this many tokens (default: 0 = unlimited) |
| denyList | extra bash patterns to deny, on top of the built-in list |
| allowedRoot | confine all dir arguments to this root |
| defaultAgent | agent to use when none is set (default: build) |
| pure | run OpenCode with --pure, bypassing your global OpenCode plugins (default: true) |
Safety
- Every delegated run is git-checkpointed first;
revertrestores the directory to that exact state in one command. opencode.jsonin the target directory gets a merged-in deny list plusexternal_directory: deny(confinement) — these hold even under--dangerously-skip-permissions, since it only auto-approves rules that would otherwise ask.doom_loopis leftallowso the worker can iterate freely; runaway loops are caught by the outer timeout, no-output watchdog, and no-progress/oscillation detection instead.- Refuses to operate on filesystem/system roots or the bare home directory.
- Known secret shapes — and any explicitly-known secret values, like
OPENROUTER_API_KEY— are redacted from everything returned to Claude. - A wall-clock timeout and a no-output watchdog kill hung runs; the opt-in auto-retry loop detects no-progress and oscillating diffs and stops instead of looping forever.
- One delegation per directory at a time, so concurrent runs can't interleave their checkpoints and leave a tree that matches neither. A lock whose owner has died is reclaimed rather than wedging the directory.
bundle.warningsis where the tool tells you its own evidence is weak — an inferred rather than supplied verify command, a run that edited the tooling verify executes, a completed write the diff can't see. Worth reading before trustingok: true.
Development
Only needed if you're working on opencode-foreman itself — regular use doesn't require a clone.
git clone https://github.com/Namit-nair/opencode-foreman.git
cd opencode-foreman
npm install
npm run typecheck # types, including the test files
npm test # node:test, no extra test dependencies
npm run buildThe test suite is weighted toward the logic the safety claims rest on — that a checkpoint captures
untracked files without moving HEAD, that revert deletes files created after the checkpoint and
restores ones deleted after it, that allowedRoot rejects a sibling directory sharing a name
prefix, that the deny list merges into an existing opencode.json without clobbering it, and that
a claimed write with an empty diff raises a warning. CI runs it on Linux, macOS and Windows.
Contributing
Issues and PRs welcome. Still open: parallel delegations across separate directories, streaming progress from a run in flight, and more free-model providers.
License
MIT © 2026 Namit Nair
