@khalilgharbaoui/opencode-claude-code-plugin 0.64.1 → 0.65.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 CHANGED
@@ -63,7 +63,7 @@ Nothing in the picker? [Troubleshooting](./docs/troubleshooting/symptoms.md) is
63
63
  | **Several accounts** | `"accounts": ["personal", "work"]` becomes one provider per account. Out of usage mid-task? One line says which account, which window and when it resets in your own time zone, and names the others to pick a model from. [Accounts](./docs/configuration/accounts.md) |
64
64
  | **Subagents: your account, their model** | `forceModel`, `reasoningEffort` and `cacheTtl` in an agent file, inheriting the caller's account. `task_batch` runs several subagents at once. [Subagents](./docs/configuration/subagents.md) |
65
65
  | **The whole model range, reasoning variants, fast mode** | Haiku 4.5 through Opus 5.5, Fable and Mythos, each with a `(N×)` list-price suffix, `low` to `max` effort variants, and a fallback chain for a model this account cannot run today. The badge above counts them. [Models](./docs/models.md) |
66
- | **`/btw` and `/claude-code-doctor`** | Side questions on the live process, and a health report whose `bundle` form is redacted by allowlist so it is safe to paste into a public issue. [`/btw`](./docs/guides/btw.md) · [Doctor](./docs/guides/doctor.md) |
66
+ | **`/btw` and `/claude-code-doctor`** | Side questions answered by the running Claude session itself, through Claude Code's own side channel, and a health report whose `bundle` form is redacted by allowlist so it is safe to paste into a public issue. [`/btw`](./docs/guides/btw.md) · [Doctor](./docs/guides/doctor.md) |
67
67
  | **Ships its own setup skill** | Ask Claude to configure it. The bundled `claude-code-plugin` skill knows every option, env var, model id and troubleshooting rule, is staged into Claude Code on every spawn, and tests keep it in step with the code. [Skills](./docs/configuration/skills.md) |
68
68
  | **Read-only preset, plan mode** | `"permissionPreset": "read-only"` holds at the CLI, the proxy and the permission layer at once. [Permissions](./docs/configuration/permissions.md) |
69
69
 
@@ -78,7 +78,7 @@ Nothing in the picker? [Troubleshooting](./docs/troubleshooting/symptoms.md) is
78
78
 
79
79
  The [full comparison](./docs/comparison.md) has nine rows, names the projects in the third column, and quotes their own READMEs.
80
80
 
81
- On opencode 2.2.3 or later with one Claude login, also look at opencode's own **Claude Agent** provider: it drives your own `claude` too, runs every tool in opencode, and leaves your `~/.claude` setup out. The comparison has [a section on it](./docs/comparison.md#opencodes-built-in-claude-agent-provider), including what this plugin still adds (opencode 1.x, several accounts, your Claude Code setup, fast mode, `/btw`, the sidebar).
81
+ On opencode 2.2.3 or later with one Claude login, also look at opencode's own **Claude Agent** provider: it drives your own `claude` too, runs every tool in opencode, and leaves your `~/.claude` setup out. The comparison has [a section on it](./docs/comparison.md#opencodes-built-in-claude-agent-provider), including what this plugin still adds (opencode 1.x, several accounts, your Claude Code setup, fast mode, a subagent picker for model, effort and account, the sidebar). opencode 2's own `/btw` and per-call subagent `model` work with either.
82
82
 
83
83
  ## Built from measurements
84
84
 
