My opencode setup, state of play: local qwen build, big-GLM planning
Someone asked recently how I run my agents and the answer is less a secret than a config that finally stands up. I opened opencode for the first time between December and January, out of curiosity more than conviction. I have been spending my days in it since June. Here is the state of play at the point where the config stopped changing every morning, with the settings that make a difference and the ones that only decorate.
The base
Everything starts from a deliberately sober opencode.json, the instructions point at a global AGENTS.md that travels with the config and the default agent is plan, you start in thinking mode and the build has to be earned.
The part I consider non-negotiable is permissions: the shell is broad but a list of denied patterns closes the doors that ruin a day.
"permission": {
"bash": {
"*": "allow",
"pkill*": "deny",
"rm -rf /": "deny",
"rm -rf ~": "deny",
"shutdown*": "deny",
":(){ :|:& };:": "deny"
}
}
You will notice that good old bash fork bomb getting top billing, just like it used to decorate this blog back in 2006-2012. The 2007 PAM post
already put it on stage.
The overall logic fits in one sentence: allow broadly, explicitly refuse what is irreversible and slide an ask level in for what deserves a human in between, directories outside the project for instance, which trigger a prompt before every access.
Compaction deserves a second look because that is where many sessions go to die: automatic, with pruning, above all a setting that preserves the recent working set, so a compacted session does not restart empty-handed.
share stays disabled, nothing leaves the machine without an explicit decision, autoupdate merely notifies and an MCP server keeps the documentation current.
One model per agent
The structuring decision is splitting by agent, not by tool, every agent has its model pinned in the config and the mood of the day gets no vote.
"agent": {
"build": { "model": "llamacpp-mlai/qwen3-coder-30b-a3b-instruct" },
"compaction": { "model": "llamacpp-mlai/qwen3-coder-30b-a3b-instruct" },
"general": { "model": "llamacpp-mlai/qwen3.5-35b-a3b" },
"mr-reviewer": { "model": "llamacpp-mlai/qwen3.5-35b-a3b" },
"explore": { "model": "llamacpp-mlai/qwen3.5-9b-mtp" },
"title": { "model": "llamacpp-mlai/qwen3.5-9b-mtp" },
"plan": { "model": "openrouter/z-ai/glm-5.2" }
}
The build fires dozens of calls per session, read a file, lay down a patch, rerun the tests, it runs locally on the qwen3-coder 30b-a3b, thirty billion parameters with three active, the MoE coder that fits at Q4 in the 24 GB of the RTX 4090, served by llama.cpp from a dedicated Linux workstation, mlai.gw.6admin.lab.
The compaction summary rides the coder as well, summarizing code is still code work.
The generalists and the reviews sit on the qwen3.5-35b-a3b, the February generation, a MoE with its MTP head for decode speed.
Session titles and the explore agent, though, went to the 3080, a second card serving the little qwen3.5-9b-mtp behind the same gateway, on a different port, the model that is enough when the task fits in a sentence.
The plan rides a big GLM on OpenRouter, 5.2 in this case, the wide-reading and arbitration work, exactly the job where a big model earns its keep. It only needs that a few times per session.
The producing camp is on the coder, the reading-and-judging one on the 35b, the loitering-and-titling one on the little 9b, the thinking one with the big GLM. Separation of duties costs nothing, you do not have the model that wrote a patch review its own work, blind spots do not stack.
Multi-provider and caching
The local provider declares like any openai-compatible provider, a baseURL pointing at the llama.cpp box and long timeouts, because local generation is slow and the agent waits without complaining as long as you told it that is normal.
"llamacpp-mlai": {
"npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "http://mlai.gw.6admin.lab:9931/v1" }
}
The little 9b answered behind the same gateway, on a different port, one more line in the config with the same llama.cpp pattern, the cheap frequent calls never touched the big card.
On the caching side, the trick that pays off fits in one key, setCacheKey, which freezes the prompt-cache key on the provider side: the shared prefix, system prompt, AGENTS.md, skills, becomes recognizable from one call to the next and the cache actually hits, the session costs less and answers faster.
The flip side shows up when a model changes, the cache starts from zero, which is easy to live with once agent routing is settled once and for all.
And because deny by default also applies to models, the mainstream providers are hard-disabled in the config: they exist, the agent cannot call them. Switching stacks is done by commenting one line, the kind of toggle that already saved an evening when the local box went down for maintenance.
The AGENTS.md, field manual
The global AGENTS.md carries everything that does not get invented mid-session and its existence changes agent behavior far more than any opencode.json knob.
A few verbatim rules, each paid for by a scar:
- “Repo-level AGENTS.md apply ONLY inside that repo tree. Reading repo files does NOT activate its rules elsewhere.” a demanding repo’s rules do not leak into peaceful projects.
- “Exactly ONE in_progress while working.” todo discipline, a single task in flight, never a batch of validations at the end of the day to buy forgiveness.
- “MUST NOT idle-wait in terminal: no sleep N.” the agent does not sleep between two checks, it waits through the tool’s own mechanism or launches in the background and resumes on signal.
- “MUST NOT install or invoke different version than project declares.” the version the project pins wins, not the one of the day nor the one the model believes it knows.
- “One topic = one worktree.” one session, one worktree, never mixed subjects in the main clone.
- “MUST NOT kill opencode processes.” restarting the agent is a human decision, not a shortcut the model grants itself.
It is long, it lives, every section was added after a specific pain rather than anticipated from an armchair.
The must-have skills
Three families stand out, the ones I plug in everywhere.
- CI and review: the
gitlab-ci-skillstraight from GitLab for pipelines, the cavecrew trio, an investigator that locates code intofile:linetables, a builder that only accepts surgical edits, a reviewer that outputs severity-tagged one-liners. Andmr-reviewer, the pre-merge gate that runs the test suite itself and returns a single verdict, MERGE OK or BLOCK. - Dev and security: the Trail of Bits skills for security review,
ui-ux-pro-maxfor interfaces, antfu’s skills for the JS ecosystem, the Node ones for the runtime. - Home-made infra: nginx, traefik, kubernetes, openbao and opentofu, ovh, everything that saves me rewriting from memory runbooks I already wrote once.
Slash commands copy-paste the routines and the rest of the collection covers the domains I cross less often, ready to serve.
The plugins
An architect’s honesty is due at this point of the tour: the AGENTS.md and the skills are soft power, conventions the model may choose to follow or ignore, they weigh on the prompt but nothing weighs on execution.
What actually constrains the model is the harness and its plugins, hard power the model gets no vote on.
openrtk routes shell commands through RTK, the filter that compresses tool output before it reaches the model, the gain shows up in tokens and therefore in bill and in available context.
The caveman plugin holds the speech mode and agent behavior, the terse tone on the session side, the build or title agents staying in line without me re-framing them every turn.
The guardrails do the rest of the hard work: worktree-guard enforces one session, one worktree, workflow-guard applies policy through hard hooks, branch protection, task and verification discipline, interception of destructive commands. damage-control eventually handed over: it blocked dangerous commands, workflow-guard took over that destructive ground and added workflow discipline on top, with checks the model cannot argue its way around.
The theme
The terminal is transparent and the interface panels sit on top translucent, the desktop background stays visible and reading happens without the usual opaque square.
The palette revolves around a peach primary, #fab283, with purple accents and greens for diffs, in a complete dark and light pair.
And this palette is not new, it is recycled from a theme I built between December and January, whose colors followed me from tool to tool until they became this config’s signature. A theme ages well when it has already survived another host.
The honest part
Per-agent routing has an entry cost: the 35b-a3b fits at Q4 in the 4090’s 24 GB with room to spare, so does the coder 30b-a3b, as long as you choose your quantizations and leave the OS some room, a model reloading or fragmenting shows up immediately in latency. The cache has a hidden price too: changing models empties it, which is a reminder to finish the routing before optimizing the bill. The big GLM is literally billed, a few calls per session make it worth it, beyond that the routing itself has a problem.
The guardrails sometimes block a move I would have called legitimate, you curse the guard the day it steps in and bless it the next morning. And the translucent theme assumes a terminal that renders transparency cleanly, on a busy background readability falls apart and you have to know when to lower the panels.
That is the state of play. The day a setting moves in here again, a new pain will have earned it and that will be a good sign.