@khalilgharbaoui/opencode-claude-code-plugin 0.1.6 → 0.2.1
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 +22 -0
- package/dist/index.d.ts +78 -9
- package/dist/index.js +469 -149
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -170,6 +170,7 @@ The account model IDs are internally suffixed, for example `claude-sonnet-4-6@wo
|
|
|
170
170
|
| `bridgeOpencodeMcp` | boolean | `true` | Auto-translate your opencode `mcp` block into Claude's `--mcp-config`. See [MCP bridge](#mcp-bridge). |
|
|
171
171
|
| `mcpConfig` | string \| string[] | – | Extra `--mcp-config` paths/JSON passed alongside the bridged config. |
|
|
172
172
|
| `strictMcpConfig` | boolean | `false` | Pass `--strict-mcp-config` so Claude loads **only** the configured servers and ignores `~/.claude/settings.json`. |
|
|
173
|
+
| `webSearch` | `"claude"` \| `"disabled"` \| `<tool>` | `"claude"` | Routing for Claude's built-in `WebSearch`. See [WebSearch routing](#websearch-routing). |
|
|
173
174
|
|
|
174
175
|
### Overriding model metadata
|
|
175
176
|
|
|
@@ -227,6 +228,27 @@ To turn off proxying entirely:
|
|
|
227
228
|
### What you give up
|
|
228
229
|
|
|
229
230
|
- A small per-call latency hop through `127.0.0.1:<random>/mcp`.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## WebSearch routing
|
|
235
|
+
|
|
236
|
+
Claude Code ships a built-in `WebSearch` tool. The `webSearch` option controls who actually executes those calls:
|
|
237
|
+
|
|
238
|
+
| `webSearch` value | Behavior | When to use |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `"claude"` (default) | Claude CLI runs WebSearch internally via Anthropic. Zero setup, no extra cost, no API key. | Most users. |
|
|
241
|
+
| `"<opencode-tool-name>"` (e.g. `"websearch_web_search_exa"`) | Forward to that opencode-side tool with `executed:false`. Requires the corresponding MCP server to be configured in opencode (e.g. [exa-mcp-server](https://github.com/exa-labs/exa-mcp-server)). | You want a specific search backend (Exa, Tavily, Brave) and have the MCP wired up in opencode. |
|
|
242
|
+
| `"disabled"` | `WebSearch` is added to `--disallowedTools` so the model can't call it. | Compliance/security scenarios where outbound search isn't allowed. |
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
"options": { "webSearch": "websearch_web_search_exa" }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Trade-offs**
|
|
249
|
+
|
|
250
|
+
- Claude-side execution: free with your Claude usage, no API key, but no opencode visibility into queries/results, no caching/rate-limit hooks.
|
|
251
|
+
- opencode-side execution: choose any backend, queries flow through opencode's audit/policy/cache, but costs money (search APIs are paid) and adds a network hop.
|
|
230
252
|
- Some Claude-specific tool features stay on the built-in side (notably `MultiEdit` — see the note above).
|
|
231
253
|
|
|
232
254
|
---
|
package/dist/index.d.ts
CHANGED
|
@@ -70,12 +70,29 @@ type OpenCodeConfig = {
|
|
|
70
70
|
models?: Record<string, unknown>;
|
|
71
71
|
}>;
|
|
72
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Bus events surface to plugins. Shape mirrors what opencode core publishes
|
|
75
|
+
* via `GlobalBus.emit("event", { directory, payload: { type, properties } })`
|
|
76
|
+
* but kept loose since opencode adds events over time and this plugin only
|
|
77
|
+
* reacts to a small subset (currently just `global.disposed`).
|
|
78
|
+
*/
|
|
79
|
+
type OpenCodeEvent = {
|
|
80
|
+
type?: string;
|
|
81
|
+
payload?: {
|
|
82
|
+
type?: string;
|
|
83
|
+
properties?: Record<string, unknown>;
|
|
84
|
+
};
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
};
|
|
73
87
|
type OpenCodeHooks = {
|
|
74
88
|
config?: (input: OpenCodeConfig) => Promise<void>;
|
|
75
89
|
provider?: {
|
|
76
90
|
id: string;
|
|
77
91
|
models?: (provider: OpenCodeProvider) => Promise<Record<string, OpenCodeModel>>;
|
|
78
92
|
};
|
|
93
|
+
event?: (input: {
|
|
94
|
+
event: OpenCodeEvent;
|
|
95
|
+
}) => Promise<void>;
|
|
79
96
|
};
|
|
80
97
|
type OpenCodePlugin = (input: unknown, options?: Record<string, unknown>) => Promise<OpenCodeHooks>;
|
|
81
98
|
|
|
@@ -95,7 +112,10 @@ interface ClaudeCodeConfig {
|
|
|
95
112
|
controlRequestToolBehaviors?: Record<string, ControlRequestBehavior>;
|
|
96
113
|
controlRequestDenyMessage?: string;
|
|
97
114
|
proxyTools?: string[];
|
|
115
|
+
webSearch?: WebSearchRouting;
|
|
116
|
+
hotReloadMcp?: boolean;
|
|
98
117
|
}
|
|
118
|
+
type WebSearchRouting = "claude" | "disabled" | (string & {});
|
|
99
119
|
interface ClaudeCodeProviderSettings {
|
|
100
120
|
cliPath?: string;
|
|
101
121
|
cwd?: string;
|
|
@@ -147,6 +167,30 @@ interface ClaudeCodeProviderSettings {
|
|
|
147
167
|
* Supported: `bash`, `write`, `edit`, `webfetch`. Leave empty or unset to disable proxying.
|
|
148
168
|
*/
|
|
149
169
|
proxyTools?: string[];
|
|
170
|
+
/**
|
|
171
|
+
* Routing for Claude's built-in `WebSearch` tool.
|
|
172
|
+
*
|
|
173
|
+
* - `"claude"` (default): Claude CLI runs WebSearch internally via
|
|
174
|
+
* Anthropic's web search. No MCP setup required, no extra cost.
|
|
175
|
+
* - `"<opencode-tool-name>"` (e.g. `"websearch_web_search_exa"`): forward
|
|
176
|
+
* the call to that opencode-side tool with `executed:false`. Requires
|
|
177
|
+
* the corresponding MCP server to be configured in opencode.
|
|
178
|
+
* - `"disabled"`: prevent the model from calling WebSearch entirely
|
|
179
|
+
* (passes `WebSearch` via `--disallowedTools`).
|
|
180
|
+
*/
|
|
181
|
+
webSearch?: WebSearchRouting;
|
|
182
|
+
/**
|
|
183
|
+
* Detect mid-session opencode MCP config changes and respawn the
|
|
184
|
+
* underlying claude process so newly enabled / disabled MCPs become
|
|
185
|
+
* visible to the model without restarting opencode or starting a new
|
|
186
|
+
* chat. Eviction happens at the start of the next user turn (never mid
|
|
187
|
+
* tool-call) and `--session-id` is preserved so the conversation
|
|
188
|
+
* continues seamlessly. Defaults to `true`.
|
|
189
|
+
*
|
|
190
|
+
* Set to `false` to keep the previous behavior (cached subprocess
|
|
191
|
+
* survives MCP changes until the chat is reset).
|
|
192
|
+
*/
|
|
193
|
+
hotReloadMcp?: boolean;
|
|
150
194
|
}
|
|
151
195
|
type PermissionMode = "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
|
|
152
196
|
type ControlRequestBehavior = "allow" | "deny";
|
|
@@ -248,9 +292,14 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
248
292
|
private toFinishReason;
|
|
249
293
|
private requestScope;
|
|
250
294
|
/**
|
|
251
|
-
* Build the combined `--mcp-config` list
|
|
252
|
-
*
|
|
253
|
-
*
|
|
295
|
+
* Build the combined `--mcp-config` list and return both the list and the
|
|
296
|
+
* hash of the bridged opencode MCP block (or null when bridging is off /
|
|
297
|
+
* yields nothing). The hash is used to detect mid-session config changes
|
|
298
|
+
* and respawn the underlying claude process.
|
|
299
|
+
*
|
|
300
|
+
* `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When
|
|
301
|
+
* provided it overlays opencode's UI-toggled state on top of disk config
|
|
302
|
+
* so `/mcps` toggles propagate without a config file write.
|
|
254
303
|
*/
|
|
255
304
|
private effectiveMcpConfig;
|
|
256
305
|
/** Resolve ProxyToolDef[] for the configured proxyTools names. */
|
|
@@ -284,15 +333,35 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
284
333
|
doStream(options: LanguageModelV3CallOptions): Promise<Awaited<ReturnType<LanguageModelV3["doStream"]>>>;
|
|
285
334
|
}
|
|
286
335
|
|
|
336
|
+
interface BridgedMcp {
|
|
337
|
+
/** Path to the temp file containing the translated `--mcp-config`. */
|
|
338
|
+
path: string;
|
|
339
|
+
/** Stable hash of the merged opencode mcp block (pre-translation). */
|
|
340
|
+
hash: string;
|
|
341
|
+
}
|
|
287
342
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
343
|
+
* Per-server runtime status from opencode's `client.mcp.status()`. Used as
|
|
344
|
+
* an overlay on top of the on-disk merged config so opencode's UI-toggled
|
|
345
|
+
* state — which lives only in-memory; `connect()`/`disconnect()` never
|
|
346
|
+
* touch disk — propagates to the bridged claude subprocess.
|
|
291
347
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
348
|
+
* Treatment per server:
|
|
349
|
+
* - "connected" → force `enabled: true` (mirror opencode)
|
|
350
|
+
* - any other status → force `enabled: false` (don't ship a server
|
|
351
|
+
* opencode can't run; user fixes it in opencode first)
|
|
352
|
+
* - missing entry → leave disk value
|
|
353
|
+
*
|
|
354
|
+
* Omit the overlay and the bridge falls back to disk-only.
|
|
355
|
+
*/
|
|
356
|
+
type RuntimeMcpStatus = Record<string, string>;
|
|
357
|
+
/**
|
|
358
|
+
* Read opencode config layers, deep-merge their `mcp` blocks per opencode's
|
|
359
|
+
* own semantics, optionally apply an opencode runtime-status overlay, then
|
|
360
|
+
* translate each server to Claude CLI format, write a scratch file, and
|
|
361
|
+
* return its path + a stable hash. Returns null when no enabled MCP servers
|
|
362
|
+
* remain after the merge + overlay.
|
|
294
363
|
*/
|
|
295
|
-
declare function bridgeOpencodeMcp(cwd: string):
|
|
364
|
+
declare function bridgeOpencodeMcp(cwd: string, runtimeStatus?: RuntimeMcpStatus): BridgedMcp | null;
|
|
296
365
|
|
|
297
366
|
declare const defaultModels: Record<string, OpenCodeModel>;
|
|
298
367
|
|