Andrew Mercer
on this page

OpenCode

Snapshot date: 8 October 2026. OpenCode ships releases very frequently and V2 is now the default on the website (binaries at 2.0.6 at time of writing), while V1 docs and installs remain widely used. This guide covers V2 first and calls out V1 differences where they matter. Always check opencode --version and the matching docs.

TL;DR

  • What: An open-source (MIT) AI coding agent — a terminal UI, desktop app, and web client — that reads your codebase, edits files, runs commands and chains multi-step tasks.
  • Why people use it: It's model-agnostic — 75+ providers via the models.dev catalog, plus local models (Ollama, LM Studio, vLLM). Same workflow whether you use Claude, GPT, Gemini, DeepSeek, Qwen or a model on your own GPU.
  • Who: Built by Anomaly (the team behind SST and OpenNext). Repo: anomalyco/opencode.
  • Scale: Over 200K GitHub stars as of early September 2026; the company reported more than 16 million monthly developers in August 2026.
  • Key concepts: primary agents (build, plan), subagents (general, explore, scout), AGENTS.md project rules, a JSON/JSONC config, fine-grained permissions, MCP servers, skills, custom commands, and a client/server architecture.

Table of contents

  1. What OpenCode is
  2. V1 vs V2
  3. Installation
  4. First run
  5. Providers and models
  6. Configuration
  7. Agents
  8. Permissions
  9. Project instructions: AGENTS.md
  10. MCP servers
  11. Custom commands and skills
  12. Everyday TUI usage
  13. Automation, server mode and CI
  14. Homelab recipe: OpenCode + local models
  15. OpenCode vs Claude Code vs Codex
  16. Gotchas and troubleshooting
  17. Cheat sheet
  18. Sources

1. What OpenCode is

OpenCode is an agent harness: the loop, tools, context management and UI that turn an LLM into a coding agent. The LLM is pluggable.

┌────────────┐   ┌──────────────┐   ┌───────────────────────────┐
│  Clients   │──▶│   OpenCode   │──▶│  Providers                │
│ TUI, mini, │   │   server     │   │  Anthropic, OpenAI,       │
│ desktop,   │◀──│ sessions,    │◀──│  Google, Bedrock, Vertex, │
│ web, IDE,  │   │ tools, perms,│   │  Azure, OpenRouter,       │
│ ACP, SDK   │   │ MCP, agents  │   │  Ollama, vLLM, LM Studio… │
└────────────┘   └──────────────┘   └───────────────────────────┘

Built-in capabilities: file read/write/edit/patch, glob and grep, shell execution, web fetch and web search, todo lists, sub-agent delegation (task/subagent tool), snapshots with undo/redo, context compaction, image attachments, MCP tools, skills, and custom tools via plugins.

Interfaces:

  • TUI — the full-screen terminal interface (opencode).
  • Mini — a minimal interactive interface (opencode mini, V2).
  • Desktop app — macOS, Windows, Linux (.deb, .rpm, AppImage).
  • Web — browser client (opencode pair in V2; opencode web in V1).
  • IDE / ACP — any editor speaking the Agent Client Protocol can drive OpenCode (opencode acp).
  • SDK / HTTP API — embed the agent server in your own tools.

Business model: The agent is free. Anomaly sells optional model access:

  • OpenCode Zen / Console — a curated, pay-as-you-go gateway to models the OpenCode team has tested for coding agents.
  • OpenCode Go — a $10/month subscription with access to leading open-source coding models.

2. V1 vs V2

Both versions use the opencode command and do not install side by side by default. The V2 curl installer replaces the V1 binary; remove a package-managed V1 install first.

What changed in V2

Area V1 V2
Architecture Each TUI starts its own server One shared background service per user; all local clients connect to it (--standalone for a private server)
Web client opencode web opencode pair (needs the shared service)
TUI settings Layered tui.json files Single global ~/.config/opencode/cli.json (auto-migrated)
Plugins V1 plugin API New plugin API — V1 plugins do not run
Server API V1 API New API + @opencode/client package
Permissions permission map grouped by tool Ordered permissions array of {action, resource, effect}
Agents agent map, prompt, disable agents map, system, disabled, model#variant
Providers provider, npm, api, options providers, package, settings.baseURL, headers, body
MCP mcp.<name>, enabled, single timeout mcp.servers.<name>, disabled, timeout.catalog / timeout.execution
LSP Runs language servers, LSP diagnostics Config accepted but LSP is not run — use lint/typecheck commands instead
Instructions AGENTS.md, plus CLAUDE.md fallback AGENTS.md only
small_model Top-level key agents.title.model
autoupdate true/false/"notify" update: "auto" \| "notify" \| "disable"

