# OpenCode 2 Upgrade: 12 Steps, 50 Min [2026]

> Source: <https://shattered.io/opencode-2-upgrade-from-1x-12-steps-2026/>
> Published: 2026-10-27 23:19:57+00:00

OpenCode 2 is not an in-place update. Version 2 became the main line of the open-source terminal coding agent on October 12, 2026, and 1.x installs no longer jump to it on their own. You have to ask for it with `/update` or `opencode upgrade --major`. A [third-party upgrade guide](https://www.apex-logic.net/news/opencode-2-0-upgrade-guide-2026) that reads the maintainers’ migration docs lists three things that break on the way: 1.x plugins, integrations that call the old server API, and the old `tui.json` terminal settings.

A clean migration takes about 50 minutes if you inventory first, back up second, and rebuild the local Ollama provider before touching anything else. Readers who built the 1.x setup from our earlier [OpenCode and Ollama walkthrough](https://shattered.io/open-source-ai-coding-assistant-opencode-ollama-2026/) can keep most of it. The model files, the Modelfile and the provider block all carry over. The instruction files, the permission rules and the plugins do not.

The stakes are not abstract. Stack Overflow’s [2025 Developer Survey](https://stackoverflow.co/company/press/archive/stack-overflow-2025-developer-survey/) found that 31% of developers currently use AI agents and that 69% of those who have used them at work agreed they raised productivity. A companion Stack Overflow report put trust in AI tools for complex tasks at 29%, down from 35% a year earlier. Developers use agents daily and still do not trust them with hard jobs. That gap is the reason this migration restores permission prompts, tests the model context window and checks every plugin instead of assuming the new version behaves like the old one.

Don't miss new tech stories on Google

Add Shattered once in the Google app and our stories show up in your news suggestions.

[Add now](https://www.google.com/preferences/source?hl=en&q=https%3A%2F%2Fshattered.io%2F)

## What Changed Between OpenCode 1.x and 2.x

OpenCode 2 rebuilt the agent around one background service. The terminal UI, the web app and the desktop app all attach to the same local process, and your own scripts can attach too. The release notes, as summarized by the upgrade guide, also list a rebuilt server API and SDK, a Code Mode that lets the agent call MCP tools from a short script, background subagents and shell commands, and a new plugin API. The guide adds that about 80 built-in features now run as internal plugins, a figure from that single source.

The release cadence since the switch has been fast. The table below lists only the dates that two or more sources agree on. Each row changes something you have to do on your own machine.

| Date (2026) | Event | What it means for your machine | 
|---|---|---|
| October 11 | OpenCode v1.19.0 stops installing updates automatically | 1.x users must run `/update` to move at all | 
| October 12 | Version 2 becomes the main line | New installs and current docs point at 2.x | 
| October 22 | v1.19.4 is the latest 1.x release, and the `@opencode/cli` package is listed at 2.1.3 | Two release lines and two package names exist side by side | 
| October 26 | An Ollama issue reports its installer pulling OpenCode v2.1.4 | `ollama launch opencode` now runs into v2 behavior | 
| October 27 | v2.2.0 ships with experimental desktop plugins and keyed session instructions | The newest release as of this writing | 

The October 27 release is tracked on [Havoptic’s OpenCode release page](https://www.havoptic.com/tools/opencode). A single-source report adds that v2.2.0 changes the exports of the `@opencode/ai` package and stops publishing the OpenCode GitHub Action. Treat that last point as reported rather than confirmed, and check your CI definitions before you rely on the Action either way.

### The package split you need to understand first

Two names matter. The 1.x line ships as the `opencode-ai` npm package and through GitHub Releases. The 2.x line reportedly ships as `@opencode/cli`, through the official install script and through Homebrew. The two lines use the same `opencode` command, which is where most migration pain starts. Install both and whichever directory comes first on your PATH wins, with no warning about the loser.

Anomaly, the company behind the project, calls the product free on its own site. The home page says OpenCode is “100% free to use” and ships with a set of free models. Paid options sit next to the client rather than inside it. OpenCode Go costs $10 per month, Go Plus costs $40 per month, and Zen sells pay-as-you-go access. The newer docs and the app call that pay-as-you-go layer OpenCode Console, while the public site still says Zen. Both names point at the same thing, so expect to see both while the rename settles. Our [four-day roundup of Claude Code, Codex CLI and OpenCode updates](https://shattered.io/claude-code-codex-cli-opencode-update-4-days-2026/) covers how the 2.0 launch compared with its rivals that week.

### Why local-model users feel this change most

Hosted-model users mostly keep working after the upgrade, because their provider keys and model names survive. Local-model users have more moving parts. The model server is a separate program, the context window lives in that program rather than in the agent, and the launcher integration between the two just changed.  An issue filed on October 26 reports that `ollama launch opencode` fails on OpenCode v2 with “Unrecognized flag: –model”, and a fix titled “fix OpenCode v2 launch and config parsing” followed within a day. Steps 6 and 7 route around that bug by configuring the provider by hand.

## Prerequisites and Tested Versions

Version drift is the enemy of a migration guide, so pin what you test. The table lists what this walkthrough assumes and why each piece matters. Where a version comes from a release tracker rather than from running it here, the cell says so.

| Component | Version | Why it matters | 
|---|---|---|
| OpenCode (starting point) | Latest 1.x, v1.19.4 as of October 22 | The upgrade path starts from the newest 1.x build | 
| OpenCode (target) | 2.2.0 or newer, released October 27 | Newest release line, with desktop plugins and keyed session instructions | 
| Ollama | v0.40.11 or newer, reported stable on October 24 | Serves the local model over an OpenAI-compatible endpoint | 
| Local coding model | `qwen3.5` (the model in Ollama’s own OpenCode example) | Must support tool calling and a 64k context window | 
| Bash | 5.x (scripts tested on 5.2) | Inventory, backup and AGENTS.md scripts | 
| Python | Not listed | Preflight checks, standard library only | 

The 64k figure comes straight from Ollama’s integration page. A [mirror of Ollama’s OpenCode docs](https://github.com/jateeter/ollama-embedded/blob/main/docs/integrations/opencode.mdx) says OpenCode needs a context length of 64k or higher and offers two routes: pick a cloud model, or set a large context window on a local one. If you have not picked a local model yet, our ranking of the [best open-source LLMs for October 2026](https://shattered.io/best-open-source-llm-october-2026-kimi-glm-deepseek/) compares five candidates on price and size. Ollama’s own blog lists NVIDIA Nemotron 3.5 Lightning, a 30 billion parameter model with 3 billion active, as built for agents that stay running and call tools. That architecture suits a laptop better than a dense 30B model does.

### Decide your path before you start

- **Upgrade in place** if you have a working 1.x setup with config you want to keep. This is the path the twelve steps follow.
- **Fresh install** if your 1.x setup was a trial. Skip steps 1 to 3, run the official 2.x install script, and start at step 5.
- **Stay on 1.x** if a plugin you depend on has no 2.x port yet. Set the update prompt to off so nobody upgrades by accident.

## Steps 1 and 2: Inventory and Back Up the 1.x Install

### Step 1: Take a read-only inventory

The inventory answers four questions. Which binary runs when you type `opencode`, how was it installed, where does its config live, and which files will the new version ignore. The script below only reads. It changes nothing, so run it from the root of a real project.

``` bash
#!/usr/bin/env bash
# Read-only snapshot of the current OpenCode install and project layout.
set -uo pipefail

echo "== binary"
if command -v opencode >/dev/null 2>&1; then
  echo "path:    $(command -v opencode)"
  echo "version: $(opencode --version 2>/dev/null || echo unknown)"
else
  echo "opencode is not on PATH"
fi

echo "== install method hints"
(npm ls -g --depth=0 2>/dev/null | grep -i opencode) || echo "npm:  no global opencode package"
(brew list --formula 2>/dev/null | grep -i opencode) || echo "brew: no opencode formula"

echo "== config and data locations"
for p in "$HOME/.config/opencode" "$HOME/.local/share/opencode" "./.opencode" "./opencode.json"; do
  if [ -e "$p" ]; then echo "found:   $p"; else echo "missing: $p"; fi
done

echo "== instruction and terminal files (depth 3)"
find . -maxdepth 3 \( -name CLAUDE.md -o -name AGENTS.md -o -name tui.json \) \
  -not -path "*/node_modules/*" | sort

echo "== 1.x plugin entries"
hits="$(grep -Hn '"plugin"' ./opencode.json "$HOME/.config/opencode/opencode.json" 2>/dev/null || true)"
if [ -n "$hits" ]; then echo "$hits"; else echo 'no "plugin" key found'; fi
```

Save it as `scripts/oc-inventory.sh`, make it executable with `chmod +x`, and run it. Example output from a project that still carries a 1.x plugin and two CLAUDE.md files looks like this. Your paths and versions will differ.

```
== binary
path:    /usr/local/bin/opencode
version: 1.19.4
== install method hints
npm:  no global opencode package
brew: no opencode formula
== config and data locations
found:   /home/dev/.config/opencode
found:   /home/dev/.local/share/opencode
missing: ./.opencode
found:   ./opencode.json
== instruction and terminal files (depth 3)
./CLAUDE.md
./services/AGENTS.md
./services/api/CLAUDE.md
== 1.x plugin entries
./opencode.json:1:{"$schema":"https://opencode.ai/config.json","plugin":["opencode-example-plugin"],...
```

Three lines matter most. The install method decides how step 3 goes, any CLAUDE.md without an AGENTS.md next to it is a rule file the new version will not read, and any plugin entry is code you must port in step 10.

### Step 2: Back up config, project folders and local data

The backup script archives the global config, the local data directory, the project `.opencode` folder, `opencode.json`, and both instruction files. According to the upgrade guide, the new version reads your existing files where they are and converts 1.x fields in memory, so in theory you will not need the archive. In practice the archive costs two seconds and saves an afternoon, because you cannot point 1.x back at files you later convert to 2.x-only formats.

``` bash
#!/usr/bin/env bash
# Archive OpenCode config, project folders and local data before touching the install.
set -euo pipefail

STAMP="$(date +%Y%m%d-%H%M%S)"
DEST="${OC_BACKUP_DIR:-$HOME/oc-backups}"
mkdir -p "$DEST"
chmod 700 "$DEST"
OUT="$DEST/opencode-$STAMP.tar.gz"

paths=()
for p in "$HOME/.config/opencode" "$HOME/.local/share/opencode" "./.opencode" "./opencode.json" "./AGENTS.md" "./CLAUDE.md"; do
  [ -e "$p" ] && paths+=("$p")
done

if [ "${#paths[@]}" -eq 0 ]; then
  echo "nothing to back up" >&2
  exit 1
fi

tar -czf "$OUT" "${paths[@]}" 2>/dev/null
chmod 600 "$OUT"
echo "backup written: $OUT"
echo "contents:"
tar -tzf "$OUT" | head -20
echo "WARNING: the archive may contain provider credentials. Keep it private."
```

Two details deserve attention. The data directory can hold provider credentials, so the script sets the archive to mode 600 and the destination to mode 700. And the archive lives in your home directory, not in the repo. A backup tarball that lands in a project folder is one careless `git add .` away from a public key leak.

Finally, commit your current `opencode.json`, `AGENTS.md` and `CLAUDE.md` to git on a branch named `pre-opencode-2`. The tarball protects global state. The branch protects project state, and it gives your teammates the same rollback point.

## Steps 3 to 5: Remove the Old Install, Upgrade and Verify

### Step 3: Remove a package-managed 1.x install

The upgrade guide says the 2.x install script replaces the 1.x binary, so any 1.x copy made through a package manager has to go first. Use the install hint from step 1 to pick the right command. If you installed through npm, remove the `opencode-ai` package. If you used Homebrew, remove the formula that `brew list` showed you. If you ran the official script, there is nothing to remove, because the script overwrites in place.

```
# npm installs (the 1.x package name is opencode-ai)
npm uninstall -g opencode-ai

# Homebrew installs: only if step 1 showed a formula
brew uninstall opencode

# confirm nothing named opencode is left on PATH
which -a opencode || echo "clean: no opencode on PATH"
```

The last line matters more than the first two. `which -a` prints every match, not just the first, so a stale copy in `~/.local/bin` or `/usr/local/bin` shows up here instead of three weeks from now. Delete any leftovers by hand.

### Step 4: Run the upgrade

Pick one of three routes. Whichever you choose, start from the newest 1.x build, which was v1.19.4 on October 22, so the migration code on the 1.x side is as recent as it gets.

- **Route A, upgrade from inside 1.x.** Open OpenCode and run`/update` , or run`opencode upgrade --major` from the shell. Version 1.19.0 turned off silent installs on October 11, so this explicit request is the only way 1.x moves to 2.x.
- **Route B, official install script.** The upgrade guide gives`curl -fsSL https://opencode.ai/v2/install | bash` as the 2.x script. Read a script before you pipe it to a shell, and fetch it with`curl` into a file first if your policy requires that.
- **Route C, package channels.** A single-source report says 2.x also ships as the`@opencode/cli` npm package and through Homebrew, while GitHub Releases and`opencode-ai` keep serving 1.x. Use this route if your machines are managed through a package inventory.

```
# Route A
opencode upgrade --major

# Route B
curl -fsSL https://opencode.ai/v2/install -o /tmp/opencode-v2-install.sh
less /tmp/opencode-v2-install.sh      # read it
bash /tmp/opencode-v2-install.sh

# Route C (reported package name; verify it before installing)
npm install -g @opencode/cli
```

A GitHub issue filed on October 21 reports that the GitHub release channel had no 2.x build to offer, which explains why the `--major` flag and the install script exist at all. If Route A says there is nothing newer, switch to Route B instead of retrying.

### Step 5: Verify the version, the paths and the startup warnings

Confirm that the shell now runs 2.x and find out where it keeps its config. The guide names `opencode debug paths config` for the second job. Start OpenCode once in a throwaway project afterwards and read every warning it prints. Version 2 converts 1.x fields in memory, and it warns about the fields it ignores.

```
opencode --version
which -a opencode
opencode debug paths config

# start once in a scratch folder and read the warnings
mkdir -p /tmp/oc-scratch && cd /tmp/oc-scratch && opencode
2.2.0
/usr/local/bin/opencode
/home/dev/.config/opencode
```

The output above is an example. Your version string should start with 2 and `which -a` should print exactly one path. Two paths mean two installs, and the first one wins. On first start, version 2 builds a single global `~/.config/opencode/cli.json` from your old layered `tui.json` files. Project-level terminal settings do not migrate, so reapply those by hand.

## The 1.x to 2.x Config Map You Have to Handle by Hand

Version 2 keeps reading 1.x config, and mixed 1.x and 2.x top-level fields can coexist in one file, so you can convert gradually. That is good news for a Friday afternoon upgrade. It also hides real differences. The table lists the renames that matter for a typical local-model setup, taken from the third-party upgrade guide that summarizes the maintainers’ migration table. Confirm every row against the official migration page before you rewrite a team config.

| 1.x field | 2.x field | Practical note | 
|---|---|---|
| `permission` and`tools` | `permissions` , an ordered array of action, resource and effect | Order matters, so test the result instead of trusting it | 
| `agent` and`mode` | `agents` | Plural, one key | 
| `provider` | `providers` | The 1.x spelling still loads, which is why step 7 keeps it | 
| `mcp` servers with`enabled` | `mcp.servers` with`disabled` | The flag inverts, a classic copy-paste bug | 
| `command` and`subtask` | `commands` and`subagent` | Rename both | 
| `skills.paths` and`skills.urls` | `skills` as one array | Merge the two lists | 
| `autoupdate` : false, notify or true | `update` : disable, notify or auto | Set this to stop surprise upgrades on shared machines | 
| `plugin` | `plugins` | Rename plus a code port, see step 10 | 
| `tui.json` layers | One global `cli.json` | Migrated on first start, project layers are not | 
| `CLAUDE.md` discovery | Only `AGENTS.md` is discovered | Copy or link the file, see step 8 | 

Three behaviors change without a config rename, and they are the ones that bite. Version 2 allows the `external_directory` action by default, so the agent can touch folders outside the project without asking. Some provider IDs were renamed, with `azure-cognitive-services` becoming `azure` and `google-vertex-anthropic` becoming `google-vertex`, while the top-level `model` field keeps its exact ID and needs a manual edit. And a few 1.x fields are ignored with a warning, including `logLevel`, which the guide says to replace with the `OPENCODE_LOG_LEVEL` environment variable, and `server`.

One more change affects anyone who relies on editor-style feedback. According to the guide, 2.x keeps the `lsp` config but does not run language servers or show LSP diagnostics. If your 1.x workflow leaned on those diagnostics, add your linter or test runner to the loop instead.

## Steps 6 and 7: Rebuild the Local Ollama Provider

### Step 6: Pull the model and give it a 64k context window

Ollama runs the model, so the context window is an Ollama setting rather than an OpenCode one. This is where local setups quietly fail. A model that loads with a small default window will still answer short prompts, then lose the start of a long agent session without raising any error. Ollama’s integration page says OpenCode needs 64k or more, and 64k is 65,536 tokens.

```
ollama --version
ollama pull qwen3.5

cat > Modelfile <<'EOF'
FROM qwen3.5
PARAMETER num_ctx 65536
EOF

ollama create qwen3.5-64k -f Modelfile
ollama show qwen3.5-64k --modelfile | grep num_ctx
```

The last command should print a line containing `PARAMETER num_ctx 65536`. If it prints nothing, the Modelfile did not take. A bigger window costs memory, and one that overflows video memory spills into system RAM. Start at 64k and measure before going higher. Ollama also documents a server-wide default through its context-length settings, which suits a shared workstation better than baking the value into one model.

Not sure which local model deserves the VRAM? The [Recursive Flint open coding model](https://shattered.io/recursive-flint-40b-moe-coding-model-2026/) and the Qwen family are both worth testing against your own repo, and our guide to a [self-hosted coding assistant built around Qwen Code](https://shattered.io/self-hosted-ai-coding-assistant-qwen-code-2026/) shows a second way to serve one. Run the same three prompts through each candidate and keep the one that finishes them without tool-call errors.

### Step 7: Point OpenCode 2 at the local endpoint

Ollama offers two ways in. The shortcut is `ollama launch opencode`, which starts OpenCode with an inline config for the model you pick. It does not overwrite `~/.config/opencode/opencode.json`, so your existing settings still apply. The manual route writes an Ollama provider into `opencode.json` yourself. Because of the October 26 launch bug, the manual route is the safer default today. Models you define only in `opencode.json` will not appear in the launcher’s model picker, according to Ollama’s integration page, so do not expect the two routes to show the same list.

```
{
  "$schema": "https://opencode.ai/config.json",
  "model": "ollama/qwen3.5-64k",
  "provider": {
    "ollama": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama",
      "options": {
        "baseURL": "http://localhost:11434/v1"
      },
      "models": {
        "qwen3.5-64k": {
          "name": "qwen3.5-64k"
        }
      }
    }
  }
}
```

The provider block follows the example in Ollama’s docs, with the model name swapped for the 64k variant from step 6. The base URL must end in `/v1`, because OpenCode talks to Ollama through its OpenAI-compatible endpoint rather than the native API. Save the file in the project root, then run a one-shot prompt to prove the whole chain works.

```
ollama launch opencode --config      # optional: writes Ollama-side config without starting a session
opencode run -m ollama/qwen3.5-64k "Reply with the single word READY"
```

Check the exact flags with `opencode --help`, since the CLI changed between major versions. A reply containing READY means the endpoint, the model name and the tool plumbing all line up. A connection error means Ollama is not listening, and the troubleshooting section lists the usual causes. If you stay on the launcher route, update Ollama until its release notes mention the OpenCode v2 launch fix, because the commit landed on October 26 and the latest stable release reported, v0.40.11 on October 24, predates it.

## Steps 8 and 9: Move the Rules and Restore Permission Prompts

### Step 8: Give every CLAUDE.md an AGENTS.md

Version 2 discovers only `AGENTS.md`. A repo that kept its agent rules in `CLAUDE.md` now runs the agent with no rules at all, and nothing warns you. The model still answers, it just stops respecting your test commands, style notes and forbidden paths. The script below creates the missing file next to each CLAUDE.md, skipping any folder that already has one.

``` bash
#!/usr/bin/env bash
# Give every CLAUDE.md a sibling AGENTS.md, because OpenCode 2 only discovers AGENTS.md.
# Default is a copy. Pass --link to symlink so both tools read one file.
set -euo pipefail

MODE="copy"
[ "${1:-}" = "--link" ] && MODE="link"

count=0
while IFS= read -r -d '' f; do
  dir="$(dirname "$f")"
  target="$dir/AGENTS.md"
  if [ -e "$target" ]; then
    echo "skip (AGENTS.md exists): $dir"
    continue
  fi
  if [ "$MODE" = "link" ]; then
    ln -s CLAUDE.md "$target"
  else
    cp "$f" "$target"
  fi
  echo "created ($MODE): $target"
  count=$((count + 1))
done < <(find . -maxdepth 4 -name CLAUDE.md -not -path "*/node_modules/*" -print0)

echo "done: $count file(s) created"
```

Run it with no arguments to copy the files, or with `--link` to create symlinks so one file serves both tools. Copies drift apart over time. Symlinks do not, but they break on Windows checkouts without developer mode. If your team also uses Claude Code, which reads CLAUDE.md, keep both files until you settle on one tool.

Keep the rules short and checkable. A rule like *run `make test` before declaring a task done* is one an agent can follow and you can verify. A rule like *write clean code* is not. Four to eight lines covering the test command, the lint command, off-limits directories and your commit style do more than a page of prose.

### Step 9: Put the ask-before-acting rule back

Version 2 allows the `external_directory` action by default. That means an agent working in your project can read and edit folders outside it without a prompt. Version 1 asked first. The upgrade guide gives the one-rule fix, which you add to `opencode.json` next to the provider block:

```
{
  "permissions": [
    { "action": "external_directory", "resource": "*", "effect": "ask" }
  ]
}
```

The 2.x action names differ from 1.x too. The mapping in the guide renames `bash` to `shell`, `task` to `subagent`, and the `write` and `patch` actions to `edit`. Because `permissions` is an ordered array, put broad rules first and narrow exceptions after them, then prove the behavior with a harmless test. Ask the agent to create a file in `/tmp` and confirm that a prompt appears. A permission rule you never tested is a hope, not a control.

A sensible local-model baseline asks before shell commands and edits, and allows reads. Local models make more tool-call mistakes than hosted frontier models, and a prompt is the cheapest way to catch a wrong `rm` before it runs. Loosen the rules later, one action at a time, once a week of logs shows the model behaves.

## Steps 10 and 11: Port Plugins, MCP Servers and Integrations

### Step 10: Port every 1.x plugin

Renaming a plugin file does not port it. The upgrade guide is blunt about this: 1.x plugins do not run on 2.x, and the work has two layers. The config entry changes first. The key `plugin` becomes `plugins`, and each package-and-options pair becomes an object.

```
// 1.x
{ "plugin": ["opencode-example-plugin", ["./plugin/local.ts", { "enabled": true }]] }

// 2.x
{ "plugins": ["opencode-example-plugin", { "package": "./plugin/local.ts", "options": { "enabled": true } }] }
```

The code changes next. Import from `@opencode/plugin` instead of `@opencode-ai/plugin`. Default-export `Plugin.define` with a stable `id` and a `setup(ctx)` function. The project directory moves under `ctx.location`, options move to `ctx.options`, and the old client gives way to domain methods such as `ctx.session`. A cleanup function returned from `setup` replaces `dispose()`. Hooks move to their domains: `tool.execute.before` becomes `ctx.tool.hook("execute.before", ...)`, `permission.ask` becomes `ctx.permission.hook("evaluate", ...)`, and `event` becomes `ctx.event.subscribe()`.

```
// plugins/audit-tools.ts  (skeleton: check hook payloads against the plugin migration page)
import { Plugin } from "@opencode/plugin"

export default Plugin.define({
  id: "audit-tools",
  setup(ctx) {
    const project = ctx.location?.directory ?? "unknown"

    ctx.tool.hook("execute.before", async (input: unknown) => {
      console.error(`[audit ${new Date().toISOString()}] ${project} ${JSON.stringify(input)}`)
    })

    return () => {
      console.error("[audit] plugin cleaned up")
    }
  },
})
```

Treat the file above as a skeleton, not a finished plugin. The hook names and the cleanup rule come from the migration guide as summarized by a third party, and the payload shape of each hook is something only the official plugin page can confirm. Port one plugin, run it against a scratch project, and read the startup log before you port the next. One mapping is not one-to-one: `command.execute.before` has no global equivalent, so use `ctx.command.transform` for the commands your plugin owns.

If a plugin is a thin wrapper around a hosted service you no longer use, delete it. A migration is the cheapest moment to drop dead weight. And if the plugin is a community package, check its repository for a 2.x release before you write your own port.

### Step 11: Retest MCP servers, server API callers and CI

Three kinds of integration need a pass. The first is MCP servers. Version 2 opts every MCP server into Code Mode by default, which means the agent can chain several tool calls inside one short script instead of calling them one by one. That is faster and cheaper in tokens, but it changes behavior, so rerun every task that depends on your own servers. Remember the flag inversion from the config map: a 1.x server marked `enabled: true` becomes one without `disabled` under `mcp.servers`.

The second kind is anything that calls the old server API, such as editor extensions, scripts and CI jobs. Those must move to the `@opencode/client` package and the new API reference, and none of that code carries over. The third kind is automation built on the OpenCode GitHub Action. A single-source report says the Action is no longer published as of v2.2.0, so replace it with a plain job step that installs the CLI and calls it directly, and watch the project’s release notes for a confirmed answer.

A smoke test makes all three checks repeatable. The script runs three short prompts through the CLI and looks for a marker in each answer. The `timeout` command ships with GNU coreutils, so on macOS install it through Homebrew as `gtimeout` and edit the script.

``` bash
#!/usr/bin/env bash
# Three-prompt smoke test for an OpenCode 2 + Ollama setup.
set -uo pipefail

MODEL="${1:-ollama/qwen3.5-64k}"
fail=0

run() {  # name, prompt, expected substring
  local out
  out="$(timeout 180 opencode run -m "$MODEL" "$2" 2>&1)" || true
  if grep -qi -- "$3" <<<"$out"; then
    echo "[PASS] $1"
  else
    echo "[FAIL] $1"
    fail=1
  fi
}

run "plain reply"    "Reply with the single word READY" "READY"
run "reads AGENTS.md" "What is the test command in AGENTS.md? Answer with the command only." "make test"
run "uses a tool"    "List the files in this directory with a tool, then say DONE" "DONE"

exit "$fail"
```

Save it as `scripts/oc-smoke.sh` and adjust the second expected string to match the test command in your own AGENTS.md.

## Step 12: Decide Where Your Models Run, Then Verify the Stack

A local model is one of several ways to feed OpenCode, and the pricing around it changed in October. The client stays free. Model access does not have to. The table compares the options from the vendors’ own pricing pages as of October 27, 2026. Prices move often, so confirm them at the source before you commit a team budget.

| Option | Price | What you get | Trade-off | 
|---|---|---|---|
| Local Ollama model | Hardware you already own | Code never leaves the machine | Limited by VRAM and by how well the model calls tools | 
| Ollama Free | $0 | Any cloud model on pay-as-you-go usage credits | Credits are metered and a cloud model is not local | 
| Ollama Pro | $20 per month, or $200 per year | $60 of usage credits per month | Aimed at shorter, well-defined day-to-day tasks | 
| Ollama Max | $100 per month | $300 of usage credits per month | Built for people who run several agents at once | 
| OpenCode Go | $10 per month | A curated lineup of coding models with per-model limits | Hosted, and limits apply per model | 
| OpenCode Go Plus | $40 per month | The same lineup with higher limits | Same hosting trade-off at a higher price | 
| OpenCode Zen (Console) | Pay as you go | Prepaid balance, models billed at listed token rates | A card fee applies, and the exact rate varies by source | 

The Go plans meter usage per model. Each model has a five-hour limit of 20% of its monthly limit, a weekly limit of 50%, and the full monthly limit, as the [OpenCode Go documentation](https://opencode.ai/v2/docs/console/go) lays out. In practice that means one heavy afternoon on a single model can lock that model for the rest of the day while others stay open. Switch models instead of waiting. Ollama Cloud works differently: current OpenCode docs, as summarized in third-party coverage, describe it as a separate provider that needs an Ollama account and an API key from Settings, connected through the `/connect` command. Do not confuse it with local execution. Code sent to a cloud model leaves your machine.

### Read the data terms before you pick a hosted model

- **Contributor models trade prompts for price.** OpenCode lists Muse Spark 1.3 Contributor and Muse Spark 1.2 Contributor with heavily discounted token pricing in exchange for permission to use your prompts and completions to train future Meta models. Using them requires turning on the setting that allows paid endpoints that train on request data. Leave that off for private code.
- **Retention differs by model.** The model listing shows Claude Haiku 5.5 with 30 days of data retention. DeepSeek runs under a zero-data-retention agreement that is renewed monthly, and the current agreement is valid through October 31, 2026.
- **Discounts expire.** The 50% discount on Gemini 4 Argon applies through November 30, 2026. Budget for the list price.
- **Free tiers can be client-locked.** Issue reports from September and October say the free Zen endpoint began refusing clients other than the official OpenCode binary. Do not build a script on a free endpoint.

For a head-to-head view of the hosted frontier options you might route hard tasks to, see our comparison of [Claude Opus 5.5 and GPT-6 Astra on Terminal-Bench](https://shattered.io/claude-opus-5-5-vs-gpt-6-astra-terminal-bench-2026/). A common pattern is a local default for routine edits and a metered hosted model for the occasional hard refactor. OpenCode makes that a one-line switch with `/models`.

### Run the final verification

With the provider chosen, run the doctor script and the smoke test back to back. The doctor checks the binary, the Ollama endpoint, the model, the context setting, the instruction files and the legacy config keys. Here is the full script. It uses only the Python standard library and exits non-zero when any check fails, so it also works as a CI or pre-commit gate.

``` bash
#!/usr/bin/env python3
"""Preflight checks for an OpenCode 2 + Ollama setup. Standard library only."""
import json
import re
import shutil
import subprocess
import sys
import urllib.request
from pathlib import Path

OLLAMA = "http://localhost:11434"
MODEL = sys.argv[1] if len(sys.argv) > 1 else "qwen3.5"
MIN_CTX = 65536  # Ollama's docs say OpenCode needs a 64k or larger context window

results = []

def check(name, ok, detail=""):
    results.append((name, ok, detail))
    print(f"[{'PASS' if ok else 'FAIL'}] {name}" + (f": {detail}" if detail else ""))

def http(path, payload=None):
    data = json.dumps(payload).encode() if payload is not None else None
    req = urllib.request.Request(OLLAMA + path, data=data)
    if data:
        req.add_header("Content-Type", "application/json")
    with urllib.request.urlopen(req, timeout=5) as r:
        return json.load(r)

# 1. OpenCode binary and major version
exe = shutil.which("opencode")
if not exe:
    check("opencode on PATH", False, "not found")
else:
    out = subprocess.run([exe, "--version"], capture_output=True, text=True).stdout.strip()
    m = re.search(r"(\d+)\.(\d+)\.(\d+)", out)
    check("opencode on PATH", True, exe)
    check("opencode is 2.x", bool(m) and m.group(1) == "2", out or "no version output")

# 2. Ollama reachable and model present
try:
    ver = http("/api/version").get("version", "?")
    check("ollama reachable", True, f"v{ver}")
    names = [m["name"] for m in http("/api/tags").get("models", [])]
    have = any(n == MODEL or n.startswith(MODEL + ":") for n in names)
    check(f"model {MODEL} pulled", have, ", ".join(names[:5]) or "no models")
    if have:
        info = http("/api/show", {"model": MODEL})
        m = re.search(r"num_ctx\s+(\d+)", info.get("parameters", ""))
        ctx = int(m.group(1)) if m else 0
        check("num_ctx >= 65536", ctx >= MIN_CTX, f"num_ctx={ctx or 'default'}")
except Exception as exc:
    check("ollama reachable", False, str(exc))

# 3. Project files that changed between 1.x and 2.x
root = Path.cwd()
claude = [p for p in root.rglob("CLAUDE.md") if "node_modules" not in p.parts]
orphans = [str(p.parent) for p in claude if not (p.parent / "AGENTS.md").exists()]
check("every CLAUDE.md has an AGENTS.md", not orphans, ", ".join(orphans[:3]))

cfg = root / "opencode.json"
if cfg.exists():
    try:
        data = json.loads(cfg.read_text())
        legacy = [k for k in ("plugin", "tools", "autoupdate", "agent", "mode") if k in data]
        check("no 1.x-only top-level keys", not legacy, ", ".join(legacy))
    except json.JSONDecodeError as exc:
        check("opencode.json is valid JSON", False, str(exc))
else:
    check("opencode.json present", False, "none in project root")

failed = [r for r in results if not r[1]]
print(f"\n{len(results) - len(failed)}/{len(results)} checks passed")
sys.exit(1 if failed else 0)
```

Run it from the project root with the model name as the first argument. Example output on a healthy machine:

``` bash
$ python3 scripts/oc-doctor.py qwen3.5-64k
[PASS] opencode on PATH: /usr/local/bin/opencode
[PASS] opencode is 2.x: 2.2.0
[PASS] ollama reachable: v0.40.11
[PASS] model qwen3.5-64k pulled: qwen3.5-64k:latest
[PASS] num_ctx >= 65536: num_ctx=65536
[PASS] every CLAUDE.md has an AGENTS.md
[PASS] no 1.x-only top-level keys

7/7 checks passed
```

## The Complete Working Project: oc-migrate-kit

Everything above fits in one small folder you can copy into any repository or keep in a dotfiles repo. The layout is flat on purpose. No dependencies, no build step, nothing to install beyond the tools you are migrating.

```
oc-migrate-kit/
  Makefile
  AGENTS.md
  opencode.json
  Modelfile
  plugins/
    audit-tools.ts
  scripts/
    oc-inventory.sh
    oc-backup.sh
    agents-migrate.sh
    oc-doctor.py
    oc-smoke.sh
```

The scripts are the ones shown in steps 1, 2, 8, 11 and 12. Three files are new here. The Makefile gives the team one vocabulary for the whole flow.

```
.PHONY: inventory backup agents doctor smoke check

inventory:
	bash scripts/oc-inventory.sh

backup:
	bash scripts/oc-backup.sh

agents:
	bash scripts/agents-migrate.sh

doctor:
	python3 scripts/oc-doctor.py qwen3.5-64k

smoke:
	bash scripts/oc-smoke.sh

check: doctor smoke
```

The merged `opencode.json` keeps the 1.x `provider` spelling from Ollama’s docs, which 2.x still reads, and adds the 2.x `permissions` array and the 2.x `update` setting. Mixing the two vocabularies is allowed during a gradual conversion. It also gives you a single file that explains the migration to the next person who opens it.

```
{
  "$schema": "https://opencode.ai/config.json",
  "model": "ollama/qwen3.5-64k",
  "update": "notify",
  "provider": {
    "ollama": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama",
      "options": { "baseURL": "http://localhost:11434/v1" },
      "models": { "qwen3.5-64k": { "name": "qwen3.5-64k" } }
    }
  },
  "permissions": [
    { "action": "shell", "resource": "*", "effect": "ask" },
    { "action": "edit", "resource": "*", "effect": "ask" },
    { "action": "external_directory", "resource": "*", "effect": "ask" }
  ]
}
```

To run the whole flow on a new repo, copy the folder in, then work down the Makefile: `make inventory`, `make backup`, upgrade, `make agents`, `make check`. For the wider picture of editors, agents and review tools around this setup, see the [developer tools coverage](https://shattered.io/category/software/).

## Common Pitfalls That Break an OpenCode 2 Migration

Most failed upgrades trace back to the same dozen mistakes. These are the ones that cost the most time, ordered roughly by how often they show up.

- **Two binaries named opencode.** A package-managed 1.x install and the 2.x script both want the same command name. Whichever directory comes first on PATH wins. Run`which -a opencode` after every install and delete the loser.
- **Skipping the backup because 2.x reads the old files.** It does, but it converts them in memory and the optional native conversion rewrites them. Once you convert, 1.x cannot read the result, and your rollback is gone.
- **A backup archive with live credentials in a repo folder.** The data directory can hold provider keys. Store the archive outside any git working tree and set mode 600.
- **Leaving the context window at the default.** The agent keeps answering while it silently forgets the start of the session. Set 64k in the Modelfile and verify it with the doctor script.
- **Forgetting AGENTS.md.** The agent stops following your rules the moment CLAUDE.md goes undiscovered. No warning appears. The only symptom is worse output.
- **Trusting the new permission defaults.** The`external_directory` action is allowed by default in 2.x. Add the ask rule before the first real session, not after the first surprise.
- **Renaming plugins instead of porting them.** A plugin with the right config key and old code does nothing. Read the startup log for ignored-field warnings.
- **Treating Ollama Cloud as local.** It is a hosted provider with an account and an API key. If your policy says code must stay on the machine, cloud models are out.

If you run more than one agent against the same Ollama model, the 64k setting and the permission habits transfer. Our setups for [Aider with Ollama and git](https://shattered.io/aider-local-ai-coding-assistant-ollama-git-2026/), [OpenHands](https://shattered.io/openhands-local-setup-agent-canvas-ollama-12-steps-2026/) and [Kilo Code in VS Code](https://shattered.io/kilo-code-local-setup-ollama-vs-code-2026/) all point at the same local endpoint, so one pulled model serves every tool.

## Troubleshooting: 12 Problems and Fixes

1. **Symptom: `opencode upgrade --major` says there is nothing newer.** Cause: the GitHub release channel did not offer a 2.x build, per an October 21 issue report. Fix: use the official install script from step 4 or a package channel instead.
2. **Symptom: `opencode --version` still prints 1.x after the upgrade.** Cause: an older binary earlier on PATH. Fix: run`which -a opencode` , delete or reorder the stale entry, and open a new shell so the command hash resets.
3. **Symptom: `ollama launch opencode` fails with “Unrecognized flag: –model”.** Cause: the launcher predates the OpenCode v2 flag changes, tracked in an Ollama issue filed October 26. Fix: configure the provider by hand as in step 7, or update to an Ollama release that includes the launch fix.
4. **Symptom: connection refused on port 11434.** Cause: Ollama is not running, or the base URL is wrong. Fix: start the server with`ollama serve` , check`curl http://localhost:11434/api/version` , and make sure the OpenCode base URL ends in`/v1` .
5. **Symptom: the model replies but never calls tools, or loops.** Cause: a context window below 64k, or a model without good tool-calling support. Fix: run the doctor script, rebuild the Modelfile, and test a second model.
6. **Symptom: the model is missing from the picker after `ollama launch`.** Cause: models defined only in` opencode.json` do not appear in the launcher’s model picker. Fix: start`opencode` directly and use`/models` .
7. **Symptom: the agent ignores your project rules.** Cause: only AGENTS.md is discovered in 2.x. Fix: run`make agents` and restart the session.
8. **Symptom: a plugin does nothing, or a warning names an ignored field.** Cause: the config still says`plugin` , or the code still imports the 1.x package. Fix: rename to`plugins` and port the code as in step 10.
9. **Symptom: the agent edits files outside the project with no prompt.** Cause:`external_directory` is allowed by default in 2.x. Fix: add the ask rule from step 9 and test it.
10. **Symptom: free Zen models return a 403 or a free-usage limit message.** Cause: reports say the free endpoint is gated to the official client, and limits apply per model. Fix: connect your own provider key, a Go plan, or a local model.
11. **Symptom: terminal settings reset after the upgrade.** Cause: layered`tui.json` files merge into one global`cli.json` , and project-level layers are not migrated. Fix: reapply the project settings to`cli.json` .
12. **Symptom: responses crawl on a laptop.** Cause: the model plus a 64k window does not fit in video memory and spills into system RAM. Fix: choose a smaller or mixture-of-experts model, shorten the window while you test, or route heavy tasks to a hosted model.

## Advanced Tips for Teams and Heavy Users

### Pin versions and control updates

On shared machines, set `update` to `notify` or `disable` and let one person move the team. Version 2 became the main line in October.

### Use the shared service on purpose

One background service means one place where sessions live. The CLI lists `--standalone` to run with a private server and `--server` to connect to a specific server URL, according to the upgrade guide.  Treat the device pairing commands, `/pair` and `opencode pair`, as an access-control decision. Whoever pairs a device gets a path into the service, so write down who may do that.

### Try keyed session instructions

Version 2.2.0 introduced keyed session instructions, which the release summary ties to preserving prompt-cache behavior. If you run long sessions against a hosted model with cached-input pricing, that is the feature to test first. The Go documentation lists per-request token profiles dominated by cached input, which shows how much of an agent’s traffic is repeated context.

### Rotate keys if you ever used Console

OpenCode reportedly disclosed a breach of a legacy Console system in mid-October, and the upgrade guide, citing a security firm’s summary, says provider keys linked in affected Console workspaces may have been exposed while keys used only with a local install were not mentioned. If you ever saved a provider key in Console, revoke and reissue it in the provider’s own dashboard, update every script that used it, confirm the old key fails, and set a hard spend limit. It takes ten minutes and it closes the question.

### Roll back in five minutes

Rollback needs the three artifacts from steps 1 and 2. Remove the 2.x binary, reinstall the last 1.x release, extract the archive over your config, and check out the `pre-opencode-2` branch.

```
# 1. remove 2.x (match how you installed it)
npm uninstall -g @opencode/cli

# 2. reinstall the last 1.x release
npm install -g [email protected]

# 3. unpack the archive from step 2 into a scratch folder and inspect it
mkdir -p /tmp/oc-restore
tar -xzf ~/oc-backups/opencode-YYYYMMDD-HHMMSS.tar.gz -C /tmp/oc-restore
find /tmp/oc-restore -maxdepth 6 -type d | head -20

# 4. restore project files
git checkout pre-opencode-2 -- opencode.json AGENTS.md CLAUDE.md
```

Copy each restored folder back to where it belongs, global config to `~/.config/opencode`, local data to `~/.local/share/opencode`, and project files into the repo. The archive mixes home-relative and project-relative paths, so unpacking into a scratch folder first avoids overwriting the wrong tree.

## Frequently Asked Questions

### Is OpenCode really free?

The client is. OpenCode’s home page says it is “100% free to use” and comes with a set of free models, with possible extra costs if you connect another provider. Paid model access is separate: Go costs $10 per month, Go Plus costs $40 per month, and Zen is pay-as-you-go.

### Do I have to upgrade from OpenCode 1.x to 2.x?

No. Version 1.x keeps working, and since v1.19.0 on October 11 it no longer installs updates on its own. The catch is that new features land in 2.x, so a 1.x install slowly falls behind. Upgrade deliberately with `/update` or `opencode upgrade --major` when you are ready.

### Will my 1.x config survive the upgrade?

Mostly. According to the upgrade guide, 2.x reads 1.x config where it already lives and converts fields in memory without rewriting your files. Plugins, server API integrations and the old terminal settings are the exceptions, along with the CLAUDE.md discovery change.

### Does OpenCode 2 work with local Ollama models?

Yes, through an Ollama provider in `opencode.json` that points at the local OpenAI-compatible endpoint. Ollama’s docs say OpenCode needs a 64k or larger context window. The `ollama launch opencode` shortcut hit a v2 bug on October 26 and a fix was committed, so configure the provider by hand until you have confirmed a release that includes it.

### Do I need Ollama Cloud or an OpenCode paid plan?

No. A local model on your own hardware needs neither. Ollama Cloud is a hosted provider with a $0 Free plan, a $20 per month Pro plan and a $100 per month Max plan, and OpenCode Go and Go Plus are separate hosted subscriptions. Choose them for models your hardware cannot run.

### Does OpenCode 2 still read CLAUDE.md?

No. According to the upgrade guide, only AGENTS.md is discovered in 2.x. Copy or symlink each CLAUDE.md to an AGENTS.md next to it, and keep both files if your team also uses Claude Code.

### Were API keys exposed in the Console breach, and what should I do?

OpenCode reportedly disclosed a breach affecting a legacy Console system in mid-October, and the risk was to provider keys linked in Console workspaces. Nothing published so far says keys used only with a local install were exposed. If you ever linked a key in Console, rotate it at the provider and set a spend limit.

### What is the fastest way to confirm the migration worked?

Run `python3 scripts/oc-doctor.py qwen3.5-64k` and then `bash scripts/oc-smoke.sh`. Seven of seven passing checks and three passing prompts mean the binary, the model, the context window, the instruction files and the tool plumbing all work.
