@khalilgharbaoui/opencode-claude-code-plugin 0.18.3 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.18.3",
3
+ "version": "0.20.0",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "author": "Khalil Gharbaoui",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "build": "tsup",
22
22
  "dev": "tsup --watch",
23
23
  "typecheck": "tsc --noEmit",
24
- "test": "tsx --test test-bridge.ts test-broker.ts test-proxy-mcp.ts test-proxy-task.ts test-auto-continue.ts test-has-new-user-content.ts test-get-claude-user-message.ts test-logger.ts test-cli-args.ts test-session-manager.ts test-compaction-model.ts test-tool-mapping.ts test-cwd-resolution.ts test-todo-ledger.ts test-session-affinity.ts test-config-models.ts test-ask-user-question.ts test-claude-session-wrapper.ts test-spawn-env.ts test-respawn.ts test-startup-diagnostics.ts test-subagent-hint.ts test-exit-plan-mode-question.ts test-compress-tool.ts test-agent-models.ts test-side-question.ts test-btw-command.ts test-effort-sessions.ts test-tool-block-index.ts test-skill-bridge.ts test-configure-skill.ts"
24
+ "test": "tsx --test test-bridge.ts test-broker.ts test-proxy-mcp.ts test-proxy-task.ts test-auto-continue.ts test-has-new-user-content.ts test-get-claude-user-message.ts test-logger.ts test-cli-args.ts test-session-manager.ts test-compaction-model.ts test-tool-mapping.ts test-cwd-resolution.ts test-todo-ledger.ts test-session-affinity.ts test-config-models.ts test-ask-user-question.ts test-claude-session-wrapper.ts test-spawn-env.ts test-respawn.ts test-startup-diagnostics.ts test-subagent-hint.ts test-exit-plan-mode-question.ts test-compress-tool.ts test-agent-models.ts test-side-question.ts test-btw-command.ts test-effort-sessions.ts test-tool-block-index.ts test-skill-bridge.ts test-turn-stats.ts test-cli-events.ts test-cli-events-stream.ts test-doctor.ts test-configure-skill.ts test-unattended-replay.ts test-process-lifecycle.ts"
25
25
  },