Good news: V2 reads the same config locations and normalises supported V1 fields in memory without rewriting them. You can keep V1 config and convert gradually. The docs even suggest asking OpenCode itself to do the migration:

Migrate my OpenCode configuration, including file-based definitions, from the V1
format to the native V2 format. Preserve its behavior and all unrelated settings.

Don't point V1 at a config you've converted to V2-only syntax.


3. Installation

V2 (current)

# Install script (Linux/macOS)
curl -fsSL https://opencode.ai/v2/install | bash

# Verify
opencode --version

V2 is also available via npm, bun, pnpm, yarn, Homebrew and the AUR (see the install page for exact package names), plus standalone binaries for macOS, Windows and Linux (glibc and musl), and versioned Docker images such as ghcr.io/anomalyco/opencode:2.0.0. The npm package uses a postinstall script to fetch the native binary for your platform. Windows package managers are not supported in V2.

V1 (still common)

curl -fsSL https://opencode.ai/install | bash      # install script
npm install -g opencode-ai                         # Node
brew install anomalyco/tap/opencode                # Homebrew tap (more current than core formula)
sudo pacman -S opencode                            # Arch stable
paru -S opencode-bin                               # Arch AUR latest
mise use -g github:anomalyco/opencode              # mise
docker run -it --rm ghcr.io/anomalyco/opencode     # Docker

On Windows, V1 recommends WSL; Chocolatey and Scoop also work.

Terminal

Use a modern, truecolor terminal — Ghostty, WezTerm, Alacritty or Kitty are the recommended options.

Uninstall

opencode uninstall --dry-run   # preview
opencode uninstall             # V2 keeps session data, config and state

4. First run

cd ~/code/my-project
opencode

Inside the TUI:

/connect      # pick a provider and enter credentials
/models       # choose a model
/init         # analyse the repo and generate AGENTS.md

Commit the generated AGENTS.md — it's how OpenCode (and other agents) learn your build commands and conventions.

A first useful session

  1. Press Tab to switch to the Plan agent (read-only).
  2. Describe the change. Reference files with @ (fuzzy file search). Drag images into the terminal to attach them.
  3. Iterate on the plan.
  4. Press Tab again to return to Build and tell it to implement.
  5. Didn't like it? /undo (repeatable). Changed your mind? /redo.

5. Providers and models

Model references always use provider/model format, e.g. anthropic/claude-sonnet-5-5, openai/gpt-6-sol, ollama/qwen3:8b, openrouter/<vendor>/<model>. List what you have:

opencode models               # all configured providers
opencode models anthropic     # filter
opencode models --refresh     # refresh catalog from models.dev (V1)

Connecting cloud providers

  • TUI: /connect
  • CLI (V1): opencode auth login — credentials live in ~/.local/share/opencode/auth.json. OpenCode also picks up provider keys from environment variables and a project .env.

Enterprise clouds (V2 syntax)

{
  "$schema": "https://opencode.ai/config.json",
  "providers": {
    // AWS Bedrock — uses the default AWS credential chain
    "amazon-bedrock": {
      "settings": { "profile": "work", "region": "us-east-1" }
    },
    // Azure — needs the resource name; supports API key or Entra ID via `az login`
    "azure": {
      "settings": { "resourceName": "my-models" }
    },
    // Google Vertex — uses ADC (`gcloud auth application-default login`)
    "google-vertex": {
      "settings": { "project": "my-project", "location": "us-central1" }
    }
  }
}

Azure notes: OpenCode lists your resource's deployments rather than the whole catalog. Your identity needs Cognitive Services OpenAI User (Azure OpenAI) or Cognitive Services User (other Foundry models). In V2, the old azure-cognitive-services and google-vertex-anthropic IDs are replaced by azure and google-vertex.

Local models (V2)

V2 has built-in discovery for local runtimes:

Runtime Default endpoint Notes
Ollama http://127.0.0.1:11434 Auto-discovered; only models with the completion capability appear
LM Studio http://127.0.0.1:1234/v1 Reads /api/v1/models
vLLM http://127.0.0.1:8000/v1 Checks /health then /v1/models; tools start disabled because discovery can't infer tool support