package/dist/index.d.ts CHANGED
@@ -113,6 +113,81 @@ interface V2ModelRequestEvent {
113
113
  interface V2ModelHookOptions {
114
114
  readonly providerID?: string;
115
115
  }
116
+ /**
117
+ * `@opencode/plugin` 2.2.2 `SessionRetry`, the `session.hook("retry")` event:
118
+ * opencode asks before it runs a failed turn again (h #g277).
119
+ */
120
+ interface V2SessionRetryEvent {
121
+ readonly sessionID: string;
122
+ readonly model: {
123
+ readonly id: string;
124
+ readonly providerID: string;
125
+ };
126
+ readonly error: {
127
+ readonly type?: string;
128
+ readonly message?: string;
129
+ };
130
+ readonly attempt: number;
131
+ decision: {
132
+ retry: false;
133
+ } | {
134
+ retry: true;
135
+ delay: number;
136
+ };
137
+ }
138
+ /** One way to connect an integration (`IntegrationMethod`); only `type` is always there. */
139
+ interface V2IntegrationMethod {
140
+ readonly type: string;
141
+ readonly id?: string;
142
+ readonly label?: string;
143
+ }
144
+ /** `@opencode/plugin` 2.2.2 `IntegrationEditor`, the calls we make. */
145
+ interface V2IntegrationEditor {
146
+ get(id: string): {
147
+ id: string;
148
+ name: string;
149
+ } | undefined;
150
+ update(id: string, update: (integration: {
151
+ id: string;
152
+ name: string;
153
+ }) => void): void;
154
+ readonly method: {
155
+ list(integrationID: string): readonly V2IntegrationMethod[];
156
+ update(input: {
157
+ integrationID: string;
158
+ method: {
159
+ id: string;
160
+ type: "external";
161
+ label: string;
162
+ };
163
+ }): void;
164
+ remove(integrationID: string, method: V2IntegrationMethod): void;
165
+ };
166
+ }
167
+ /** `@opencode/client` `ConnectionInfo`, passed back to opencode untouched. */
168
+ interface V2ConnectionInfo {
169
+ readonly type: string;
170
+ readonly id?: string;
171
+ readonly label?: string;
172
+ }
173
+ /** `@opencode/schema/connection` `Connection.Status`. */
174
+ interface V2ConnectionStatus {
175
+ status: "needs_auth";
176
+ message: string;
177
+ url?: string;
178
+ }
179
+ /** `@opencode/plugin` 2.2.2 `IntegrationDomain`, the slice `/connect` needs. */
180
+ interface V2IntegrationDomain {
181
+ transform(callback: (editor: V2IntegrationEditor) => void): Promise<V2Registration>;
182
+ readonly connection: {
183
+ active(integrationID: string): Promise<V2ConnectionInfo | undefined>;
184
+ status(input: {
185
+ integrationID: string;
186
+ connection: V2ConnectionInfo;
187
+ status: V2ConnectionStatus | undefined;
188
+ }): Promise<void>;
189
+ };
190
+ }
116
191
  /** `@opencode/schema/prompt-input` `Prompt`: we only rewrite `text`. */
117
192
  type V2Prompt = {
118
193
  text?: string;
@@ -158,6 +233,8 @@ interface V2Context {
158
233
  readonly options: Record<string, unknown>;
159
234
  readonly provider: {
160
235
  transform(callback: (editor: V2ProviderEditor) => void): Promise<V2Registration>;
236
+ /** Re-runs the transforms. In `@opencode/plugin` 2.2.5; optional because older hosts may lack it. */
237
+ reload?(): Promise<void>;
161
238
  };
162
239
  readonly aisdk: {
163
240
  hook(name: "sdk", callback: (event: V2AISDKSdkEvent) => Promise<void> | void, options?: V2ModelHookOptions): Promise<V2Registration>;
@@ -165,6 +242,7 @@ interface V2Context {
165
242
  };
166
243
  readonly session: {
167
244
  hook(name: "model.request", callback: (event: V2ModelRequestEvent) => Promise<void> | void, options?: V2ModelHookOptions): Promise<V2Registration>;
245
+ hook(name: "retry", callback: (event: V2SessionRetryEvent) => Promise<void> | void, options?: V2ModelHookOptions): Promise<V2Registration>;
168
246
  prompt?(input: V2Prompt & {
169
247
  sessionID: string;
170
248
  delivery?: V2Delivery;
@@ -180,6 +258,8 @@ interface V2Context {
180
258
  signal?: AbortSignal;
181
259
  }): AsyncIterable<V2Event>;
182
260
  };
261
+ /** `/connect`'s entry for each provider (h #g277). Optional: early 2.0 releases lack parts of it. */
262
+ readonly integration?: V2IntegrationDomain;
183
263
  /**
184
264
  * Absent before 2.0.31's measured shape: the definition is plain JSON
185
265
  * Schema, which `Rpc.PortableDefinition` accepts, and the handlers object is
@@ -444,7 +524,11 @@ interface ClaudeCodeConfig {
444
524
  controlRequestDenyMessage?: string;
445
525
  proxyTools?: string[];
446
526
  proxyOpencodeTools?: string[];
527
+ /** See `ClaudeCodeProviderSettings.alwaysLoadProxyTools`. Default bash, edit, write. */
528
+ alwaysLoadProxyTools?: boolean | string[];
447
529
  stripContextReminders?: boolean;
530
+ /** See `ClaudeCodeProviderSettings.verbatimPrompts`. Default true. */
531
+ verbatimPrompts?: boolean;
448
532
  extraDisallowedTools?: string[];
449
533
  /** See `ClaudeCodeProviderSettings.mapReadRules`. Default true. */
450
534
  mapReadRules?: boolean;
@@ -481,6 +565,8 @@ interface ClaudeCodeConfig {
481
565
  resumeAfterRestart?: boolean;
482
566
  /** Carry a conversation's Claude session over when its model or effort changes. Default true. */
483
567
  resumeAcrossModelChanges?: boolean;
568
+ /** Apply a model or effort change to the live `claude` process. Default true. */
569
+ liveModelSwitch?: boolean;
484
570
  /** Carry a conversation's Claude transcript over on an account switch. Default true. */
485
571
  crossAccountResume?: boolean;
486
572
  /** See `ClaudeCodeProviderSettings.maxBudgetUsd`. Resolved; unset is off. */
@@ -536,6 +622,26 @@ type AccountFailoverMode = "ask" | "off" | "auto";
536
622
  type SubagentDispatchMode = "ask" | "off";
537
623
  interface ClaudeCodeProviderSettings {
538
624
  cliPath?: string;
625
+ /**
626
+ * Register the model catalog as the installed Claude Code describes it.
627
+ * Default `true`.
628
+ *
629
+ * A background probe asks the CLI, once per CLI version, which models it
630
+ * knows, the context window it runs each in and the effort levels it
631
+ * accepts, and keeps the answer in `cli-models.json` under
632
+ * `$XDG_STATE_HOME`. Providers are registered from that file: windows and
633
+ * effort variants follow the CLI, and a model the CLI's own picker offers
634
+ * that the catalog lacks is added at an unknown (zero) price. Costs and the
635
+ * `(N×)` labels stay the catalog's, because the CLI carries no prices.
636
+ *
637
+ * Free: each probe is a `--bare` process with no credential, sent
638
+ * `initialize` and `get_context_usage` and never a message (measured: no
639
+ * API request, h #g279). Registration never waits for it, so a fresh answer
640
+ * applies at once on opencode 2 (a provider reload) and from the next
641
+ * start on opencode 1. `false` registers the built-in catalog as it ships
642
+ * and runs no probe.
643
+ */
644
+ cliModelCatalog?: boolean;
539
645
  /** Internal: set by the opencode 2 entrypoint. See `ClaudeCodeConfig.hostApi`. */
540
646
  hostApi?: "v1" | "v2";
541
647
  /** Wins over the legacy interactive option and its environment fallback. */
@@ -839,6 +945,39 @@ interface ClaudeCodeProviderSettings {
839
945
  * reminder is then something the model can actually act on.
840
946
  */
841
947
  stripContextReminders?: boolean;
948
+ /**
949
+ * Which proxy tools Claude Code loads into every request instead of
950
+ * deferring them behind its `ToolSearch` tool. Default `["bash", "edit",
951
+ * "write"]`.
952
+ *
953
+ * Claude Code defers every MCP tool, the proxy's included, so the first
954
+ * proxied call in a `claude` process costs one extra API request to find
955
+ * the tool. A listed tool is served with `_meta["anthropic/alwaysLoad"]`,
956
+ * which skips that request: measured on Claude Code 2.1.314, a turn that
957
+ * ran a proxied `bash` took 2 API calls instead of 3, with 32% fewer prompt
958
+ * tokens and 41% lower cost, while a turn that called no tool carried 531
959
+ * more prompt tokens. `true` loads every tool the proxy serves (the MCP
960
+ * tools `proxyOpencodeMcpTools` adds included), `false` defers them all as
961
+ * before. Names are the proxy's lowercase tool names. An older Claude Code
962
+ * ignores the field.
963
+ */
964
+ alwaysLoadProxyTools?: boolean | string[];
965
+ /**
966
+ * Deliver the text of every message as written. Default true.
967
+ *
968
+ * Claude Code reads the last text block of a headless user message the way
969
+ * it reads typing: a leading `/` runs one of its commands and an `@path`
970
+ * attaches that file. Measured on Claude Code 2.1.314, a message `/model
971
+ * claude-sonnet-5-5` switched the running process to Sonnet without a
972
+ * model call, and a message starting with a path such as `/etc/hosts` lost
973
+ * the environment, deferred-tool, agent and skill context of its turn. On,
974
+ * each envelope carries `client_composed: true`, the field the Agent SDK's
975
+ * `verbatimPrompts` sets, and the text reaches Claude unchanged; hooks,
976
+ * CLAUDE.md and the skill listing still arrive. The one thing lost is the
977
+ * `ultrathink` keyword's reminder: use an effort variant instead. Headless
978
+ * transport only; an older Claude Code ignores the field.
979
+ */
980
+ verbatimPrompts?: boolean;
842
981
  /**
843
982
  * Extra Claude Code built-ins to switch off with `--disallowedTools`,
844
983
  * on top of the ones implied by `proxyTools`.
@@ -1079,6 +1218,29 @@ interface ClaudeCodeProviderSettings {
1079
1218
  * false` turns this off too.
1080
1219
  */
1081
1220
  resumeAcrossModelChanges?: boolean;
1221
+ /**
1222
+ * Apply a model or reasoning effort change to the conversation's running
1223
+ * `claude` process, with the CLI's own `set_model` and `apply_flag_settings`
1224
+ * control requests, instead of stopping it and starting another with
1225
+ * `--resume`. Default `true`.
1226
+ *
1227
+ * Measured on Claude Code 2.1.288 and 2.1.314 (h #g278): the conversation is
1228
+ * kept, the next request goes out under the new model or effort, and the
1229
+ * prompt cache reads and writes are the same as the respawn's. What it saves
1230
+ * is the respawn itself, 4.6 to 6.6 seconds to `system`/`init` per change on
1231
+ * a real install, against about 25 ms for an effort change and 0.5 to 0.7 s
1232
+ * for a model change. To make an effort change possible, a headless spawn
1233
+ * carries its effort as `--effort` and drops CLAUDE_CODE_EFFORT_LEVEL: the
1234
+ * variable beats a live change silently.
1235
+ *
1236
+ * Only between two models on the same account and in the same fast mode,
1237
+ * between two explicit efforts, on an idle child (a change that lands
1238
+ * mid-turn still finishes that turn on the old settings, h #g271), and on
1239
+ * the headless transport: the interactive one has no control channel and
1240
+ * keeps respawning. Any refusal or timeout falls back to the respawn. Needs
1241
+ * `resumeAcrossModelChanges`; `false` here restores the respawn exactly.
1242
+ */
1243
+ liveModelSwitch?: boolean;
1082
1244
  /**
1083
1245
  * Continue the same Claude conversation when it moves to another account,
1084
1246
  * instead of replaying the whole thread as text into a fresh session there.