@khalilgharbaoui/opencode-claude-code-plugin 0.9.3 → 0.11.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 CHANGED
@@ -163,7 +163,7 @@ The account model IDs are internally suffixed, for example `claude-sonnet-4-6@wo
163
163
  "claude-code": {
164
164
  "options": {
165
165
  "cliPath": "claude",
166
- "proxyTools": ["Bash", "Edit", "Write", "WebFetch"],
166
+ "proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task"],
167
167
  "skipPermissions": true,
168
168
  "permissionMode": "default",
169
169
  "bridgeOpencodeMcp": true,
@@ -181,7 +181,8 @@ The account model IDs are internally suffixed, for example `claude-sonnet-4-6@wo
181
181
  | `cwd` | string | `process.cwd()` | Working directory for the spawned CLI. Resolved **lazily per request**, so opencode's project switching works. |
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
- | `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). |
184
+ | `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Claude built-in tools to route through opencode's executor + permission UI. See [Selective tool proxy](#selective-tool-proxy). |
185
+ | `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). |
185
186
  | `controlRequestBehavior` | `allow` \| `deny` | `allow` | Default response when `skipPermissions: false` and Claude sends a `can_use_tool` control request. |
186
187
  | `controlRequestToolBehaviors` | `Record<string, "allow" \| "deny">` | – | Per-tool override for `can_use_tool`. Example: `{ "Bash": "deny", "Read": "allow" }`. |
187
188
  | `controlRequestDenyMessage` | string | built-in message | Message returned to Claude on a deny. |
@@ -259,7 +260,7 @@ Set `interactiveSystemPrompt: false` only for diagnostics. While disabled, the i
259
260
 
260
261
  This is the core feature.
261
262
 
262
- 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.
263
+ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`. It disables Claude's corresponding built-in tool and exposes an equivalent through an in-process MCP server. Claude calls the MCP version, which blocks until opencode runs the tool through its own executor and permission system.
263
264
 
264
265
  ### Default proxied tools
265
266
 
@@ -271,11 +272,18 @@ By default, when Claude Code's CLI uses `Bash`, `Edit`, `Write`, etc., it execut
271
272
  | `"WebFetch"` | `WebFetch` | `mcp__opencode_proxy__webfetch` |
272
273
  | `"Task"` | `Agent` | `mcp__opencode_proxy__task` |
273
274
 
274
- The `Task` proxy is the way to let Claude orchestrate opencode's configured subagents (`build`, `general`, custom subagents defined in `opencode.json`) instead of Claude CLI's internal-only general-purpose / Explore / Plan options. With `"Task"` in `proxyTools` and `permission.task: allow` granted to the calling agent, a Claude session can invoke `task(subagent_type="build", prompt="...")` and the subagent runs natively under opencode (with its own permission UI, lifecycle, model assignment, and Tab visibility). Without `"Task"`, Claude's built-in `Agent` tool stays enabled and Claude orchestrates subagents internally with no opencode visibility.
275
+ ### OpenCode-native subagents
276
+
277
+ `Task` is proxied by default. The proxy disables Claude CLI's `Agent` tool and emits an unexecuted `task` call; it does not register a replacement task tool. OpenCode's built-in TaskTool remains responsible for permission checks, creating or resuming the child session, selecting the configured subagent, and foreground/background lifecycle.
278
+
279
+ - **Permissions:** the calling agent's `permission.task` rule applies to the target `subagent_type`. Grant `task: "allow"` on agents that should delegate without a prompt; an `ask` or `deny` rule remains authoritative. The plugin never bypasses this decision.
280
+ - **Resume:** pass the child session ID back as `task_id` to continue that subagent session. Omit it to create a fresh child.
281
+ - **Nested tasks:** current opencode defaults `subagent_depth` to `1`, so a first-level child cannot launch another child. Increase top-level `subagent_depth` to permit deeper nesting, and explicitly grant `permission.task` on every subagent that should delegate; opencode otherwise adds a task deny to spawned subagent sessions.
282
+ - **Background:** `background: true` returns after starting the child and lets opencode notify the parent when it finishes. Current opencode requires `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` in the environment of the opencode process. Foreground is the default.
275
283
 
276
284
  Only those five values are actually proxied; anything else you put in `proxyTools` is ignored. Proxying `Edit` also disables `MultiEdit` — opencode has no batched-edit equivalent, so Claude is forced to fan out into single `Edit` calls that each flow through the permission UI.
277
285
 
