@khalilgharbaoui/opencode-claude-code-plugin 0.29.2 → 0.31.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.29.2",
3
+ "version": "0.31.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": "OPENCODE_CLAUDE_CODE_LOG_FILE=0 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-permission-presets.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-result-fallback.ts test-doctor.ts test-configure-skill.ts test-unattended-replay.ts test-process-lifecycle.ts test-account-failover.ts test-host-tools.ts test-v2-entrypoint.ts test-v2-client.ts test-tmp-dir.ts test-cleanup-stale.ts test-account-wrapper.ts test-runtime-status-sessions.ts test-index-hooks.ts test-silent-turn.ts test-mcp-tool-result-name.ts test-model-fallback.ts test-do-generate.ts"
24
+ "test": "OPENCODE_CLAUDE_CODE_LOG_FILE=0 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-permission-presets.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-context-usage.ts test-skill-bridge.ts test-turn-stats.ts test-cli-events.ts test-cli-events-stream.ts test-result-fallback.ts test-doctor.ts test-configure-skill.ts test-unattended-replay.ts test-process-lifecycle.ts test-account-failover.ts test-host-tools.ts test-v2-entrypoint.ts test-v2-client.ts test-tmp-dir.ts test-cleanup-stale.ts test-account-wrapper.ts test-runtime-status-sessions.ts test-index-hooks.ts test-silent-turn.ts test-mcp-tool-result-name.ts test-model-fallback.ts test-do-generate.ts"
25
25
  },
