# OpenCode vs Claude Code — Capability Matrix

> Research date: 2026-05-26  
> OpenCode version: v1.15.10 (latest stable)  
> Claude Code: @anthropic-ai/claude-code (claude-code CLI)  
> Purpose: Drives graceful-degradation abstraction design for InfiniBot agent bridge

---

## TL;DR

OpenCode is a full-featured, open-source coding agent with its own HTTP server, TypeScript SDK, TUI, and 75+ provider support. Claude Code is Anthropic's proprietary CLI tightly coupled to Claude models. They are **functional peers** for non-interactive/headless use, but differ sharply in: model scope, SDK maturity, config format, and the permission/trust model. Most gaps are bridgeable at the abstraction layer.

---

## 1. Installation

| Capability | Claude Code (CC) | OpenCode | Gap / Notes |
|---|---|---|---|
| Primary install | `npm install -g @anthropic-ai/claude-code` | `curl -fsSL https://opencode.ai/install \| bash` | Different mechanics; CC is npm-only |
| npm package name | `@anthropic-ai/claude-code` → `claude` binary | `opencode-ai` → `opencode` binary | OC also published as `opencode` on Homebrew/pacman |
| npm global install | `npm i -g @anthropic-ai/claude-code` | `npm i -g opencode-ai@latest` | Both work |
| Homebrew | Not official | `brew install anomalyco/tap/opencode` (always latest); `brew install opencode` (official, slower) | CC has no Homebrew formula |
| Windows | npx / npm only | Scoop, Chocolatey, winget, npm | OC has more Windows options |
| Arch Linux | No | `sudo pacman -S opencode`; `paru -S opencode-bin` | Gap: CC not in any distro repos |
| Nix | No | `nix run nixpkgs#opencode` | Gap: CC not in nixpkgs |
| Desktop app | No | Beta .dmg / .exe / .deb / .rpm / .AppImage | **Gap**: CC CLI only; OC has full desktop |
| npx (zero-install) | `npx @anthropic-ai/claude-code` | `npx opencode-ai` (untested but package exists) | Similar capability |
| Install dir override | No env var | `OPENCODE_INSTALL_DIR`, `XDG_BIN_DIR`, `HOME/bin`, `HOME/.opencode/bin` | OC is more XDG-compliant |

---

## 2. CLI Invocation & Commands

### Top-level flags (both CLIs)

| Flag | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| Version | `--version` / `-v` | `--version` / `-v` | Same |
| Help | `--help` / `-h` | `--help` / `-h` | Same |
| Log level | No global flag | `--log-level DEBUG\|INFO\|WARN\|ERROR` | Gap: CC uses env var `ANTHROPIC_LOG` |
| Print logs to stderr | No | `--print-logs` | OC only |
| Disable external plugins | No | `--pure` | OC only |

### Subcommands

| Command | Claude Code | OpenCode |
|---|---|---|
| **Primary run** | `claude` (default, interactive TUI) | `opencode` (default, interactive TUI) |
| **Non-interactive run** | `claude -p "prompt"` | `opencode run "prompt"` |
| List models | `claude models` (limited) | `opencode models [provider] [--verbose] [--refresh]` |
| List providers | No | `opencode providers` |
| Manage MCP servers | No CLI subcommand | `opencode mcp add\|list\|auth\|remove` |
| Manage sessions | No | `opencode session list\|delete` |
| Agent management | No | `opencode agent create\|list` |
| Start headless server | No | `opencode serve` |
| Open web console | No | `opencode web` |
| Export session | No | `opencode export` |
| Import session | No | `opencode import` |
| Upgrade | `claude update` | `opencode upgrade` |
| Uninstall | No | `opencode uninstall` |
| Shell completion | No | `opencode completion` |
| Plugin management | No | `opencode plug` |
| PR integration | No | `opencode pr` |
| GitHub integration | No | `opencode github` |
| ACP (Agent Client Protocol) | No | `opencode acp` |
| DB migration | No | `opencode db` |
| Debug | No | `opencode debug` |
| Stats | No | `opencode stats` |