278
- To turn off proxying entirely:
286
+ Without `"Task"` in `proxyTools`, Claude's built-in `Agent` tool stays enabled and Claude orchestrates subagents internally with no opencode child-session visibility. To opt out of all proxying, including Task, use an explicit empty list:
279
287
 
280
288
  ```json
281
289
  "options": { "proxyTools": [] }
@@ -292,6 +300,24 @@ To turn off proxying entirely:
292
300
  - A small per-call latency hop through `127.0.0.1:<random>/mcp`.
293
301
  - Batched-edit ergonomics: with `Edit` proxied, Claude can no longer use `MultiEdit`, so a refactor that would have been one tool call becomes N single `Edit` calls.
294
302
 
303
+ ### Per-tool proxy timeouts
304
+
305
+ Every proxied tool call has a deadline: if opencode hasn't resolved it (run the underlying tool and returned a result) within that many milliseconds, the call is rejected and Claude receives a timeout error. Deadlines are resolved per tool, most-specific layer winning:
306
+
307
+ 1. flat default — 10 min (matches Claude CLI's own Bash ceiling)
308
+ 2. per-tool default — **`task`: 60 min**, **`question`: 30 min**, everything else: 10 min
309
+ 3. your `proxyToolTimeoutMs` override (case-insensitive key)
310
+ 4. for `bash` only, the call's own `input.timeout` — the proxy never undercuts a build the caller explicitly asked to run long (`max(resolved, input.timeout)`)
311
+
312
+ The `task` and `question` defaults are deliberately generous. Subagents routinely run 20–40 min, and a question can sit on a slow operator; under the old flat 10-minute ceiling the proxy fired mid-call, Claude believed its dispatch had failed, and the subagent's eventual result was dropped (the parent turn had already ended on the timeout error). If a `task` call *does* time out, the error tells Claude not to "schedule a wake-up" — that is a Claude Code affordance which cannot fire in this headless/proxy context, so deferring silently loses the work.
313
+
314
+ ```json
315
+ "options": {
316
+ "proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task"],
317
+ "proxyToolTimeoutMs": { "Task": 5400000, "bash": 1800000 }
318
+ }
319
+ ```
320
+
295
321
  ---
296
322
 
297
323
  ## WebSearch routing
@@ -505,6 +531,46 @@ Boolean env vars accept `1/true/on/yes` for on and `0/false/no/off` for
505
531
  off; empty / unset falls through to config. Invalid `level` values fall
506
532
  through to config.
507
533
 
534
+ ### Startup diagnostics
535
+
536
+ Once per process, right after the provider(s) register, the plugin logs a
537
+ single `NOTICE: claude-code plugin ready` line summarizing everything worth
538
+ knowing before you start debugging anything else:
539
+
540
+ ```bash
541
+ OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode
542
+ grep "plugin ready" ~/.local/share/opencode-claude-code/plugin.log
543
+ ```
544
+
545
+ ```json
546
+ {
547
+ "plugin": "0.10.0",
548
+ "opencode": "unknown",
549
+ "cwd": { "resolved": "/Users/you/code/app", "source": "process" },
550
+ "providers": ["claude-code-default", "claude-code-work"],
551
+ "accounts": ["default", "work"],
552
+ "proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task"],
553
+ "mcpServers": ["github", "slack"],
554
+ "interactiveTransport": false,
555
+ "anthropicApiKeyInEnv": false,
556
+ "claudeCli": { "path": "claude", "version": "2.1.211 (Claude Code)" }
557
+ }
558
+ ```
559
+
560
+ Reading it:
561
+
562
+ - **`cwd.source`** is which rule picked the working directory Claude will be
563
+ spawned in — `configured` (you pinned `options.cwd`), `process` (normal),
564
+ `captured` (`process.cwd()` was unusable and opencode's project directory
565
+ rescued it, the macOS GUI-launch case), or `unresolved` (neither worked).
566
+ - **`claudeCli.version`** reading `not detected` means the `claude` binary at
567
+ that path didn't answer `--version`, which also disables version-gated
568
+ flags like `--thinking-display`.
569
+ - **`mcpServers`** is the on-disk merge, before opencode's runtime toggles
570
+ are applied (those aren't settled yet at startup).
571
+ - **`opencode`** reads `unknown` on current opencode: as of 1.17.18 it does
572
+ not expose its own version to plugins.
573
+
508
574
  ### Default behavior (no config, no env)
509
575
 
510
576
  Nothing persists; only WARN and ERROR bubble in the TUI. The plugin
@@ -535,6 +601,7 @@ Workaround for autonomous compression: trigger it manually with `/dcp compress`
535
601
  - No streaming of tool inputs as they're being constructed (Anthropic's `input_json_delta`); the plugin emits them once complete.
536
602
  - Raw chain-of-thought is not available. Claude 4 family models ship summarized thinking only. See [Extended thinking](#extended-thinking) for the full picture.
537
603
  - Recommended Claude Code CLI: **2.1.142+**. Older CLIs work for everything else but skip the `--thinking-display` flag, so Claude Opus 4.7 turns may render empty Thinking rows. If something breaks after a Claude Code update, the CLI version is the first thing to check.
604
+ - **Foreground Task calls have a 30-minute proxy timeout.** The same timeout is written into Claude's generated HTTP MCP configuration so long-running opencode subagents are not cut off by Claude's 60-second default. For independent longer work, use `background: true` after enabling opencode's experimental background-subagent flag.
538
605
  - **Subagent todos require explicit permission.** opencode's task tool gates `todowrite` per subagent: without a `permission: { todowrite: "allow" }` rule on the subagent definition, opencode injects `todowrite: false` into the tools dict and the plugin's synthetic `todowrite` emissions surface as `⚙ invalid todowrite` rows. The built-in `general` subagent denies `todowrite` by default; use a custom subagent for parallel work that needs todo visibility. Subagent todos render inline in the **subagent's** session view (navigate with the TUI's `session.child.next` / `session.parent` commands), not in the parent session's panel.
539
606
 
540
607
  ---
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
+ proxyToolTimeoutMs?: Record<string, number>;
164
165
  webSearch?: WebSearchRouting;
165
166
  hotReloadMcp?: boolean;
166
167
  proxyOpencodeMcpTools?: boolean;
@@ -268,6 +269,23 @@ interface ClaudeCodeProviderSettings {
268
269
  * (see opencode's agent docs).
269
270
  */
270
271
  proxyTools?: string[];
272
+ /**
273
+ * Per-tool proxy call timeouts in milliseconds, keyed by the proxy tool
274
+ * name (`bash`, `edit`, `write`, `webfetch`, `task`, `question` —
275
+ * case-insensitive). When a proxied tool call waits longer than its
276
+ * deadline for opencode to resolve it, the call is rejected and Claude
277
+ * receives a timeout error.
278
+ *
279
+ * Defaults (used when a tool is absent here): `bash`/`edit`/`write`/
280
+ * `webfetch` → 10 min (matches Claude CLI's Bash ceiling); `task` →
281
+ * 60 min (subagents routinely run 20–40 min); `question` → 30 min
282
+ * (operator AFK). Setting a key here replaces the default for that tool.
283
+ *
284
+ * For `bash` specifically the call's own `input.timeout` is honoured on
285
+ * top: the effective deadline is `max(resolved, input.timeout)`, so a
286
+ * long build the caller explicitly asked to run is never undercut.
287
+ */
288
+ proxyToolTimeoutMs?: Record<string, number>;
271
289
  /**
272
290
  * Strip `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` from the environment of
273
291
  * every spawned `claude` process. When an API key is present, Claude Code
@@ -449,6 +467,11 @@ interface ClaudeStreamMessage {
449
467
  index?: number;
450
468
  }
451
469
 
470
+ interface DiagnosticsProviderEntry {
471
+ name?: string;
472
+ options?: Record<string, unknown>;
473
+ }
474
+
452
475
  declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
453
476
  readonly specificationVersion = "v3";
454
477
  readonly modelId: string;
@@ -580,9 +603,15 @@ declare function createClaudeCode(settings?: ClaudeCodeProviderSettings): Claude
580
603
  * so the config-path provider loader parses them correctly.
581
604
  */
582
605
  declare function configModelsForProvider(providerModels: OpenCodeProvider["models"], providerID: string, modelSuffix?: string): Record<string, Record<string, unknown>>;
606
+ /**
607
+ * Narrow opencode's full provider map down to the ones this plugin owns
608
+ * (`claude-code` plus every `claude-code-<account>` expansion) so startup
609
+ * diagnostics never report another provider's options.
610
+ */
611
+ declare function claudeCodeProviders(providers: Record<string, DiagnosticsProviderEntry> | undefined): Record<string, DiagnosticsProviderEntry>;
583
612
  declare const _default: {
584
613
  id: string;
585
614
  server: OpenCodePlugin;
586
615
  };
587
616
 
588
- export { type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, bridgeOpencodeMcp, configModelsForProvider, createClaudeCode, _default as default, defaultModels };
617
+ export { type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels };