@khalilgharbaoui/opencode-claude-code-plugin 0.49.0 → 0.50.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.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "homepage": "https://opencode-claude-code-plugin.dev/",
6
6
  "funding": {
@@ -132,6 +132,7 @@ Defaults below describe normal headless opencode use when the key is absent.
132
132
  | `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. On the interactive transport it fires and is verified live (1.18.34): the form's answer is pressed into the TUI's approval dialog. Headless prose yes/no is not a plan-mode unlock; interactive typed yes is. |
133
133
  | `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`. |
134
134
  | `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. |
135
+ | `bridgeMcpOauthTokens` | boolean | `false` | Give a bridged **remote** MCP server the OAuth bearer token opencode already holds for it, so the Claude CLI can use a server the user authenticated through opencode's own flow instead of reporting it `needs-auth` for the whole session (`MCP server "<name>" is needs-auth in Claude Code`). **Off by default and never a silent recommendation**: it copies a credential out of opencode's own store (`$XDG_DATA_HOME/opencode/mcp-auth.json`, usually `~/.local/share/opencode/`) into the `0600` bridged config the plugin writes for a second program, which then lets a model drive calls with it. Say that before suggesting it. opencode **1 only**: opencode 2 keeps MCP credentials in its database behind a typed plugin API and has no such file, so the option finds nothing there. Purely additive when on: a token is matched by opencode's own key for it (the server's **name**) and only when the stored `serverUrl` still equals the configured URL, a server with no usable token is bridged unchanged rather than dropped, an `Authorization` header the user wrote wins in any casing, and a local (stdio) server is never touched. An expired token (30 s margin) is not used and WARNs once per server per process; the plugin never runs an OAuth flow and never refreshes. A rotation forces a respawn through `hotReloadMcp` because the token's **expiry** is folded into the bridge hash (never anything derived from the token). No token ever reaches a log line, the doctor report or `/claude-code-doctor bundle`. |
135
136
  | `mcpConfig` | string or string[] | unset | Extra `--mcp-config` paths or inline JSON passed alongside the bridged config. |
136
137
  | `strictMcpConfig` | boolean | `false` | Headless `--strict-mcp-config`: use only explicitly supplied MCP configs, ignoring other MCP sources, not all settings/credentials/hooks. The interactive wrapper adds it whenever it passes MCP paths, independently of this option. |
137
138
  | `hotReloadMcp` | boolean | `true` | With bridging on, compare merged MCP config/status at turn start and respawn on drift, so a server enabled, disabled or finished connecting since the spawn reaches the model. Keeps the session via `--resume` on both transports. Acts only at a safe boundary: never during compaction, and never while a proxied call is pending, a turn is in flight or a plan-mode approval is outstanding. Logs the joined and left server names at INFO. One respawn per conversation per `CLAUDE_CODE_MCP_HOT_RELOAD_COOLDOWN_MS` (default 60000) so a flapping server cannot respawn every turn. Does not reload arbitrary provider options or watch explicit `mcpConfig` contents. |
@@ -1114,6 +1115,7 @@ Prefer the doctor: it needs no logging change and no restart.
1114
1115
  | `proxy call still waiting` in the log, or a `task` that looks stuck | Expected: `task`/`task_batch` carry no default deadline, and the line is a status report | `/claude-code-doctor` lists pending calls with tool, age and deadline. Tell a working subagent from a wedged one there before proposing any timeout change; see the note under "Proxy tool names" |
1115
1116
  | An MCP server's tool returns nothing useful, and `plugin.log` carries `MCP server asked for operator input and was declined` | That server raised an MCP **elicitation**, a mid-call request for the operator to type or choose something. The Claude Code session the plugin drives is headless, so there is nobody to ask: the plugin answers `{"action":"decline"}` under the CLI's documented schema and warns once per server. The tool call still returns and the turn continues | Not configurable, and deliberately so: accepting would mean inventing the answer the server asked for. Run that server's flow in Claude Code directly, where its own dialog can take the input, or configure the server so that path does not elicit |
1116
1117
  | An MCP server's tools are simply absent | Claude Code could not connect that server | Read the once-per-process WARN at session start, and the doctor's **MCP config entries Claude Code skipped** section for one the CLI refused outright. `mcpServers` in the ready block is disk discovery, not live connectivity; fix the server where it is configured |
