@khalilgharbaoui/opencode-claude-code-plugin 0.1.0 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -173
- package/dist/index.d.ts +7 -0
- package/dist/index.js +205 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
# @khalilgharbaoui/opencode-claude-code-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An [opencode](https://opencode.ai) plugin that wraps the **Claude Code CLI** (`claude`) and routes model traffic through it instead of the Anthropic HTTP API. You get to use opencode's UI, agents, MCP, and permission system while authenticating and billing through whichever method `claude` is logged into (Pro/Max plan, Bedrock, Vertex, or API key).
|
|
4
4
|
|
|
5
|
-
> Maintained fork of [`unixfox/opencode-claude-code-plugin`](https://github.com/unixfox/opencode-claude-code-plugin)
|
|
5
|
+
> Maintained fork of [`unixfox/opencode-claude-code-plugin`](https://github.com/unixfox/opencode-claude-code-plugin). Published as `@khalilgharbaoui/opencode-claude-code-plugin` on npm.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Make sure `claude` is installed and logged in
|
|
13
|
+
claude --version
|
|
14
|
+
|
|
15
|
+
# 2. Add this to your opencode.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"plugin": ["@khalilgharbaoui/opencode-claude-code-plugin"]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's it. Restart opencode, pick a `claude-code` model, done.
|
|
25
|
+
|
|
26
|
+
The plugin self-registers the `claude-code` provider, all current Claude Code models (Haiku 4.5, Sonnet 4.5/4.6, Opus 4.5/4.6/4.7) with reasoning variants (`low` / `medium` / `high` / `xhigh` / `max`), and sensible defaults for tool proxying. You don't need to write a `provider` block at all unless you want to override something.
|
|
27
|
+
|
|
28
|
+
---
|
|
8
29
|
|
|
9
30
|
## Prerequisites
|
|
10
31
|
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
32
|
+
- [opencode](https://opencode.ai) installed
|
|
33
|
+
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated (`claude` on your `$PATH`)
|
|
34
|
+
- Node 18+ / Bun
|
|
13
35
|
|
|
14
|
-
##
|
|
36
|
+
## Install
|
|
15
37
|
|
|
16
|
-
### From npm
|
|
38
|
+
### From npm (recommended)
|
|
17
39
|
|
|
18
40
|
```bash
|
|
19
41
|
npm install @khalilgharbaoui/opencode-claude-code-plugin
|
|
20
42
|
```
|
|
21
43
|
|
|
44
|
+
Then add it to `opencode.json` as shown in the TL;DR.
|
|
45
|
+
|
|
22
46
|
### Local development
|
|
23
47
|
|
|
24
48
|
```bash
|
|
@@ -28,116 +52,156 @@ bun install
|
|
|
28
52
|
bun run build
|
|
29
53
|
```
|
|
30
54
|
|
|
31
|
-
|
|
55
|
+
In your `opencode.json`, point at the local build with a `file://` URL:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"plugin": ["file:///absolute/path/to/opencode-claude-code-plugin"]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Models
|
|
66
|
+
|
|
67
|
+
The plugin auto-registers the following. They appear in the model picker without any extra config.
|
|
68
|
+
|
|
69
|
+
| ID | Display name | Context | Output | Reasoning variants |
|
|
70
|
+
|---|---|---|---|---|
|
|
71
|
+
| `claude-haiku-4-5` | Claude Code Haiku 4.5 | 200k | 8,192 | – |
|
|
72
|
+
| `claude-sonnet-4-5` | Claude Code Sonnet 4.5 | 1M | 16,384 | low/medium/high/xhigh/max |
|
|
73
|
+
| `claude-sonnet-4-6` | Claude Code Sonnet 4.6 | 1M | 16,384 | low/medium/high/xhigh/max |
|
|
74
|
+
| `claude-opus-4-5` | Claude Code Opus 4.5 | 1M | 16,384 | low/medium/high/xhigh/max |
|
|
75
|
+
| `claude-opus-4-6` | Claude Code Opus 4.6 | 1M | 16,384 | low/medium/high/xhigh/max |
|
|
76
|
+
| `claude-opus-4-7` | Claude Code Opus 4.7 | 1M | 16,384 | low/medium/high/xhigh/max |
|
|
77
|
+
|
|
78
|
+
Capabilities for every model: text + image input, text output, tool use, attachments. No temperature control, no PDF/audio/video, no interleaved streaming.
|
|
79
|
+
|
|
80
|
+
The model ID is passed straight through to `claude --model`, so anything Claude Code accepts works.
|
|
81
|
+
|
|
82
|
+
### Picking a variant
|
|
83
|
+
|
|
84
|
+
Variants set the underlying reasoning effort. They're regular opencode model variants — pick them in the model selector. If you'd previously declared variants in your project's `opencode.json`, they're merged on top of the defaults so nothing gets lost.
|
|
85
|
+
|
|
86
|
+
---
|
|
32
87
|
|
|
33
88
|
## Configuration
|
|
34
89
|
|
|
35
|
-
|
|
90
|
+
The minimum config is just the `plugin` entry above. Everything below is optional override that goes in a `provider.claude-code` block.
|
|
91
|
+
|
|
92
|
+
### Multiple Claude Code accounts
|
|
93
|
+
|
|
94
|
+
Declare account names once and the plugin expands them into separate opencode providers:
|
|
36
95
|
|
|
37
96
|
```json
|
|
38
97
|
{
|
|
98
|
+
"plugin": ["@khalilgharbaoui/opencode-claude-code-plugin"],
|
|
39
99
|
"provider": {
|
|
40
100
|
"claude-code": {
|
|
41
|
-
"npm": "@khalilgharbaoui/opencode-claude-code-plugin",
|
|
42
|
-
"models": {
|
|
43
|
-
"haiku": {
|
|
44
|
-
"name": "Claude Code Haiku",
|
|
45
|
-
"attachment": false,
|
|
46
|
-
"limit": { "context": 200000, "output": 8192 },
|
|
47
|
-
"capabilities": { "reasoning": false, "toolcall": true }
|
|
48
|
-
},
|
|
49
|
-
"sonnet": {
|
|
50
|
-
"name": "Claude Code Sonnet",
|
|
51
|
-
"attachment": false,
|
|
52
|
-
"limit": { "context": 1000000, "output": 16384 },
|
|
53
|
-
"capabilities": { "reasoning": true, "toolcall": true }
|
|
54
|
-
},
|
|
55
|
-
"opus": {
|
|
56
|
-
"name": "Claude Code Opus",
|
|
57
|
-
"attachment": false,
|
|
58
|
-
"limit": { "context": 1000000, "output": 16384 },
|
|
59
|
-
"capabilities": { "reasoning": true, "toolcall": true }
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
101
|
"options": {
|
|
63
|
-
"
|
|
64
|
-
"proxyTools": ["Bash", "Edit", "Write", "WebFetch"]
|
|
102
|
+
"accounts": ["personal", "work"]
|
|
65
103
|
}
|
|
66
104
|
}
|
|
67
105
|
}
|
|
68
106
|
}
|
|
69
107
|
```
|
|
70
108
|
|
|
71
|
-
|
|
109
|
+
`default` is always implicit, so the config above creates:
|
|
72
110
|
|
|
73
|
-
|
|
111
|
+
| Provider ID | Display name | Claude config dir |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `claude-code-default` | `Claude Code (Default)` | normal `~/.claude` |
|
|
114
|
+
| `claude-code-personal` | `Claude Code (Personal)` | `~/.claude-personal` |
|
|
115
|
+
| `claude-code-work` | `Claude Code (Work)` | `~/.claude-work` |
|
|
116
|
+
|
|
117
|
+
Non-default accounts use `CLAUDE_CONFIG_DIR` through a generated wrapper script, so auth/session state stays isolated per account. Shared capability files and folders are symlinked from `~/.claude` into each account dir when present:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
CLAUDE.md
|
|
121
|
+
settings.json
|
|
122
|
+
skills/
|
|
123
|
+
agents/
|
|
124
|
+
commands/
|
|
125
|
+
plugins/
|
|
126
|
+
```
|
|
74
127
|
|
|
75
|
-
|
|
128
|
+
Identity/session state is not shared.
|
|
76
129
|
|
|
77
|
-
|
|
78
|
-
- `cwd` (string, default `process.cwd()`): working directory for the spawned CLI.
|
|
79
|
-
- `skipPermissions` (boolean, default `true`): pass `--dangerously-skip-permissions` to the CLI. Ignored when `proxyTools` is set (the proxy handles permissions instead).
|
|
80
|
-
- `permissionMode` (string, optional): pass Claude CLI `--permission-mode` (`acceptEdits`, `auto`, `bypassPermissions`, `default`, `dontAsk`, `plan`).
|
|
81
|
-
- `proxyTools` (string[], optional): list of Claude built-in tools to route through opencode instead of letting the CLI execute them directly. See [Selective Tool Proxy](#selective-tool-proxy) below.
|
|
82
|
-
- `controlRequestBehavior` (`allow` | `deny`, default `allow`): default behavior for Claude stream-json `control_request` messages with subtype `can_use_tool` when `skipPermissions` is `false`.
|
|
83
|
-
- `controlRequestToolBehaviors` (`Record<string, "allow" | "deny">`, optional): per-tool overrides for `can_use_tool` requests (eg. `{ "Bash": "deny", "Read": "allow" }`).
|
|
84
|
-
- `controlRequestDenyMessage` (string, optional): custom deny message returned to Claude for denied `can_use_tool` requests.
|
|
85
|
-
- `bridgeOpencodeMcp` (boolean, default `true`): auto-translate the `mcp` block from your opencode config (`opencode.jsonc` / `opencode.json`, discovered via `cwd`, `OPENCODE_CONFIG`, `OPENCODE_CONFIG_DIR`, and `$XDG_CONFIG_HOME/opencode`) into Claude CLI's `--mcp-config` format. Set to `false` to disable the bridge and manage MCP servers only via `~/.claude/settings.json`.
|
|
86
|
-
- `mcpConfig` (string | string[]): extra `--mcp-config` file path(s) or JSON string(s) passed through alongside the bridged config.
|
|
87
|
-
- `strictMcpConfig` (boolean, default `false`): pass `--strict-mcp-config` so the CLI loads **only** the servers from `--mcp-config` and ignores `~/.claude/settings.json` / user MCP registrations.
|
|
130
|
+
Login each account once:
|
|
88
131
|
|
|
89
|
-
|
|
132
|
+
```bash
|
|
133
|
+
CLAUDE_CONFIG_DIR="$HOME/.claude-personal" claude auth login
|
|
134
|
+
CLAUDE_CONFIG_DIR="$HOME/.claude-work" claude auth login
|
|
135
|
+
```
|
|
90
136
|
|
|
91
|
-
|
|
137
|
+
The account model IDs are internally suffixed, for example `claude-sonnet-4-6@work`, so long-lived Claude subprocess sessions do not collide across accounts. The generated wrapper strips the suffix before calling `claude --model`.
|
|
92
138
|
|
|
93
|
-
|
|
94
|
-
opencode --> streamText() --> ClaudeCodeLanguageModel.doStream()
|
|
95
|
-
|
|
|
96
|
-
v
|
|
97
|
-
claude CLI subprocess
|
|
98
|
-
(stream-json mode)
|
|
99
|
-
|
|
|
100
|
-
+-------------+-------------+
|
|
101
|
-
| |
|
|
102
|
-
native tools proxy MCP server
|
|
103
|
-
(Read, Glob, Grep, (127.0.0.1:random)
|
|
104
|
-
TodoWrite, etc.) |
|
|
105
|
-
| v
|
|
106
|
-
executed by CLI opencode tool executor
|
|
107
|
-
(bash, edit, write)
|
|
108
|
-
|
|
|
109
|
-
v
|
|
110
|
-
opencode permission UI
|
|
111
|
-
```
|
|
139
|
+
### Options reference
|
|
112
140
|
|
|
113
|
-
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"plugin": ["@khalilgharbaoui/opencode-claude-code-plugin"],
|
|
144
|
+
"provider": {
|
|
145
|
+
"claude-code": {
|
|
146
|
+
"options": {
|
|
147
|
+
"cliPath": "claude",
|
|
148
|
+
"proxyTools": ["Bash", "Edit", "Write", "WebFetch"],
|
|
149
|
+
"skipPermissions": true,
|
|
150
|
+
"permissionMode": "default",
|
|
151
|
+
"bridgeOpencodeMcp": true,
|
|
152
|
+
"strictMcpConfig": false
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
114
158
|
|
|
115
|
-
|
|
159
|
+
| Option | Type | Default | Description |
|
|
160
|
+
|---|---|---|---|
|
|
161
|
+
| `cliPath` | string | `process.env.CLAUDE_CLI_PATH ?? "claude"` | Path to the `claude` binary. |
|
|
162
|
+
| `accounts` | string[] | – | Optional account list. `default` is implicit. Expands into `Claude Code (Default)`, `Claude Code (Personal)`, etc. |
|
|
163
|
+
| `cwd` | string | `process.cwd()` | Working directory for the spawned CLI. Resolved **lazily per request**, so opencode's project switching works. |
|
|
164
|
+
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to `claude`. Ignored when `proxyTools` is set — the proxy handles permissions through opencode instead. |
|
|
165
|
+
| `permissionMode` | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` | – | Forwarded to `claude --permission-mode`. |
|
|
166
|
+
| `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch"]` | Claude built-in tools to route through opencode's executor + permission UI. See [Selective tool proxy](#selective-tool-proxy). |
|
|
167
|
+
| `controlRequestBehavior` | `allow` \| `deny` | `allow` | Default response when `skipPermissions: false` and Claude sends a `can_use_tool` control request. |
|
|
168
|
+
| `controlRequestToolBehaviors` | `Record<string, "allow" \| "deny">` | – | Per-tool override for `can_use_tool`. Example: `{ "Bash": "deny", "Read": "allow" }`. |
|
|
169
|
+
| `controlRequestDenyMessage` | string | built-in message | Message returned to Claude on a deny. |
|
|
170
|
+
| `bridgeOpencodeMcp` | boolean | `true` | Auto-translate your opencode `mcp` block into Claude's `--mcp-config`. See [MCP bridge](#mcp-bridge). |
|
|
171
|
+
| `mcpConfig` | string \| string[] | – | Extra `--mcp-config` paths/JSON passed alongside the bridged config. |
|
|
172
|
+
| `strictMcpConfig` | boolean | `false` | Pass `--strict-mcp-config` so Claude loads **only** the configured servers and ignores `~/.claude/settings.json`. |
|
|
173
|
+
|
|
174
|
+
### Overriding model metadata
|
|
175
|
+
|
|
176
|
+
To rename a model, change a limit, or add a custom one:
|
|
116
177
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"plugin": ["@khalilgharbaoui/opencode-claude-code-plugin"],
|
|
181
|
+
"provider": {
|
|
182
|
+
"claude-code": {
|
|
183
|
+
"models": {
|
|
184
|
+
"claude-sonnet-4-6": {
|
|
185
|
+
"name": "Sonnet (custom)",
|
|
186
|
+
"limit": { "context": 1000000, "output": 32768 }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
122
193
|
|
|
123
|
-
|
|
194
|
+
Anything you supply is merged on top of the defaults; you don't need to redeclare every model.
|
|
124
195
|
|
|
125
|
-
|
|
196
|
+
---
|
|
126
197
|
|
|
127
|
-
|
|
198
|
+
## Selective tool proxy
|
|
128
199
|
|
|
129
|
-
|
|
200
|
+
This is the core feature.
|
|
130
201
|
|
|
131
|
-
|
|
132
|
-
2. For each tool listed in `proxyTools`, the plugin:
|
|
133
|
-
- Passes `--disallowedTools <ToolName>` to the CLI, disabling Claude's built-in version.
|
|
134
|
-
- Exposes an equivalent tool via the MCP server (e.g. `mcp__opencode_proxy__bash`).
|
|
135
|
-
3. When Claude decides to use a proxied tool, the MCP call blocks.
|
|
136
|
-
4. The plugin emits a client-executed `tool-call` to opencode.
|
|
137
|
-
5. Opencode runs the tool through its own executor (with permission checks, UI prompts, etc.).
|
|
138
|
-
6. The tool result flows back into the blocked MCP call, and Claude continues.
|
|
202
|
+
By default, when Claude Code's CLI uses `Bash`, `Edit`, `Write`, etc., it executes them itself — bypassing opencode's permission UI, audit trail, and policy rules entirely. With `proxyTools`, you tell the plugin to disable Claude's built-in version of a tool and expose an equivalent through an in-process MCP server. Claude calls the MCP version, which blocks until opencode runs the tool through its own executor.
|
|
139
203
|
|
|
140
|
-
|
|
204
|
+
### Default proxied tools
|
|
141
205
|
|
|
142
206
|
| `proxyTools` value | Claude built-in disabled | Proxy MCP tool exposed |
|
|
143
207
|
|---|---|---|
|
|
@@ -146,133 +210,139 @@ The key feature of this plugin is the ability to selectively route Claude's buil
|
|
|
146
210
|
| `"Write"` | `Write` | `mcp__opencode_proxy__write` |
|
|
147
211
|
| `"WebFetch"` | `WebFetch` | `mcp__opencode_proxy__webfetch` |
|
|
148
212
|
|
|
149
|
-
|
|
213
|
+
Only those four values are actually proxied; anything else you put in `proxyTools` is ignored. Note that `MultiEdit` is **not** disabled when you proxy `Edit` — Claude can still use its built-in `MultiEdit` directly, which won't go through opencode's permission UI. If that matters, manage `MultiEdit` separately through your Claude settings.
|
|
150
214
|
|
|
151
|
-
|
|
215
|
+
To turn off proxying entirely:
|
|
152
216
|
|
|
153
217
|
```json
|
|
154
|
-
{
|
|
155
|
-
"provider": {
|
|
156
|
-
"claude-code": {
|
|
157
|
-
"npm": "@khalilgharbaoui/opencode-claude-code-plugin",
|
|
158
|
-
"options": {
|
|
159
|
-
"cliPath": "claude",
|
|
160
|
-
"proxyTools": ["Bash", "Edit", "Write", "WebFetch"]
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
218
|
+
"options": { "proxyTools": [] }
|
|
165
219
|
```
|
|
166
220
|
|
|
167
|
-
|
|
168
|
-
- All LLM reasoning, planning, and tool selection
|
|
169
|
-
- System prompts, conversation state, multi-turn continuation
|
|
170
|
-
- Native execution of non-proxied tools (Read, Glob, Grep, TodoWrite, etc.)
|
|
171
|
-
- Authentication via your Claude CLI subscription
|
|
221
|
+
### What you get with proxying on
|
|
172
222
|
|
|
173
|
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
- Policy enforcement via opencode's permission rules
|
|
223
|
+
- opencode's **permission prompts** for every Bash/Edit/Write/WebFetch call (the default `claude --dangerously-skip-permissions` is NOT applied to proxied tools).
|
|
224
|
+
- opencode's **audit log** captures the calls.
|
|
225
|
+
- Per-tool **policy rules** in opencode apply.
|
|
177
226
|
|
|
178
|
-
###
|
|
227
|
+
### What you give up
|
|
179
228
|
|
|
180
|
-
|
|
229
|
+
- A small per-call latency hop through `127.0.0.1:<random>/mcp`.
|
|
230
|
+
- Some Claude-specific tool features stay on the built-in side (notably `MultiEdit` — see the note above).
|
|
181
231
|
|
|
182
|
-
|
|
232
|
+
---
|
|
183
233
|
|
|
184
|
-
|
|
185
|
-
- **Built-in tools**: `Edit` -> `edit`, `Write` -> `write`, `Bash` -> `bash`, etc. (lowercased)
|
|
186
|
-
- **MCP tools**: `mcp__server__tool` -> `server_tool` (Claude CLI format to opencode format)
|
|
187
|
-
- **Proxy tools**: `mcp__opencode_proxy__bash` -> `bash` (proxy prefix stripped)
|
|
188
|
-
- **Claude CLI internal tools**: `ToolSearch`, `Agent`, `AskFollowupQuestion` are silently skipped
|
|
189
|
-
- **Questions**: `AskUserQuestion` is rendered as text in the stream
|
|
234
|
+
## MCP bridge
|
|
190
235
|
|
|
191
|
-
|
|
236
|
+
If `bridgeOpencodeMcp` is true (the default), the plugin reads your opencode config's `mcp` block, translates it into Claude's MCP schema, writes it to a temp file, and passes that to `claude --mcp-config`. So whatever MCP servers you've already configured in opencode become available to Claude with no extra setup.
|
|
192
237
|
|
|
193
|
-
|
|
238
|
+
### Discovery order (highest to lowest priority)
|
|
194
239
|
|
|
195
|
-
|
|
240
|
+
1. `OPENCODE_CONFIG` env var (file path)
|
|
241
|
+
2. `OPENCODE_CONFIG_DIR` env var
|
|
242
|
+
3. Walk up from the current `cwd` looking for `opencode.jsonc`, `opencode.json`, `config.json`, or a `.opencode/` directory
|
|
243
|
+
4. Global `$XDG_CONFIG_HOME/opencode` or `~/.config/opencode`
|
|
196
244
|
|
|
197
|
-
|
|
245
|
+
Later sources override earlier ones **by server name**, so a project-level MCP server replaces a global one with the same id.
|
|
198
246
|
|
|
199
|
-
|
|
200
|
-
- `controlRequestToolBehaviors` - per-tool allow/deny overrides
|
|
201
|
-
- `controlRequestDenyMessage` - message returned on denied requests
|
|
247
|
+
### Translation
|
|
202
248
|
|
|
203
|
-
|
|
249
|
+
| opencode `type` | Claude `type` |
|
|
250
|
+
|---|---|
|
|
251
|
+
| `local` | `stdio` |
|
|
252
|
+
| `remote` | `http` |
|
|
204
253
|
|
|
205
|
-
|
|
206
|
-
- `text-start` -> `text-delta`* -> `text-end`
|
|
207
|
-
- `reasoning-start` -> `reasoning-delta`* -> `reasoning-end`
|
|
208
|
-
- `tool-input-start` -> `tool-input-delta`* -> `tool-call` -> `tool-result`
|
|
254
|
+
If you want to manage MCP servers only via `~/.claude/settings.json`, set `bridgeOpencodeMcp: false`.
|
|
209
255
|
|
|
210
|
-
|
|
256
|
+
To replace (rather than augment) bridged MCP with your own:
|
|
211
257
|
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
message-builder.ts # AI SDK prompt -> Claude CLI JSON messages
|
|
219
|
-
session-manager.ts # CLI process lifecycle (spawn, reuse, cleanup)
|
|
220
|
-
proxy-mcp.ts # In-process HTTP MCP server for tool proxying
|
|
221
|
-
proxy-broker.ts # Pause/resume broker for proxied tool calls
|
|
222
|
-
mcp-bridge.ts # Opencode MCP config -> Claude CLI translation
|
|
223
|
-
logger.ts # Debug logging
|
|
258
|
+
```json
|
|
259
|
+
"options": {
|
|
260
|
+
"bridgeOpencodeMcp": false,
|
|
261
|
+
"mcpConfig": "/path/to/your/mcp.json",
|
|
262
|
+
"strictMcpConfig": true
|
|
263
|
+
}
|
|
224
264
|
```
|
|
225
265
|
|
|
226
|
-
|
|
266
|
+
---
|
|
227
267
|
|
|
228
|
-
|
|
229
|
-
bun install
|
|
230
|
-
bun run build # Build with tsup
|
|
231
|
-
bun run dev # Build in watch mode
|
|
232
|
-
bun run typecheck # Type check without emitting
|
|
233
|
-
```
|
|
268
|
+
## Sessions
|
|
234
269
|
|
|
235
|
-
|
|
270
|
+
Each chat keeps a long-lived `claude` subprocess so the model retains its native context across turns.
|
|
236
271
|
|
|
237
|
-
|
|
272
|
+
- **Session key**: `(cwd, model, tool-scope, opencode-session-id)`. The opencode session id comes from the `x-session-affinity` header opencode sets on third-party provider calls. Two chats in the same project on the same model run in **separate** CLI processes — they don't race. In account mode, model IDs are suffixed per account, so account sessions do not collide.
|
|
273
|
+
- **Same chat, multiple turns** → process reused, full Claude context retained.
|
|
274
|
+
- **New chat** → fresh process under the new session key.
|
|
275
|
+
- **Resumed chat after restart** → in-memory state is gone; a new process spawns and the conversation history is summarized and prepended.
|
|
276
|
+
- **Abort (Ctrl+C)** → stream closes, process stays alive for the next message in that chat.
|
|
277
|
+
- **Cap**: 16 active processes, LRU eviction.
|
|
238
278
|
|
|
239
|
-
|
|
240
|
-
DEBUG=opencode-claude-code opencode
|
|
241
|
-
```
|
|
279
|
+
---
|
|
242
280
|
|
|
243
|
-
|
|
281
|
+
## Plan mode
|
|
282
|
+
|
|
283
|
+
Set `permissionMode: "plan"` to forward `--permission-mode plan` to Claude. The plugin handles `ExitPlanMode` specially — instead of forwarding it as a tool call, it converts it to a confirmation prompt that flows through opencode normally.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Quirks worth knowing
|
|
288
|
+
|
|
289
|
+
- **Empty text blocks are dropped.** Claude sometimes opens a `content_block_start` for text but never sends a delta. The plugin no longer emits the empty block (which was triggering Anthropic 400s like `cache_control cannot be set for empty text blocks`).
|
|
290
|
+
- **`AskUserQuestion`** from the CLI is converted into plain text content rather than forwarded as a tool call.
|
|
291
|
+
- **Result fallback timer.** If the CLI finishes a text block but never sends a `result` message, the stream closes gracefully after 5 seconds rather than hanging.
|
|
292
|
+
- **Per-iteration usage.** When the CLI internally retries with tools, the plugin only counts the last iteration's usage so opencode's context accounting stays accurate.
|
|
293
|
+
- **Lazy `cwd`.** The working directory is re-resolved at every request, so opencode's project-aware behavior works without restarting the plugin.
|
|
294
|
+
- **Variants survive merge.** opencode recalculates variant lists after the plugin loads; the plugin re-injects defaults into runtime config so your variants don't disappear.
|
|
295
|
+
|
|
296
|
+
## Debug logging
|
|
244
297
|
|
|
245
298
|
```bash
|
|
246
|
-
|
|
299
|
+
DEBUG=opencode-claude-code opencode
|
|
247
300
|
```
|
|
248
301
|
|
|
249
|
-
|
|
302
|
+
Goes to stderr.
|
|
250
303
|
|
|
251
|
-
##
|
|
304
|
+
## Known limitations
|
|
252
305
|
|
|
253
|
-
|
|
306
|
+
- No streaming of tool inputs as they're being constructed (Anthropic's `input_json_delta`); the plugin emits them once complete.
|
|
307
|
+
- No interleaved thinking — Claude Code CLI doesn't expose reasoning tokens to the SDK.
|
|
308
|
+
- The CLI must be a recent enough version to support `--mcp-config` and `--disallowedTools`. If something breaks after a Claude Code update, that's the first thing to check.
|
|
254
309
|
|
|
255
|
-
|
|
256
|
-
1. Switch to **build mode** using `Tab`
|
|
257
|
-
2. Enter `yes` (or `no` to reject) into the prompt
|
|
310
|
+
---
|
|
258
311
|
|
|
259
|
-
##
|
|
312
|
+
## Development
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
bun install
|
|
316
|
+
bun run typecheck # tsc --noEmit
|
|
317
|
+
bun run build # tsup -> dist/
|
|
318
|
+
```
|
|
260
319
|
|
|
261
|
-
|
|
262
|
-
- **Non-proxied tools bypass opencode permissions**: tools that remain native to Claude CLI (Read, Glob, Grep, etc.) are executed by the CLI directly without opencode permission checks. This is by design for performance, but means those tools are not subject to opencode's permission rules.
|
|
263
|
-
- **Claude upstream bug [#34046](https://github.com/anthropics/claude-code/issues/34046)**: Claude CLI does not reliably emit `can_use_tool` control requests for built-in tools even when `--permission-prompt-tool` is set. The selective proxy approach works around this entirely by disabling the built-in tools and replacing them with MCP equivalents.
|
|
320
|
+
Source layout:
|
|
264
321
|
|
|
265
|
-
|
|
322
|
+
```
|
|
323
|
+
src/
|
|
324
|
+
index.ts # opencode plugin entry, config + provider hooks
|
|
325
|
+
models.ts # default models + variants
|
|
326
|
+
claude-code-language-model.ts # AI-SDK provider that drives `claude`
|
|
327
|
+
proxy-mcp.ts # in-process MCP server for proxied tools
|
|
328
|
+
mcp-bridge.ts # opencode → Claude --mcp-config translator
|
|
329
|
+
session-manager.ts # LRU cache of CLI subprocesses
|
|
330
|
+
logger.ts # DEBUG=opencode-claude-code stderr logger
|
|
331
|
+
types.ts # public option types
|
|
332
|
+
opencode-types.ts # mirrored opencode types
|
|
333
|
+
```
|
|
266
334
|
|
|
267
|
-
|
|
335
|
+
## Publishing (maintainers)
|
|
268
336
|
|
|
269
337
|
```bash
|
|
270
|
-
|
|
271
|
-
git push origin
|
|
338
|
+
npm version patch # or minor/major — bumps package.json + creates the tag
|
|
339
|
+
git push origin master --follow-tags
|
|
272
340
|
```
|
|
273
341
|
|
|
274
|
-
The GitHub Actions workflow
|
|
342
|
+
The GitHub Actions workflow at `.github/workflows/publish.yml` runs `npm publish --access public` on tag push (requires `NPM_TOKEN` secret in the repo settings — use a classic automation token so 2FA isn't required at workflow time).
|
|
275
343
|
|
|
276
344
|
## License
|
|
277
345
|
|
|
278
|
-
MIT
|
|
346
|
+
MIT. See [LICENSE](./LICENSE).
|
|
347
|
+
|
|
348
|
+
Original work © `unixfox`. Fork modifications © Khalil Gharbaoui.
|
package/dist/index.d.ts
CHANGED
|
@@ -83,6 +83,9 @@ interface ClaudeCodeConfig {
|
|
|
83
83
|
provider: string;
|
|
84
84
|
cliPath: string;
|
|
85
85
|
cwd?: string;
|
|
86
|
+
account?: string;
|
|
87
|
+
configDir?: string;
|
|
88
|
+
providerID?: string;
|
|
86
89
|
skipPermissions?: boolean;
|
|
87
90
|
permissionMode?: PermissionMode;
|
|
88
91
|
mcpConfig?: string | string[];
|
|
@@ -97,6 +100,10 @@ interface ClaudeCodeProviderSettings {
|
|
|
97
100
|
cliPath?: string;
|
|
98
101
|
cwd?: string;
|
|
99
102
|
name?: string;
|
|
103
|
+
providerID?: string;
|
|
104
|
+
account?: string;
|
|
105
|
+
configDir?: string;
|
|
106
|
+
accounts?: string[];
|
|
100
107
|
skipPermissions?: boolean;
|
|
101
108
|
permissionMode?: PermissionMode;
|
|
102
109
|
mcpConfig?: string | string[];
|