26
26
  "dependencies": {
27
27
  "@ai-sdk/provider": "^3.0.8",
@@ -110,6 +110,7 @@ Defaults below describe normal headless opencode use when the key is absent.
110
110
  | `failoverAccounts` | string[] | unset/derived | Account expansion supplies the resolved account list so a limited account can offer the others. Do not hand-wire it; set `accounts` instead. |
111
111
  | `baseCliPath` | string | unset/derived | The `cliPath` before the per-account wrapper substitution, so a failover can build another account's wrapper on the same binary. Supplied by the config hook. Do not hand-wire it. |
112
112
  | `defaultSubagentModel` | string | unset | Seed-config default for discovered `mode: subagent` agents without a full `provider/model` pin; `forceModel` takes precedence. Keeps the caller's account. Unknown ids warn and keep the inherited model. Not independently read per expanded account. |
113
+ | `defaultSubagentCacheTtl` | string | unset | Prompt cache TTL (`5m` / `1h`) for discovered `mode: subagent` agents that declare no `cacheTtl`; the agent's own value takes precedence. Unset leaves the CLI's default (1 hour on a subscription). Unknown values warn and change nothing. Headless spawns only (not compaction, not the interactive transport). |
113
114
  | `fallbackModels` | string[] | unset | Ordered models to try when the model a turn would run on is refused. Default for agents declaring no `fallbackModels`; a per-agent list replaces it rather than extending it. Same account throughout, never a switch. Armed only by the CLI refusing the model (`model_not_found`) or by a usage limit when `accountFailover` has no other account to offer; with another account the switch form wins. Entries must be registered model ids, unknown ones warn and are skipped, the current model is dropped from its own chain, each entry is tried at most once per turn, and an exhausted chain surfaces the original error. Never on compaction, title stubs or the interactive transport. Writes a `▌ **model fallback:**` note that transcript rebuilds strip. Not independently read per expanded account. |
114
115
  | `cwd` | string | automatic | Pin an absolute existing directory. Otherwise: session directory from SDK, usable `process.cwd()`, captured project directory, final `process.cwd()` fallback. Startup diagnostics cannot show the per-call session tier. |
115
116
  | `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to headless Claude, even with proxies enabled. Proxied calls still use opencode permissions, but unproxied CLI tools do not. `false` removes the bypass flag; it does not by itself create human approval prompts. Ignored when `permissionMode` is `"plan"`, which always drops the flag. |
@@ -292,13 +293,17 @@ description: Designs and builds UI work
292
293
  mode: subagent
293
294
  forceModel: claude-haiku-4-5
294
295
  reasoningEffort: high
296
+ cacheTtl: 5m
295
297
  ---
296
298
  ```
297
299
 
298
300
  Or once for every discovered subagent without a full provider/model pin:
299
301
 
300
302
  ```json
301
- { "provider": { "claude-code": { "options": { "defaultSubagentModel": "claude-opus-5" } } } }
303
+ { "provider": { "claude-code": { "options": {
304
+ "defaultSubagentModel": "claude-opus-5",
305
+ "defaultSubagentCacheTtl": "5m"
306
+ } } } }
302
307
  ```
303
308
 
304
309
  Rules, in order: `forceModel` wins; else `mode: subagent` with `defaultSubagentModel`
@@ -307,8 +312,19 @@ account and all (`model: claude-code-work/claude-opus-5@work` pins the account t
307
312
  Undeclared built-ins are not discovered; a user definition with a built-in name can
308
313
  enter the registry and is subject to these rules. This is not a built-in-name denylist.
309
314
  `reasoningEffort` in the agent file beats the effort the call arrived with; compaction is
310
- exempt. Effort and model are part of the CLI session key, so a changed agent respawns
311
- rather than sharing a process.
315
+ exempt. Effort, model and cache TTL are part of the CLI session key, so a changed agent
316
+ respawns rather than sharing a process.
317
+
318
+ `cacheTtl` sets the prompt cache TTL of that agent's own `claude` process, as
319
+ `CLAUDE_CODE_PROMPT_CACHE_TTL` at spawn. Unset (the default) leaves the CLI deciding,
320
+ which is 1 hour on a subscription. Claude Code also has a per-agent
321
+ `experimental.cacheTtl` and a `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL`: neither does
322
+ anything here, because both apply only to subagents the CLI runs through its own `Task`
323
+ tool, and this plugin disallows that tool by default so opencode runs the subagent
324
+ instead. An opencode subagent is a separate `claude --print` process, which the CLI
325
+ counts as a main conversation. Use `cacheTtl: 5m` on short-lived workers that never
326
+ re-read the cache they wrote, since a 1-hour write is billed above a 5-minute one and
327
+ both come out of the same usage limit; leave a long-lived main session at the default.
312
328
 
313
329
  Only grant `permission.task` for approved target agents if delegation is wanted.
314
330
  `permission.todowrite: "allow"` is needed for subagent todos; opencode otherwise denies
@@ -324,6 +340,7 @@ the opencode schema requires it. Markdown fallback reads top-level scalar fields
324
340
  | `model` | Full `provider/model` pins bypass plugin model overrides, not the separate effort override. |
325
341
  | `forceModel` | Registered bare model id, preserving the caller's account even if an account suffix is supplied. Works for any discovered agent mode. |
326
342
  | `reasoningEffort` | `minimal`, `low`, `medium`, `high`, `xhigh`, `max`; invalid declarations warn and keep inherited effort. `minimal` maps to CLI `low`. Compaction skips this override. |
343
+ | `cacheTtl` | `5m` or `1h`; anything else warns and leaves the CLI's default alone. Exported as `CLAUDE_CODE_PROMPT_CACHE_TTL`, beating a shell export of the same name. Works for any discovered agent mode; compaction skips it. |
327
344
  | `fallbackModels` | Ordered registered bare model ids to try when this agent's model is refused. Both YAML spellings (`[a, b]` or a `- ` block). Replaces the provider-level `fallbackModels` rather than extending it. Keeps the caller's account; an entry carrying `@account` has it stripped. Unknown ids warn and are skipped. |
328
345
 
329
346
  ### Degrade to another model instead of failing
@@ -373,6 +390,17 @@ The proxy's loopback endpoint has bearer, Host, Origin and Content-Type guards.
373
390
  Never weaken them, publish its token or relax the generated MCP file's `0600` mode.
374
391
  Restart all old processes after a security upgrade; changing files cannot patch them.
375
392
 
393
+ Proxying a tool costs **one extra Claude Code API call per `claude` process**: a proxied
394
+ tool is an MCP tool, Claude Code 2.1.280 defers MCP tools behind `ToolSearch`, so the
395
+ model spends a request finding it before the first proxied call. Built-ins are never
396
+ deferred. Measured on 2.1.280 (`docs/agents-history.md`, `#g166`): 3 API calls with
397
+ `Bash` proxied against 2 without, and it is paid once per process, not per call (two
398
+ commands measured 4 against 3, one `ToolSearch` either way). It is not caused by a large
399
+ tool list. Never suggest `ENABLE_TOOL_SEARCH=0` to avoid it: that inlines every tool
400
+ definition into each prompt and measured 2.5 to 4 times the cost. Say the extra call is
401
+ inherent to the CLI's MCP flow and weigh it against the permission prompts and audit log
402
+ proxying buys.
403
+
376
404
  ### Make a provider read-only
377
405
 
378
406
  ```json
@@ -547,15 +575,15 @@ No manual skill copy/update is needed. Do not publish or release as part of conf
547
575
  ### Registered model ids
548
576
 
549
577
  Registered ids: `claude-haiku-4-5`, `claude-sonnet-4-5`, `claude-sonnet-4-6`,
550
- `claude-sonnet-5`, `claude-opus-4-5`, `claude-opus-4-6`, `claude-opus-4-7`,
551
- `claude-opus-4-8`, `claude-opus-4-8-fast`, `claude-opus-5`, `claude-opus-5-fast`,
552
- `claude-opus-5-5`, `claude-opus-5-5-fast`, `claude-fable-5`, `claude-fable-5-1`,
553
- `claude-mythos-5`, `claude-mythos-5-1`.
578
+ `claude-sonnet-5`, `claude-sonnet-5-5`, `claude-opus-4-5`, `claude-opus-4-6`,
579
+ `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-4-8-fast`, `claude-opus-5`,
580
+ `claude-opus-5-fast`, `claude-opus-5-5`, `claude-opus-5-5-fast`, `claude-fable-5`,
581
+ `claude-fable-5-1`, `claude-mythos-5`, `claude-mythos-5-1`.
554
582
 
555
583
  ### Variants and costs
556
584
 
557
585
  - Display names end in a `(N×)` list-price multiplier relative to Haiku: 1× haiku,
558
- 3× sonnet, 4× opus 5.5, 5× other opus, 8× fast-mode opus 5.5, 10× fable, mythos
586
+ 2× sonnet 5 and 5.5, 3× sonnet 4.5/4.6, 4× opus 5.5, 5× other opus, 8× fast-mode opus 5.5, 10× fable, mythos
559
587
  and fast-mode opus 5 / 4.8. It is display only.
560
588
  - Every model except Haiku has reasoning variants `low`, `medium`, `high`, `xhigh`,
561
589
  `max`, picked in opencode's model selector. A variant becomes
@@ -568,6 +596,9 @@ Registered ids: `claude-haiku-4-5`, `claude-sonnet-4-5`, `claude-sonnet-4-6`,
568
596
  reason. Switch to a non-fast id rather than silently enabling paid usage credits.
569
597
  Review eligibility/billing with the user; the enabled state needs live verification
570
598
  on their account. CLI floors are gates, not proof of model access.
599
+ - `claude-sonnet-5-5` needs Claude Code 2.1.284+ to run on its real limits. An older CLI
600
+ still serves it on fallback limits (200k context, an estimated cost), and the plugin
601
+ logs a WARN naming the model and the floor: the fix is `claude update`.
571
602
  - `claude-mythos-5` and `claude-mythos-5-1` are limited availability (Project Glasswing).
572
603
  Without access `claude --model` errors; use the corresponding `claude-fable-*`.
573
604
  - Ordinary calls can pass through unregistered ids; availability and opencode model
@@ -632,13 +663,29 @@ proxy URL (`401, good`; anything else is flagged unsafe). Prefer it over asking
632
663
  prompt. A user-defined `claude-code-doctor` command is never overwritten. The name has
633
664
  no space in it: opencode would read the second word as an argument.
634
665
 
666
+ It also prints an **MCP config entries Claude Code skipped** section, but only when the
667
+ CLI refused an entry in an `--mcp-config` it was given. Read it whenever MCP tools are
668
+ missing: a skipped server is absent from the CLI's server list rather than listed
669
+ broken, so nothing else hints at it. A skipped `opencode_proxy` is the plugin's own
670
+ server, not the user's config, and means every proxied tool call in the session fails.
671
+
672
+ `/claude-code-doctor usage` adds a **Plan usage** section: the CLI's own `/cost` answer
673
+ (subscription vs API key, 5-hour and 7-day window use, reset times, what is driving
674
+ them). Measured free on 2.1.280 (`num_turns: 0`, `$0`, no API call), so suggest it for
675
+ "how much have I used" and limit questions. It is opt-in only because it starts a
676
+ short-lived `claude`, which runs the user's `SessionStart` hooks and takes a few
677
+ seconds; say that when suggesting it. The plain command stays instant and says how to
678
+ ask. Do not propose `--bare` to skip the hooks: it never reads OAuth, so it reports
679
+ nothing about a subscription.
680
+
635
681
  Claude Code stream events the plugin now surfaces without debug logging: a rate-limit
636
682
  rejection, a context compaction the CLI did on its own, a `result` subtype other than
637
683
  `success` (which now finishes the turn as an error, not a clean stop), and a failed
638
684
  CLI-executed tool (forwarded with the error flag, so the row renders as failed). A
639
- failed MCP server at session start and an `apiKeySource` that means API-key billing
640
- each warn once per process. None of these are actions the plugin may take on the user's
641
- behalf; enabling paid usage or changing auth still needs approval.
685
+ failed MCP server at session start, an `--mcp-config` entry the CLI skipped, and an
686
+ `apiKeySource` that means API-key billing each warn once per identity per process.
687
+ None of these are actions the plugin may take on the user's behalf; enabling paid
688
+ usage or changing auth still needs approval.
642
689
 
643
690
  `/btw <question>` needs an existing headless Claude conversation and CLI 2.1.258+.
644
691
  It asks through the side channel and keeps the answer in the conversation (inline
@@ -696,6 +743,7 @@ Prefer the doctor: it needs no logging change and no restart.
696
743
  | Claude "forgot" the earlier part of a long conversation | Claude Code compacted its own context | Look for the `▌ **context compacted:**` note in the transcript |
697
744
  | Claude forgot the whole conversation at once | Claude Code cleared it (`/clear` sent as a message, or a plan-mode exit that clears context) | Look for the `▌ **claude code reset:**` note. The plugin does not replay history there on purpose; a new opencode session gets a clean slate |
698
745
  | Wanting the per-turn cost in the chat | Not shown by default | Set `turnStats: true` and restart opencode |
746
+ | opencode auto-compacts a Claude session far below the model's window, often several times in a row after tool-heavy turns | Plugin older than this fix reported the CLI's turn-summed usage (every API call's cache reads added up) as the context size | Upgrade and relaunch. opencode's per-message tokens are now the last call's context, so its cost figure for a multi-call turn is lower than the real one; the real cost is in `turnStats` and `providerMetadata["claude-code"].costUsd` |
699
747
  | 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 |
700
748
  | 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 |
701
749
  | 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 |