@khalilgharbaoui/opencode-claude-code-plugin 0.32.0 → 0.33.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
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-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 test-interactive-usage.ts test-background-subagents.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 test-interactive-usage.ts test-background-subagents.ts test-interactive-result.ts"
25
25
  },
26
26
  "dependencies": {
27
27
  "@ai-sdk/provider": "^3.0.8",
@@ -129,7 +129,7 @@ Defaults below describe normal headless opencode use when the key is absent.
129
129
  | `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. |
130
130
  | `hotReloadMcp` | boolean | `true` | With bridging on, compare merged MCP config/status at turn start and respawn on drift after pending proxy calls resolve. Keeps the session via headless `--resume`. Does not reload arbitrary provider options or watch explicit `mcpConfig` contents. |
131
131
  | `proxyOpencodeMcpTools` | boolean | `false` | Route opencode's MCP-backed tools through opencode's executor instead of Claude's own `--mcp-config` child, so each call is permission-prompted and rendered as an opencode tool row. Default changed `true` to `false` here, with no behaviour change: at `true` it routed nothing, because discovery read opencode's tool registry, which never contains MCP tools. Discovery now reads the model tool set opencode passes the provider, verified live on opencode 1.18.31 / Claude Code 2.1.263. **Tell the user to set `strictMcpConfig: true` alongside it**: a server also present in Claude Code's own config is reached directly and the proxy is bypassed, which looks exactly like the option doing nothing. A routed call runs with the calling agent's permissions. Servers whose tools are not found stay on the direct bridge and log a warning. Do not promise exactly-once side effects across failures, retries or opencode versions; verify routing before using write-capable tools. |
132
- | `proxyOpencodeTools` | string[] | `[]` | Forward named opencode tools through the proxy by registry id (`client.tool.list()`, matched case-insensitively). Covers tools another opencode plugin declares directly, which belong to no MCP server and so are never matched by `proxyOpencodeMcpTools`: opencode-dcp's `compress` is the motivating case. Same broker as every other proxy tool, so the same events release the call. Unknown name is skipped with a warning; a name a proxy def already holds is dropped with a warning and the existing tool keeps it. Explicit allowlist only, because a forwarded tool runs in opencode with the calling agent's permissions. |
132
+ | `proxyOpencodeTools` | string[] | `[]` | Forward explicitly named opencode tools (case-insensitive): V1 resolves registry ids; V2 resolves the current model tool snapshot and its actual JSON Schema, including synthesized Code Mode `execute`, without re-exposing tools absent from that snapshot. Covers plugin-declared tools such as DCP's `compress` and V2 Code Mode. Same broker as other proxies; collisions and unknown names warn. Explicit allowlist only, because calls run in opencode with the agent's permissions. `execute` grants access to the session's whole Code Mode catalog, not just MCP, and is refused by the read-only preset. |
133
133
  | `stripContextReminders` | boolean | `false` | Strip opencode-dcp `<dcp-system-reminder>` blocks from user/assistant message text, including the fresh-session rebuild. Only when no `compress` is proxied via `proxyTools` or `proxyOpencodeTools`; reachable compress makes it inert. Resolved from config, so a configured-but-unregistered name still counts as reachable. Leaves opencode's own `<system-reminder>` blocks alone. |
134
134
  | `multiStepContinuation` | boolean | `true` | Append a system-prompt hint to chain tool calls in one turn instead of stopping between subtasks. |
135
135
  | `autoContinueIncompleteTurns` | boolean or `"smart"` | `"smart"` | `true`/`"smart"` continue a turn truncated at `max_tokens`, bounded by 8 attempts and 10 minutes, and otherwise run the keyword heuristic only when stop reason is missing. Every other stop reason, plus error, abort or latched question, stops it. Current measured CLIs always report a reason, so truncation is the only case that resumes in practice. Also gates the `▌ **no reply:**` note written when a turn finishes cleanly with no text and no tool call; `false` turns off the note as well as the continuation. |
@@ -223,6 +223,35 @@ Same package, same config. 2.x's native key is `plugins` (plural), but it still
223
223
  - A local checkout is loaded by pointing `plugins` at its **`dist`** directory, never the repository root.
224
224
  - Known 2.x differences: `/btw` is answered after the running turn rather than inside it, and there is no todo panel (2.x has no `todowrite` tool). Do not set `hostApi`; the 2.x entrypoint sets it, and forcing it on 1.x breaks every proxied tool call.
225
225
 
226
+ #### V2 MCP and Code Mode
227
+
228
+ V2 MCP config is `mcp.servers.<name>`, with `disabled` rather than `enabled`.
229
+ The disk bridge accepts both shapes. V2 discovery includes ancestors above the
230
+ repo, and nearest `.opencode` config wins after all direct configs. Higher
231
+ precedence server entries replace the entire spec; repeat required fields.
232
+ Doctor must report actual names, never the container name `servers`.
233
+
234
+ V2 defaults to Code Mode, where MCP functions are behind `execute` rather than
235
+ individual tools. `proxyOpencodeMcpTools` cannot prefix-match that tool and
236
+ warns before leaving servers on the direct bridge. Two deliberate choices:
237
+
238
+ - Individual proxies: set `codemode: false` on selected MCP servers and pair
239
+ `proxyOpencodeMcpTools: true` with `strictMcpConfig: true`.
240
+ - Preserve Code Mode: after explaining that `execute` can invoke **all tools
241
+ in the session catalog**, add it to `proxyOpencodeTools`, set
242
+ `bridgeOpencodeMcp: false` and `strictMcpConfig: true`. In native V2 config
243
+ these belong under `providers.claude-code.settings`; preserve other
244
+ allowlisted entries. Do not also pass those servers through `mcpConfig`.
245
+
246
+ The headless proxy exposes `mcp__opencode_proxy__execute` using the actual
247
+ per-turn schema and catalog. Claude uses ToolSearch to discover that full name,
248
+ then the original `search(...)` and `tools[...]` signatures inside its code.
249
+ No execute proxy is automatic; the read-only preset refuses this code runner.
250
+ With the bridge off, OpenCode still owns MCP connections and catalog updates,
251
+ but the plugin's disk-MCP hot-reload mechanism does not apply. This path is
252
+ verified offline with a fake CLI, not a paid live Claude probe. Fully restart
253
+ all opencode processes after provider/plugin changes; ask before live probes.
254
+
226
255
  ### Two accounts
227
256
 
228
257
  ```json
@@ -484,6 +513,14 @@ a new message. Delivery is automatic, so polling is wrong and the tool descripti
484
513
  so. `task_status` and `task_cancel` join the tool list, keyed on the `id` from that
485
514
  envelope (the child's opencode session id).
486
515
 
516
+ opencode 2 uses different envelopes and the plugin's tool descriptions follow the host:
517
+ there a background dispatch answers in prose,
518
+ `The subagent is working in the background (sessionID: ses_...)`, and the completion
519
+ arrives as `<subagent sessionID="..." state="completed" description="...">`. The
520
+ `sessionID` is the `task_id`. Both extra tools work on both majors (on opencode 2 over
521
+ `session.context` and `session.interrupt`, the session routes a plugin is given there);
522
+ verified live on 2.0.16.
523
+
487
524
  Without it, opencode rejects a `background: true` call outright
488
525
  (`Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`), losing
489
526
  the dispatch, so the plugin strips `background` from the `task` and `task_batch` schemas
@@ -499,6 +536,13 @@ opencode started does nothing, and a plugin upgrade cannot change it). A `task_s
499
536
  that answers `not a subagent of this conversation` means the id came from a different
500
537
  conversation.
501
538
 
539
+ `/claude-code-doctor` has a **Background subagents** section answering the same question
540
+ without reading the log: whether `background` was offered and the two tools registered,
541
+ the opencode major, what decided it (the live `task` schema, a registry that did not
542
+ answer, or opencode 2 offering it unconditionally), and the background tasks this
543
+ process has collected or cancelled. The gate is read while a turn plans its proxy tools,
544
+ so a fresh process reports `Not read yet this process` until one message has been sent.
545
+
502
546
  A proxied call is held open until an event ends it, and the plugin listens to the
503
547
  `claude` process, the stream and the control protocol for those events rather than
504
548
  inferring failure from elapsed time: opencode's result resolves the call; an abort
@@ -712,6 +756,14 @@ warned about content that did not load. Read it whenever bridged skills are miss
712
756
  `opencode-skills@...` row is the skill bridge itself, which is a plugin bug to report,
713
757
  not something to fix in the user's config.
714
758
 
759
+ A **Background subagents** section is always printed: whether `background` was offered
760
+ to Claude and `task_status` / `task_cancel` registered, the opencode major, what decided
761
+ it, and the background tasks this process collected or cancelled. It reads
762
+ `Not read yet this process` until a turn has planned its proxy tools, which is not the
763
+ same as "no": on a fresh process, send a message and run it again before concluding
764
+ anything. Only a 1.x host that said no is told about
765
+ `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`.
766
+
715
767
  `/claude-code-doctor usage` adds a **Plan usage** section: the CLI's own `/cost` answer
716
768
  (subscription vs API key, 5-hour and 7-day window use, reset times, what is driving
717
769
  them). Measured free on 2.1.280 (`num_turns: 0`, `$0`, no API call), so suggest it for
@@ -786,6 +838,8 @@ Prefer the doctor: it needs no logging change and no restart.
786
838
  | Claude "forgot" the earlier part of a long conversation | Claude Code compacted its own context | Look for the `▌ **context compacted:**` note in the transcript |
787
839
  | 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 |
788
840
  | Wanting the per-turn cost in the chat | Not shown by default | Set `turnStats: true` and restart opencode |
841
+ | 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 this fix 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 |
842
+ | On the interactive transport with a working directory under `/tmp` (or any symlinked path), the turn hangs until the 30-minute turn timeout and then reports no terminal stop reason | Plugin older than this fix named the transcript directory from `path.resolve`, which does not follow symlinks, so it tailed a file Claude Code never writes. On macOS `/tmp` is a symlink to `/private/tmp` | Upgrade and relaunch. The directory is now named from the cwd's resolved real path, which is what the CLI uses (`/tmp/scratch` is `~/.claude/projects/-private-tmp-scratch`). Check the `jsonlPath` in the `prepared interactive claude session` log line against the directory that actually exists under `<CLAUDE_CONFIG_DIR>/projects/` |
789
843
  | On the interactive transport, a turn's output tokens look about double, and `turnStats` shows one call's input where the turn used many | Plugin older than this fix summed the session transcript's usage per RECORD, and the JSONL writes one record per content block with the call's usage repeated on each | Upgrade and relaunch. Counting is now once per API call: a four-tool turn that reported 1,306 output tokens reports its real 653, and the stats line carries the turn's totals as it does headlessly. Headless turns were never affected by this one |
790
844
  | 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` |
791
845
  | 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 |