opencode-map
← guides/

OpenCode v1 to v2: what moved where

IntermediateLast updated: Oct 21, 2026

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:

  1. Keep existing config and file-based definitions; start v2; verify models, credentials, agents, permissions, MCP servers.
  2. Port plugins — v1 implementations do not run.
  3. Migrate integrations calling the v1 server API to @opencode/client.
  4. Convert config to native v2 shape when ready — or ask opencode to do it: it can apply the mappings above without touching unrelated settings.
  5. 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.
  6. Re-check external_directory — v2 allows it by default where v1 asked.

Further reading