Pointing at a remote box:

{
  "$schema": "https://opencode.ai/config.json",
  "providers": {
    "ollama":   { "settings": { "baseURL": "http://gpu-host:11434/v1" } },
    "lmstudio": { "settings": { "baseURL": "http://gpu-host:1234/v1" } },
    "vllm":     { "settings": { "baseURL": "http://gpu-host:8000/v1" } }
  }
}

Any OpenAI-compatible endpoint (V2)

{
  "$schema": "https://opencode.ai/config.json",
  "model": "company/coder",
  "providers": {
    "company": {
      "name": "Company Gateway",
      "env": ["COMPANY_GATEWAY_KEY"],
      "package": "@opencode/ai/providers/openai-compatible",
      "settings": { "baseURL": "https://gateway.example.com/v1" },
      "models": {
        "coder": {
          "modelID": "upstream/coder-v2",
          "name": "Coder v2",
          "limit": { "context": 200000, "output": 32000 }
        }
      }
    }
  }
}

The same file in V1 syntax for comparison:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "company": {
      "npm": "@ai-sdk/openai-compatible",
      "api": "https://gateway.example.com/v1",
      "options": { "apiKey": "{env:COMPANY_GATEWAY_KEY}" },
      "models": { "coder": {} }
    }
  }
}

Proxies, headers and timeouts (V2)

{
  "providers": {
    "anthropic": {
      "settings": {
        "baseURL": "https://llm-proxy.example.com/anthropic",
        "headerTimeout": 600000,   // ms; default 5 min
        "chunkTimeout": false      // disable inter-chunk timeout
      },
      "headers": { "X-Gateway-Tenant": "engineering" }
    }
  }
}

Timed-out requests are retried up to three times. OpenAI, xAI and supported Azure Responses models can reuse a WebSocket per session to avoid resending the whole prompt each step (settings.transport: "websocket" | "http").

A note on subscriptions

Model vendors' consumer subscriptions generally aren't valid for third-party harnesses. In particular, Claude Pro/Max subscriptions can't be used through OpenCode — use an Anthropic API key, Bedrock/Vertex/Foundry, or another provider. GitHub Copilot is supported via device OAuth (/connect → GitHub Copilot) if your account has Copilot Chat access.


6. Configuration

Files and precedence

Scope Location
Global ~/.config/opencode/opencode.json or .jsonc
Project <project>/opencode.json(c) and/or <project>/.opencode/opencode.json(c)
TUI/CLI client (V2) ~/.config/opencode/cli.json (global only)
TUI (V1) ~/.config/opencode/tui.json, project tui.json
File-based definitions .opencode/agents/, commands/, skills/, plugins/, themes/ (plural preferred) and the same under ~/.config/opencode/
Managed (admin) /etc/opencode/ (Linux), /Library/Application Support/opencode/ (macOS), %ProgramData%\opencode (Windows); macOS MDM via ai.opencode.managed

Configs merge; later layers override only conflicting keys. In V2, OpenCode walks from the current directory up to the filesystem root, merging direct opencode.json(c) files from farthest to closest, then .opencode/ files in the same order — so any .opencode/ config beats any direct one.

Useful environment overrides (V1 names; most remain): OPENCODE_CONFIG, OPENCODE_CONFIG_DIR, OPENCODE_CONFIG_CONTENT, OPENCODE_PERMISSION, OPENCODE_DISABLE_AUTOUPDATE, OPENCODE_SERVER_PASSWORD.

Always add the schema for editor validation:

{ "$schema": "https://opencode.ai/config.json" }

Variable substitution

{
  "model": "{env:OPENCODE_MODEL}",
  "providers": {
    "openai": { "settings": { "apiKey": "{file:~/.secrets/openai-key}" } }
  }
}

{env:NAME} becomes an empty string if unset; {file:path} paths are relative to the config file or absolute (/, ~). This plays nicely with pass-style secret files.

A solid V2 global config