26
26
  "dependencies": {
27
27
  "@ai-sdk/provider": "^3.0.8",
@@ -88,7 +88,7 @@ Defaults below describe normal headless opencode use when the key is absent.
88
88
  | `controlRequestDenyMessage` | string | built-in text | Override ordinary deny text. `AskUserQuestion` always uses its own stop-and-wait message. |
89
89
  | `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Case-insensitive replacement list, not additive and not a capability allowlist. Known entries expose `mcp__opencode_proxy__<name>`; omitted/unknown tools are not disabled. `Task` also brings `task_batch`; `[]` disables this list, not MCP proxying. See the proxy table for exceptions. |
90
90
  | `extraDisallowedTools` | string[] | unset | Claude built-ins to switch off outright with `--disallowedTools`, for tools that have no proxy (`["NotebookEdit"]`). Removes the capability rather than routing it. |
91
- | `proxyToolTimeoutMs` | object of proxy tool name to ms | unset | Positive deadlines, case-insensitive keys. Fallback 10 min (including dynamic MCP tools); `task` and `task_batch` 60 min each; `question` 30 min. Set both task keys to override both. Zero/negative values do not disable deadlines; values above 2147483647 are clamped. Bash `input.timeout` raises the resolved deadline, but executor/client ceilings still apply. `compress` is intercepted without a deadline. |
91
+ | `proxyToolTimeoutMs` | object of proxy tool name to ms | unset | Optional wall-clock backstop per tool, in ms, case-insensitive keys. A proxied call normally ends on an event the plugin listens for, not on a timer: opencode's result, an abort (the CLI is interrupted), the next user message (calls the previous turn left pending are rejected as orphaned), the `claude` process exiting, the chat being deleted, or opencode exiting. Fallback 10 min (including dynamic MCP tools); `task` and `task_batch` have no deadline, so a subagent runs to completion and a chat parked in one holds its worker until one of those events; `question` 30 min. Set both task keys to cover both. A positive value replaces the default, `0` removes that tool's deadline, negative or non-numeric values are ignored, and values above 2147483647 are clamped. Bash `input.timeout` raises the resolved deadline (and restores one after `bash: 0`); executor ceilings still apply. The generated MCP client timeout is the largest effective deadline, or the CLI's maximum while any tool has none. `compress` is intercepted without a deadline. |
92
92
  | `planModeQuestion` | boolean | `false` | Bridge `ExitPlanMode` approval to opencode's `question` and return a real CLI tool result. Requires a live question registry entry; otherwise keeps text fallback. Cannot fire on the headless transport: CLI 2.1.258 does not offer `ExitPlanMode` under `--print`, measured directly and through a full plugin probe, so the text path is what runs. Prose yes/no is not a verified CLI plan-mode unlock. |
93
93
  | `webSearch` | `"claude"` / `"disabled"` / `"<opencode tool name>"` | `"claude"` | Default: CLI search with the query rendered as text. Custom target forwards a tool call to an existing opencode tool accepting `query`; this is mapping, not the authenticated proxy replacement, so do not assume CLI search is suppressed. `"disabled"` disallows headless `WebSearch`. |
94
94
  | `bridgeOpencodeMcp` | boolean | `true` | Discover/translate disk MCP config plus runtime enabled status. False stops this bridge, not explicit `mcpConfig`, the built-in-tool proxy, or Claude's own MCP settings. Only bridge trusted servers. |
@@ -100,9 +100,10 @@ Defaults below describe normal headless opencode use when the key is absent.
100
100
  | `autoContinueIncompleteTurns` | boolean or `"smart"` | `"smart"` | `true`/`"smart"` continue a turn truncated at `max_tokens`, bounded by 8 attempts and 10 minutes, and otherwise run the keyword heuristic only when stop reason is missing. Every other stop reason, plus error, abort or latched question, stops it. Current measured CLIs always report a reason, so truncation is the only case that resumes in practice. |
101
101
  | `compactionModel` | string | `"claude-haiku-4-5"` | `/compact` uses a fresh short-lived headless process without the usual bridge/proxy/skill wiring. Nonblank `CLAUDE_CODE_COMPACTION_MODEL` wins. This is inference and can be billed. |
102
102
  | `ignoreAnthropicApiKey` | boolean | `false` | Strip `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` from headless/interactive spawn env, allowing stored auth to be used. Does not log in, change the parent env, or guarantee subscription billing if other CLI/cloud auth is configured. Warns at startup when either nonempty variable is present, regardless of the flag. |
103
- | `idleProcessTimeoutMs` | number | unset | Kill a conversation's idle `claude` worker this many ms after a finished turn. The session id is kept, so the next message resumes transparently. `0` or unset keeps workers until LRU eviction (16 processes). Values above `2147483647` are ignored. Not applied to the interactive transport. |
104
- | `bridgeOpencodeSkills` | boolean | `false` | Opt-in user skill staging for ordinary headless streams, as `opencode-skills:<name>`. Requires the CLI's `--help` to advertise `--plugin-dir`; otherwise no-op. Adds prompt overhead and exposes skill instructions to Claude. Bundled skill staging does not require this opt-in, but still requires flag support and successful discovery/staging. |
105
- | `interactive` | boolean | unset (headless) | Experimental PTY transport; explicit boolean wins over `CLAUDE_CODE_INTERACTIVE_TRANSPORT`. Needs `Bun.Terminal`; otherwise headless fallback. Compaction stays headless. Does not wire the headless proxy server/skill bridge/disallowed-tools controls; no equivalent opencode permission guarantee or `/btw`. Never enable to bypass a billing/access restriction. |
103
+ | `idleProcessTimeoutMs` | number | unset | Kill a conversation's idle `claude` worker this many ms after a finished turn. The timer starts when a turn completes, reuse cancels it, and a worker found mid-turn when it fires is re-timed rather than killed. The session id is kept, so the next message resumes transparently. Unset or `0` keeps workers until LRU eviction (16 processes, oldest idle first). Values above `2147483647` are ignored. Not applied to the interactive transport. Deleting a chat in opencode releases its workers and session ids immediately regardless. |
104
+ | `turnStats` | boolean | `false` | Append one `▌ **stats:**` line to each finished turn: cost, wall duration, CLI turn count, and input/output/cache-read/cache-write tokens, taken from the CLI's own `result`. Never on a compaction turn or a turn that ended in error. Its own text part, stripped from transcripts rebuilt for the CLI, so the model never sees it. The same numbers are logged at INFO regardless, and `modelUsage` plus `permission_denials` always reach `providerMetadata`. Reported cost is the CLI's figure, not a billing guarantee. |
105
+ | `bridgeOpencodeSkills` | boolean | `false` | Stage the user's opencode skills for Claude's native Skill tool as `opencode-skills:<name>`, on headless, interactive and direct `doGenerate` spawns (never compaction). Requires the CLI's `--help` to advertise `--plugin-dir`; otherwise no-op. Bridged skills are also listed in opencode's forwarded system prompt, so a large skill set costs prompt tokens twice, which is why it is off by default; `true` opts the user's skills in. Bundled skill staging ignores this option, but still requires flag support and successful discovery/staging. |
106
+ | `interactive` | boolean | unset (headless) | Experimental PTY transport; explicit boolean wins over `CLAUDE_CODE_INTERACTIVE_TRANSPORT`. Needs `Bun.Terminal`; otherwise headless fallback. Compaction stays headless. Does not wire the headless proxy server or disallowed-tools controls; no equivalent opencode permission guarantee or `/btw`. The skill bridge does apply. Never enable to bypass a billing/access restriction. |
106
107
  | `interactiveBypass` | boolean | `false` | Deprecated no-op. The TUI asks for a manual safety confirmation on `bypassPermissions`, so the plugin never passes it. |
107
108
  | `interactiveAllowTools` | string[] | `["Bash", "Edit", "Write", "Read", "WebFetch"]` | With `interactive`: replaces the built-in pre-allow list. MCP wildcards from discovered bridge names plus `mcp__opencode_proxy__*` are added even with `[]`. Not a capability denylist; review permissions before enabling. |
108
109
  | `interactiveSystemPrompt` | boolean | `true` | With `interactive`: append the plugin's own prompt. opencode's forwarded system prompt is deliberately not sent on this transport (it can trip Claude's third-party usage gate). `false` is for diagnostics only. |
@@ -260,25 +261,40 @@ Names below become `mcp__opencode_proxy__<name>`; input config is case-insensiti
260
261
  | `edit` | `"Edit"`, default; replaces CLI Edit. |
261
262
  | `write` | `"Write"`, default; replaces CLI Write. |
262
263
  | `webfetch` | `"WebFetch"`, default; replaces CLI WebFetch. |
263
- | `task` | `"Task"`, default; disables CLI Agent and dispatches opencode subagents under its permissions. |
264
+ | `task` | `"Task"`, default; disables CLI Agent and dispatches opencode subagents under its permissions. No proxy deadline by default; a positive `proxyToolTimeoutMs` entry adds one. |
264
265
  | `task_batch` | Included with Task; one MCP call fans out two or more independent task inputs concurrently. Separate task calls were measured serial on CLI 2.1.258. |
265
266
  | `question` | `"Question"`, opt-in; replaces AskUserQuestion only if the live opencode registry has question. Round-trip verified on plugin 0.18.0 / CLI 2.1.258 / opencode 1.18.29, headless and as a real TUI form, with no `permission` block; grant `permission.question` only if a subagent's form is refused. Opt-in because it disables Claude's own AskUserQuestion. |
266
267
  | `compress` | `"Compress"`, opt-in; in-process summary/reset interceptor, no opencode permission prompt and no built-in replacement. Discards prior CLI detail on a later eligible turn, retaining the summary, not the full transcript. Keep off unless explicitly requested; end-to-end reset remains unverified live. |
267
268
 
269
+ A proxied call is held open until an event ends it, and the plugin listens to the
270
+ `claude` process, the stream and the control protocol for those events rather than
271
+ inferring failure from elapsed time: opencode's result resolves the call; an abort
272
+ interrupts the CLI and rejects the turn's pending calls, even when it lands while
273
+ opencode is running the tool; the next user message rejects what the previous turn left pending
274
+ and tells the CLI; the process exiting, the chat being deleted, or opencode exiting
275
+ rejects the rest. That is why `task` and `task_batch` carry no default deadline and a
276
+ subagent runs to completion. Three timers remain and are distinct from that: the
277
+ optional per-tool deadlines above (a backstop the user chooses), the start and
278
+ inactivity watchdogs (for a process that is alive but silent, which emits nothing to
279
+ listen to; a CLI parked in a proxied call is exempt), and the connection keepalives
280
+ (SSE comments or JSON whitespace every 15 s, so the CLI's HTTP client does not give up
281
+ on a long call; they never extend a deadline). Do not present a raised deadline as the
282
+ fix for a long subagent; the default already waits for it.
283
+
268
284
  ### Let Claude load the user's opencode skills
269
285
 
270
286
  ```json
271
287
  { "bridgeOpencodeSkills": true }
272
288
  ```
273
289
 
274
- Use only after approval when `Skill("<name>")` fails for a trusted opencode skill.
275
- Headless bridged names are `opencode-skills:<name>`, including this bundled skill as
290
+ The bridge is off by default. With it on, `Skill("<name>")` works for any skill opencode
291
+ advertises. Bridged names are `opencode-skills:<name>`, including this bundled skill as
276
292
  `opencode-skills:claude-code-plugin`. The package also registers its skill directory
277
293
  with opencode's `skills.paths`; older opencode versions may not support that surface.
278
- The native Claude bridge needs `--plugin-dir` support and is wired into ordinary
279
- headless streaming calls, not interactive, compaction or direct `doGenerate` calls.
280
- The bundled skill does not require `bridgeOpencodeSkills: true`; that option adds
281
- the user's skills. Reusing a process does not load a new skill catalog.
294
+ The native Claude bridge needs `--plugin-dir` support and is wired into headless
295
+ streaming, interactive and direct `doGenerate` spawns, never compaction. Set `true`
296
+ only when the user asks for it, since a large skill set costs prompt tokens twice; the
297
+ bundled skill is staged either way. Reusing a process does not load a new skill catalog.
282
298
 
283
299
  User roots: `.opencode/skills` walking from cwd to filesystem root, home `.opencode/skills`,
284
300
  `OPENCODE_CONFIG_DIR/skills`, then `XDG_CONFIG_HOME/opencode/skills` (home `.config`
@@ -288,14 +304,16 @@ singular `skill/`, `~/.agents/skills` and `~/.claude/skills` are not scanned by
288
304
  bridge; Claude can already discover its own skills independently. Broad bridging can
289
305
  duplicate advertised skill context and exposes every discovered skill, not just one.
290
306
 
291
- ### Free idle workers
307
+ ### Change when idle workers are freed
292
308
 
293
309
  ```json
294
310
  { "idleProcessTimeoutMs": 900000 }
295
311
  ```
296
312
 
297
- Fifteen minutes after a turn ends with no new message, that conversation's `claude`
298
- process exits; the next message resumes the same conversation.
313
+ The default is thirty minutes: that long after a turn ends with no new message, the
314
+ conversation's `claude` process exits, and the next message resumes the same
315
+ conversation. This example shortens it to fifteen; `0` keeps workers until the
316
+ 8-process LRU cap evicts the oldest idle one. Neither ever kills a worker mid-turn.
299
317
 
300
318
  ### Different `/compact` model
301
319
 
@@ -398,6 +416,25 @@ the correct `127.0.0.1:<port>` Host, no Origin and JSON Content-Type should get
398
416
  `200` on a confirmed proxy endpoint is unsafe; restart/upgrade. Other status codes
399
417
  alone do not prove it patched. Never call `tools/call` or obtain the bearer to probe.
400
418
 
419
+ `/claude-code-doctor` prints the same fields as the startup block plus live runtime
420
+ state, in the chat, with no model inference and at zero tokens: plugin/opencode/CLI
421
+ versions, cwd and its resolution tier, providers, accounts, `proxyTools`, disk MCP
422
+ servers, transport, whether an `ANTHROPIC_API_KEY` is present (never its value), the
423
+ live `claude` processes (opencode session, model, pid, in flight, age, effort), pending
424
+ proxy calls with their deadlines, and one unauthenticated `initialize` against each
425
+ proxy URL (`401, good`; anything else is flagged unsafe). Prefer it over asking for
426
+ `plugin.log` for a first look. It carries no bearer token, no key value and no system
427
+ prompt. A user-defined `claude-code-doctor` command is never overwritten. The name has
428
+ no space in it: opencode would read the second word as an argument.
429
+
430
+ Claude Code stream events the plugin now surfaces without debug logging: a rate-limit
431
+ rejection, a context compaction the CLI did on its own, a `result` subtype other than
432
+ `success` (which now finishes the turn as an error, not a clean stop), and a failed
433
+ CLI-executed tool (forwarded with the error flag, so the row renders as failed). A
434
+ failed MCP server at session start and an `apiKeySource` that means API-key billing
435
+ each warn once per process. None of these are actions the plugin may take on the user's
436
+ behalf; enabling paid usage or changing auth still needs approval.
437
+
401
438
  `/btw <question>` needs an existing headless Claude conversation and CLI 2.1.258+.
402
439
  It asks through the side channel and keeps the answer in the conversation (inline
403
440
  when possible); it is excluded from Claude's normal turn history. It is still
@@ -411,7 +448,7 @@ commands are preserved. Do not use it as an automatic diagnostic probe.
411
448
  | A config change did nothing | Options are read at startup; another opencode window is still running the old process | Fully quit every opencode window and relaunch |
412
449
  | New plugin version or model not in the picker after upgrading | Frozen `@latest` in opencode's package cache | Remove the cache dir (recipe "Upgrade the plugin") and relaunch |
413
450
  | `/btw` shows "Queued" or "requires an idle Claude Code session" | Plugin older than 0.15.2, or a window started before the current build | Upgrade and restart. `/btw` also needs Claude Code 2.1.258+ |
414
- | Model calls `Skill("x")` and gets `Unknown skill` | Wrong namespace, unsupported flag/transport, unscanned root, or user bridging off | Check catalog/`--help`/transport; enable `bridgeOpencodeSkills` only with approval |
451
+ | Model calls `Skill("x")` and gets `Unknown skill` | Wrong namespace (`opencode-skills:x`), a CLI without `--plugin-dir`, an unscanned root, a compaction turn, or `bridgeOpencodeSkills: false` | Check the namespace, `claude --help` and the skill root; remove the `false` only with approval |
415
452
  | `Subagent failed (task_id …): Tool execution aborted` while the child finished fine | Bug fixed in 0.15.1 | Upgrade |
416
453
  | A `subtask: true` command's subagent output is "lost" | Bug fixed in 0.15.4 | Upgrade |
417
454
  | Two subagents run one after another | The CLI serialises MCP calls | Plugin 0.17.0+; the model must use `mcp__opencode_proxy__task_batch` |
@@ -426,6 +463,14 @@ commands are preserved. Do not use it as an automatic diagnostic probe.
426
463
  | `⚙ invalid` rows for `todowrite` inside a subagent | Subagent lacks `permission.todowrite: "allow"` | Grant it on the agent definition with approval |
427
464
  | Other `⚙ invalid` or `⚙ unknown` tool rows | A Claude tool the plugin does not map for this version | Note plugin version, CLI version and the tool name; upgrade or report |
428
465
  | `AGENTS.md` appears twice in Claude's system prompt | Plugin older than 0.16.0 | Upgrade |
466
+ | "What does the plugin actually think is going on?" | Startup diagnostics go to a log that is off by default | Run `/claude-code-doctor` in the session; paste that instead of the log |
467
+ | A turn ended with no answer and nothing said why | The CLI's `result` carried a failure subtype, or a rate limit was rejected | Both are now written into the transcript as `▌` lines; read the subtype or the limit reason there |
468
+ | A CLI tool row looks successful but its output is an error | Plugin older than this release forwarded `is_error` results as successes | Upgrade; failed CLI tools now render as failed |
469
+ | Claude "forgot" the earlier part of a long conversation | Claude Code compacted its own context | Look for the `▌ **context compacted:**` note in the transcript |
470
+ | Wanting the per-turn cost in the chat | Not shown by default | Set `turnStats: true` and restart opencode |
471
+ | Turn ends with an error naming an exit code or signal and a stderr tail | The `claude` child died mid-turn without emitting its terminal `result` | Read the quoted stderr; that is the CLI's own reason. Older builds reported this as a normal stop, so a truncated answer looked finished |
472
+ | An answer is cut off with no error, in a window with many open chats | Plugin older than this fix: LRU eviction could kill a process mid-turn | Upgrade. Eviction now takes the oldest idle process and skips the round when all 8 are busy; the 30-minute idle timer spares a busy worker too |
473
+ | A `claude` worker lingers after its chat was deleted, or after opencode quit | Plugin older than this release | Upgrade. Deleting a chat now releases its workers; every retained worker is killed when opencode exits |
429
474
 
430
475
  ## Do not
431
476