@khalilgharbaoui/opencode-claude-code-plugin 0.40.0 → 0.42.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/dist/index.d.ts +9 -1
- package/dist/index.js +483 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +68 -14
package/package.json
CHANGED
|
@@ -120,7 +120,7 @@ Defaults below describe normal headless opencode use when the key is absent.
|
|
|
120
120
|
| `fallbackModels` | string[] | unset | Ordered models to try when the model a turn would run on is refused. Default for agents declaring no `fallbackModels`; a per-agent list replaces it rather than extending it. Same account throughout, never a switch. Armed only by the CLI refusing the model (`model_not_found`) or by a usage limit when the `accountFailover` form is not taking the turn, which is the case whenever it is `"off"` (its default) or has no other account to offer; with `"ask"` and another account the switch form wins. Entries must be registered model ids, unknown ones warn and are skipped, the current model is dropped from its own chain, each entry is tried at most once per turn, and an exhausted chain surfaces the original error. Never on compaction, title stubs or the interactive transport. Writes a `▌ **model fallback:**` note that transcript rebuilds strip. Not independently read per expanded account. |
|
|
121
121
|
| `cwd` | string | automatic | Pin an absolute existing directory. Otherwise: session directory from SDK, usable `process.cwd()`, captured project directory, final `process.cwd()` fallback. Startup diagnostics cannot show the per-call session tier. |
|
|
122
122
|
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to headless Claude, even with proxies enabled. Proxied calls still use opencode permissions, but unproxied CLI tools do not. `false` removes the bypass flag; it does not by itself create human approval prompts. Ignored when `permissionMode` is `"plan"`, which always drops the flag. |
|
|
123
|
-
| `permissionMode` | `acceptEdits` / `auto` / `bypassPermissions` / `default` / `dontAsk` / `plan` | unset | Headless `--permission-mode`, not version-gated: verify the installed CLI supports the value. `plan` is enforced: it overrides `skipPermissions: true` and the plugin drops `--dangerously-skip-permissions` for it, so claude cannot edit or run commands. Every other value governs prompting and still passes the skip flag, so `plan` is the only one that makes a run read-only.
|
|
123
|
+
| `permissionMode` | `acceptEdits` / `auto` / `bypassPermissions` / `default` / `dontAsk` / `plan` | unset | Headless `--permission-mode`, not version-gated: verify the installed CLI supports the value. `plan` is enforced: it overrides `skipPermissions: true` and the plugin drops `--dangerously-skip-permissions` for it, so claude cannot edit or run commands. Every other value governs prompting and still passes the skip flag, so `plan` is the only one that makes a run read-only. Headless: nothing releases plan mode mid-session (no headless `ExitPlanMode`), so leaving it means a config change and an opencode restart; the plugin warns once at startup. Interactive transport: forwarded (not `bypassPermissions`), and plan mode CAN be left: the TUI's `ExitPlanMode` dialog is parked and the operator's next message (bare yes approves, anything else is "what to change") or the `planModeQuestion` form answers it; in plan mode there the proxy drops `write`/`edit` so Claude's own Write can write the plan file. |
|
|
124
124
|
| `permissionPreset` | `"read-only"` | unset | One named posture instead of hand-combining the options around it. Unset changes nothing. An applied preset replaces `permissionMode`, `skipPermissions`, `controlRequestBehavior` and `controlRequestToolBehaviors` outright, filters `proxyTools`, and unions its own names into `extraDisallowedTools`. `read-only` forces `skipPermissions: false` (the CLI exits with `bypassPermissions not supported in restricted mode` if both are passed), replaces any `permissionMode` with `--restricted` (CLI 2.1.258+: no Bash, REPL or other code runners, no WebFetch, file tools confined to the working directories, bypass refused), adds `--permission-prompts none` (CLI 2.1.263+), disallows `Bash`, `Write`, `Edit`, `NotebookEdit`, `REPL`, `JavaScript` and `WebFetch` via `--disallowedTools`, drops `bash`/`write`/`edit`/`webfetch`/`task`/`task_batch` from `proxyTools`, forces `controlRequestBehavior: "deny"` and ignores `controlRequestToolBehaviors` entirely. Every override is logged at NOTICE. An unknown preset name applies nothing and WARNs rather than guessing. On a CLI below either flag gate the preset still holds through `--disallowedTools` plus the plugin's own deny, with a WARN naming what is lost. Reads (`Read`, `Grep`, `Glob`, `WebSearch`) still work; anything else that would prompt, including bridged MCP tools and the `question` proxy, is denied. |
|
|
125
125
|
| `controlRequestBehavior` | `allow` / `deny` | `allow` | Automatically answer CLI `can_use_tool` requests if emitted. Forced to `deny` by `permissionPreset: "read-only"`. Not an opencode permission prompt or a sandbox; bypass/pre-allowed tools may never ask. `AskUserQuestion` defaults to deny. |
|
|
126
126
|
| `controlRequestToolBehaviors` | object of tool name to `allow`/`deny` | unset | Case-insensitive per-tool override of the above (`Bash`, `Read`, `mcp__github__list_prs`). Do not allow `AskUserQuestion`: that can let headless Claude self-answer. |
|
|
@@ -128,30 +128,31 @@ Defaults below describe normal headless opencode use when the key is absent.
|
|
|
128
128
|
| `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Case-insensitive replacement list, not additive and not a capability allowlist. Known entries expose `mcp__opencode_proxy__<name>`; omitted/unknown tools are not disabled. `Task` also brings `task_batch`; `[]` disables this list, not MCP proxying. See the proxy table for exceptions. |
|
|
129
129
|
| `extraDisallowedTools` | string[] | unset | Claude built-ins to switch off outright with `--disallowedTools`, for tools that have no proxy (`["NotebookEdit"]`). Removes the capability rather than routing it. |
|
|
130
130
|
| `proxyToolTimeoutMs` | object of proxy tool name to ms | unset | Optional wall-clock backstop per tool, in ms, case-insensitive keys. A proxied call normally ends on an event the plugin listens for, not on a timer: opencode's result, an abort (the CLI is interrupted), the next user message (calls the previous turn left pending are rejected as orphaned), the `claude` process exiting, the chat being deleted, or opencode exiting. Fallback 10 min (including dynamic MCP tools); `task` and `task_batch` have no deadline, so a subagent runs to completion and a chat parked in one holds its worker until one of those events; `question` 30 min. Set both task keys to cover both. A positive value replaces the default, `0` removes that tool's deadline, negative or non-numeric values are ignored, and values above 2147483647 are clamped. Bash `input.timeout` raises the resolved deadline (and restores one after `bash: 0`); executor ceilings still apply. The generated MCP client timeout is the largest effective deadline, or the CLI's maximum while any tool has none. `compress` is intercepted without a deadline. |
|
|
131
|
-
| `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.
|
|
131
|
+
| `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. |
|
|
132
132
|
| `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`. |
|
|
133
133
|
| `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. |
|
|
134
134
|
| `mcpConfig` | string or string[] | unset | Extra `--mcp-config` paths or inline JSON passed alongside the bridged config. |
|
|
135
135
|
| `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. |
|
|
136
|
-
| `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
|
|
136
|
+
| `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. |
|
|
137
137
|
| `mcpConnectWaitMs` | number | `3000` | How long the first turn waits for MCP servers the host reports as still connecting before planning the spawn without them. Only opencode 2 reports that state (`pending`); opencode 1's status call blocks until every server decides, so this is a no-op there and costs one status call as before. `0` disables the wait; negative or non-numeric values fall back to the default. Aborting the turn ends the wait at once, and the turn then spawns nothing. A server slower than the budget is still bridged (pending is not read as disabled) and `hotReloadMcp` brings a later one in on the next turn. |
|
|
138
138
|
| `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. Inert with `bridgeOpencodeMcp: false`, which leaves no bridged server list to match names against. Do not promise exactly-once side effects across failures, retries or opencode versions; verify routing before using write-capable tools. |
|
|
139
139
|
| `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. |
|
|
140
140
|
| `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. |
|
|
141
141
|
| `multiStepContinuation` | boolean | `true` | Append a system-prompt hint to chain tool calls in one turn instead of stopping between subtasks. |
|
|
142
142
|
| `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. |
|
|
143
|
-
| `compactionModel` | string | `"claude-haiku-4-5"` | `/compact` uses a fresh short-lived
|
|
143
|
+
| `compactionModel` | string | `"claude-haiku-4-5"` | `/compact` uses a fresh short-lived process on the selected transport without the usual bridge/proxy/skill wiring. On the PTY it disables built-in tools, uses an empty strict MCP config, submits summary instructions with the transcript as text, and closes the TUI after its answer. Nonblank `CLAUDE_CODE_COMPACTION_MODEL` wins. This is inference and can be billed. |
|
|
144
144
|
| `ignoreAnthropicApiKey` | boolean | `false` | Strip `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` from headless/interactive spawn env, allowing stored auth to be used. Does not log in, change the parent env, or guarantee subscription billing if other CLI/cloud auth is configured. Warns at startup when either nonempty variable is present, regardless of the flag. |
|
|
145
|
-
| `idleProcessTimeoutMs` | number | unset | Kill a conversation's idle `claude` worker this many ms after a finished turn. The timer starts when a turn completes, reuse cancels it, and a worker found mid-turn when it fires is re-timed rather than killed. The session id is kept, so the next message resumes transparently. Unset or `0` keeps workers until LRU eviction (16 processes, oldest idle first). Values above `2147483647` are ignored.
|
|
145
|
+
| `idleProcessTimeoutMs` | number | unset | Kill a conversation's idle `claude` worker this many ms after a finished turn. The timer starts when a turn completes, reuse cancels it, and a worker found mid-turn when it fires is re-timed rather than killed. The session id is kept, so the next message resumes transparently. Unset or `0` keeps workers until LRU eviction (16 processes, oldest idle first). Values above `2147483647` are ignored. Applies to the interactive transport too (the TUI is closed; the next message resumes it). Deleting a chat in opencode releases its workers and session ids immediately regardless. |
|
|
146
146
|
| `turnStats` | boolean | `false` | Append one `▌ **stats:**` line to each finished turn: cost, wall duration, CLI turn count, input/output/cache-read/cache-write tokens, and a permission-denial count when the turn had any, taken from the CLI's own `result`. Never on a compaction turn or a turn that ended in error. Its own text part, stripped from transcripts rebuilt for the CLI, so the model never sees it. The same numbers are logged at INFO regardless, and `modelUsage` plus `permission_denials` always reach `providerMetadata`. Reported cost is the CLI's figure, not a billing guarantee. |
|
|
147
147
|
| `forkSessions` | boolean | `false` | When a new opencode session turns out to be a fork of one this provider already served, branch the parent's Claude conversation with `claude --resume <parent> --fork-session` instead of re-rendering the whole thread as text into the first message. Measured on CLI 2.1.280 with haiku 4.5 over a ~13k-token thread: 814 cache tokens written and 39,710 read, against 22,355 written and 17,385 read for the replay, so $0.0058 against $0.0467 for that turn; the parent's transcript is byte-identical afterwards. Neither opencode major tells a provider that a session is a fork, so the parent is found by matching this prompt's history against what each sibling session key was last asked to continue. Off by default because a resumed Claude conversation reuses the system prompt recorded on its FIRST request (`--system-prompt-snapshot`, default `on`), so a forked session answers under the parent's appended system prompt rather than this turn's; measured directly, a parent seeded with codename ZEBRA and forked while passing QUAIL answered ZEBRA. Falls back to the replay, unchanged, for: another account, an unknown or released parent session id, a busy parent (live process, proxied call in flight, unanswered plan-mode question), a fork cut mid-conversation, a fork taken mid tool round trip, a different cwd / model / agent / effort / prompt-cache TTL, compaction, the interactive transport, an account-failover switch, and a `claude` whose `--help` does not advertise `--fork-session`. Recording costs nothing while the option is off. |
|
|
148
148
|
| `resumeAfterRestart` | boolean | `true` | After an opencode restart, resume the conversation's Claude session (`--resume`) instead of replaying the thread as text. Persists session id + conversation digest per session key in `$XDG_STATE_HOME/opencode-claude-code-plugin/claude-sessions.json` (0600, 256 entries, 30 days) after each successful turn. Resumes only on the same session key and binary, with the transcript on disk and the history equal to the recorded conversation plus Claude's reply; anything else (edit, revert, compaction, account switch, open tool round trip) replays. Not on compaction. Log: `resuming the claude session from before the restart` (NOTICE) or `not resuming ...` with a reason (INFO). `false` disables reading and writing. |
|
|
149
149
|
| `bridgeOpencodeSkills` | boolean | `false` | Stage the user's opencode skills for Claude's native Skill tool as `opencode-skills:<name>`, on the headless and interactive spawns (never compaction). Covers every root opencode reads: project `.opencode/`, `.claude/`, `.agents/` walking up, the opencode config dirs (`skill/` and `skills/`), and global `~/.claude/skills` and `~/.agents/skills` under opencode's own `OPENCODE_DISABLE_EXTERNAL_SKILLS` / `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` switches. Requires the CLI's `--help` to advertise `--plugin-dir`; otherwise no-op. Bridged skills are also listed in opencode's forwarded system prompt, so a large skill set costs prompt tokens twice, which is why it is off by default; `true` opts the user's skills in. Bundled skill staging ignores this option, but still requires flag support and successful discovery/staging. |
|
|
150
150
|
| `bridgeSkipNativeSkills` | boolean | `true` | Leave a skill unbridged when the Claude session already loads it: from `<CLAUDE_CONFIG_DIR>/skills`, the project's `.claude/skills`, or an installed plugin's `skills/`. Matched by resolved directory, by byte-identical SKILL.md, or (user/project scope only, since plugin skills are namespaced `<plugin>:<name>`) by name. A name match means `Skill("<name>")` answers from Claude's copy, not opencode's, so it is logged at WARN with both paths. The plugin scan reads `installed_plugins.json` and does not check whether the plugin is enabled. `false` bridges everything and reinstates the duplicates. |
|
|
151
|
-
| `
|
|
151
|
+
| `transport` | `"auto"` / `"headless"` / `"interactive"` | unset (headless) | Explicit choice wins over `interactive` and its env var. `auto` prefers headless and probes required headless flags with free help/argument parsing and no prompt. Only definitive unsupported flags select the PTY; timeout, auth failure, missing binary and unknown output stay headless. Never retries a submitted request. Explicit or automatic PTY selection requires `Bun.Terminal`, otherwise errors. Title stubs do not probe. `/compact` follows the selected transport. On the PTY, `read-only` is `--restricted` plus `dontAsk` (CLI 2.1.263+, refused below rather than weakened) and plan mode parks `ExitPlanMode`'s dialog for the operator. `/claude-code-doctor usage` reports the free headless check unavailable when those flags are absent; it does not run a PTY inference prompt. |
|
|
152
|
+
| `interactive` | boolean | unset (headless) | Legacy experimental PTY opt-in when `transport` is unset; explicit boolean wins over `CLAUDE_CODE_INTERACTIVE_TRANSPORT`. Needs `Bun.Terminal`; this legacy setting retains headless fallback without it. Wires the same proxy MCP server, `proxyTools` and `--disallowedTools` as headless (proxied tools, subagent dispatch and `question` run in opencode with its permission prompts); tools the TUI runs itself are pre-allowed by `interactiveAllowTools`. `permissionMode` is forwarded (not `bypassPermissions`); `read-only` and plan mode hold as described under `transport`; MCP hot reload, idle eviction, images and `/btw` (answered by a short-lived `--fork-session` TUI) work. The skill bridge, effort, prompt cache TTL and CLI hygiene env do apply. Stopping a reply sends Esc and keeps the session; a dead or evicted TUI is replaced with `--resume`; folder trust is accepted (moving off the default "No, exit"), a login or first-run screen fails the start with the fix, the usage-limit auto-continue is cancelled. Never enable to bypass a billing/access restriction. |
|
|
152
153
|
| `interactiveBypass` | boolean | `false` | Deprecated no-op. The TUI asks for a manual safety confirmation on `bypassPermissions`, so the plugin never passes it. |
|
|
153
|
-
| `interactiveAllowTools` | string[] | `["Bash", "Edit", "Write", "Read", "WebFetch"]` | With
|
|
154
|
-
| `interactiveSystemPrompt` | boolean | `true` | With
|
|
154
|
+
| `interactiveAllowTools` | string[] | `["Bash", "Edit", "Write", "Read", "WebFetch"]` | With the interactive transport: replaces the built-in pre-allow list. MCP wildcards from discovered bridge names plus `mcp__opencode_proxy__*` are added even with `[]`. Not a capability denylist; review permissions before enabling. A tool outside the list raises the TUI's permission dialog, which the transport denies with Esc (nobody is there to answer): the turn ends interrupted and the result's `permission_denials` names the tool. |
|
|
155
|
+
| `interactiveSystemPrompt` | boolean | `true` | With the interactive transport: append the plugin's own prompt. opencode's forwarded system prompt is deliberately not sent on this transport (it can trip Claude's third-party usage gate). `false` is for diagnostics only. |
|
|
155
156
|
| `logging` | object | see below | File logging plus how much reaches the operator. |
|
|
156
157
|
| `name` | string | unset | Low-level `createClaudeCode()` provider identity fallback after `providerID`, not the opencode display-name setting. Display name lives at `provider.<id>.name`; account expansion supplies its own label. Leave this option unset. |
|
|
157
158
|
| `providerID` | string | derived | Config hook writes the actual provider id (`claude-code` or `claude-code-work`). Do not override manually. |
|
|
@@ -202,7 +203,7 @@ their secret values. Arbitrary MCP `{env:NAME}` placeholders are outside this li
|
|
|
202
203
|
| `CLAUDE_CODE_SHOW_THINKING_SUMMARIES` | Headless spawn fills in `1` only if unset and neither disable flag is enabled. Any explicit value is preserved and suppresses the plugin's `--thinking-display` override; `0` requests suppression from the CLI. |
|
|
203
204
|
| `CLAUDE_CODE_COMPACTION_MODEL` | Nonblank, trimmed value wins over `compactionModel`. |
|
|
204
205
|
| `CLAUDE_CODE_DISABLE_FAST_MODE` | CLI-owned kill switch, conventionally `1`; plugin does not interpret it or change picker prices. Use the non-fast id if fast mode is disabled. |
|
|
205
|
-
| `CLAUDE_CODE_INTERACTIVE_TRANSPORT` | Fallback when `interactive`
|
|
206
|
+
| `CLAUDE_CODE_INTERACTIVE_TRANSPORT` | Fallback when both `transport` and `interactive` are absent: `1` enables; empty/`0`/`false`/`no`/`off` disable (case-insensitive). Explicit `transport` or `interactive: false` wins. |
|
|
206
207
|
| `CLAUDE_CODE_INTERACTIVE_BYPASS` | Deprecated no-op, like `interactiveBypass`. |
|
|
207
208
|
| `CLAUDE_CODE_START_WATCHDOG_MS` | Positive integer ms before a headless start or proxy-result continuation is considered silent; default 90000 for missing/invalid/nonpositive values. First expiry respawns, second errors. Bookkeeping-only output is not progress. Keep within timer range; do not lower for routine config checks. |
|
|
208
209
|
| `CLAUDE_CODE_RESULT_FALLBACK_MS` | Positive integer ms of stdout silence, after the CLI has produced output, before the turn is closed with no `result`; default 60000 for missing/invalid/nonpositive values. The close is announced in the reply as a `▌ **stream timeout:**` note, which is stripped from any rebuilt transcript. An aborted turn gets no note. |
|
|
@@ -290,6 +291,46 @@ but the plugin's disk-MCP hot-reload mechanism does not apply. This path is
|
|
|
290
291
|
verified offline with a fake CLI, not a paid live Claude probe. Fully restart
|
|
291
292
|
all opencode processes after provider/plugin changes; ask before live probes.
|
|
292
293
|
|
|
294
|
+
### Transport selection
|
|
295
|
+
|
|
296
|
+
To opt into fallback when the CLI removes headless flags, put this in the provider
|
|
297
|
+
options (or `providers.claude-code.settings` on opencode 2):
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{ "transport": "auto" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Unset keeps headless as the default. `"headless"` forces that path; `"interactive"`
|
|
304
|
+
requires the PTY directly. An explicit `transport` wins over the legacy `interactive`
|
|
305
|
+
boolean and `CLAUDE_CODE_INTERACTIVE_TRANSPORT`. Fully quit and relaunch opencode.
|
|
306
|
+
|
|
307
|
+
`"auto"` probes the required headless flags with `--help` before submitting work.
|
|
308
|
+
Only a definite missing flag selects PTY; timeout, authentication failure, a missing
|
|
309
|
+
binary or inconclusive output stays headless. Nothing retries a submitted request on
|
|
310
|
+
another transport. Explicit PTY and an automatic PTY selection require `Bun.Terminal`;
|
|
311
|
+
without it they fail clearly. This changes transport, not account access or billing.
|
|
312
|
+
|
|
313
|
+
Interactive transport takes text plus images (PNG/JPEG/GIF/WebP staged as 0600 files
|
|
314
|
+
whose paths the TUI attaches, deleted after the turn; PDFs and other blocks are
|
|
315
|
+
dropped with a warning) and reads completed transcript blocks, not token
|
|
316
|
+
deltas. It currently re-reads the transcript while polling. Normal interactive turns
|
|
317
|
+
do not forward opencode's system prompt, and have no account-failover form or
|
|
318
|
+
model-fallback chain. `/btw` is answered by a short-lived fork of the conversation
|
|
319
|
+
(`--resume <id> --fork-session`, same spawn arguments so the prompt cache is shared,
|
|
320
|
+
`dontAsk` with nothing pre-approved so it runs no tool), closed and its transcript copy
|
|
321
|
+
deleted once it answers; the main conversation is never written. MCP hot reload and `idleProcessTimeoutMs` work as on
|
|
322
|
+
headless (the TUI is replaced and resumed). `read-only` is `--restricted` plus
|
|
323
|
+
`--permission-mode dontAsk` and a read-only allow list (CLI 2.1.263+, refused below);
|
|
324
|
+
`plan` is forwarded and its `ExitPlanMode` approval is parked for the operator's next
|
|
325
|
+
message or the `planModeQuestion` form; other `permissionMode` values are forwarded
|
|
326
|
+
except `bypassPermissions`. Proxy tools use opencode's permissions, while CLI permission dialogs are
|
|
327
|
+
denied with Esc. `/compact` uses a fresh tool-free PTY with an empty strict MCP config,
|
|
328
|
+
explicit summary instructions and no session resume, proxy or skills.
|
|
329
|
+
|
|
330
|
+
The doctor remains available even if a PTY cannot start. Its optional usage lookup
|
|
331
|
+
reports unavailable when headless output is demonstrably unsupported; it does not
|
|
332
|
+
send `/cost` as an interactive model prompt.
|
|
333
|
+
|
|
293
334
|
### Two accounts
|
|
294
335
|
|
|
295
336
|
```json
|
|
@@ -426,7 +467,8 @@ which is 1 hour on a subscription. Claude Code also has a per-agent
|
|
|
426
467
|
`experimental.cacheTtl` and a `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL`: neither does
|
|
427
468
|
anything here, because both apply only to subagents the CLI runs through its own `Task`
|
|
428
469
|
tool, and this plugin disallows that tool by default so opencode runs the subagent
|
|
429
|
-
instead. An opencode subagent is a separate `claude
|
|
470
|
+
instead. An opencode subagent is a separate `claude` process using the selected
|
|
471
|
+
transport, which the CLI
|
|
430
472
|
counts as a main conversation. Use `cacheTtl: 5m` on short-lived workers that never
|
|
431
473
|
re-read the cache they wrote, since a 1-hour write is billed above a 5-minute one and
|
|
432
474
|
both come out of the same usage limit; leave a long-lived main session at the default.
|
|
@@ -816,8 +858,9 @@ options an applied preset replaced), `interactiveTransport`, `planModeQuestion`,
|
|
|
816
858
|
`anthropicApiKeyInEnv`, `claudeCli.path` and `.version`
|
|
817
859
|
(`not detected` means the binary did not answer `--version`, which also disables
|
|
818
860
|
version-gated flags). Cwd is a startup fallback snapshot, not the per-session spawn
|
|
819
|
-
directory. MCP names are disk discovery, not proof of live connectivity.
|
|
820
|
-
|
|
861
|
+
directory. MCP names are disk discovery, not proof of live connectivity. The startup
|
|
862
|
+
`interactiveTransport` boolean reports the legacy preference only, not the new
|
|
863
|
+
`transport` selection or proof that Bun PTY transport was used. Check a
|
|
821
864
|
relevant, redacted spawn/bridge entry for actual routing after an approved normal turn.
|
|
822
865
|
|
|
823
866
|
Useful log lines to search for (redact payloads): `spawning new claude process`,
|
|
@@ -864,6 +907,10 @@ else is flagged unsafe), and the last stderr of any child that produced some. Pr
|
|
|
864
907
|
prompt. A user-defined `claude-code-doctor` command is never overwritten. The name has
|
|
865
908
|
no space in it: opencode would read the second word as an argument.
|
|
866
909
|
|
|
910
|
+
Diagnostics remain accessible when Bun PTY support is missing or an interactive
|
|
911
|
+
permission posture is refused. The transport row is not evidence that a child started;
|
|
912
|
+
for `auto`, check an actual spawn entry after an approved normal turn.
|
|
913
|
+
|
|
867
914
|
The `plugin build` row, directly under `plugin`, is the one field that says whether the
|
|
868
915
|
version above it is the code actually answering. It compares the build this opencode
|
|
869
916
|
process loaded at startup with the one on disk right now and reads `current`,
|
|
@@ -916,6 +963,10 @@ runs the user's `SessionStart` hooks and takes a few seconds; say that when sugg
|
|
|
916
963
|
it. Do not propose `--bare` to skip the hooks: it never reads OAuth, so it reports
|
|
917
964
|
nothing about a subscription.
|
|
918
965
|
|
|
966
|
+
When the capability probe definitively finds headless output unsupported, this section
|
|
967
|
+
reports that plan usage is unavailable and directs the user to the Claude CLI. It does
|
|
968
|
+
not scrape a PTY screen or submit `/cost` for interactive inference.
|
|
969
|
+
|
|
919
970
|
### Filing an issue: /claude-code-doctor bundle
|
|
920
971
|
|
|
921
972
|
`/claude-code-doctor bundle` is what to tell a user to paste into a GitHub issue. It
|
|
@@ -958,7 +1009,9 @@ failed MCP server at session start, an `--mcp-config` entry the CLI skipped, and
|
|
|
958
1009
|
None of these are actions the plugin may take on the user's behalf; enabling paid
|
|
959
1010
|
usage or changing auth still needs approval.
|
|
960
1011
|
|
|
961
|
-
`/btw <question>` needs an existing
|
|
1012
|
+
`/btw <question>` needs an existing Claude conversation: headless with CLI 2.1.258+
|
|
1013
|
+
(the `side_question` control request), or interactive (a short-lived fork of the
|
|
1014
|
+
conversation answers it, a few seconds slower, the same cache-read cost).
|
|
962
1015
|
It asks through the side channel and keeps the answer in the conversation (inline
|
|
963
1016
|
when possible); it is excluded from Claude's normal turn history. It is still
|
|
964
1017
|
inference: zero reported usage for the aside does not mean free. User-defined `btw`
|
|
@@ -1060,7 +1113,8 @@ before stripping a key, switching accounts, enabling usage credits or changing t
|
|
|
1060
1113
|
- Do not enable `planModeQuestion` or `"Question"` without the user asking. `"Question"`
|
|
1061
1114
|
works (round-trip verified headless and as a real TUI form) but disables Claude's own
|
|
1062
1115
|
AskUserQuestion; `planModeQuestion` cannot fire at all on the headless transport,
|
|
1063
|
-
because CLI 2.1.258 does not offer `ExitPlanMode` under `--print
|
|
1116
|
+
because CLI 2.1.258 does not offer `ExitPlanMode` under `--print` (it does fire on the
|
|
1117
|
+
interactive transport). The historical
|
|
1064
1118
|
blanket TUI diagnosis was confounded by a local macOS notification hook; do not
|
|
1065
1119
|
repeat it as established fact.
|
|
1066
1120
|
- Do not make `--dangerously-skip-permissions` unconditional again. The CLI lets it
|