// ~/.config/opencode/opencode.jsonc
{
  "$schema": "https://opencode.ai/config.json",

  "model": "anthropic/claude-sonnet-5-5",
  "default_agent": "build",
  "update": "notify",
  "share": "disabled",
  "snapshots": true,
  "formatter": true,

  "compaction": {
    "auto": true,
    "keep": { "tokens": 15000 },
    "buffer": 20000
  },

  "watcher": { "ignore": ["target/**", "node_modules/**", "dist/**", ".git/**"] },

  "tool_output": { "max_lines": 2000, "max_bytes": 51200 },

  "agents": {
    "title": { "model": "anthropic/claude-haiku-5-5" }
  },

  "permissions": [
    { "action": "shell", "resource": "*",              "effect": "ask"   },
    { "action": "shell", "resource": "git status*",    "effect": "allow" },
    { "action": "shell", "resource": "git diff*",      "effect": "allow" },
    { "action": "shell", "resource": "cargo check*",   "effect": "allow" },
    { "action": "shell", "resource": "cargo test*",    "effect": "allow" },
    { "action": "shell", "resource": "git push *",     "effect": "ask"   },
    { "action": "shell", "resource": "rm -rf *",       "effect": "deny"  },
    { "action": "edit",  "resource": "*",              "effect": "allow" }
  ]
}

Notable V2 options

Key What it does
warming Keeps recently active sessions' prompt caches warm with periodic tiny requests (off by default; costs tokens)
references Expose other local dirs or Git repos as named, read-only supporting context
worktree.directory Where new git worktrees for parallel sessions are created
websearch.provider Choose web search backend ("random" picks an available one)
media.image Resize/reject oversized images before sending
experimental.policies Hard allow/deny of providers or permissions (enterprise guardrails)
skills Extra skill directories or URLs

7. Agents

Built-in agents

Agent Mode Purpose
build primary (default) Full tool access for development
plan primary Planning/analysis; edits and shell set to ask
general subagent Research and multi-step work; can edit; good for parallel work
explore subagent Fast, read-only codebase search
scout subagent Read-only external docs/dependency research; can clone dependency repos into a managed cache
compaction, title, summary hidden System agents for compaction, session titles and summaries
  • Tab cycles primary agents.
  • @name invokes a subagent manually (@explore where is auth handled?).
  • Primary agents also call subagents automatically based on their description.
  • Sub-agent child sessions are navigable from the parent session (V1 defaults: Leader+Down to enter, Left/Right to cycle, Up to return).

Defining agents in Markdown

~/.config/opencode/agents/<name>.md (global) or .opencode/agents/<name>.md (project). The filename becomes the agent name; the body is the system prompt.

---
description: Reviews Rust changes for correctness, safety and missing tests
mode: subagent
model: anthropic/claude-opus-5-5
permission:
  edit: deny
  bash:
    "*": ask
    "git diff*": allow
    "cargo clippy*": allow
    "cargo test*": allow
---

You are a senior Rust reviewer. Focus on:
- ownership/borrowing mistakes, unnecessary clones, panics in library code
- error handling (prefer `thiserror`/`anyhow` patterns already in the repo)
- missing tests for new branches
- clippy warnings (`-D warnings` is enforced in CI)

Never edit files. Report findings as a prioritised list with file:line references.

