Claude Code inside OpenCode — Pro/Max subscription OAuth,
Agent SDK harness, effort variants, tools, images, and compact.
Install · Authenticate · Why this plugin · Architecture · Changelog
Run Claude Code from your Claude Pro/Max subscription inside OpenCode: Fable, Opus, Sonnet, Haiku — with thinking effort low→max, streaming, OpenCode tool calls that park and resume, MCP, image/PDF attachments, and auto-compact.
Uses the same Agent SDK + claude CLI stack as the OpenChamber Claude harness. Plugin shape mirrors @otto-assistant/opencode-cursor.
claude-code is not a built-in OpenCode provider. Install the plugin first, or
opencode auth login --provider claude-code fails with Unknown provider "claude-code".
# global (recommended)
opencode plugin @otto-assistant/opencode-claude -g
# or project-local (writes .opencode/opencode.json)
opencode plugin @otto-assistant/opencode-claudeOptional provider naming (also seeded when the plugin loads):
Or build from source:
git clone https://github.com/otto-assistant/opencode-claude.git
cd opencode-claude
bun install && bun run build
opencode plugin file://$PWDRequires the plugin to be installed (see above).
# Option A — sync from Claude Code CLI (recommended)
claude auth login
opencode auth login --provider claude-code
# pick "Use Claude Code CLI login"
# Option B — browser OAuth (Pro/Max)
opencode auth login --provider claude-code
# pick "Login with Claude Pro/Max"Then start OpenCode, pick provider claude-code, choose a model, and set the
effort variant (low / medium / high / xhigh / max) when you want
deeper thinking.
opencode run "Summarise this repository in five bullets." --model claude-code/sonnet| Agent SDK harness | Runs Claude through @anthropic-ai/claude-agent-sdk + the local claude CLI — same stack as OpenChamber. |
| Subscription auth | Claude Pro/Max OAuth (CLI sync or browser). API keys are stripped from the child env so billing stays on the subscription. |
| Effort / thinking | Native OpenCode variants low→max map to Claude --effort + adaptive thinking. |
| Agent-grade tools | OpenCode tools bridge as in-process MCP; calls park and resume instead of deadlocking or inventing output. |
| Attachments | Images and PDFs from OpenCode reach Claude (data URLs + remote URLs). |
| Auto-compact | Long sessions compact like Claude Code; boundary events are surfaced in the stream. |
| Session resume | Sticky foreign Claude session IDs so follow-ups continue the same Agent SDK turn. |
| History transfer | When no Claude session can be resumed (first claude-code turn of a chat, model switch mid-conversation, pruned transcript), the full prior conversation is serialized into the prompt — Claude never starts blind. |
| Rate-limit counter | Subscription limit state is tracked with its reset time; GET /v1/rate-limit answers "when are limits back", and doomed turns fail fast with 429 + Retry-After. |
OpenCode
└─ /v1/chat/completions
└─ Bun.serve proxy (ephemeral port; published via auth loader)
└─ Claude Agent SDK query()
└─ claude CLI (subscription OAuth)
Model catalog: aliases fable / opus / sonnet / haiku plus pinned ids.
Effort selection is encoded in x-opencode-claude-effort so the proxy passes the
exact effort (+ adaptive thinking) into the Agent SDK.
The proxy records Agent SDK rate_limit_event telemetry and hard session-limit
errors (including the parsed reset time) to
~/.local/share/opencode-claude/rate-limit.json.
GET /v1/rate-limit→{ limited, status, rateLimitType, utilization, resetsAt, resetsAtISO, resetInSeconds, message, updatedAt }— poll this for a "limits reset in …" countdown.utilizationis only present when the latest SDK event reported it — it is never carried over from an earlier limit window, so a freshly reset window never shows a stale percentage.GET /healthincludes a compactrateLimitsummary.- While a confirmed hard limit is active, new chat turns return HTTP 429
with
Retry-After+x-claude-rate-limit-resetheaders and anerror.type = "rate_limit_error"body (title/summary meta requests are never gated). The block lifts automatically at reset time; the next turn then resumes the same Claude session (sticky session store is untouched). OPENCODE_CLAUDE_RATE_LIMIT_FAST_FAIL=0disables the 429 gate (turns are attempted and error normally).
- OpenCode
- Claude Code CLI on
PATH - Claude Pro/Max subscription (or CLI OAuth credentials)
- Bun (plugin runtime) · Node.js ≥ 18
bun install
bun run build
bun run testDebug logging: OPENCODE_CLAUDE_DEBUG=1.
Optional knobs:
OPENCODE_CLAUDE_PROXY_PORT— optional pinned proxy port (default: ephemeral / OS-assigned; live URL is published to OpenCode via config + auth loader)OPENCODE_CLAUDE_CWD— working directory passed to the Agent SDKCLAUDE_CODE_OAUTH_TOKEN— inject a subscription token (CI / headless)OPENCODE_CLAUDE_RATE_LIMIT_FAST_FAIL—0disables the 429 rate-limit gateOPENCODE_CLAUDE_RATE_LIMIT_STORE— override the rate-limit store path (tests)OPENCODE_CLAUDE_HISTORY_MAX_CHARS— budget for transferred conversation history when a Claude session cannot be resumed (default400000; newest messages are kept,0disables transfer)
Publish via GitHub Actions → Actions → Release → Run workflow:
| Input | Purpose |
|---|---|
version |
Explicit semver (0.6.0). Empty → use bump |
bump |
minor (default) / patch / major |
dry_run |
Skip npm publish; create a draft GitHub release |
Requires repo secrets: NPM_TOKEN, optional DISCORD_WEBHOOK_URL.
Local pin refresh after a release:
./scripts/update-plugin.sh --dry-run
./scripts/update-plugin.sh
{ "$schema": "https://opencode.ai/config.json", "plugin": ["@otto-assistant/opencode-claude"], "provider": { "claude-code": { "name": "Claude Code" } } }