@khalilgharbaoui/opencode-claude-code-plugin 0.31.0 → 0.32.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 +41 -2
- package/dist/index.js +651 -198
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/claude-code-plugin/SKILL.md +45 -1
package/README.md
CHANGED
|
@@ -576,6 +576,7 @@ This is the part to read before turning it on. Three whole features of this plug
|
|
|
576
576
|
- **Permissions:** the interactive TUI has no `can_use_tool` control channel, so tools can't be approved per-call through opencode. Built-in tools are pre-allowed via a settings allow list (default `Bash, Edit, Write, Read, WebFetch`; override with `interactiveAllowTools`). `bypassPermissions` is intentionally not used here because Claude Code shows a manual safety confirmation in the TUI and defaults to exit.
|
|
577
577
|
- **Input is text-only:** images and other non-text blocks are dropped (with a logged warning); tool results are rendered as labeled text.
|
|
578
578
|
- **Output granularity:** text arrives per transcript record, not token-by-token, so it can feel chunkier than headless streaming.
|
|
579
|
+
- **Token counts come from the transcript, one count per API call.** The session JSONL writes one record per content block (thinking, text, tool_use) and every record of a call repeats that call's final usage, so the transport counts each call once, keyed by its message id. The numbers then mean exactly what they do on the headless transport: [`turnStats`](#per-turn-stats) gets the turn's totals and opencode gets the last call's context plus the turn's output. Before this was fixed a four-tool turn reported 1,306 output tokens against a real 653, and its input and cache counts were one call's instead of the turn's. An all-zero `<synthetic>` record (how the CLI writes "Login expired" or a session limit into the transcript) is not counted as a call.
|
|
579
580
|
- **Turn timeout:** a turn that produces no terminal stop within 30 minutes is reported honestly as an error result (visible truncation), not silently ended.
|
|
580
581
|
- **No idle eviction:** `idleProcessTimeoutMs` does not apply to interactive sessions.
|
|
581
582
|
- `/compact` always uses the headless transport regardless of this setting.
|
|
@@ -596,7 +597,7 @@ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`.
|
|
|
596
597
|
| `"Edit"` | `Edit`, `MultiEdit` | `mcp__opencode_proxy__edit` |
|
|
597
598
|
| `"Write"` | `Write` | `mcp__opencode_proxy__write` |
|
|
598
599
|
| `"WebFetch"` | `WebFetch` | `mcp__opencode_proxy__webfetch` |
|
|
599
|
-
| `"Task"` | `Agent` | `mcp__opencode_proxy__task`, `mcp__opencode_proxy__task_batch` |
|
|
600
|
+
| `"Task"` | `Agent` | `mcp__opencode_proxy__task`, `mcp__opencode_proxy__task_batch`, and on a host that runs background subagents `mcp__opencode_proxy__task_status`, `mcp__opencode_proxy__task_cancel` |
|
|
600
601
|
| `"Question"` | `AskUserQuestion` | `mcp__opencode_proxy__question` |
|
|
601
602
|
| `"Compress"` | none | `mcp__opencode_proxy__compress` |
|
|
602
603
|
|
|
@@ -607,7 +608,7 @@ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`.
|
|
|
607
608
|
- **Permissions:** the calling agent's `permission.task` rule applies to the target `subagent_type`. Grant `task: "allow"` on agents that should delegate without a prompt; an `ask` or `deny` rule remains authoritative. The plugin never bypasses this decision.
|
|
608
609
|
- **Resume:** pass the child session ID back as `task_id` to continue that subagent session. Omit it to create a fresh child.
|
|
609
610
|
- **Nested tasks:** current opencode defaults `subagent_depth` to `1`, so a first-level child cannot launch another child. Increase top-level `subagent_depth` to permit deeper nesting, and explicitly grant `permission.task` on every subagent that should delegate; opencode otherwise adds a task deny to spawned subagent sessions.
|
|
610
|
-
- **Background:**
|
|
611
|
+
- **Background:** see [Background subagents](#background-subagents) below. Foreground is the default.
|
|
611
612
|
- **Several at once:** `mcp__opencode_proxy__task_batch` takes a `tasks` array of ordinary task inputs and runs them concurrently. It exists because Claude Code sends MCP requests one at a time: when the model emits two `task` calls in one response, the second only leaves the CLI after the first has returned (measured live, 2026-09-06), so "launch two subagents" was always serial. The plugin turns one `task_batch` call into N opencode `task` calls inside a single tool boundary, which opencode executes in parallel, then hands the model every result together, labelled in task order. Same permissions, same no-deadline default, same `subagent_type` list. Enabled whenever `Task` is proxied. Designed and first implemented by [@broskees](https://github.com/broskees) on his fork.
|
|
612
613
|
|
|
613
614
|
**Steering models to it.** Headless Claude Code CLIs expose no `Agent`/`Task`
|
|
@@ -624,6 +625,42 @@ recovery step for harnesses that defer MCP tool schemas. Both apply per Claude
|
|
|
624
625
|
process at spawn, and provider options are read once at opencode startup, so
|
|
625
626
|
`proxyTools` changes need a full opencode restart.
|
|
626
627
|
|
|
628
|
+
### Background subagents
|
|
629
|
+
|
|
630
|
+
A foreground `task` call blocks the conversation until the subagent finishes, and so does `task_batch`. Background dispatch is the other shape: start a subagent, keep working, collect the result later. opencode owns it, this plugin surfaces it, and it is **off unless the opencode process has `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`** (or the blanket `OPENCODE_EXPERIMENTAL=true`) in its environment on opencode 1.x. On opencode 2.x it is unconditional.
|
|
631
|
+
|
|
632
|
+
```sh
|
|
633
|
+
OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
With it set, `mcp__opencode_proxy__task` takes `background: true` and the call comes straight back:
|
|
637
|
+
|
|
638
|
+
```xml
|
|
639
|
+
<task id="ses_f10789724ffes9OfQApCB04IRe" state="running">
|
|
640
|
+
<summary>Background task started</summary>
|
|
641
|
+
<task_result>
|
|
642
|
+
The task is working in the background. You will be notified automatically when it finishes.
|
|
643
|
+
</task_result>
|
|
644
|
+
</task>
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
Claude keeps working. When the subagent finishes, opencode prompts the same conversation with the result as a new message, so it arrives as its own turn rather than as that call's result. Measured end to end on opencode 1.18.33 with claude-haiku-4-5: the dispatch returned in 14 s while the child's 30-second command was still running, Claude ran another tool and ended its turn 18 s in, and the `<task ... state="completed">` message landed 43 s later. That notification is automatic, so the right thing after a background dispatch is to end the turn, not to wait or poll.
|
|
648
|
+
|
|
649
|
+
**The gate is enforced at the schema, not at the call.** On a host without the flag opencode rejects `background: true` outright with `Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`, which costs a whole dispatch. So the plugin reads the host's own `task` schema (the same registry fetch that supplies the agent-type list) and, when it has no `background` property, strips the field before Claude ever sees it. Nothing about a default install changes: the model is shown exactly `description`, `prompt`, `subagent_type`, `task_id`, `command`. Either way `plugin.log` says which:
|
|
650
|
+
|
|
651
|
+
```
|
|
652
|
+
background subagent gate {"supported":true,"registryResolved":true,"hostApi":"v1", ...}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
**Collect and cancel.** opencode delivers a background result by pushing it into the conversation and offers nothing else: no route reads a result back, and nothing stops a background child. A notification that never lands (an interrupted turn, an errored turn, a compaction across it) would lose the work, and a subagent running away could only be stopped from another pane. So on a host that runs background subagents, and only there, two more proxy tools ride along with `Task` in the same way `task_batch` does:
|
|
656
|
+
|
|
657
|
+
| Tool | What it does |
|
|
658
|
+
| --- | --- |
|
|
659
|
+
| `mcp__opencode_proxy__task_status` | Reads the state of a background subagent by its `task_id` and returns its result if it has finished. A recovery path, not a progress poll: a healthy background task delivers its own result. A result is handed over once, so asking again reports the state without repeating the output. |
|
|
660
|
+
| `mcp__opencode_proxy__task_cancel` | Stops a background subagent. A cancelled subagent sends no completion notification. |
|
|
661
|
+
|
|
662
|
+
The `task_id` is the `id` in the `<task …>` envelope, which is the child's own opencode session id. Both tools are answered inside the plugin rather than executed by opencode, because opencode has no tools of these names, and both refuse any session whose parent is not the conversation doing the asking. Neither can be named in `proxyTools`: they appear only when the host advertises background support, so upgrading changes nothing about what the model can do or spend on a default install.
|
|
663
|
+
|
|
627
664
|
### Proxy endpoint security
|
|
628
665
|
|
|
629
666
|
The proxy is a small HTTP MCP server on an ephemeral loopback port, and calling it runs Bash, Edit and Write through opencode's executor. Since 0.13.2 it requires a 256-bit bearer token, generated per server and handed to Claude in the `headers` block of the `0600` MCP config file the plugin writes. Requests are also rejected unless the `Host` header matches the bound `127.0.0.1:<port>` authority, no `Origin` header is present, and the content type is `application/json`.
|
|
@@ -890,6 +927,8 @@ It carries the startup-diagnostics fields (plugin version, opencode version, `cl
|
|
|
890
927
|
|
|
891
928
|
When Claude Code refused an entry in an `--mcp-config` it was handed, an **MCP config entries Claude Code skipped** section names each one with the CLI's own category and sentence. That section only appears when there is something in it. It matters because a skipped server is absent from the CLI's server list entirely rather than listed as broken, so the model silently does not have those tools; if the skipped name is `opencode_proxy` the report says so plainly, because then it is the plugin's own server and every proxied tool call in the session fails. The same thing is a warning in your terminal when it happens.
|
|
892
929
|
|
|
930
|
+
A **Plugins Claude Code did not load** section works the same way for Claude plugins: one the CLI demoted at load time (for example, a dependency that is not installed) is absent from its plugin list, so its skills, commands and MCP servers are silently missing. The skill bridge is such a plugin (`opencode-skills`), and a failure there is reported as the plugin's own bug rather than your config. A plugin warning only counts when its content did not load; advisory feedback about a plugin that did load stays in the log at INFO.
|
|
931
|
+
|
|
893
932
|
```text
|
|
894
933
|
/claude-code-doctor usage
|
|
895
934
|
```
|