(That frontmatter is V1-style and is auto-translated by V2. Native V2 would use permissions: as an ordered list and model: provider/model#variant.)

Defining agents in JSON (V2)

{
  "agents": {
    "reviewer": {
      "description": "Review changes without editing files",
      "mode": "subagent",
      "model": "anthropic/claude-opus-5-5#high",
      "system": "Focus on correctness, security and missing tests.",
      "steps": 30,
      "permissions": [
        { "action": "edit", "resource": "*", "effect": "deny" }
      ]
    },
    "plan": {
      "model": "openai/gpt-6-sol"
    }
  }
}

Agent options worth knowing

Option Notes
description Required; drives automatic delegation
mode primary, subagent or all (default)
model Override per agent; subagents otherwise inherit the caller's model
steps Cap agentic iterations (cost control); the agent then summarises remaining work
hidden Hide a subagent from @ autocomplete (still callable by other agents)
Task permissions Control which subagents an agent may delegate to (task in V1, subagent in V2)
color UI colour
Sampling / provider options temperature, top_p, reasoningEffort etc. — under request.body in V2

Interactive scaffolding:

opencode agent create   # asks scope, description, permissions; writes a .md file
opencode agent list

Sub-agent nesting depth defaults to 1 (subagents can't spawn subagents); V2 sets it via experimental.subagent_depth.


8. Permissions

Default is permissive — OpenCode allows all operations unless you configure otherwise. Set permissions before pointing it at anything important.

V2: ordered rules

{
  "permissions": [
    { "action": "shell",     "resource": "*",            "effect": "ask"   },
    { "action": "shell",     "resource": "git status*",  "effect": "allow" },
    { "action": "edit",      "resource": "*",            "effect": "allow" },
    { "action": "websearch", "resource": "*",            "effect": "deny"  }
  ]
}

Action names changed in V2: bash → shell, task → subagent, write/patch → edit.

V1: per-tool map

{
  "permission": {
    "edit": "ask",
    "bash": {
      "*": "ask",
      "git status *": "allow",
      "rm -rf *": "deny"
    },
    "webfetch": "deny",
    "external_directory": "ask"
  }
}

V1 permission keys: read, edit, glob, grep, list, bash, task, external_directory, todowrite, webfetch, websearch, lsp, skill, question, doom_loop. Keys are wildcard-matched against tool names, so "mymcp_*": "deny" blocks every tool from an MCP server.

Precedence rules

  • Last matching rule wins — put broad * rules first and specific exceptions after.
  • Agent-level permissions override global ones.
  • --auto on opencode / opencode run auto-approves anything not explicitly denied — useful in sandboxes and CI, dangerous on your workstation.
  • Organisations can enforce non-overridable rules via managed config or experimental.policies.

9. Project instructions: AGENTS.md

/init generates an AGENTS.md in the project root. V2 discovers:

  • ~/.config/opencode/AGENTS.md (global, personal preferences)
  • AGENTS.md files from the current directory up to $HOME (or the project root for projects outside home)

V2 does not read CLAUDE.md — move that content into AGENTS.md. (V1 had a CLAUDE.md fallback and could read .claude/skills; disable with OPENCODE_DISABLE_CLAUDE_CODE* env vars.)

Example for a Rust CLI project:

# AGENTS.md

## Build & test
- Build: `cargo build`
- Lint: `cargo fmt --check && cargo clippy --all-targets -- -D warnings`
- Test: `cargo test`
- Pre-commit hooks enforce fmt + clippy + test; never bypass with `--no-verify`.

## Conventions
- Binary name matches crate name; subcommands via `clap` derive.
- Errors: `anyhow` in the binary, `thiserror` in library modules.
- Credentials come from `pass`; never hard-code or log secrets.
- Config lives in `~/.config/<tool>/config.toml`.

## CI
- GitLab CI; shared includes come from the `skel` repo — do not edit the include header by hand.

## Don'ts
- Don't add dependencies without saying why.
- Don't modify `.gitlab-ci.yml` include/stages blocks.

10. MCP servers

MCP tools appear alongside built-in tools. Every enabled server costs context — the docs specifically warn that servers like GitHub's can add a lot of tokens. Enable sparingly and per agent where possible.

V2

{
  "mcp": {
    "servers": {
      "playwright": {
        "type": "local",
        "command": ["npx", "-y", "@playwright/mcp"],
        "disabled": false,
        "timeout": { "catalog": 30000, "execution": 30000 }
      },
      "sentry": {
        "type": "remote",
        "url": "https://mcp.sentry.dev/mcp",
        "oauth": {}
      },
      "context7": {
        "type": "remote",
        "url": "https://mcp.context7.com/mcp",
        "headers": { "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}" }
      }
    }
  }
}

V1

{
  "mcp": {
    "playwright": {
      "type": "local",
      "command": ["npx", "-y", "@playwright/mcp"],
      "enabled": true,
      "environment": { "DEBUG": "0" },
      "timeout": 30000
    },
    "gitlab": {
      "type": "remote",
      "url": "https://gitlab.example.com/api/v4/mcp",
      "oauth": false,
      "headers": { "Authorization": "Bearer {env:GITLAB_TOKEN}" }
    }
  }
}

Remote OAuth is automatic (including Dynamic Client Registration). Manage it with:

opencode mcp add           # interactive
opencode mcp list
opencode mcp auth <name>   # browser OAuth flow
opencode mcp logout <name>
opencode mcp debug <name>  # connectivity + OAuth discovery diagnostics

V1 stores MCP tokens in ~/.local/share/opencode/mcp-auth.json. In V2, OAuth fields are snake_case (client_id, client_secret).


11. Custom commands and skills

Commands

Reusable prompt templates invoked as /name. Markdown version — .opencode/commands/review.md:

---
description: Review the current diff
agent: plan
model: anthropic/claude-opus-5-5
---

Review the current `git diff` for correctness, security issues and missing
tests. Group findings by severity. Focus area: $ARGUMENTS

JSON version (V2):

{
  "commands": {
    "release-notes": {
      "description": "Draft release notes since the last tag",
      "template": "Summarise commits since the last git tag as release notes grouped by feat/fix/chore.",
      "subagent": true
    }
  }
}

$ARGUMENTS receives whatever follows the command. In V2, subagent: true (V1: subtask) runs the command in the background and reports back to the parent session.

Skills

Skills are folders with a SKILL.md plus any scripts/references, loaded on demand by the agent:

.opencode/skills/
└── helm-chart/
    ├── SKILL.md
    └── templates/

Keep the directory name stable — it's the skill ID. Extra skill locations or URLs go in the skills config array.


12. Everyday TUI usage

Action How
Switch primary agent Tab
Reference a file @ + fuzzy search
Invoke a subagent @explore …, @general …
Attach an image Drag and drop into the terminal
Undo / redo agent changes /undo, /redo (snapshots must be enabled)
Connect provider / pick model /connect, /models
Generate AGENTS.md /init
Share a session link /share (sessions are not shared by default; V2 accepts the setting but sharing isn't supported yet)
Command palette command_list keybind (configurable, e.g. ctrl+p)

Keybinds are configurable (V1: tui.json → keybinds; V2: cli.json).


13. Automation, server mode and CI

One-shot runs

opencode run "Explain this repository"
opencode run -m openai/gpt-6-luna --agent plan "List TODOs in src/"
opencode run --format json "Summarise the last 10 commits" | jq .
opencode run --continue "Now write tests for that"
opencode run -f error.log "What's causing this?"

Useful flags (V1): --model/-m, --agent, --file/-f, --format json, --continue/-c, --session/-s, --fork, --variant (reasoning effort), --thinking, --auto, --attach <url>.

Shared server (avoid MCP cold starts)

# V1
opencode serve --port 4096
opencode run --attach http://localhost:4096 "Explain async/await"
opencode attach http://10.20.30.40:4096          # TUI against a remote backend

# V2 — a shared background service is started automatically
opencode --standalone                            # private server instead
opencode --server http://localhost:4096          # connect to a specific server
opencode service set disabled true               # opt out of the shared service
opencode pair                                    # web UI (URL + generated credentials)

Set OPENCODE_SERVER_PASSWORD (and optionally OPENCODE_SERVER_USERNAME) before exposing a server beyond localhost. mDNS discovery (--mdns) lets other devices on the LAN find it.

Sessions and stats

opencode session list -n 20
opencode export <sessionID> --sanitize > session.json
opencode import session.json
opencode stats --days 30 --models 5     # token & cost usage
opencode debug paths                    # V2: where db/config/cache/log live

GitHub / GitLab

opencode github install    # sets up a GitHub Actions workflow
opencode pr 123            # check out a PR branch and start OpenCode

OpenCode also documents a GitLab integration. A generic GitLab CI job using run:

opencode-review:
  image: ghcr.io/anomalyco/opencode:2.0.0
  stage: test
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  variables:
    ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
  script:
    - git fetch origin "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    - >
      opencode run --agent plan --format json
      "Review the diff against origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME.
       Report bugs, security issues and missing tests."
      > review.json
  artifacts:
    paths: [review.json]

Use the plan agent (or a custom read-only agent) in CI unless the job is meant to push changes, and never use --auto with write permissions on untrusted MR content — that's a prompt-injection path straight into your runner.


14. Homelab recipe: OpenCode + local models

Goal: routine work on a local open model, frontier model only when needed.

1. Serve a model on the GPU box

# Option A: Ollama
ollama serve
ollama pull qwen3:32b

# Option B: vLLM (better throughput; enable tool calling)
vllm serve Qwen/Qwen3-32B \
  --host 0.0.0.0 --port 8000 \
  --max-model-len 32768 \
  --enable-auto-tool-choice --tool-call-parser hermes

2. Point OpenCode at it and keep a frontier fallback

// ~/.config/opencode/opencode.jsonc  (V2)
{
  "$schema": "https://opencode.ai/config.json",
  "model": "ollama/qwen3:32b",
  "providers": {
    "ollama": { "settings": { "baseURL": "http://gpu-host:11434/v1" } },
    "vllm": {
      "settings": { "baseURL": "http://gpu-host:8000/v1" },
      "models": {
        "Qwen/Qwen3-32B": {
          "name": "Qwen3 32B (vLLM)",
          "capabilities": { "tools": true }
        }
      }
    }
  },
  "agents": {
    "architect": {
      "description": "Hard design questions and tricky refactors",
      "mode": "primary",
      "model": "anthropic/claude-opus-5-5"
    }
  }
}

Tab between build (local) and architect (frontier) as needed.

3. Tips for local models

  • Tool calling quality varies a lot between models and quantisations — test a multi-step task before trusting it. vLLM-discovered models start with tools disabled; enable them explicitly as above.
  • Keep context realistic (32K–64K) on consumer GPUs; enable compaction.
  • Smaller models benefit from a tighter AGENTS.md and fewer MCP servers.
  • Behind a reverse proxy with TLS, use the https://…/v1 URL and pass an API key via settings.apiKey with {env:…}.

15. OpenCode vs Claude Code vs Codex

OpenCode Claude Code Codex
Vendor Anomaly Anthropic OpenAI
Licence MIT, open source Proprietary CLI open source; service proprietary
Models Any (75+ providers, local) Claude GPT
Subscription use Its own Zen/Go; vendor subs generally not allowed Claude Pro/Max/Team/Enterprise ChatGPT plans
Interfaces TUI, mini, desktop, web, ACP, SDK Terminal, IDE, desktop, web, mobile Terminal, IDE, cloud, ChatGPT
Config opencode.json(c), AGENTS.md settings.json, CLAUDE.md config.toml, AGENTS.md
Agents/subagents build/plan + general/explore/scout + custom Subagents, plugins, hooks Agents
MCP Yes (local + remote OAuth) Yes Yes
Skills Yes Yes Yes
Strengths Portability, local models, inspectable, client/server Tightest integration with Claude, polish Tightest integration with GPT/Codex cloud
Weaknesses Integration burden, fast-moving (occasional regressions), V1→V2 transition Single vendor Single vendor

Pick OpenCode when you want one workflow across many models, need local/air-gapped models, want to inspect or extend the agent, or want to avoid vendor lock-in. Pick the vendor agent when you're committed to one model family and want the most tuned experience, or want to use a consumer subscription.


16. Gotchas and troubleshooting

  • Wrong docs for your version. V1 and V2 configs differ; check opencode --version and use /docs (V1) or /v2/docs (V2).
  • V1 plugins silently don't work in V2. Port them via the V2 plugin migration guide.
  • LSP diagnostics disappeared after upgrading to V2. Expected — V2 doesn't run language servers. Put lint/typecheck commands in AGENTS.md.
  • CLAUDE.md ignored in V2. Move it to AGENTS.md.
  • Model not in the list. opencode models --refresh (V1), or add it explicitly under the provider's models map. Check the exact provider/model ID.
  • Ollama shows no models. Ensure /api/tags and /api/show are reachable at the baseURL-derived path and that the model reports the completion capability (embedding models are hidden).
  • Context fills quickly. Disable unused MCP servers, enable compaction, use explore subagents for searches.
  • Large monorepos are slow / disk-hungry. Snapshots use an internal git repo; tune watcher.ignore or disable snapshots (you lose /undo).
  • Agent did something you didn't expect. Default permissions are allow-all — configure ask for shell and edits.
  • Shared service confusion (V2). opencode --standalone for an isolated run; opencode service set disabled true to stop using it by default.
  • Inspecting effective config: opencode debug config (V1); opencode debug paths (V2).
  • Resource usage: Community reports note RAM use above 1GB; release velocity is high, so pin versions in CI.

17. Cheat sheet

# install / update
curl -fsSL https://opencode.ai/v2/install | bash
opencode upgrade
opencode --version

# start
opencode                       # TUI in cwd
opencode ~/code/project        # TUI in another dir
opencode mini                  # minimal UI (V2)
opencode -c                    # continue last session
opencode -m anthropic/claude-sonnet-5-5 --agent plan

# headless
opencode run "prompt"
opencode run --format json -f file.txt "prompt"

# providers / models
opencode auth login            # V1
opencode models [provider]

# agents / mcp
opencode agent create | list
opencode mcp add | list | auth <n> | logout <n> | debug <n>

# server
opencode serve                 # V1 headless API
opencode attach <url>
opencode pair                  # V2 web UI
opencode --standalone          # V2 private server

# sessions
opencode session list
opencode export <id> --sanitize
opencode import <file|share-url>
opencode stats --days 7
TUI:   Tab = switch agent   @ = file/subagent   /init   /connect   /models
       /undo   /redo   /share

18. Sources