@khalilgharbaoui/opencode-claude-code-plugin 0.13.0 → 0.13.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 +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +42 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -182,6 +182,7 @@ The account model IDs are internally suffixed, for example `claude-sonnet-4-6@wo
|
|
|
182
182
|
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to `claude`. Ignored when `proxyTools` is set — the proxy handles permissions through opencode instead. |
|
|
183
183
|
| `permissionMode` | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` | – | Forwarded to `claude --permission-mode`. |
|
|
184
184
|
| `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Claude built-in tools to route through opencode's executor + permission UI. Opt-in extras: `"Question"`, `"Compress"`. See [Selective tool proxy](#selective-tool-proxy). |
|
|
185
|
+
| `extraDisallowedTools` | string[] | – | Extra Claude built-ins to switch off with `--disallowedTools`, on top of what `proxyTools` implies. Claude's names, e.g. `["NotebookEdit"]`. See [Closing a tool with no proxy](#closing-a-tool-with-no-proxy). |
|
|
185
186
|
| `proxyToolTimeoutMs` | `Record<string, number>` | – | Per-tool proxy call deadline in ms, keyed by proxy tool name (`bash`, `task`, …). Defaults: 10 min flat, `task` → 60 min. For `bash`, the call's own `input.timeout` is honoured on top (`max(resolved, input.timeout)`). See [Selective tool proxy](#selective-tool-proxy). |
|
|
186
187
|
| `planModeQuestion` | boolean | `false` | Route `ExitPlanMode` approval through opencode's native `question` tool instead of a text "(yes/no)" prompt. Off because opencode's question form is currently broken upstream. See [Plan mode](#plan-mode). |
|
|
187
188
|
| `controlRequestBehavior` | `allow` \| `deny` | `allow` | Default response when `skipPermissions: false` and Claude sends a `can_use_tool` control request. |
|
|
@@ -298,6 +299,20 @@ recovery step for harnesses that defer MCP tool schemas. Both apply per Claude
|
|
|
298
299
|
process at spawn, and provider options are read once at opencode startup, so
|
|
299
300
|
`proxyTools` changes need a full opencode restart.
|
|
300
301
|
|
|
302
|
+
### Closing a tool with no proxy
|
|
303
|
+
|
|
304
|
+
`proxyTools` only reaches built-ins the plugin can replace. A built-in with no opencode equivalent, `NotebookEdit` today and whatever Claude Code ships next, stays enabled and unmediated no matter what you put in that list. `extraDisallowedTools` names them directly:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
"options": {
|
|
308
|
+
"extraDisallowedTools": ["NotebookEdit"]
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
These go straight to `claude --disallowedTools`, so use Claude's tool names rather than opencode's. There is no replacement: the capability goes away rather than being routed through opencode, which is the point, but the model then has to work without it.
|
|
313
|
+
|
|
314
|
+
Unknown entries in `proxyTools` are logged as a warning at spawn rather than passing silently, so a typo shows up as "ignoring unknown proxyTools entries" in the plugin log instead of quietly leaving the matching built-in unmediated.
|
|
315
|
+
|
|
301
316
|
### Context compression
|
|
302
317
|
|
|
303
318
|
`"Compress"` is off by default. Add it when you run a harness that expects the model to manage its own context (opencode-dcp injects exactly those instructions), and the plugin exposes `mcp__opencode_proxy__compress`:
|
package/dist/index.d.ts
CHANGED
|
@@ -161,6 +161,7 @@ interface ClaudeCodeConfig {
|
|
|
161
161
|
controlRequestToolBehaviors?: Record<string, ControlRequestBehavior>;
|
|
162
162
|
controlRequestDenyMessage?: string;
|
|
163
163
|
proxyTools?: string[];
|
|
164
|
+
extraDisallowedTools?: string[];
|
|
164
165
|
proxyToolTimeoutMs?: Record<string, number>;
|
|
165
166
|
/**
|
|
166
167
|
* Route `ExitPlanMode` through opencode's native `question` tool so plan
|
|
@@ -283,6 +284,20 @@ interface ClaudeCodeProviderSettings {
|
|
|
283
284
|
* entry, in which case the deny/markdown fallback applies.
|
|
284
285
|
*/
|
|
285
286
|
proxyTools?: string[];
|
|
287
|
+
/**
|
|
288
|
+
* Extra Claude Code built-ins to switch off with `--disallowedTools`,
|
|
289
|
+
* on top of the ones implied by `proxyTools`.
|
|
290
|
+
*
|
|
291
|
+
* `proxyTools` can only disable built-ins the plugin knows how to
|
|
292
|
+
* replace, so a built-in with no proxy equivalent (`NotebookEdit`, and
|
|
293
|
+
* anything Claude Code adds after this release) has no off switch
|
|
294
|
+
* otherwise. Names are Claude's, not opencode's: `["NotebookEdit"]`.
|
|
295
|
+
*
|
|
296
|
+
* Disabling a tool with no replacement removes the capability rather
|
|
297
|
+
* than routing it through opencode — that is the point, but it does mean
|
|
298
|
+
* the model has to work without it.
|
|
299
|
+
*/
|
|
300
|
+
extraDisallowedTools?: string[];
|
|
286
301
|
/**
|
|
287
302
|
* Per-tool proxy call timeouts in milliseconds, keyed by the proxy tool
|
|
288
303
|
* name (`bash`, `edit`, `write`, `webfetch`, `task`, `question` —
|
package/dist/index.js
CHANGED
|
@@ -294,6 +294,9 @@ var CLAUDE_INTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
294
294
|
"TaskGet",
|
|
295
295
|
"TaskStop"
|
|
296
296
|
]);
|
|
297
|
+
function singleQuoteForShell(value) {
|
|
298
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
299
|
+
}
|
|
297
300
|
function emitTodoWrite(todos) {
|
|
298
301
|
return {
|
|
299
302
|
name: "todowrite",
|
|
@@ -348,7 +351,7 @@ function mapTool(name, input, opts) {
|
|
|
348
351
|
return {
|
|
349
352
|
name: "bash",
|
|
350
353
|
input: {
|
|
351
|
-
command: `
|
|
354
|
+
command: `printf '%s\\n' ${singleQuoteForShell(`TASK OUTPUT: ${String(output)}`)}`,
|
|
352
355
|
description: "Displaying task output"
|
|
353
356
|
},
|
|
354
357
|
executed: false
|
|
@@ -2810,6 +2813,20 @@ function disallowedToolFlags(tools) {
|
|
|
2810
2813
|
}
|
|
2811
2814
|
return out;
|
|
2812
2815
|
}
|
|
2816
|
+
function resolveDisallowedTools(options) {
|
|
2817
|
+
const out = [];
|
|
2818
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2819
|
+
const push = (name) => {
|
|
2820
|
+
const trimmed = name.trim();
|
|
2821
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
2822
|
+
seen.add(trimmed);
|
|
2823
|
+
out.push(trimmed);
|
|
2824
|
+
};
|
|
2825
|
+
for (const name of disallowedToolFlags(options.proxyTools ?? [])) push(name);
|
|
2826
|
+
for (const name of options.extraDisallowedTools ?? []) push(String(name));
|
|
2827
|
+
if (options.disableWebSearch) push("WebSearch");
|
|
2828
|
+
return out;
|
|
2829
|
+
}
|
|
2813
2830
|
function readBody(req) {
|
|
2814
2831
|
return new Promise((resolve4, reject) => {
|
|
2815
2832
|
const chunks = [];
|
|
@@ -3353,9 +3370,22 @@ var ClaudeCodeLanguageModel = class {
|
|
|
3353
3370
|
DEFAULT_PROXY_TOOLS.map((t) => [t.name.toLowerCase(), t])
|
|
3354
3371
|
);
|
|
3355
3372
|
const picked = [];
|
|
3373
|
+
const unknown = [];
|
|
3356
3374
|
for (const n of names) {
|
|
3357
3375
|
const def = defsByName.get(String(n).toLowerCase());
|
|
3358
3376
|
if (def) picked.push(def);
|
|
3377
|
+
else unknown.push(String(n));
|
|
3378
|
+
}
|
|
3379
|
+
if (unknown.length > 0) {
|
|
3380
|
+
const known = [...defsByName.keys()].join(", ");
|
|
3381
|
+
if (picked.length === 0) {
|
|
3382
|
+
log.warn(
|
|
3383
|
+
"no proxyTools entry was recognised; nothing will be proxied this turn",
|
|
3384
|
+
{ unknown, known }
|
|
3385
|
+
);
|
|
3386
|
+
} else {
|
|
3387
|
+
log.warn("ignoring unknown proxyTools entries", { unknown, known });
|
|
3388
|
+
}
|
|
3359
3389
|
}
|
|
3360
3390
|
return picked.length > 0 ? picked : null;
|
|
3361
3391
|
}
|
|
@@ -4453,10 +4483,11 @@ ${plan}
|
|
|
4453
4483
|
proxyServer = await self.ensureProxyServer(combinedProxyTools, sk);
|
|
4454
4484
|
}
|
|
4455
4485
|
const questionProxyActive = enrichedProxy?.some((t) => t.name === "question") ?? false;
|
|
4456
|
-
const
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4486
|
+
const allDisallowed = resolveDisallowedTools({
|
|
4487
|
+
proxyTools: enrichedProxy,
|
|
4488
|
+
extraDisallowedTools: self.config.extraDisallowedTools,
|
|
4489
|
+
disableWebSearch: self.config.webSearch === "disabled"
|
|
4490
|
+
});
|
|
4460
4491
|
const mcp = self.effectiveMcpConfig(
|
|
4461
4492
|
cwd,
|
|
4462
4493
|
proxyServer?.configPath(),
|
|
@@ -5707,11 +5738,11 @@ function defineModel(opts) {
|
|
|
5707
5738
|
variants: opts.reasoning ? reasoningVariants : void 0
|
|
5708
5739
|
};
|
|
5709
5740
|
}
|
|
5710
|
-
var haikuCost = { input:
|
|
5711
|
-
var sonnetCost = { input:
|
|
5712
|
-
var sonnet5Cost = { input:
|
|
5713
|
-
var opusCost = { input:
|
|
5714
|
-
var fableCost = { input:
|
|
5741
|
+
var haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 };
|
|
5742
|
+
var sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
|
|
5743
|
+
var sonnet5Cost = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 };
|
|
5744
|
+
var opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 };
|
|
5745
|
+
var fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 };
|
|
5715
5746
|
function toConfigModel(model) {
|
|
5716
5747
|
const inputMods = [];
|
|
5717
5748
|
const outputMods = [];
|
|
@@ -6310,6 +6341,7 @@ function createClaudeCode(settings = {}) {
|
|
|
6310
6341
|
controlRequestToolBehaviors: settings.controlRequestToolBehaviors,
|
|
6311
6342
|
controlRequestDenyMessage: settings.controlRequestDenyMessage,
|
|
6312
6343
|
proxyTools,
|
|
6344
|
+
extraDisallowedTools: settings.extraDisallowedTools,
|
|
6313
6345
|
proxyToolTimeoutMs: settings.proxyToolTimeoutMs,
|
|
6314
6346
|
planModeQuestion: settings.planModeQuestion ?? false,
|
|
6315
6347
|
webSearch: settings.webSearch,
|