@khalilgharbaoui/opencode-claude-code-plugin 0.14.0 → 0.15.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
@@ -25,7 +25,7 @@ claude --version
25
25
 
26
26
  That's it. Restart opencode, pick a `claude-code` model, done.
27
27
 
28
- The plugin self-registers the `claude-code` provider, all current Claude Code models (Haiku 4.5, Sonnet 4.5/4.6, Opus 4.5/4.6/4.7/4.8, Fable 5, Mythos 5) with reasoning variants (`low` / `medium` / `high` / `xhigh` / `max`), and sensible defaults for tool proxying. You don't need to write a `provider` block at all unless you want to override something.
28
+ The plugin self-registers the `claude-code` provider, all current Claude Code models (Haiku 4.5, Sonnet 4.5/4.6/5, Opus 4.5/4.6/4.7/4.8/5, Fable 5/5.1, Mythos 5/5.1) with reasoning variants (`low` / `medium` / `high` / `xhigh` / `max`), and sensible defaults for tool proxying. You don't need to write a `provider` block at all unless you want to override something.
29
29
 
30
30
  ---
31
31
 
@@ -73,7 +73,7 @@ The plugin auto-registers the following. They appear in the model picker without
73
73
  | `claude-haiku-4-5` | Claude Haiku 4.5 | 200k | 64,000 | – | 1× |
74
74
  | `claude-sonnet-4-5` | Claude Sonnet 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 3× |
75
75
  | `claude-sonnet-4-6` | Claude Sonnet 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 3× |
76
- | `claude-sonnet-5` | Claude Sonnet 5 | 1M | 128,000 | low/medium/high/xhigh/max | 2×* |
76
+ | `claude-sonnet-5` | Claude Sonnet 5 | 1M | 128,000 | low/medium/high/xhigh/max | |
77
77
  | `claude-opus-4-5` | Claude Opus 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 5× |
78
78
  | `claude-opus-4-6` | Claude Opus 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
79
79
  | `claude-opus-4-7` | Claude Opus 4.7 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
@@ -82,13 +82,17 @@ The plugin auto-registers the following. They appear in the model picker without
82
82
  | `claude-opus-5` | Claude Opus 5 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
83
83
  | `claude-opus-5-fast` | Claude Opus 5 Fast | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
84
84
  | `claude-fable-5` | Claude Fable 5 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
85
+ | `claude-fable-5-1` | Claude Fable 5.1 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
85
86
  | `claude-mythos-5` | Claude Mythos 5 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
87
+ | `claude-mythos-5-1` | Claude Mythos 5.1 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
86
88
 
