@khalilgharbaoui/opencode-claude-code-plugin 0.33.1 → 0.34.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
@@ -21,7 +21,7 @@ Three ways to reach Claude from opencode. They differ in who authenticates, who
21
21
  | **Authentication** | An Anthropic Platform API key, held in opencode's own auth store. | Whatever the official `claude` CLI already holds: a subscription login, an API key, Bedrock, or Vertex. The plugin never reads, stores, or replays a token of its own, and there is no subscription token here to lift. | The Claude OAuth session, used outside the official client. Meridian runs a local proxy that maps Anthropic-style HTTP onto the Claude Agent SDK and your Claude session; `opencode-claude-auth` reads the OAuth tokens out of the macOS Keychain or `~/.claude/.credentials.json` and refreshes them against Anthropic's OAuth endpoint itself. |
22
22
  | **What is billed, and to whom** | Pay as you go on the Platform account that owns the key. | Whatever the CLI's own authentication bills. Headless `--print` is the Agent SDK path; an API key found anywhere the CLI looks switches the same turn onto Console pay-as-you-go instead. `apiKeySource` on the CLI's `system` init event is the field that says which, and the plugin warns once per process when a key is in effect. On a subscription, headless and interactive turns both draw from the plan's ordinary usage limits. See [which login bills what](#which-login-bills-what). | The subscription the reused session belongs to. Meridian's own FAQ: "Usage limits follow your Max subscription, not Anthropic API billing tiers." |
23
23
  | **Terms-of-service status** | The ordinary API route. Nothing unusual about it. | Sanctioned: the official client does the authenticating, and driving `claude` is what `claude` is for. | Disallowed. Anthropic disallowed reusing subscription authentication for third-party Claude use in February 2026, and each project says so in its own words: Meridian's wrapper "makes no claims regarding compliance with Anthropic's Terms of Service"; `opencode-claude-auth` calls itself "a community workaround" and notes that the terms say subscription tokens "should only be used with official Anthropic clients"; `opencode-claude-plan` quotes Consumer Terms 3.7 and asks you to accept that your account "could be suspended or terminated". |
