OpenCode 2 is a real rework, not a version bump. The official migration doc counts three intentional breaking changes: a new plugin API, new server-API contracts, and layered tui.json(c) files becoming one global cli.json. Everything else is meant to keep working — v2 reads v1 config locations and normalizes old keys in memory, so converting to the native v2 shape is optional. This guide maps every surface.
Install and coexistence
Both versions ship a binary called opencode and are no longer installed side by side by default — remove a package-managed v1 install first; the v2 curl installer replaces the v1 binary.
| Surface |
v1 |
v2 |
| Install script |
curl -fsSL https://opencode.ai/install | bash |
curl -fsSL https://opencode.ai/v2/install | bash |
| npm package |
opencode-ai |
@opencode/cli |
| Homebrew |
anomalyco/tap/opencode |
anomalyco/tap/opencode-v2 |
| Arch/AUR |
opencode / opencode-bin |
opencode-beta |
| mise |
mise use -g github:anomalyco/opencode |
mise use -g opencode (needs mise ≥ 2026.10.9 — older installs v1) |
| Windows |
choco / scoop / npm |
package managers not supported — binary downloads |
| Docker |
ghcr.io/anomalyco/opencode |
ghcr.io/anomalyco/opencode:2.0.0 (versioned tags) |
Config locations — unchanged
v2 reads the same system, global and project locations as v1 — /etc/opencode, /Library/Application Support/opencode, %ProgramData%\opencode, macOS managed prefs under ai.opencode.managed, ~/.config/opencode/opencode.json(c), project opencode.json(c) and .opencode/opencode.json(c). Every discovered .opencode config overrides every direct config, not just same-directory ones. Managed settings still outrank everything; the v2 way to actually restrict users is policies, not settings.
Key-by-key mapping
Agents and modes
| v1 |
v2 |
agent object; mode map |
agents — mode entries become primary agents |
prompt |
system |
disable |
disabled |
permission (object) |
permissions — ordered {action, resource, effect} rules, last match wins |
tools |
permissions — false becomes a deny rule |
variant (separate field) |
joins the model ref — provider/model#high |
temperature, top_p, options |
under request.body — caveat: the v2 runner preserves but does not yet send them with requests; set active request settings on the provider/model/variant |
maxSteps |
steps |
built-in scout |
gone |
agent/, agents/, mode/, modes/ dirs |
all still discovered; preferred .opencode/agents/ — files from mode(s)/ are primary agents, add mode: primary; file bodies need no system field |
| flat agent ids |
nested dirs name agents — team/reviewer |
Permissions
| v1 |
v2 |
permission object |
permissions ordered rule list |
actions bash, task |
shell, subagent — and write/patch fold into edit |
.env reads denied by default |
.env reads ask |
external_directory asks by default |
allowed by default — re-add { "action": "external_directory", "resource": "*", "effect": "ask" } to keep the v1 behavior |
doom_loop, lsp keys |
not v2 actions |
| todos always on |
off by default — experimental.todo: true |
enabled_providers / disabled_providers |
policies statements |
MCP servers
| v1 |
v2 |
mcp.<name> flat |
mcp.servers.<name> nested |
enabled |
disabled (inverted) |
timeout single |
timeout: { catalog, execution }; experimental.mcp_timeout seeds the defaults |
OAuth clientId, clientSecret, callbackPort, redirectUri |
client_id, client_secret, callback_port, redirect_uri |
interactive mcp add |
flag-driven mcp add --url/--env/--header/--global |
| (nothing) |
/mcps manages servers in-app |
Commands
| v1 |
v2 |
command map |
commands |
subtask |
subagent (legacy subtask still accepted); delegated commands run in background and report to the parent |
variant field |
model#variant |
command/, commands/ dirs |
both discovered; preferred commands/ |
| flat names only |
directory namespacing — team/review.md → /team/review |
@file expanded at store time |
attached at invocation |
| restart to load new files |
hot-reload on save |
Terminal client — tui.json → cli.json
v1 already layered TUI settings in tui.json(c) (global and project-local). v2 collapses them into one global ~/.config/opencode/cli.json — the terminal client owns it, the server doesn’t load it, and the first v2 TUI start auto-migrates global tui.json settings while leaving v1 files untouched. Project-local client config is not migrated because v2 client config is global-only. Inside: keybinds keeps the same value forms ("none"/false unbinds, key/event/preventDefault/fallthrough objects) but command ids switch from snake_case to dot-notation (app_exit→app.exit, input_paste→prompt.paste) and leader_timeout becomes a top-level leader.timeout sibling; plus theme, session, tabs, diffs, attention, plugins and more. Env override changes shape too: v1 OPENCODE_TUI_CONFIG pointed at a file path; v2 OPENCODE_CLI_CONFIG_CONTENT carries inline JSON merged over cli.json.
Plugins and providers
| v1 |
v2 |
plugin array |
plugins — tuple form becomes { "package", "options" } object |
.opencode/plugin/ dir |
.opencode/plugins/ (both discovered) |
| v1 plugin code runs |
v1 plugins do not run — port via the plugin migration guide |
provider |
providers; id/whitelist/blacklist fields ignored |
provider npm |
package — AI SDK packages take an aisdk: prefix |
provider api |
settings.baseURL |
provider options |
split by role into settings / headers / body; options.setCacheKey→compatibility.supportsPromptCacheKey |
| provider ids |
consolidated — azure-cognitive-services→azure, google-vertex-anthropic→google-vertex (top-level model keeps its exact id; update it yourself) |
| model fields |
id→modelID · tool_call/modalities→capabilities.{tools,input,output} · status:"deprecated"→disabled: true · cache_read/cache_write→cache.read/cache.write · options→settings · variants object→array with id per entry |
small_model |
agents.title.model |
Assorted renames, same-shape, ignored
|
|
| renamed / promoted |
snapshot→snapshots · attachment→media · autoupdate→update (notify/auto/disable) · reference→references (v1 already accepted the plural) · experimental.policies→policies (v1 key still merges) · skills object {paths, urls} → flat array · compaction.preserve_recent_tokens→keep.tokens + reserved→buffer |
| same shape, no migration |
tool_output, instructions, shell, formatter, watcher, model, default_agent, enterprise |
| ignored with a warning |
server (use the v2 service + explicit server options; the v1 API is a breaking change — integrations should move to @opencode/client), logLevel (use OPENCODE_LOG_LEVEL env), top-level subagent_depth (use experimental.subagent_depth), agent name, an enabled-only MCP entry without a type, compaction.tail_turns/prune, experimental batch_tool/openTelemetry/primary_tools/continue_loop_on_deny, provider id/whitelist/blacklist, provider-model release_date/attachment/reasoning/temperature/experimental/a non-deprecated status/boolean interleaved |
| v2-only |
warming, websearch, worktree, username |
| inert |
lsp config is accepted and preserved but v2 runs no language servers — use lint/typecheck commands instead |
| notes |
the /share TUI command no longer appears in the command docs; the share config key is still in the schema (manual/auto/disabled) alongside enterprise.url · a v1 CLAUDE.md fallback is not discovered in v2 — move it into AGENTS.md (v2 discovers global + ambient AGENTS.md up to home; for projects outside home, discovery stops at the project root) · skill dirs: .opencode/skill/ + .opencode/skills/ both discovered, move the whole directory · mixing v1/v2 keys is bounded — only mcp/compaction/experimental recurse; a native v2 value wins conflicts · the root model does not take a #variant suffix |
Product surfaces
| v1 |
v2 |
| OpenCode Zen (curated models, per-request billing) |
OpenCode Console |
| — |
OpenCode Go — $10/month subscription |
TUI + opencode web + IDE extension + desktop app |
TUI + desktop + opencode pair web UI (one-time link, expires in 5 minutes) |
docs opencode.ai/docs/ |
docs opencode.ai/v2/docs/ — CLI settings under /cli/ |
Migration checklist
Per the official doc you can keep your v1 config and just start v2 — conversion is optional. A practical order:
- Keep existing config and file-based definitions; start v2; verify models, credentials, agents, permissions, MCP servers.
- Port plugins — v1 implementations do not run.
- Migrate integrations calling the v1 server API to
@opencode/client.
- Convert config to native v2 shape when ready — or ask opencode to do it: it can apply the mappings above without touching unrelated settings.
- While validating, keep a copy of the v1 setup — both versions read the same locations, so don’t point v1 at files after converting them to v2-only shapes.
- Re-check
external_directory — v2 allows it by default where v1 asked.
Further reading