@khalilgharbaoui/opencode-claude-code-plugin 0.1.6 → 0.2.0
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 +84 -9
- package/dist/index.js +367 -138
- 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,35 @@ 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
|
+
/**
|
|
94
|
+
* Called for every bus event opencode publishes. We use this to react to
|
|
95
|
+
* `global.disposed` (fired when opencode invalidates its config — e.g.
|
|
96
|
+
* after a UI MCP toggle or `updateGlobal`) and evict cached claude
|
|
97
|
+
* subprocesses so the next turn picks up the fresh config.
|
|
98
|
+
*/
|
|
99
|
+
event?: (input: {
|
|
100
|
+
event: OpenCodeEvent;
|
|
101
|
+
}) => Promise<void>;
|
|
79
102
|
};
|
|
80
103
|
type OpenCodePlugin = (input: unknown, options?: Record<string, unknown>) => Promise<OpenCodeHooks>;
|
|
81
104
|
|
|
@@ -95,7 +118,10 @@ interface ClaudeCodeConfig {
|
|
|
95
118
|
controlRequestToolBehaviors?: Record<string, ControlRequestBehavior>;
|
|
96
119
|
controlRequestDenyMessage?: string;
|
|
97
120
|
proxyTools?: string[];
|
|
121
|
+
webSearch?: WebSearchRouting;
|
|
122
|
+
hotReloadMcp?: boolean;
|
|
98
123
|
}
|
|
124
|
+
type WebSearchRouting = "claude" | "disabled" | (string & {});
|
|
99
125
|
interface ClaudeCodeProviderSettings {
|
|
100
126
|
cliPath?: string;
|
|
101
127
|
cwd?: string;
|
|
@@ -147,6 +173,30 @@ interface ClaudeCodeProviderSettings {
|
|
|
147
173
|
* Supported: `bash`, `write`, `edit`, `webfetch`. Leave empty or unset to disable proxying.
|
|
148
174
|
*/
|
|
149
175
|
proxyTools?: string[];
|
|
176
|
+
/**
|
|
177
|
+
* Routing for Claude's built-in `WebSearch` tool.
|
|
178
|
+
*
|
|
179
|
+
* - `"claude"` (default): Claude CLI runs WebSearch internally via
|
|
180
|
+
* Anthropic's web search. No MCP setup required, no extra cost.
|
|
181
|
+
* - `"<opencode-tool-name>"` (e.g. `"websearch_web_search_exa"`): forward
|
|
182
|
+
* the call to that opencode-side tool with `executed:false`. Requires
|
|
183
|
+
* the corresponding MCP server to be configured in opencode.
|
|
184
|
+
* - `"disabled"`: prevent the model from calling WebSearch entirely
|
|
185
|
+
* (passes `WebSearch` via `--disallowedTools`).
|
|
186
|
+
*/
|
|
187
|
+
webSearch?: WebSearchRouting;
|
|
188
|
+
/**
|
|
189
|
+
* Detect mid-session opencode MCP config changes and respawn the
|
|
190
|
+
* underlying claude process so newly enabled / disabled MCPs become
|
|
191
|
+
* visible to the model without restarting opencode or starting a new
|
|
192
|
+
* chat. Eviction happens at the start of the next user turn (never mid
|
|
193
|
+
* tool-call) and `--session-id` is preserved so the conversation
|
|
194
|
+
* continues seamlessly. Defaults to `true`.
|
|
195
|
+
*
|
|
196
|
+
* Set to `false` to keep the previous behavior (cached subprocess
|
|
197
|
+
* survives MCP changes until the chat is reset).
|
|
198
|
+
*/
|
|
199
|
+
hotReloadMcp?: boolean;
|
|
150
200
|
}
|
|
151
201
|
type PermissionMode = "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
|
|
152
202
|
type ControlRequestBehavior = "allow" | "deny";
|
|
@@ -248,9 +298,14 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
248
298
|
private toFinishReason;
|
|
249
299
|
private requestScope;
|
|
250
300
|
/**
|
|
251
|
-
* Build the combined `--mcp-config` list
|
|
252
|
-
*
|
|
253
|
-
*
|
|
301
|
+
* Build the combined `--mcp-config` list and return both the list and the
|
|
302
|
+
* hash of the bridged opencode MCP block (or null when bridging is off /
|
|
303
|
+
* yields nothing). The hash is used to detect mid-session config changes
|
|
304
|
+
* and respawn the underlying claude process.
|
|
305
|
+
*
|
|
306
|
+
* `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When
|
|
307
|
+
* provided it overlays opencode's UI-toggled state on top of disk config
|
|
308
|
+
* so `/mcps` toggles propagate without a config file write.
|
|
254
309
|
*/
|
|
255
310
|
private effectiveMcpConfig;
|
|
256
311
|
/** Resolve ProxyToolDef[] for the configured proxyTools names. */
|
|
@@ -284,15 +339,35 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
284
339
|
doStream(options: LanguageModelV3CallOptions): Promise<Awaited<ReturnType<LanguageModelV3["doStream"]>>>;
|
|
285
340
|
}
|
|
286
341
|
|
|
342
|
+
interface BridgedMcp {
|
|
343
|
+
/** Path to the temp file containing the translated `--mcp-config`. */
|
|
344
|
+
path: string;
|
|
345
|
+
/** Stable hash of the merged opencode mcp block (pre-translation). */
|
|
346
|
+
hash: string;
|
|
347
|
+
}
|
|
287
348
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
349
|
+
* Per-server runtime status from opencode's `client.mcp.status()`. Used as
|
|
350
|
+
* an overlay on top of the on-disk merged config so opencode's UI-toggled
|
|
351
|
+
* state — which lives only in-memory; `connect()`/`disconnect()` never
|
|
352
|
+
* touch disk — propagates to the bridged claude subprocess.
|
|
353
|
+
*
|
|
354
|
+
* Treatment per server:
|
|
355
|
+
* - "connected" → force `enabled: true` (mirror opencode)
|
|
356
|
+
* - any other status → force `enabled: false` (don't ship a server
|
|
357
|
+
* opencode can't run; user fixes it in opencode first)
|
|
358
|
+
* - missing entry → leave disk value
|
|
291
359
|
*
|
|
292
|
-
*
|
|
293
|
-
|
|
360
|
+
* Omit the overlay and the bridge falls back to disk-only.
|
|
361
|
+
*/
|
|
362
|
+
type RuntimeMcpStatus = Record<string, string>;
|
|
363
|
+
/**
|
|
364
|
+
* Read opencode config layers, deep-merge their `mcp` blocks per opencode's
|
|
365
|
+
* own semantics, optionally apply an opencode runtime-status overlay, then
|
|
366
|
+
* translate each server to Claude CLI format, write a scratch file, and
|
|
367
|
+
* return its path + a stable hash. Returns null when no enabled MCP servers
|
|
368
|
+
* remain after the merge + overlay.
|
|
294
369
|
*/
|
|
295
|
-
declare function bridgeOpencodeMcp(cwd: string):
|
|
370
|
+
declare function bridgeOpencodeMcp(cwd: string, runtimeStatus?: RuntimeMcpStatus): BridgedMcp | null;
|
|
296
371
|
|
|
297
372
|
declare const defaultModels: Record<string, OpenCodeModel>;
|
|
298
373
|
|