24
- | **Model list and fast mode** | Whatever opencode's own provider registers. | 17 ids auto-registered, Haiku 4.5 through Opus 5.5 plus Fable and Mythos, each carrying a `(N×)` list-price suffix, and any other id `claude --model` accepts passes straight through. Three `-fast` Opus ids are this plugin's own markers and opt a headless session into fast mode through `--settings` (CLI 2.1.220+). See [Models](#models). | `opencode-claude-auth`'s README lists 14 model ids. Meridian's lists none, because model metadata comes from opencode's own `anthropic` provider. Neither README mentions fast mode. |
24
+ | **Model list and fast mode** | Whatever opencode's own provider registers. | 18 ids auto-registered, Haiku 4.5 through Opus 5.5 plus Fable and Mythos, each carrying a `(N×)` list-price suffix, and any other id `claude --model` accepts passes straight through. Three `-fast` Opus ids are this plugin's own markers and opt a headless session into fast mode through `--settings` (CLI 2.1.220+). See [Models](#models). | `opencode-claude-auth`'s README lists 14 model ids. Meridian's lists none, because model metadata comes from opencode's own `anthropic` provider. Neither README mentions fast mode. |
25
25
  | **Which tools run where, under whose permissions** | All of them are opencode's, behind opencode's permission prompts and audit log. | Your choice, per tool. `Bash`, `Edit`, `Write`, `WebFetch` and `Task` are proxied by default: Claude calls an in-process MCP tool and **opencode** executes it, under its own permissions and audit log. Anything neither proxied nor named in `extraDisallowedTools` runs inside Claude Code under `--dangerously-skip-permissions`. See [Selective tool proxy](#selective-tool-proxy) and [Read-only mode](#read-only-mode). | All of them are opencode's, because the model call is an ordinary provider call. This is the one row where the third column matches the native provider and this plugin has to work for the same result. |
26
26
  | **Reasoning and effort** | opencode's own reasoning controls. | Five picker variants per model, `low` through `max`, handed to the CLI as `CLAUDE_CODE_EFFORT_LEVEL` at spawn. Effort is fixed for the life of a `claude` process, so it is part of the session key, and an agent's own `reasoningEffort` beats the effort a call arrived with. Thinking is Anthropic's summarized digest, not raw chain-of-thought. See [Extended thinking](#extended-thinking). | Meridian's SDK-features file exposes a `thinking` key. Neither README documents per-model effort variants. |
27
27
  | **Context window** | Whatever the model exposes. | The registered limits: 200k context / 64k output on the 4.5 generation, 1M / 128k on 4.6 and later, all at standard pricing with no above-200K tier. Claude Code may also compact or clear its own context mid-conversation, which the plugin can announce but not prevent. | Not stated in either README. |
@@ -102,7 +102,7 @@ The same package runs on opencode 1.x and 2.x, and nothing changes for 1.x. The
102
102
  }
103
103
  ```
104
104
 
105
- Your existing `provider.claude-code.options` block keeps working, because opencode 2 still reads 1.x config files. Its native spelling is `provider.claude-code.settings`, and plugin-level settings such as `accounts` may also go in the plugin entry itself: `{"package": "@khalilgharbaoui/opencode-claude-code-plugin", "options": {"accounts": ["work"]}}`.
105
+ Your existing `provider.claude-code.options` block keeps working, because opencode 2 still reads 1.x config files. opencode 2's own spelling is `providers.claude-code.settings` (note the plural `providers`). All four places the plugin reads its settings from, lowest precedence first: `provider.claude-code.options`, `provider.claude-code.settings`, `providers.claude-code.settings`, and the plugin entry's own `options`, which wins over all of them. Plugin-level settings such as `accounts` usually go in that last one: `{"package": "@khalilgharbaoui/opencode-claude-code-plugin", "options": {"accounts": ["work"]}}`.
106
106
 
107
107
  For a local checkout, point opencode 2 at the **`dist` directory**, not the repository root. It loads `<dir>/server` or `<dir>/index` from a directory and never reads `package.json#main`:
108
108
 
@@ -468,7 +468,8 @@ reaches the CLI, so there is nothing there to fall back from.
468
468
  | `bridgeOpencodeMcp` | boolean | `true` | Auto-translate your opencode `mcp` block into Claude's `--mcp-config`. See [MCP bridge](#mcp-bridge). |
469
469
  | `mcpConfig` | string \| string[] | – | Extra `--mcp-config` paths/JSON passed alongside the bridged config. |
470
470
  | `strictMcpConfig` | boolean | `false` | Pass `--strict-mcp-config` so Claude loads **only** the configured servers and ignores `~/.claude/settings.json`. |
471
- | `hotReloadMcp` | boolean | `true` | With MCP bridging on, compare the merged MCP config and runtime status at the start of each turn and respawn the `claude` process when they drifted, so a server you just enabled or disabled becomes visible without restarting opencode or opening a new chat. Eviction waits for pending proxy calls, never happening mid tool-call, and the session id is preserved for `--resume`. Set `false` to keep a cached subprocess until the chat is reset. It does not reload other provider options and does not watch the contents of files named in `mcpConfig`. |
471
+ | `hotReloadMcp` | boolean | `true` | With MCP bridging on, compare the merged MCP config and runtime status at the start of each turn and respawn the `claude` process when they drifted, so a server you just enabled, disabled or finished connecting becomes visible without restarting opencode or opening a new chat. It only ever acts at a safe boundary: never during `/compact`, never on the interactive transport, never while a proxied call is still in the air, a turn is still running or a plan-mode approval is outstanding, and the Claude session id is preserved for `--resume` so the conversation continues. A log line at INFO names which servers joined and left. A server that flaps buys at most one respawn per minute per conversation (`CLAUDE_CODE_MCP_HOT_RELOAD_COOLDOWN_MS`). Set `false` to keep a cached subprocess until the chat is reset. It does not reload other provider options and does not watch the contents of files named in `mcpConfig`. |
472
+ | `mcpConnectWaitMs` | number | `3000` | How long the first turn of a conversation waits for MCP servers opencode reports as still connecting before planning the `claude` spawn without them. Only opencode 2 can report that state (`pending`); opencode 1's own status call blocks until every server has decided, so this budget is what makes the two majors behave alike. Set `0` to always plan with whatever the host says at that instant. A server slower than the budget is not lost either way: it is still bridged, and `hotReloadMcp` moves the conversation onto a process that has it on the next turn. |
472
473
  | `proxyOpencodeMcpTools` | boolean | `false` | Route opencode's MCP-backed tools through the in-process `opencode_proxy` server instead of bridging them straight into Claude's `--mcp-config`, so each call executes once, inside opencode, with its permission prompt and its tool row. **The default changed from `true` to `false` in this release, and no behaviour changed with it:** at `true` it used to route nothing at all, because discovery read opencode's tool registry, which contains built-ins and plugin-declared tools and has never contained an MCP tool. Discovery now reads the model tool set opencode passes the provider, which is where MCP tools actually are, so the option works, and turning it on is the operator's decision rather than a silent migration of traffic that the direct bridge is handling today. Two caveats before enabling it: pair it with `strictMcpConfig: true`, because a server also registered in Claude Code's own config is reached directly and bypasses the proxy entirely; and a routed call runs in opencode with the calling agent's permissions, the same trade [`proxyOpencodeTools`](#options-reference) makes. Servers whose tools are not found stay on the direct bridge, and a warning says so, so do not treat this as an exactly-once guarantee for write-capable tools. |
473
474
  | `proxyOpencodeTools` | string[] | `[]` | Forward explicitly named opencode tools through the proxy (for example, a plugin's `compress` or V2 Code Mode `execute`). V1 uses registry ids; V2 uses the current model tool snapshot, including its real JSON Schema and agent visibility, not the registry's empty schemas. A forwarded tool runs inside opencode with the calling agent's permissions. A name already held by a proxy def is dropped with a warning. The read-only preset refuses `execute`. See [Forwarding opencode's own tools](#forwarding-opencode-s-own-tools) and [V2 Code Mode](#v2-code-mode). |
474
475
  | `stripContextReminders` | boolean | `false` | Remove opencode-dcp's `<dcp-system-reminder>` blocks from message text when no `compress` tool is proxied, so an order the model cannot follow stops being re-sent with every message that carries it. Inert as soon as `compress` is reachable. See [Trimming unsatisfiable context reminders](#trimming-unsatisfiable-context-reminders). |
@@ -507,6 +508,7 @@ Every variable the plugin itself reads, in one place. Config is read once at ope
507
508
  | `OPENCODE_CLAUDE_CODE_PLUGIN_FORCE_CLEANUP` | startup cleanup | `1` runs that cleanup even when the marker at `$XDG_STATE_HOME/opencode-claude-code-plugin/cleanup-stale.json` (default `~/.local/state/...`) records that this plugin version already swept. Without it the cleanup walks opencode's plugin cache once per installed version rather than on every launch. |
508
509
  | `OPENCODE_CLAUDE_CODE_NO_TMP_SWEEP` | scratch directory | `1` skips the sweep of `<tmpdir>/opencode-claude-code-<pid>` directories left behind by plugin processes that were killed. See [Scratch files on disk](#scratch-files-on-disk). |
509
510
  | `OPENCODE_WORKTREE` | MCP bridge | Overrides worktree-root detection, which otherwise walks up from the working directory looking for a `.git` entry. |
511
+ | `CLAUDE_CODE_MCP_HOT_RELOAD_COOLDOWN_MS` | MCP bridge | Minimum gap between two `hotReloadMcp` respawns of one conversation, default `60000`. A server that flaps between connected and failed would otherwise cost a kill and a `--resume` spawn on every turn. `0` disables the guard; a real second change lands on the first turn after the gap. |
510
512
  | `OPENCODE_CONFIG` / `OPENCODE_CONFIG_DIR` | config discovery | Where the plugin looks for your opencode config when bridging MCP and skills. See [Discovery order](#discovery-order-highest-to-lowest-priority). |
511
513
  | `OPENCODE_VERSION` | startup diagnostics | Reported as the opencode version when set, sparing the plugin a `--version` spawn. Diagnostics only. |
512
514
  | `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` | spawn environment | Not set by the plugin: these are yours, and Claude Code authenticates with them in preference to your subscription login when present. `ignoreAnthropicApiKey: true` strips them from the spawn. See [Billing](#billing). |
@@ -1071,6 +1073,22 @@ The disk bridge reads global config, then `OPENCODE_CONFIG`, then project direct
1071
1073
 
1072
1074
  On V2, project discovery walks to the filesystem root (including ancestors above the repo). Direct files are applied parent-first, then `.opencode` files parent-first: the closest file wins within each group, and all `.opencode` files override direct files. Each higher-precedence server entry **replaces the entire server object**, so repeat its type, URL/command and other required fields in an override. Both `.json` and `.jsonc` are read, with `.jsonc` winning within a directory. Runtime toggles continue to participate in the hot-reload hash.
1073
1075
 
1076
+ ### Servers that connect late
1077
+
1078
+ A server opencode has not finished connecting to when a turn is planned used to be dropped from that turn's `claude` spawn, and a reused process keeps the `--mcp-config` it was spawned with, so it stayed invisible for the rest of the conversation. Two things stop that now.
1079
+
1080
+ First, "still connecting" is no longer read as "not connected". opencode 2 reports such a server as `pending`, which is not a decision, so the bridge leaves its configured state alone instead of forcing it off, and the turn waits up to `mcpConnectWaitMs` (3 s by default, `0` to disable) for the host to decide. opencode 1 has no `pending` status: its own status call blocks until every server resolves, so nothing changes there and the wait costs one status call exactly as before.
1081
+
1082
+ Second, if the server joins later anyway, `hotReloadMcp` moves the conversation onto a `claude` process with the new config and `--resume`, at the start of a later turn. That only happens at a safe boundary: not during `/compact`, not on the interactive transport, and not while a proxied call is still in the air, a turn is still running or a plan-mode approval is outstanding. The log line names the servers:
1083
+
1084
+ ```
1085
+ INFO: opencode MCP servers changed, respawning claude {"joined":["slowmcp"],"left":[],...}
1086
+ ```
1087
+
1088
+ A server that flaps between connected and failed is capped at one respawn per minute per conversation; override with `CLAUDE_CODE_MCP_HOT_RELOAD_COOLDOWN_MS`.
1089
+
1090
+ This matters most where a turn can arrive before the host has started its servers: `opencode run`, scripted use and slow servers. The TUI normally connects everything before the first prompt.
1091
+
1074
1092
  ### V2 Code Mode
1075
1093
 
1076
1094
  V2 normally exposes MCP tools through Code Mode's `execute` and its catalog, rather than as individual server-prefixed model tools. `proxyOpencodeMcpTools` matches individual tools only; on a Code Mode-only snapshot it warns and falls back to the direct Claude MCP bridge. This fallback does **not** execute tools under opencode's permission policy.
package/dist/index.d.ts CHANGED
@@ -406,6 +406,7 @@ interface ClaudeCodeConfig {
406
406
  planModeQuestion?: boolean;
407
407
  webSearch?: WebSearchRouting;
408
408
  hotReloadMcp?: boolean;
409
+ mcpConnectWaitMs?: number;
409
410
  proxyOpencodeMcpTools?: boolean;
410
411
  multiStepContinuation?: boolean;
411
412
  autoContinueIncompleteTurns?: boolean | "smart";
@@ -784,6 +785,25 @@ interface ClaudeCodeProviderSettings {
784
785
  * survives MCP changes until the chat is reset).
785
786
  */
786
787
  hotReloadMcp?: boolean;
788
+ /**
789
+ * How long a turn waits for opencode MCP servers it reports as still
790
+ * connecting (`pending`) before planning the spawn without them, in
791
+ * milliseconds. Defaults to 3000; `0` disables the wait.
792
+ *
793
+ * This only ever engages on opencode 2, which answers its MCP status call
794
+ * immediately and reports a server it has not finished connecting to as
795
+ * `pending`. opencode 1 has no such status: its own status call blocks
796
+ * until every server has reached a decision, so this budget is what makes
797
+ * a 2.x host behave like a 1.x one. Without it, a conversation whose first
798
+ * turn arrives while opencode is still starting a server spawns `claude`
799
+ * without that server, and only the next turn's hot reload brings it in.
800
+ *
801
+ * Raise it for a slow server, or set `0` to always plan with whatever the
802
+ * host says at that instant. A server slower than the budget is not lost
803
+ * either way: `hotReloadMcp` moves the conversation onto a process that
804
+ * has it on the next fresh turn.
805
+ */
806
+ mcpConnectWaitMs?: number;
787
807
  /**
788
808
  * Route opencode MCP server tools through the in-process `opencode_proxy`
789
809
  * MCP server instead of bridging them directly into Claude CLI's
@@ -1257,6 +1277,8 @@ interface BridgedMcp {
1257
1277
  *
1258
1278
  * Treatment per server:
1259
1279
  * - "connected" → force `enabled: true` (mirror opencode)
1280
+ * - "pending" → leave disk value (opencode 2 is still connecting;
1281
+ * see `MCP_PENDING_STATUS`)
1260
1282
  * - any other status → force `enabled: false` (don't ship a server
1261
1283
  * opencode can't run; user fixes it in opencode first)
1262
1284
  * - missing entry → leave disk value