**Gap summary**: OpenCode has a significantly richer CLI surface. CC is simpler / more focused on the core run-loop.

---

## 3. Run / Non-Interactive Mode

This is the most critical section for InfiniBot's programmatic use.

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Basic invocation** | `claude -p "do the thing"` | `opencode run "do the thing"` | Different flag (`-p` vs positional) |
| Pipe stdin as prompt | `echo "prompt" \| claude` | `echo "prompt" \| opencode run` | Both merge stdin + positional arg |
| Combine stdin + arg | No (stdin replaces prompt) | `opencode run "prefix" <<< "piped"` concatenates both | OC merges: `arg + "\n" + stdin` |
| Choose model | `claude --model claude-3-5-sonnet-20241022` | `opencode run --model provider/model` (or `-m`) | OC format is `provider/modelID` |
| Choose agent | No | `opencode run --agent build\|plan\|custom` | Gap: CC has no agent switching |
| Continue last session | `claude --continue` or `-c` | `opencode run --continue` or `-c` | Same semantic |
| Resume specific session | `claude --resume <sessionID>` | `opencode run --session <id>` or `-s` | Similar |
| Fork session | No | `opencode run --fork --session <id>` | OC only |
| Attach file(s) | `claude --file f1 --file f2` | `opencode run --file f1 -f f2` | Both support multiple; same alias `-f` |
| Set session title | No | `opencode run --title "name"` | OC only |
| Model variant/effort | No | `opencode run --variant high\|max\|minimal` | OC only (maps to provider-specific effort) |
| Show thinking blocks | No (thinking shown by default) | `opencode run --thinking` (default false non-interactive) | Different defaults |
| Attach to remote server | No | `opencode run --attach http://server:4096` | OC only — enables remote agent execution |
| Remote server auth | No | `opencode run --attach ... --password pw --username u` | OC only |
| Run in specific dir | `claude --add-dir /path` (whitelists) | `opencode run --dir /path` (changes working dir) | Different semantics |
| Specify port for local server | No | `opencode run --port 4096` | OC only |
| **Output format** | Plain text (streaming) or `--output-format json` / `stream-json` | `opencode run --format default\|json` | Both have JSON event streaming |
| **Skip permissions** | `claude --dangerously-skip-permissions` | `opencode run --dangerously-skip-permissions` | **Identical flag name and semantics** |
| Slash command execution | `claude --command /test` | `opencode run --command /test --message args` | OC separates command from message |
| Interactive mode flag | `claude` (default) | `opencode run --interactive` or `-i` | CC defaults to interactive; OC non-interactive unless `-i` |
| Replay session history | No | `opencode run --interactive --replay [--replay-limit N]` | OC only |
| Demo mode | No | `opencode run --interactive --demo` | OC only |
| Share session | No | `opencode run --share` | OC only (requires account) |

---

## 4. Prompt Input Methods

| Method | Claude Code | OpenCode | Notes |
|---|---|---|---|
| CLI positional arg | `claude -p "text"` | `opencode run "text"` | OC: array of words joined with spaces |
| Stdin pipe | Yes (`echo "..." \| claude`) | Yes (`echo "..." \| opencode run`) | Both |
| Combined stdin + arg | No (one or the other) | Yes — concatenates `arg + "\n" + stdin` | OC advantage |
| File attachment | `--file path` | `--file path` or `-f path` | Both; OC supports directories (type: `application/x-directory`) |
| `--` passthrough | No | Yes (`opencode run -- extra words`) | OC supports `--` for extra args |
| JSON parts (SDK only) | `prompt()` with parts array | `session.prompt({ parts: [...] })` in SDK | Both have rich SDK input |

---

