OpenCode V2 Is No Longer Just My Plan B
In May I wrote that Claude Code was my daily driver and OpenCode was my Plan B. That label aged quickly.
I meant it as a compliment. Plan B is the tool you’re glad you installed — the one you fall back to during an outage or a client engagement that won’t approve the supply chain. But Plan B also means second choice, and I listed three specific reasons it was second: a less-polished agent loop, a weaker subagent and verification story, and a smaller skills and integration ecosystem. Those were fair criticisms in May. The OpenCode 2.0 beta addresses all three head-on.
One thing up front, because it changes how you should read the rest of this: I have not run the V2 beta. I’ve used OpenCode V1 alongside Claude Code for months and I have opinions about it earned the normal way. Everything I say about V2 specifically comes from its official beta documentation and public release material, not from my terminal. I’m reading architecture, not reporting latency numbers.
First: there are two different things called “v2”
This trips people up constantly, so let’s separate them before anything else.
OpenCode 2.0 beta is the future core — the CLI, the server, the
runtime. It installs from the next npm tag with
npm install -g @opencode-ai/cli@next and runs as opencode2. It keeps
its own separate beta data and ships a new server/client/plugin
architecture. It is explicitly a beta, and the docs say so without
softening it.
Desktop v2 is something else entirely: a redesigned desktop layout whose migration completed in stable OpenCode v1.18.0 on July 14, 2026, with stable at v1.18.3 as of today. The effort itself ran across the v1.17 and v1.18 series — browser-style tabs and per-window restoration, a rebuilt review panel with persistent file browsing, integrated terminal improvements, per-prompt model selection, and server-aware session routing all landed through v1.17.x, and v1.18 finished the job with the old/new layout transition setting, faster cold loads, timeline reconnect and backfill, and better WSL startup readiness.
Both are real. They’re not the same release, not on the same track, and there is no stable 2.0 on GitHub to point at. This post is about the 2.0 beta. I mention the desktop work only because it’s evidence that the product’s UX is maturing on a separate front — not because a tab bar is why V2 matters.
The thesis
V1 was a good multi-provider CLI. V2 is becoming an inspectable, programmable agent runtime.
That’s the whole argument, and the distinction matters more than any feature list: the first is a program you run, the second is infrastructure you build on.
Background subagents, and being able to see them
The V2 agents documentation describes
two built-in primary agents — Build and Plan — and two built-in
subagents, General and Explore. Custom agents can be declared as
primary, subagent, or all. Subagents run in child sessions with
fresh context, which is the correct design: the point of a subagent is
that it burns its own context window and hands back a summary rather
than polluting the main conversation.
A primary agent can invoke subagents in the foreground or the
background. Visible subagents can be addressed directly with @
mentions. The public beta material highlights
background subagents and shell commands
as headline features, including a management view that lists what’s
running and lets you background or kill it. That UI isn’t described in
the beta docs yet — the session-background and shell-process endpoints
behind it are, so the capability is real, but treat the interface details
as provisional.
That last part is the one I care about, and it’s the least glamorous. Plenty of tools will happily spawn agents for you. Far fewer give the operator a lifecycle: what is running right now, and how do I stop it. Every agent harness I’ve used eventually produces the moment where something is clearly working on the wrong thing and your only recourse is Ctrl-C and hope. A UI that enumerates running work and offers a kill switch is not a flashy autonomy feature. It’s the difference between a system you can operate and a system you have to trust.
Permissions became policy instead of vibes
This is my favorite change, and it’s underrated.
V1 had a grouped permission object. V2 replaces it with an
ordered array of rules. Each rule
matches an action and a resource and carries an effect — allow, ask,
or deny — and the last matching rule wins. Actions cover read,
edit, shell, subagent, skill, web tools, MCP tools, and
external-directory boundaries. Agent-specific rules append after the
global rules, so an agent can refine policy without redefining it.
Directories outside the workspace get their own permission boundary.
Defaults treat environment files and out-of-workspace access
cautiously. And a configured deny cannot be overridden by a saved
interactive approval — you can’t click your way out of a policy you
wrote down.
If you’ve written firewall rules, IAM policies, or sudoers entries,
you know this shape and why it beats the alternative. Ordered,
last-match-wins rules are boring, auditable, and
reviewable in a pull request. The alternative that most of this industry
has settled for is asking a language model nicely to stay in its lane
and hoping the system prompt holds. One of those is a control. The other
is a wish.
Skills close the ecosystem gap
The ecosystem complaint from May was the one I expected to age worst, because ecosystems take years. V2’s answer is skills: Markdown instruction files advertised to the model and loaded on demand.
Project skills live in .opencode/skills, global ones in
~/.config/opencode/skills. The part that matters is the compatibility
list — V2 also reads .claude/skills, ~/.claude/skills,
.agents/skills, and ~/.agents/skills. Skills can ship scripts and
reference material alongside the instructions, can be invoked explicitly
as slash commands, and are governed by the same permission rules as
everything else. Sources can be local directories or HTTP catalogs.
Two things stand out. First, skills are advertised by ID, name, and
description rather than every skill body being stuffed into every
prompt — the difference between a skills system that scales past a dozen
entries and one that quietly eats your context window.
Second, and more importantly: reading .claude/skills means the
operational knowledge a team has already written down is portable. I
have Hugo-aware skills, memory conventions, and deployment runbooks
encoded for one harness. The prospect of not rewriting all of that to
evaluate a different runtime is, practically speaking, the entire
difference between “I’ll try it someday” and “I’ll try it this week.”
Portability is the whole point. After watching Anthropic restrict third-party access to Claude and break working setups overnight, the lesson I took was to never let a single vendor own a layer I can’t move. Skills that live in a directory I control, readable by more than one harness, are that lesson applied.
State management as a runtime responsibility
Long agent sessions rarely fail because the model got dumb. They fail because state got lost — context evaporated mid-task, or an edit went sideways and there was no clean way back.
V2 treats both as runtime problems. Snapshots
are on by default and capture worktree state immediately before each
model call and again after a cleanly completed step, using a separate
internal Git object database. They do not create commits in your repository, move your
branches, or intentionally touch your index — which is exactly the
constraint I’d want, because an agent that quietly commits on my behalf
is an agent I’d uninstall. /undo stages a conversation rollback and
can restore the affected file changes; /redo puts the conversation and
files back.
The docs are refreshingly blunt about the limits, and so should anyone recommending this be: it is best effort, and it does not reverse external side effects. Not databases, not services, not running processes, not network resources, not Git operations, not ignored build output, not files outside the active directory. If your agent ran a migration or pushed a branch, snapshots will not save you. This is undo for the worktree, not a time machine.
Compaction handles the other half. V2 builds structured checkpoints of older context while preserving durable earlier messages, estimates context pressure automatically and can compact before a model call rather than after failure, and can compact and retry once when a provider classifies an error as context overflow — a second overflow surfaces as a hard error rather than an infinite squeeze. The checkpoint captures objectives, important details, completed and active work, blockers, next moves, and relevant files.
That checkpoint schema is a design opinion, and it’s the right one — roughly what I’d write in a handover doc before going on holiday.
The API is the real story
Everything above is good. This is the part that changes what OpenCode is.
V2 exposes a documented HTTP API and a
generated TypeScript client, published as @opencode-ai/client@next.
The same server backs the TUI and the
desktop app, and is documented for custom clients and for embedding in
your own tooling. The surface is broad: sessions, durable
history, event streams, models and providers, permissions,
questions and forms, PTY sessions, shell process management, skills,
MCP, filesystem access, snapshots and reverts, compaction, and VCS
status and diff. Clients can
create sessions, send prompts, switch model or agent mid-flight,
interrupt work, wait for idle, and stream events. A Node service API can
discover, start, and stop a compatible local background OpenCode
service. Dax has described the API publicly as
“very very good” and
made a point of the bundled CLI client making it easy for a human — or
another agent — to query.
Read that list as an operator rather than a user. Wait for idle and stream events are how you put an agent in a pipeline. Durable history is how you audit what it did last Tuesday. Shell process management and snapshots are how you contain it. Start and stop a local background service is how you run it on a box with no terminal attached.
V1 looked like an application. V2 looks like infrastructure. That’s a category change, and it’s the reason “Plan B” stopped fitting — you don’t call the thing you can embed, automate, observe, and script your emergency fallback.
Provider neutrality, now with a serious harness
None of this replaces the original reason to care. The product still leads with more than 75 model providers through Models.dev including local ones, multi-session operation, LSP integration, terminal and desktop and IDE surfaces, ChatGPT Plus/Pro and GitHub Copilot login where officially available, and a privacy-first stance in which OpenCode itself stores no code or context. That last claim has an obvious asterisk worth stating plainly: your model provider still receives whatever you send it, under their policy, not OpenCode’s.
Provider neutrality was always the pitch. In May it came bundled with a harness that was merely fine, which is why Claude Code won my day. The V2 argument is that you no longer have to make that trade. Neutrality plus a serious runtime is a different product than neutrality plus a decent CLI, and it’s the combination — not the desktop redesign — that makes this worth your attention.
What’s still rough
I’d be doing the exact thing I complain about if I wrote 2,000 enthusiastic words and buried the caveats.
This is a beta. Not a stable 2.0, not a release you should treat as one.
The docs warn that beta data may be wiped and that features, APIs,
configuration, and plugin APIs may change. V1 plugins do not work in
V2 during the beta. Three areas are documented as intentionally
breaking: the plugin API, the server API and client contracts, and TUI
configuration moving to a global cli.json (migrated for you on first
startup, at least). Compatibility is the intent
everywhere else — V2 reads your existing V1 configuration and translates
V1-shaped config in memory rather than rewriting your files, which is a
genuinely considerate touch — but “intended compatible” and “verified
compatible” are different words, and beta regressions are normal.
Installation is also thin: the beta is package-manager-only — npm, Bun,
pnpm, or Yarn. No curl installer, no Homebrew, no standalone binaries,
no Chocolatey, Scoop, Docker, or mise path yet. Smaller edges show too:
custom commands still accept a subtask field and currently ignore it,
running in the current session instead of spawning a child. And plugin
and SDK contracts are still being finalized ahead of stable 2.0, so
anything you build against them now is built on sand you were warned
about.
The architecture is impressive. Production stability is unproven. Those statements are both true and neither cancels the other, and I’d rather say both than pretend the first one settles it.
So: try it now if you build agent tooling, care about provider
neutrality, want permissions you can actually read, or can absorb beta
breakage. Wait if you depend on V1 plugins, need stable automation
contracts, or can’t tolerate a local data reset. The nice part is that
this isn’t a migration decision at all — opencode and opencode2
coexist during the beta, so evaluating V2 doesn’t require replacing or
uninstalling V1. That’s also the only responsible way to do it. I’ve
argued before that
changing tools too often is its own tax,
and nothing here justifies moving critical workflows onto a beta because
the architecture reads well.
Where this leaves the Plan B label
Claude Code is still my daily driver, and I want to be precise about why, because “OpenCode got better” is not the same claim as “Claude Code got worse.” It didn’t. The harness is mature, Opus is co-tuned with it, and that pairing still produces results I haven’t matched elsewhere. Nothing in the V2 docs changes that.
What changed is the shape of the alternative. In May, OpenCode was the tool I kept installed for the day Plan A wasn’t available. The 2.0 beta is something else: a provider-neutral runtime with inspectable permissions, portable skills, real state management, and an API that makes it embeddable. Beta caveats fully intact, that is not a fallback. That’s a peer.
I’m going to keep running Claude Code tomorrow. But “Plan B” was a description of a gap, and the gap is closing faster than I expected it to.