87
- `claude-mythos-5` is Mythos-class like Fable 5 but without safety classifiers, and is **limited availability via [Project Glasswing](https://anthropic.com/glasswing)**. It's registered unconditionally; if your Claude account lacks access, `claude --model claude-mythos-5` just errors. Use `claude-fable-5` (generally available) otherwise.
89
+ `claude-mythos-5` and `claude-mythos-5-1` are Mythos-class counterparts to the corresponding Fable models, but without safety classifiers, and are **limited availability via [Project Glasswing](https://anthropic.com/glasswing)**. They're registered unconditionally; if your Claude account lacks access, `claude --model` just errors. Use the corresponding generally available `claude-fable-5` or `claude-fable-5-1` otherwise.
88
90
 
89
91
  Capabilities for every model: text + image input, text output, tool use, attachments. No temperature control, no PDF/audio/video, no interleaved streaming.
90
92
 
91
- **Price ×** is each model's per-token list price relative to Haiku, the cheapest model. It's derived exactly from Anthropic's published pricing (input and output ratios both come out the same: Haiku $1/$5 = 1×, Sonnet $3/$15 = 3×, Opus $5/$25 = 5×, Fable 5 / Mythos 5 / Opus fast mode $10/$50 = 10×). So **Fable 5, Mythos 5, and fast-mode Opus all cost 2× standard Opus 5**. Sonnet 5's `2×` uses its introductory $2/$10 pricing through August 31, 2026; standard $3/$15 pricing begins September 1. The same multiplier is shown as a `(N×)` suffix on the display name in opencode's model picker, since opencode has no dedicated multiplier field. On a flat Max/Pro subscription it doubles as a rough guide to how fast each model drains your usage limit.
93
+ **Price ×** is each model's per-token list price relative to Haiku, the cheapest model. It's derived exactly from Anthropic's published pricing (input and output ratios both come out the same: Haiku $1/$5 = 1×, Sonnet $3/$15 = 3×, Opus $5/$25 = 5×, Fable/Mythos 5 and 5.1 / Opus fast mode $10/$50 = 10×). So **Fable/Mythos 5 and 5.1, and fast-mode Opus, all cost 2× standard Opus 5**. The same multiplier is shown as a `(N×)` suffix on the display name in opencode's model picker, since opencode has no dedicated multiplier field. On a flat Max/Pro subscription it doubles as a rough guide to how fast each model drains your usage limit.
94
+
95
+ Fable 5.1 and Mythos 5.1 keep the same $10/M input and $50/M output rates as 5.0, but cache reads cost $0.25/M instead of $1/M. Their cache-write rate remains $12.50/M.
92
96
 
93
97
  The model ID is passed straight through to `claude --model`, so anything Claude Code accepts works. The two `-fast` IDs are the one exception, described below.
94
98
 
@@ -172,6 +176,66 @@ CLAUDE_CONFIG_DIR="$HOME/.claude-work" claude auth login
172
176
 
173
177
  The account model IDs are internally suffixed, for example `claude-sonnet-4-6@work`, so long-lived Claude subprocess sessions do not collide across accounts. The generated wrapper strips the suffix before calling `claude --model`.
174
178
 
179
+ ### Subagents: your account, their model
180
+
181
+ opencode's agent config cannot express "inherit the account, choose the model". A subagent that omits `model` inherits the invoking agent's whole model string; one that pins `model` inherits neither half, so pinning Opus also pins whichever account was written into it. This plugin closes that gap, because it is the piece that knows the account is the *provider* while the model is only a `--model` flag.
182
+
183
+ Write an agent markdown file. Nothing goes in `opencode.json`.
184
+
185
+ ```markdown
186
+ ---
187
+ description: Designs and builds UI work
188
+ mode: subagent
189
+ ---
190
+ You are a designer...
191
+ ```
192
+
193
+ `@designer` now runs on **the account of the session that invoked it**, on whatever model you point it at. Which model comes from one of two places.
194
+
195
+ Per agent, in the agent's own file:
196
+
197
+ ```yaml
198
+ forceModel: claude-haiku-4-5
199
+ ```
200
+
201
+ Or once, for every subagent that pins nothing, in the provider options:
202
+
203
+ ```json
204
+ { "provider": { "claude-code": { "options": { "defaultSubagentModel": "claude-opus-5" } } } }
205
+ ```
206
+
207
+ The rules, in order:
208
+
209
+ | The agent | Runs on |
210
+ | --- | --- |
211
+ | `forceModel: <id>` | the caller's account, that model |
212
+ | `mode: subagent`, no model, `defaultSubagentModel` set | the caller's account, that model |
213
+ | `mode: subagent`, no model, no default set | untouched, inherits the caller's model |
214
+ | `model: <provider>/<id>` | exactly that, account and all (untouched) |
215
+ | anything opencode ships (`explore`, `general`, `compaction`) | untouched |
216
+
217
+ **`defaultSubagentModel` is unset by default and nothing is overridden without it.** That is deliberate: this feature rewrites what the model picker said would run, so an existing setup that upgrades the plugin has to behave exactly as it did before. Built-ins are excluded for the same reason, since forcing Opus onto a cheap exploration agent would be an expensive surprise nobody asked for. An unknown model id is refused and the original kept, rather than spawning the CLI with a `--model` it will reject.
218
+
219
+ Two things worth knowing. The overridden model is part of the Claude session key, so a subagent forced to Opus never shares a `claude` process with a Fable parent in the same directory. And opencode still prices the turn against the model *it* routed, so a cost readout attributes the work to the caller's model, not the one that actually ran.
220
+
221
+ ### The effort an agent runs at
222
+
223
+ The same file can state its own thinking budget:
224
+
225
+ ```yaml
226
+ reasoningEffort: high
227
+ ```
228
+
229
+ That beats whatever effort the call arrived with. It has to, because opencode resolves one effort for a session and a subagent inherits it, which is wrong in the expensive direction: a caller who picked `max` for their own turn otherwise hands `max` to every worker it dispatches, and a mechanical lane burns a weekly cap at the costliest setting available. Model and effort together are what a turn costs, so both belong with the agent rather than with whoever happened to dispatch it.
230
+
231
+ An agent that declares nothing keeps the inherited effort, so this changes nothing until a file asks for it. An unrecognised level is refused and the inherited one kept, since the CLI rejects a level it does not know. Compaction is exempt: its summary always gets the full budget.
232
+
233
+ To force an **account** rather than a model, pin the full string. Both halves are needed, because the provider selects the account's config dir and the `@account` marker is what the model was registered under for that provider:
234
+
235
+ ```yaml
236
+ model: claude-code-appical/claude-opus-5@appical
237
+ ```
238
+
175
239
  ### Options reference
176
240
 
177
241
  ```json
@@ -435,6 +499,10 @@ Every proxied tool call has a deadline: if opencode hasn't resolved it (run the
435
499
 
436
500
  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.
437
501
 
502
+ Starting with 0.15.0, clients advertising SSE receive immediate headers and keepalive comments every 15 seconds while a proxy call runs. This prevents long unanswered HTTP requests from being abandoned before the configured tool deadline; JSON-only clients retain their existing response format. Keepalives do not extend the tool deadline.
503
+
504
+ If Claude nevertheless abandons the HTTP call, the plugin preserves narration emitted while opencode was running the tool, renders it on return, and delivers the late completion as a plain-text continuation naming the original call. It tells Claude not to run the tool again. A silent post-tool continuation gets one resumed-process retry, preserving the original model, account, effort, and proxy configuration; a second failure ends with an error rather than an indefinite hang. Buffered narration is capped at 500 lines and 2 MiB, with a warning if output was dropped.
505
+
438
506
  ```json
439
507
  "options": {
440
508
  "proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task"],
@@ -444,6 +512,25 @@ The `task` and `question` defaults are deliberately generous. Subagents routinel
444
512
 
445
513
  ---
446
514
 
515
+ ## Side questions with /btw
516
+
517
+ After a normal Claude Code turn, use:
518
+
519
+ ```text
520
+ /btw Why did you choose that approach?
521
+ ```
522
+
523
+ The plugin registers the command without replacing an existing user-defined `btw` command. It calls Claude Code's native `side_question` control protocol on the current process, using the same model and account. The answer renders in the opencode conversation, but neither the question nor answer is sent as a normal Claude user turn or included in plugin-generated history and compaction transcripts.
524
+
525
+ - Requires Claude Code CLI **2.1.258 or newer**, the oldest verified version.
526
+ - Requires an existing, idle **headless** session with the same model and effort. Send a normal message first if the process has not started or was evicted. Interactive transport is not supported.
527
+ - This is not a concurrent TUI overlay: opencode may queue the command while a turn runs, and the plugin refuses it while a tool or another aside is outstanding.
528
+ - Each aside sees the main conversation, not previous aside exchanges. Include the relevant detail explicitly when asking a follow-up.
529
+ - The control response has no token/cost usage fields. Aside usage is not reported in opencode's counters; this does not mean the request is free.
530
+ - A request times out after two minutes. Abort and timeout cancel that side request without killing the main session.
531
+
532
+ Fully restart opencode after upgrading to load the command and runtime changes. Other providers do not gain Claude's native side-question behavior from this command.
533
+
447
534
  ## WebSearch routing
448
535
 
449
536
  Claude Code ships a built-in `WebSearch` tool. The `webSearch` option controls who actually executes those calls:
@@ -614,16 +701,19 @@ The plugin forwards Claude's thinking blocks (`thinking_delta` stream events) to
614
701
 
615
702
  What you see is a **summary** of the model's thinking, not the raw chain-of-thought. Anthropic [stopped exposing raw thinking on the Claude 4 family](https://platform.claude.com/docs/en/build-with-claude/extended-thinking#summarized-thinking) and ships a server-generated digest instead. For Claude Opus 4.7 specifically, [thinking content is omitted from responses by default](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7#thinking-content-omitted-by-default); the plugin opts back in by passing `--thinking-display summarized` on every spawn. Claude Code CLI 2.1.142+ is required for that flag to take effect; older CLIs skip it silently.
616
703
 
617
- ### Reasoning effort variants
704
+ ### Reasoning effort
705
+
706
+ Each model exposes `low` / `medium` / `high` / `xhigh` / `max` variants, and an agent can set `reasoningEffort` in its own frontmatter (`minimal` is also accepted and maps to the CLI's `low`). The plugin hands the level to the CLI as `CLAUDE_CODE_EFFORT_LEVEL` at spawn, which Claude Code treats as the session-wide override: it beats the `effortLevel` in that account's `settings.json` and a shell export of the same variable. Effort is fixed for the life of a `claude` process, so it is part of the session key. Changing effort retires the previous effort's process and remembered transcript ID before replaying the conversation into a fresh process. Switching back cannot resume stale context; same-effort streaming turns still reuse their process. This reset is scoped to the same directory, model, provider/account, agent, and conversation. If the previous effort still has pending work (including tool results, plan approval, recovery, or `/btw`), the switch is rejected: finish that work at its original effort first. Title, compaction, and `/btw` calls do not trigger effort resets.
618
707
 
619
- Each model exposes `low` / `medium` / `high` / `xhigh` / `max` variants. Picking one injects the corresponding Claude CLI thinking keyword (e.g. `(ultrathink)` for `max`) into the user message. Compaction calls skip this injection so the full output budget goes to the summary.
708
+ Earlier versions injected a thinking keyword such as `(ultrathink)` into the user message instead. Claude Code stopped recognising every keyword except `ultrathink`, so that path is gone and nothing is appended to your messages any more. Compaction skips request and agent effort overrides, but still inherits a shell-level `CLAUDE_CODE_EFFORT_LEVEL` when set.
620
709
 
621
710
  ### Env-var overrides
622
711
 
623
- The plugin respects the standard Claude Code thinking env vars. If you set them in your shell, they pass through to the spawned process untouched.
712
+ The plugin respects the standard Claude Code thinking env vars. If you set them in your shell, they pass through to the spawned process untouched, with the one exception in the first row.
624
713
 
625
714
  | Env var | Effect |
626
715
  |---|---|
716
+ | `CLAUDE_CODE_EFFORT_LEVEL=<level>` | Session effort override. Passes through when no effort was requested; a variant or an agent's `reasoningEffort` replaces it for that spawn. |
627
717
  | `CLAUDE_CODE_DISABLE_THINKING=1` | Disable thinking entirely. |
628
718
  | `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` | Disable adaptive thinking only. |
629
719
  | `CLAUDE_CODE_SHOW_THINKING_SUMMARIES=0` | Suppress summaries (the plugin sets this to `1` by default when unset). |
@@ -819,9 +909,9 @@ The GitHub Actions workflow at `.github/workflows/publish.yml` runs `npm publish
819
909
 
820
910
  <a href="https://www.star-history.com/?repos=khalilgharbaoui%2Fopencode-claude-code-plugin&type=date&legend=top-left">
821
911
  <picture>
822
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&theme=dark&legend=top-left" />
823
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left" />
824
- <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left" />
912
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&theme=dark&legend=top-left&sealed_token=XBPNnYotm7Eti4lpRGsbKl_dsq6XGUtRkvCxE4UpQH2HM4LifiiTNV1hqjCOsivRZ-e2hFDohid8iERSP5XO5JdkNhHcuS2bLZFIdQIWZO1NldJLD2TjaaSYK6GJcnXYZHivkbiiynG7b8-V8z9LLn8Uo2ED15OWnUd3devehrMyKJJO_dtOW1ivZ3yJ" />
913
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left&sealed_token=XBPNnYotm7Eti4lpRGsbKl_dsq6XGUtRkvCxE4UpQH2HM4LifiiTNV1hqjCOsivRZ-e2hFDohid8iERSP5XO5JdkNhHcuS2bLZFIdQIWZO1NldJLD2TjaaSYK6GJcnXYZHivkbiiynG7b8-V8z9LLn8Uo2ED15OWnUd3devehrMyKJJO_dtOW1ivZ3yJ" />
914
+ <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=khalilgharbaoui/opencode-claude-code-plugin&type=date&legend=top-left&sealed_token=XBPNnYotm7Eti4lpRGsbKl_dsq6XGUtRkvCxE4UpQH2HM4LifiiTNV1hqjCOsivRZ-e2hFDohid8iERSP5XO5JdkNhHcuS2bLZFIdQIWZO1NldJLD2TjaaSYK6GJcnXYZHivkbiiynG7b8-V8z9LLn8Uo2ED15OWnUd3devehrMyKJJO_dtOW1ivZ3yJ" />
825
915
  </picture>
826
916
  </a>
827
917
 
package/dist/index.d.ts CHANGED
@@ -62,6 +62,14 @@ type OpenCodeProvider = {
62
62
  models: Record<string, OpenCodeModel>;
63
63
  };
64
64
  type OpenCodeConfig = {
65
+ command?: Record<string, {
66
+ template: string;
67
+ description?: string;
68
+ agent?: string;
69
+ model?: string;
70
+ variant?: string;
71
+ subtask?: boolean;
72
+ }>;
65
73
  provider?: Record<string, {
66
74
  name?: string;
67
75
  npm?: string;
@@ -69,6 +77,7 @@ type OpenCodeConfig = {
69
77
  options?: Record<string, unknown>;
70
78
  models?: Record<string, unknown>;
71
79
  }>;
80
+ agent?: Record<string, Record<string, unknown>>;
72
81
  };
73
82
  /**
74
83
  * Bus events surface to plugins. Shape mirrors what opencode core publishes
@@ -227,6 +236,12 @@ interface ClaudeCodeProviderSettings {
227
236
  account?: string;
228
237
  configDir?: string;
229
238
  accounts?: string[];
239
+ /**
240
+ * Model that subagents run on when their own definition pins nothing.
241
+ * Unset means no implicit override at all, so an agent keeps inheriting the
242
+ * caller's model exactly as opencode intends. See `src/agent-models.ts`.
243
+ */
244
+ defaultSubagentModel?: string;
230
245
  skipPermissions?: boolean;
231
246
  permissionMode?: PermissionMode;
232
247
  mcpConfig?: string | string[];
@@ -665,6 +680,38 @@ type RuntimeMcpStatus = Record<string, string>;
665
680
  */
666
681
  declare function bridgeOpencodeMcp(cwd: string, runtimeStatus?: RuntimeMcpStatus, excludeServers?: ReadonlySet<string>): BridgedMcp | null;
667
682
 
683
+ type AgentRecord = {
684
+ mode?: string;
685
+ /** A fully-qualified `provider/model` the agent pinned for itself. */
686
+ model?: string;
687
+ /** Model NAME this agent wants, on whatever account the caller is using. */
688
+ forceModel?: string;
689
+ /** Thinking budget this agent wants, whatever the caller's picker says. */
690
+ reasoningEffort?: string;
691
+ };
692
+ declare function getAgentRegistry(): Record<string, AgentRecord>;
693
+ declare function getDefaultSubagentModel(): string | undefined;
694
+ /**
695
+ * The model a request should actually spawn with.
696
+ *
697
+ * Order, first match wins:
698
+ * 1. The agent declared `forceModel`.
699
+ * 2. The agent is a discovered subagent and `defaultSubagentModel` is set.
700
+ * 3. Anything else: the id opencode asked for, untouched.
701
+ *
702
+ * An agent that pinned a full `provider/model` is out of scope entirely:
703
+ * opencode already routed the call to that provider, and second-guessing it
704
+ * here would silently undo a choice the user made explicitly.
705
+ *
706
+ * Fails closed. An id that is not in the model registry is refused and the
707
+ * original kept, because the alternative is spawning the CLI with a `--model`
708
+ * it will reject, on a turn someone is waiting for.
709
+ */
710
+ declare function resolveAgentModel(agent: string | undefined, modelId: string, overrides?: {
711
+ records?: Record<string, AgentRecord>;
712
+ defaultSubagentModel?: string;
713
+ }): string;
714
+
668
715
  declare const defaultModels: Record<string, OpenCodeModel>;
669
716
 
670
717
  interface ClaudeCodeProvider {
@@ -673,6 +720,7 @@ interface ClaudeCodeProvider {
673
720
  languageModel(modelId: string): LanguageModelV3;
674
721
  }
675
722
  declare const DEFAULT_PROXY_TOOL_NAMES: string[];
723
+ declare function registerSideQuestionCommand(config: OpenCodeConfig): void;
676
724
  declare function createClaudeCode(settings?: ClaudeCodeProviderSettings): ClaudeCodeProvider;
677
725
  /**
678
726
  * Build models in OpenCode's config schema format (flat properties like
@@ -691,4 +739,4 @@ declare const _default: {
691
739
  server: OpenCodePlugin;
692
740
  };
693
741
 
694
- export { type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels };
742
+ export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, getAgentRegistry, getDefaultSubagentModel, registerSideQuestionCommand, resolveAgentModel };