## 5. MCP Server Support

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Supported** | Yes | Yes | Both |
| **Config file key** | `mcpServers` in `claude_desktop_config.json` or `.mcp.json` | `mcp` in `opencode.json` / `opencode.jsonc` | Different key name |
| **Local (stdio) server** | `{ "command": "npx", "args": [...], "env": {} }` | `{ "type": "local", "command": ["npx", "..."], "environment": {} }` | OC: `type: "local"`, `command` is array, env key is `environment` not `env` |
| **Remote (SSE/HTTP) server** | `{ "url": "https://..." }` (newer format) | `{ "type": "remote", "url": "https://...", "headers": {} }` | OC: explicit `type: "remote"` discriminant |
| **OAuth for remote servers** | No | `{ "type": "remote", "url": "...", "oauth": { "clientId": "...", "scope": "..." } }` | **Gap**: CC has no MCP OAuth support |
| **OAuth auto-detect** | No | `oauth: true` (default for remote) or `oauth: false` to disable | OC only |
| **Enable/disable toggle** | No | `{ "enabled": false }` to disable a named server | OC advantage |
| **Timeout** | No per-server timeout | `"timeout": 5000` (ms, default 5000) | OC advantage |
| **CLI management** | No | `opencode mcp add\|list\|auth\|remove` | **Gap**: CC has no CLI for MCP mgmt |
| **Config file location** | `~/.claude/claude_desktop_config.json` or `.mcp.json` in project | `~/.config/opencode/opencode.json` (global) or `opencode.json` in project | Different paths |
| **Named servers** | Yes (keys in `mcpServers` object) | Yes (keys in `mcp` object) | Same pattern |
| **Auth storage** | Not persisted across sessions | Stored in `~/.local/share/opencode/auth.json` | OC persists MCP OAuth tokens |

### Config format comparison