1118
+ | `MCP server "<name>" is needs-auth in Claude Code`, for a remote server that works in opencode | Whether the user authenticated it through opencode's own OAuth flow. opencode holds that token; the bridged config carries no credential, so the CLI cannot connect and the server is dead for the whole session | On opencode **1**, `bridgeMcpOauthTokens: true` hands the CLI the token opencode already has. Tell the user what it does before suggesting it: it copies a credential out of opencode's own store into the bridged `0600` config. On opencode **2** there is no such file and the option finds nothing, so the fix is configuring that server for Claude Code directly |
1117
1119
  | An MCP server opencode has configured is missing on the FIRST turn of a fresh `opencode run`, but present in the TUI | Not a config fault, and mostly fixed. The bridge reads opencode's live MCP status when the turn plans its spawn. On opencode 2 a server still connecting is reported `pending`, which the bridge no longer reads as disabled, and the turn waits up to `mcpConnectWaitMs` (3 s) for the host to decide; opencode 1 cannot reach this state, because its own status call blocks until every server resolves. If the server is slower than the budget it is still bridged, and if it genuinely joins later `hotReloadMcp` moves the conversation onto a process that has it on the next turn, logging `opencode MCP servers changed, respawning claude` with the joined names | Nothing, usually. For a very slow server raise `mcpConnectWaitMs`. Check `plugin.log` for `waited for opencode MCP servers to finish connecting` and for the respawn line; if neither appears and the server is still absent, the status the host reported was a real refusal (`failed`, `needs_auth`), which is opencode's to fix |
1118
1120
  | `permissionPreset` set but nothing about the session looks restricted | The option never reached that provider, or the name is not one the plugin knows (only `read-only` exists) | Read the `permissionPreset` row in `/claude-code-doctor`, or `permissionPresets` in the ready block, for the provider the conversation is on: `none` means it is not configured there (each account is its own provider id), `applied: false` with a name means an unrecognised name applied nothing, and `overrides` lists what an applied preset replaced |
1119
1121
  | `permissionPreset: "read-only"` set, but reads are unconfined or something still prompts | `--restricted` needs CLI 2.1.258 and `--permission-prompts none` needs 2.1.263; below those the preset falls back to `--disallowedTools` plus the plugin's own deny and WARNs naming what is lost | `claude --version`. Below 2.1.258 the working-directory confinement on reads is gone; below 2.1.263 the denial happens in the plugin instead of the CLI. The preset still holds, with one layer fewer |
@@ -1143,6 +1145,7 @@ Prefer the doctor: it needs no logging change and no restart.
1143
1145
  | `Provider did not return a tool result` on a tool row (opencode 2), or a row that never leaves pending (opencode 1) | An older plugin lost the result of a tool Claude Code ran itself when the step had to end first: Claude Code answers a batch of tool calls in one message and withholds its own results while one of them is proxied, and a refused tool's refusal arrives a frame ahead of the call's registration | Upgrade. The model always had the real output; only opencode's row was unanswered. The row now carries a result, and a deferred one says plainly that Claude Code kept the output in its own context |
1144
1146
  | A line at the top of a reply that answers the PREVIOUS message | A reused `claude` process kept talking after the plugin closed the turn (most often after a proxied tool call, or when a background command it started reported back and the CLI answered on its own), and the next turn replays it | Working as designed, and it is led by `▌ **between turns:**` so it is distinguishable; older plugins replayed it as bare text. It is stripped from any transcript rebuilt for the CLI. A default `proxyTools` prevents the background-command route entirely |
1145
1147
  | Claude "forgot" the earlier part of a long conversation | Claude Code compacted its own context | Look for the `▌ **context compacted:**` note in the transcript |
1148
+ | On a turn that replayed the thread as text, Claude knows what tools the previous reply ran but not what it concluded | Plugin 0.49.0 or older kept each replayed message's first 2,000 characters, and Claude Code runs its own tools without ending a step, so a reply's tool calls and their output (one result alone is capped at 10,000 characters) sit in the same message ahead of the prose and the answer was cut | Upgrade. Each replayed message is clipped in the middle now, keeping the answer it ends on, and the newest reply gets a larger budget. Better still, avoid the replay: it only happens when the Claude session cannot be resumed, and the `▌` note or the plugin log names the reason |
1146
1149
  | 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 |
1147
1150
  | Wanting the per-turn cost in the chat | Not shown by default | Set `turnStats: true` and restart opencode |
1148
1151
  | On the interactive transport, every turn ends with a `▌ **claude code error:**` note naming `end_turn` (or `stop_sequence` / `max_tokens`), and `turnStats` never prints | Plugin older than 0.33.0 put the stop reason in the synthesized `result`'s `subtype`, and any non-`success` subtype finishes the turn as an error, which also suppresses the stats footer | Upgrade and relaunch. A turn that reaches a terminal stop reason now synthesizes the shape a headless turn emits (`subtype: "success"`, the stop reason in a top-level `stop_reason`), so it finishes as an ordinary reply. A turn that reaches NO terminal stop reason is still an error on purpose, so truncation stays visible |