**Claude Code** (`.mcp.json` or `claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["-y", "@company/mcp-server"],
      "env": { "API_KEY": "value" }
    },
    "remote-server": {
      "url": "https://mcp.example.com/sse"
    }
  }
}
```

**OpenCode** (`opencode.json`):
```json
{
  "mcp": {
    "my-server": {
      "type": "local",
      "command": ["npx", "-y", "@company/mcp-server"],
      "environment": { "API_KEY": "value" },
      "enabled": true,
      "timeout": 10000
    },
    "remote-server": {
      "type": "remote",
      "url": "https://mcp.example.com/sse",
      "headers": { "Authorization": "Bearer token" },
      "oauth": { "clientId": "abc", "scope": "openid" }
    }
  }
}
```

Key differences for bridge abstraction:
- `mcpServers` → `mcp`
- `command` + `args` array → `command` single array
- `env` → `environment`
- No `type` field in CC → required `type: "local"|"remote"` in OC

---

## 6. Model & Provider Support

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Provider scope** | Anthropic only (Claude models) | 75+ providers via AI SDK + Models.dev | **Major gap**: CC is single-vendor |
| **Model format** | Model name only (`claude-opus-4-5`) | `provider/model` (`anthropic/claude-opus-4-5`) | OC requires provider prefix |
| **Built-in providers** | Anthropic (claude-*) | Anthropic, OpenAI, Google, Azure, Bedrock, Groq, DeepSeek, xAI, Mistral, Cohere, Together AI, OpenRouter, Perplexity, Fireworks, DeepInfra, Cerebras, Vertex AI, GitHub Copilot, GitLab Duo, Ollama, LM Studio, local models, 40+ more | |
| **Local models** | No | Yes (Ollama, LM Studio, llama.cpp) | Gap: CC no local model support |
| **Model listing** | `claude models` (shows available Claude models) | `opencode models [provider]` (lists all 75+ providers) | OC is richer |
| **Model cache refresh** | No | `opencode models --refresh` (pulls from models.dev) | OC only |
| **First-party curated set** | All Claude models | OpenCode Zen (handpicked, benchmarked for coding) | Different approach |
| **Model variants** | No | `--variant high\|max\|minimal` (maps to provider-specific effort settings) | OC only |
| **Small model** | No separate config | `small_model` in config (for title gen, etc.) | OC splits concerns |

---

## 7. Authentication

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Primary auth** | `ANTHROPIC_API_KEY` env var | Provider-specific env vars or `/connect` interactive flow | OC is multi-provider |
| **Env var (Anthropic)** | `ANTHROPIC_API_KEY` | Detected automatically for `anthropic` provider | Same underlying mechanism |
| **Env var (other providers)** | N/A | Per-provider: `OPENAI_API_KEY`, `AWS_ACCESS_KEY_ID`, `GOOGLE_APPLICATION_CREDENTIALS`, etc. | OC maps each provider's standard env vars |
| **OAuth (Anthropic)** | No browser OAuth | Yes — Claude Pro/Max login via browser | OC has this; CC does not |
| **OAuth (OpenAI)** | No | Yes — ChatGPT Plus/Pro browser login | OC only |
| **OAuth (GitHub Copilot)** | No | Yes — device code flow to `github.com/login/device` | OC only |
| **Credential storage** | Not persisted locally (env var only) | `~/.local/share/opencode/auth.json` (600 perms) | OC persists; CC relies on shell env |
| **Inline config auth** | No | `OPENCODE_AUTH_CONTENT` env var (JSON blob) | OC supports injecting auth via env |
| **API key in config** | No (discouraged) | `provider.options.apiKey: "{env:MY_VAR}"` with variable substitution | OC supports but recommends env vars |
| **AWS credential chain** | N/A | Full AWS SDK chain (profile, instance role, env, etc.) for Bedrock | OC advantage for AWS environments |
| **Server password** | No | `OPENCODE_SERVER_PASSWORD` / `OPENCODE_SERVER_USERNAME` for headless server | OC only |
| **MCP OAuth** | No | Full OAuth 2.0 with PKCE, dynamic client registration (RFC 7591) | OC only |

---

## 8. Configuration

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Config format** | JSON (`.mcp.json`, `claude.json`, `settings.json`) | JSON or JSONC (JSON with Comments) | OC adds comment support |
| **Schema validation** | No published JSON schema | `"$schema": "https://opencode.ai/config.json"` | OC has machine-readable schema |
| **Global config** | `~/.claude/claude.json` or `~/.claude/settings.json` | `~/.config/opencode/opencode.json` | XDG-compliant |
| **Project config** | `.claude/settings.json` in project | `opencode.json` or `opencode.jsonc` in project root | Different location |
| **Config hierarchy** | Global → Project (project wins) | Remote well-known → Global → `OPENCODE_CONFIG` → Project → `.opencode/` dirs → `OPENCODE_CONFIG_CONTENT` → Managed → MDM (8 levels) | OC is much more layered |
| **Config via env** | No | `OPENCODE_CONFIG` (path to file), `OPENCODE_CONFIG_CONTENT` (inline JSON) | OC advantage for containerized use |
| **Managed/enterprise config** | No | macOS MDM profiles (highest priority, cannot be overridden) | OC enterprise feature |
| **Config disable flag** | No | `OPENCODE_DISABLE_PROJECT_CONFIG=1` skips project config | OC only |
| **Config dir override** | No | `OPENCODE_CONFIG_DIR` env var | OC only |
| **Variable substitution** | No | `{env:VAR_NAME}`, `{file:path}` in config values | OC only |
| **Array merging** | Not applicable | `instructions` arrays are de-duped and concatenated across config layers | OC advantage |
| **TUI config** | Integrated in main config | Split: `opencode.json` (runtime) + `tui.json` (UI-specific) | OC separation of concerns |

### Config keys comparison (selected)

| Config key | Claude Code | OpenCode | Notes |
|---|---|---|---|
| Default model | `model` (Claude model name) | `model: "provider/model"` | Same key, different value format |
| Shell | No | `shell: "/bin/bash"` | OC allows shell override |
| MCP servers | `mcpServers` | `mcp` | Different key |
| Instructions / CLAUDE.md | `CLAUDE.md` auto-loaded | `instructions: ["glob/**"]` + `AGENTS.md` | Similar; OC is more explicit |
| Permissions | `permissions` (allow/deny lists) | `permission` (per-tool rules with glob patterns) | Similar concept, different syntax |
| Tools toggle | No per-tool toggle in config | `tools: { "write": false, "bash": false }` | OC per-tool disable |
| Compaction | No config | `compaction: { auto, prune, tail_turns, ... }` | OC fine-grained |
| Share | No | `share: "auto"\|"manual"\|"disabled"` | OC only |
| Auto-update | No config | `autoupdate: true\|false\|"notify"` | OC only |
| Provider whitelist | No | `enabled_providers`, `disabled_providers` | OC only |

---

## 9. Permission / Trust Model

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Default non-interactive** | Requires `--dangerously-skip-permissions` or allowlist in config | Requires `--dangerously-skip-permissions` or config `permission` rules | Both default to requiring permission for sensitive ops |
| **Skip-permissions flag** | `--dangerously-skip-permissions` | `--dangerously-skip-permissions` | **Identical** — probably intentional |
| **Config-level rules** | `permissions: { allow: [...], deny: [...] }` with tool patterns | `permission: { bash: "allow"\|"ask"\|"deny", edit: { "~/sensitive/*": "deny" } }` | OC is more granular |
| **Per-tool rules** | Coarse (tool-level allow/deny) | Per-tool + per-pattern (glob) per action | OC is finer-grained |
| **Known permission keys** | `Bash`, `Read`, `Write`, `WebFetch`, etc. | `read`, `edit`, `glob`, `grep`, `list`, `bash`, `task`, `external_directory`, `todowrite`, `question`, `webfetch`, `websearch`, `repo_clone`, `repo_overview`, `lsp`, `doom_loop`, `skill` | OC has more named permissions |
| **Reply semantics** | `allow once` / `allow always` / `deny` | `once` / `always` / `reject` | Same concepts, different names |
| **Session-level permission** | No | Can inject ruleset at session creation via SDK | OC advantage |
| **Non-interactive auto-reject** | Permissions cause exit/error | Permissions auto-rejected (logged as warning) in headless mode unless `--dangerously-skip-permissions` | OC is more graceful |
| **Config `deny` = tool disabled** | Yes | `"deny"` completely blocks tool | Same |
| **Managed policy** | No | MDM-level policies cannot be overridden | OC enterprise feature |

---

## 10. Output / Streaming

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Default non-interactive output** | Streaming text to stdout | Streaming text to stdout | Same |
| **JSON event stream** | `--output-format stream-json` → newline-delimited JSON events | `--format json` → newline-delimited JSON events | Same concept, different flag syntax |
| **JSON event types (CC)** | `assistant`, `tool_use`, `tool_result`, `result`, `system` | N/A | |
| **JSON event types (OC)** | N/A | `tool_use`, `step_start`, `step_finish`, `text`, `reasoning`, `error` (emitted by run.ts) | Different event vocabulary |
| **SSE for HTTP API** | No HTTP API | Yes — `GET /event/subscribe` returns SSE stream | OC only |
| **Reasoning/thinking output** | Shown inline by default | `--thinking` flag (default false non-interactive, true interactive) | Different defaults |
| **TTY detection** | Yes — different behavior for TTY vs pipe | Yes — `process.stdout.isTTY` gates formatting | Both |
| **Exit on idle** | Yes (session ends when model stops) | Yes (`session.status.idle` event breaks stream loop) | Same |

---

## 11. SDK / Programmatic Interface

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Official SDK** | `@anthropic-ai/claude-code` (same package, `ClaudeCode` class) | `@opencode-ai/sdk` (separate npm package) | Both |
| **SDK type** | Node.js/TypeScript SDK wrapping the CLI | TypeScript SDK that talks to the OpenCode HTTP server | Different architecture |
| **Embedded server** | No — SDK spawns CLI process | Yes — `createOpencodeServer()` spawns `opencode serve`, then HTTP | OC SDK is HTTP-based |
| **Client creation** | `new ClaudeCode({ cwd, ... })` | `createOpencodeClient({ baseUrl, directory })` | Different API |
| **Session management** | `claude.query(...)` — single-shot | `client.session.create()`, `client.session.prompt()`, `client.session.list()` | OC has full CRUD sessions |
| **Event subscription** | Callback on stdout events | `client.event.subscribe()` → async iterator of SSE events | OC async iterator pattern |
| **Permission reply** | Not exposed in SDK | `client.permission.reply({ requestID, reply: "once"\|"always"\|"reject" })` | OC advantage |
| **File attachment (SDK)** | `--file` flag only | `parts: [{ type: "file", url: "file:///...", mime: "text/plain" }]` in prompt | OC rich parts model |
| **Parallel sessions** | No | Yes — multiple `client.session.create()` + `client.session.prompt()` in parallel | **OC major advantage** |
| **Fork session** | No | `client.session.fork({ sessionID })` | OC only |
| **Share session** | No | `client.session.share({ sessionID })` | OC only |
| **Remote attach** | No | `createOpencodeClient({ baseUrl: "http://remote:4096" })` | OC only |
| **Config injection** | Env vars only | `OPENCODE_CONFIG_CONTENT` or `config` option in `createOpencodeServer(options)` | OC allows programmatic config |
| **Headless server** | No | `opencode serve --hostname 0.0.0.0 --port 4096` | OC only |
| **OpenAPI spec** | No | Yes — `opencode generate` outputs OpenAPI JSON; spec committed at `packages/sdk/js/openapi.json` | OC advantage for code-gen |
| **VSCode extension** | Yes (official) | Yes (`packages/sdks/vscode`) | Both |

### SDK usage example

**Claude Code**:
```typescript
import { ClaudeCode } from "@anthropic-ai/claude-code"
const cc = new ClaudeCode({ cwd: "/project" })
for await (const event of cc.query({ prompt: "Fix the bug" })) {
  // handle events
}
```

**OpenCode**:
```typescript
import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk"

const server = await createOpencodeServer()
const client = createOpencodeClient({ baseUrl: server.url })

const session = await client.session.create()
await client.session.prompt({
  sessionID: session.data.id,
  parts: [{ type: "text", text: "Fix the bug" }]
})
for await (const event of (await client.event.subscribe()).stream) {
  if (event.type === "session.status" && event.properties.status.type === "idle") break
}

server.close()
```

---

## 12. Headless / Server Mode

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **HTTP server** | No | `opencode serve` — HTTP server on configurable port | **Gap**: CC has no standalone server |
| **Default port** | N/A | 4096 | |
| **mDNS / service discovery** | No | `server.mdns: true`, `server.mdnsDomain` | OC supports local network discovery |
| **CORS config** | N/A | `server.cors: ["http://localhost:5173"]` | OC only |
| **Auth** | N/A | Basic auth via `OPENCODE_SERVER_PASSWORD` / `OPENCODE_SERVER_USERNAME` | OC only |
| **Multi-client** | No | Yes — multiple SDK clients can attach to one server | OC only |
| **Directory header** | N/A | `x-opencode-directory` header to scope requests to a project | OC unique; enables multi-project server |
| **WebSocket** | No | Yes (websocket-tracker, used by TUI) | OC internal |

---

## 13. Version & Update Management

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Latest stable version** | ~1.x (check npm) | **v1.15.10** (as of 2026-05-25) | |
| **Auto-update** | No config for this | `autoupdate: true\|false\|"notify"` | OC configurable |
| **Update check** | Manually run `claude update` | Automatic on startup (patch = auto, minor/major = notify) | OC proactive |
| **Update methods** | npm only | curl script, npm, brew, scoop, pacman, etc. | OC is install-method aware |
| **Version in output** | `claude --version` | `opencode --version` or `-v` | Same |
| **Disable auto-update env** | No | `OPENCODE_DISABLE_AUTOUPDATE=1` | OC only |
| **Channel** | stable only | latest / preview channels | OC multi-channel |
| **Release frequency** | Low (weeks) | High (multiple per week — 3 releases in 3 days shown) | OC faster iteration |

---

## 14. Agent Architecture

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **Built-in agents** | Single agent ("claude") | `build` (default, full access), `plan` (read-only, ask before bash/edit) | OC has agent modes |
| **Switch agents** | No | Tab key cycles between primary agents | OC only |
| **Custom agents** | No | Yes — JSON config or Markdown with YAML frontmatter in `agents/` dir | **Gap**: CC has no custom agent definition |
| **Subagents** | No | `general`, `explore`, `scout` (built-in); `@mention` to invoke | OC only |
| **Per-agent model** | No | `agent.my-agent.model: "provider/model"` | OC only |
| **Per-agent permissions** | No | Full permission ruleset per agent | OC only |
| **Per-agent system prompt** | No (single system prompt via `--system-prompt`) | `agent.my-agent.prompt: "path/to/prompt.md"` | OC richer |
| **Parallel agents** | No | Yes — multiple sessions can run parallel agents | OC advantage |
| **Default agent config** | No | `default_agent: "plan"` in config | OC only |
| **Agent temperature** | No | `agent.my-agent.temperature: 0.7` | OC only |

---

## 15. Context / Memory Management

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **CLAUDE.md / AGENTS.md** | `CLAUDE.md` in project root auto-loaded | `AGENTS.md` auto-loaded; `instructions` glob in config | Same concept, different file name |
| **Context compaction** | Automatic (no config) | `compaction.auto`, `compaction.prune`, `compaction.tail_turns`, `compaction.reserved` | OC fine-grained |
| **File snapshot / undo** | No | `snapshot: true` (default) — tracks file changes; `undo`/`redo` in TUI | **Gap**: CC has no file-level undo |
| **Session persistence** | In-memory only | SQLite DB (drizzle-orm) at `~/.local/share/opencode/` | OC persists to disk |
| **Session history** | No | `opencode session list`, `client.session.list()` | OC queryable history |
| **Tool output truncation** | No config | `tool_output.max_lines: 2000`, `tool_output.max_bytes: 51200` — overflow saved to disk | OC configurable |

---

## 16. LSP / Formatter / Tooling Integration

| Capability | Claude Code | OpenCode | Gap / Notes |
|---|---|---|---|
| **LSP integration** | No | Automatic LSP detection per project language; configurable via `lsp` | **Gap**: CC has no LSP support |
| **Code formatters** | No | Auto-detects and runs formatters (prettier, black, etc.); `formatter: true\|false\|{ ... }` | Gap: CC delegates to agent instructions |
| **Tree-sitter** | No | Built-in tree-sitter for bash + powershell parsing | OC only |
| **Git integration** | Git-aware (reads .gitignore) | Full git integration including PR commands, GitHub actions integration | OC richer |
| **Worktrees** | No | `opencode run` supports git worktrees via `InstanceContext` | OC only |

---

## 17. Exit Codes

| Scenario | Claude Code | OpenCode | Notes |
|---|---|---|---|
| **Success** | 0 | 0 | |
| **CLI/user error** | 1 | 1 (via `fail()` → `CliError`) | Same |
| **Permission error** | 1 | `process.exitCode = 1` (soft; process still exits via `process.exit()`) | Same effect |
| **Unhandled error** | 1 | 1 (caught in top-level try/catch, `process.exitCode = 1`) | Same |
| **Prompt error in SDK** | Non-zero | `process.exitCode = 1`, then `process.exit()` | OC always calls `process.exit()` in finally |
| **Explicit exit code** | Fixed 1 | `CliError` accepts optional `exitCode` number | OC can return custom exit codes |

Both use standard Unix exit code semantics. No documented multi-value exit code table for either.

---

## 18. Environment Variables

| Variable | Claude Code | OpenCode | Notes |
|---|---|---|---|
| `ANTHROPIC_API_KEY` | Primary auth | Picked up by anthropic provider | Both |
| `ANTHROPIC_BASE_URL` | Override API base | N/A (use `provider.anthropic.options.baseURL`) | Different |
| `OPENAI_API_KEY` | No | OpenAI provider auto-detected | OC only |
| `AWS_*` | No | Bedrock credential chain | OC only |
| `GOOGLE_APPLICATION_CREDENTIALS` | No | Vertex AI | OC only |
| `OPENCODE_CONFIG` | No | Path to config file | OC only |
| `OPENCODE_CONFIG_CONTENT` | No | Inline config JSON | OC only |
| `OPENCODE_AUTH_CONTENT` | No | Inline auth JSON (replaces auth.json) | OC only |
| `OPENCODE_INSTALL_DIR` | No | Custom install location | OC only |
| `OPENCODE_SERVER_PASSWORD` | No | Basic auth for headless server | OC only |
| `OPENCODE_SERVER_USERNAME` | No | Username for headless server (default: `opencode`) | OC only |
| `OPENCODE_DISABLE_AUTOUPDATE` | No | Disable auto-update | OC only |
| `OPENCODE_DISABLE_PROJECT_CONFIG` | No | Ignore project-level config files | OC only |
| `OPENCODE_CONFIG_DIR` | No | Extra config directory | OC only |
| `OPENCODE_PURE` | No | Disable external plugins | OC only |
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | CC only | No | CC only |
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | CC only | No (use `provider.model.limit.output`) | Different |

---

## 19. Summary of Key Gaps

### OpenCode lacks vs Claude Code
1. **Nothing significant for coding tasks.** OpenCode is a superset in almost all areas.

### Claude Code lacks vs OpenCode (gaps to handle in abstraction)
1. **Multi-provider / multi-model**: CC is Anthropic-only. Bridge must translate `provider/model` format.
2. **Headless HTTP server**: CC has no `serve` command. Cannot attach multiple SDK clients.
3. **Parallel sessions via SDK**: CC processes one session at a time. OC is fully concurrent.
4. **MCP OAuth**: CC cannot OAuth-authenticate MCP servers. Bridge must note this capability gap.
5. **Custom agents / agent switching**: CC has no agent modes. Bridge cannot use `--agent`.
6. **File undo/snapshot**: CC has no undo. Bridge should not advertise this capability for CC.
7. **Session persistence / history**: CC sessions are ephemeral. `session.list()` unavailable.
8. **LSP integration**: CC has none. Language-aware context is OC-only.
9. **`--variant` (reasoning effort)**: CC uses `--thinking` or model-specific flags; OC unifies via `--variant`.
10. **Remote attach mode**: CC cannot connect to a remote server. `--attach` is OC-only.
11. **Session fork**: CC has no concept. `--fork` is OC-only.
12. **Config schema / JSONC**: CC config is plain JSON, no published schema URL.
13. **`OPENCODE_CONFIG_CONTENT`**: CC has no equivalent inline config injection (useful in containers).
14. **Auto-update control**: CC update behavior is fixed; OC exposes `autoupdate` config.

### Bridge abstraction recommendations
- Detect runtime by checking `which opencode` vs `which claude`.
- For model spec: CC accepts `claude-*` bare names; OC requires `provider/model` — normalize at bridge layer.
- For MCP config: generate both `{ "mcpServers": {...} }` (CC) and `{ "mcp": { "type": "local"|"remote", ... } }` (OC) from a common internal schema.
- For `--dangerously-skip-permissions`: flag is identical in both — direct pass-through.
- For non-interactive mode: CC uses `-p "prompt"`, OC uses `run "prompt"` — wrap in adapter.
- For JSON streaming: both emit newline-delimited JSON but with different event types — normalize in stream consumer.
- OC's `OPENCODE_CONFIG_CONTENT` env var enables clean config injection without temp files — prefer this for containerized/programmatic use.
