@khalilgharbaoui/opencode-claude-code-plugin 0.18.2 → 0.19.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 +167 -45
- package/dist/index.d.ts +72 -9
- package/dist/index.js +2186 -1405
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/claude-code-plugin/SKILL.md +27 -0
package/README.md
CHANGED
|
@@ -2,49 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@khalilgharbaoui/opencode-claude-code-plugin)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use Claude models inside [opencode](https://opencode.ai) by driving the official **Claude Code CLI** (`claude`) as a subprocess. opencode therefore inherits whatever authentication that CLI already holds: a Claude subscription login, an API key, Bedrock, or Vertex. This plugin never reads, stores, or replays an OAuth token of its own.
|
|
6
|
+
|
|
7
|
+
- **Your CLI's auth, untouched.** Because `claude` does the authenticating, there is no subscription token here to lift and replay against the Anthropic API. That replay is what proxy-style opencode plugins do, it is a practice Anthropic has disallowed for third-party tools in 2026, and it is structurally not something this plugin can do.
|
|
8
|
+
- **opencode stays in charge of your machine.** Bash, Edit, Write, WebFetch and subagent dispatch are executed by opencode, behind its permission prompts and audit log, rather than by Claude Code. See [Selective tool proxy](#selective-tool-proxy).
|
|
9
|
+
- **Headless by default, which has a billing consequence.** `claude --print` usage on a subscription plan draws from the separate Agent SDK / extra-usage allowance rather than from normal plan usage; API-key authentication is unaffected. See [Billing](#billing).
|
|
6
10
|
|
|
7
11
|
> Maintained fork of [`unixfox/opencode-claude-code-plugin`](https://github.com/unixfox/opencode-claude-code-plugin). Published as `@khalilgharbaoui/opencode-claude-code-plugin` on npm.
|
|
8
12
|
|
|
9
13
|
---
|
|
10
14
|
|
|
11
|
-
##
|
|
15
|
+
## Quickstart
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
# 1. Make sure `claude` is installed and logged in
|
|
15
|
-
claude --version
|
|
17
|
+
### 1. Install and log in the Claude Code CLI
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
The plugin drives an existing [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code); it does not bundle one. Check that `claude` is on your `$PATH` and authenticated:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude --version # e.g. 2.1.263 (Claude Code)
|
|
23
|
+
claude auth status # which account you are signed in as
|
|
24
|
+
claude auth login # run this if you are not signed in yet
|
|
18
25
|
```
|
|
19
26
|
|
|
27
|
+
`login`, `status` and `logout` are the `claude auth` subcommands as of 2.1.263. Run `claude auth --help` if your install differs.
|
|
28
|
+
|
|
29
|
+
### 2. Add the plugin to your opencode config
|
|
30
|
+
|
|
31
|
+
opencode reads a global config at `~/.config/opencode/opencode.json` (or `$XDG_CONFIG_HOME/opencode/` when that is set). A project-level `opencode.json` in your repo overrides the global one, and `OPENCODE_CONFIG=/path/to/config.json` points opencode at one specific file instead. Put the plugin in the global config so every project gets it:
|
|
32
|
+
|
|
20
33
|
```json
|
|
21
34
|
{
|
|
22
35
|
"plugin": ["@khalilgharbaoui/opencode-claude-code-plugin"]
|
|
23
36
|
}
|
|
24
37
|
```
|
|
25
38
|
|
|
26
|
-
That
|
|
39
|
+
That package spec is the whole install. Do **not** `npm install` the package yourself: opencode resolves and caches plugin packages on its own. You do not need a `provider` block either, unless you want to change one of the [options](#options-reference).
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
### 3. Restart opencode and verify
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
Quit opencode fully and relaunch it: plugins are loaded once, at process start, so a reload is not enough.
|
|
44
|
+
|
|
45
|
+
In the model picker you should now see a provider called **Claude Code (Default)** holding entries such as `Claude Haiku 4.5 (1×)`, `Claude Sonnet 5 (3×)` and `Claude Opus 5 (5×)`. The `(N×)` suffix is each model's list price relative to Haiku; see [Models](#models). Pick one and send a message.
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
If the provider does not appear, turn on the plugin's log file and look for its one startup line:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode
|
|
51
|
+
grep "plugin ready" ~/.local/share/opencode-claude-code/plugin.log
|
|
52
|
+
```
|
|
33
53
|
|
|
34
|
-
- [
|
|
35
|
-
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated (`claude` on your `$PATH`)
|
|
36
|
-
- Node 18+ / Bun
|
|
54
|
+
That single `NOTICE: claude-code plugin ready` entry reports the plugin version, the `claude` binary and version it found, the directory it will spawn in, and which providers registered. [Startup diagnostics](#startup-diagnostics) explains every field.
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
### Not seeing a version you just upgraded to?
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
opencode resolves the `@latest` plugin spec once and freezes the concrete version into its own package cache, so restarting never re-resolves the tag. Delete the cache entry and relaunch:
|
|
41
59
|
|
|
42
60
|
```bash
|
|
43
|
-
|
|
61
|
+
rm -rf ~/.cache/opencode/packages/@khalilgharbaoui/opencode-claude-code-plugin@latest
|
|
44
62
|
```
|
|
45
63
|
|
|
46
|
-
Then add it to `opencode.json` as shown in the TL;DR.
|
|
47
|
-
|
|
48
64
|
### Local development
|
|
49
65
|
|
|
50
66
|
```bash
|
|
@@ -62,11 +78,13 @@ In your `opencode.json`, point at the local build with a `file://` URL:
|
|
|
62
78
|
}
|
|
63
79
|
```
|
|
64
80
|
|
|
81
|
+
CI installs and builds on **Node 24** (`.github/workflows/publish.yml`), which is the only version this package is built against. `package.json` declares no `engines` range, so older Node versions are untested rather than deliberately unsupported. opencode itself may run under Bun; the [interactive transport](#interactive-transport-experimental) requires that.
|
|
82
|
+
|
|
65
83
|
---
|
|
66
84
|
|
|
67
85
|
## Models
|
|
68
86
|
|
|
69
|
-
The plugin auto-registers the following
|
|
87
|
+
The plugin auto-registers the following, and they appear in the model picker with no extra config: Haiku 4.5, Sonnet 4.5/4.6/5, Opus 4.5/4.6/4.7/4.8/5 (plus two fast-mode Opus entries), Fable 5/5.1 and Mythos 5/5.1, each except Haiku carrying `low` / `medium` / `high` / `xhigh` / `max` reasoning variants.
|
|
70
88
|
|
|
71
89
|
| ID | Display name | Context | Output | Reasoning variants | Price × |
|
|
72
90
|
|---|---|---|---|---|---|
|
|
@@ -120,8 +138,11 @@ Variants set the underlying reasoning effort. They're regular opencode model var
|
|
|
120
138
|
|
|
121
139
|
## Billing
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
By default this plugin drives Claude Code headlessly (the Agent SDK path, `claude --print`). Since June 2026, headless usage on a Claude subscription plan draws from a separate Agent SDK credit / extra usage rather than from normal plan usage. Authenticating the CLI with an API key is unaffected by that policy and bills as ordinary API usage.
|
|
142
|
+
|
|
143
|
+
Anthropic's own page is the authoritative and current source, including the amounts, which change: <https://support.claude.com/en/articles/15036540-use-the-claude-agent-sdk-with-your-claude-plan>
|
|
144
|
+
|
|
145
|
+
Two things in this plugin interact with the above. [`ignoreAnthropicApiKey`](#options-reference) stops a stray `ANTHROPIC_API_KEY` in your environment from silently redirecting the CLI onto pay-as-you-go API billing. The experimental [interactive transport](#interactive-transport-experimental) drives the real `claude` TUI instead of `--print`, which bills as normal plan usage.
|
|
125
146
|
|
|
126
147
|
---
|
|
127
148
|
|
|
@@ -230,10 +251,10 @@ That beats whatever effort the call arrived with. It has to, because opencode re
|
|
|
230
251
|
|
|
231
252
|
An agent that declares nothing keeps the inherited effort, so this changes nothing until a file asks for it. An unrecognised level is refused and the inherited one kept, since the CLI rejects a level it does not know. Compaction is exempt: its summary always gets the full budget.
|
|
232
253
|
|
|
233
|
-
To force an **account** rather than a model, pin the full string. Both halves are needed, because the provider selects the account's config dir and the `@account` marker is what the model was registered under for that provider:
|
|
254
|
+
To force an **account** rather than a model, pin the full string. This only applies if you declared [`accounts`](#multiple-claude-code-accounts) in the first place; with the default single-account setup there is nothing to pin. Both halves are needed, because the provider selects the account's config dir and the `@account` marker is what the model was registered under for that provider:
|
|
234
255
|
|
|
235
256
|
```yaml
|
|
236
|
-
model: claude-code-
|
|
257
|
+
model: claude-code-work/claude-opus-5@work
|
|
237
258
|
```
|
|
238
259
|
|
|
239
260
|
### Options reference
|
|
@@ -259,33 +280,61 @@ model: claude-code-appical/claude-opus-5@appical
|
|
|
259
280
|
|
|
260
281
|
| Option | Type | Default | Description |
|
|
261
282
|
|---|---|---|---|
|
|
262
|
-
| `cliPath` | string | `
|
|
263
|
-
| `accounts` | string[] | – | Optional
|
|
264
|
-
| `cwd` | string |
|
|
265
|
-
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to `claude`.
|
|
266
|
-
| `permissionMode` | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` | – | Forwarded to `claude --permission-mode`. |
|
|
283
|
+
| `cliPath` | string | `"claude"` | Path to the `claude` executable (a binary, not a shell command with flags). opencode's config hook seeds this with `"claude"`, so under opencode this default always applies; `CLAUDE_CLI_PATH` is only consulted when `createClaudeCode()` is called directly and the option is absent. Account providers wrap it with a generated script; never point it at one of those yourself. |
|
|
284
|
+
| `accounts` | string[] | – | **Optional.** Most setups need no accounts at all: with this unset you get a single `Claude Code (Default)` provider on your normal `~/.claude` login. Supply names only to run several Claude logins side by side; `default` stays implicit, so `["work", "personal"]` gives you `Claude Code (Default)`, `Claude Code (Work)` and `Claude Code (Personal)`. See [Multiple Claude Code accounts](#multiple-claude-code-accounts). |
|
|
285
|
+
| `cwd` | string | see description | Working directory for the spawned CLI. Resolved **lazily per request**, first match winning: this explicit value, then the opencode session's own `directory` (so `opencode serve` and the web UI spawn in the right project even though one server handles many), then `process.cwd()` when it is a real directory, then the project directory captured at plugin init (this rescues macOS GUI launches, where `process.cwd()` is `/`), and finally `process.cwd()` regardless. [Startup diagnostics](#startup-diagnostics) reports which tier won. Session tier contributed by [@galvani](https://github.com/galvani). |
|
|
286
|
+
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to `claude`. It is still passed when `proxyTools` is set: proxied calls go through opencode's permission system regardless, but unproxied CLI built-ins do not. The one case where the flag is dropped is `permissionMode: "plan"`, because the CLI lets the skip flag override plan mode outright. See [Plan mode](#plan-mode). |
|
|
287
|
+
| `permissionMode` | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` | – | Forwarded to headless `claude --permission-mode`. `"plan"` also suppresses `--dangerously-skip-permissions` (see the row above). Not version-gated, so check that your installed CLI accepts the value. The [interactive transport](#interactive-transport-experimental) does not forward it. |
|
|
288
|
+
| `defaultSubagentModel` | string | – | Model that plugin-discovered `mode: subagent` agents run on when their own definition pins nothing. The caller's account is kept; only the model name changes. An agent's own `forceModel` wins over it, and an unknown id is refused rather than spawned. Unset means no implicit override at all. See [Subagents: your account, their model](#subagents-your-account-their-model). |
|
|
267
289
|
| `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Claude built-in tools to route through opencode's executor + permission UI. Opt-in extras: `"Question"`, `"Compress"`. See [Selective tool proxy](#selective-tool-proxy). |
|
|
268
290
|
| `extraDisallowedTools` | string[] | – | Extra Claude built-ins to switch off with `--disallowedTools`, on top of what `proxyTools` implies. Claude's names, e.g. `["NotebookEdit"]`. See [Closing a tool with no proxy](#closing-a-tool-with-no-proxy). |
|
|
269
291
|
| `proxyToolTimeoutMs` | `Record<string, number>` | – | Per-tool proxy call deadline in ms, keyed by proxy tool name (`bash`, `task`, …). Defaults: 10 min flat, `task` → 60 min. For `bash`, the call's own `input.timeout` is honoured on top (`max(resolved, input.timeout)`). See [Selective tool proxy](#selective-tool-proxy). |
|
|
270
|
-
| `planModeQuestion` | boolean | `false` | Route `ExitPlanMode` approval through opencode's native `question` tool instead of a text "(yes/no)" prompt. Opt-in
|
|
292
|
+
| `planModeQuestion` | boolean | `false` | Route `ExitPlanMode` approval through opencode's native `question` tool instead of a text "(yes/no)" prompt. Opt-in, and currently unreachable on the default headless transport, which is not offered an `ExitPlanMode` tool at all. See [Plan mode](#plan-mode). |
|
|
271
293
|
| `controlRequestBehavior` | `allow` \| `deny` | `allow` | Default response when `skipPermissions: false` and Claude sends a `can_use_tool` control request. |
|
|
272
294
|
| `controlRequestToolBehaviors` | `Record<string, "allow" \| "deny">` | – | Per-tool override for `can_use_tool`. Example: `{ "Bash": "deny", "Read": "allow" }`. |
|
|
273
295
|
| `controlRequestDenyMessage` | string | built-in message | Message returned to Claude on a deny. |
|
|
274
296
|
| `bridgeOpencodeMcp` | boolean | `true` | Auto-translate your opencode `mcp` block into Claude's `--mcp-config`. See [MCP bridge](#mcp-bridge). |
|
|
275
297
|
| `mcpConfig` | string \| string[] | – | Extra `--mcp-config` paths/JSON passed alongside the bridged config. |
|
|
276
298
|
| `strictMcpConfig` | boolean | `false` | Pass `--strict-mcp-config` so Claude loads **only** the configured servers and ignores `~/.claude/settings.json`. |
|
|
299
|
+
| `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`. |
|
|
300
|
+
| `proxyOpencodeMcpTools` | boolean | `true` | Route the MCP tools discovered from opencode through the in-process `opencode_proxy` server instead of bridging them straight into Claude's `--mcp-config`. With both layers pointed at the same server, direct bridging executes every call twice, once in Claude's own MCP child process and once in opencode; proxying keeps opencode as the single execution site while preserving its permission prompts and tool rows. Falls back to direct bridging when discovery is unavailable, so do not treat it as an exactly-once guarantee for write-capable tools. |
|
|
277
301
|
| `webSearch` | `"claude"` \| `"disabled"` \| `<tool>` | `"claude"` | Routing for Claude's built-in `WebSearch`. See [WebSearch routing](#websearch-routing). |
|
|
278
302
|
| `multiStepContinuation` | boolean | `true` | Append a system-prompt hint nudging Claude to chain tool calls within one turn instead of pausing between subtasks. Each opencode turn boundary requires the user to manually press "continue", so for multi-step tasks this reduces friction. Set `false` to disable. |
|
|
279
303
|
| `autoContinueIncompleteTurns` | boolean \| `"smart"` | `"smart"` | Smartly continue incomplete Claude CLI results inside the same opencode turn. Reduces manual "continue" presses when Claude ends after reasoning/tool activity without a useful final answer. Set `false` to disable. |
|
|
280
304
|
| `compactionModel` | string | `"claude-haiku-4-5"` | Model used when opencode invokes `/compact`. Override per-process via the `CLAUDE_CODE_COMPACTION_MODEL` env var (env wins over config). See [Compaction](#compaction). |
|
|
281
|
-
| `ignoreAnthropicApiKey` | boolean | `false` | Strip `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` from every spawned `claude` process so it authenticates with your logged-in subscription instead of pay-as-you-go API billing. The plugin warns once at startup whenever an API key is detected, regardless of this setting. See [Billing](#billing
|
|
305
|
+
| `ignoreAnthropicApiKey` | boolean | `false` | Strip `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` from every spawned `claude` process so it authenticates with your logged-in subscription instead of pay-as-you-go API billing. The plugin warns once at startup whenever an API key is detected, regardless of this setting. See [Billing](#billing). |
|
|
282
306
|
| `idleProcessTimeoutMs` | number | – | Kill a retained headless Claude worker after this many idle milliseconds following a completed turn. The session id is preserved for `--resume`; a new turn cancels the timer. Values above Node's maximum timer delay (`2147483647`) are ignored. Omit or set to `0` to retain workers until LRU eviction. Interactive transport is excluded. Contributed by [@bernardofortes](https://github.com/bernardofortes). |
|
|
283
307
|
| `bridgeOpencodeSkills` | boolean | `false` | Expose your opencode skills to Claude's native `Skill` tool. See [Skill bridge](#skill-bridge). Written by [@broskees](https://github.com/broskees). |
|
|
284
|
-
| `
|
|
308
|
+
| `logging` | object | all defaults | The plugin's own logger, four independent fields: `file` (boolean, default `false`), `dir` (string, default `~/.local/share/opencode-claude-code/`), `mode` (`"silent"` \| `"debug"`, default `"silent"`) and `level` (`"debug"` \| `"info"` \| `"notice"` \| `"warn"` \| `"error"`, default `"info"`). Goes under `provider.claude-code.options` like every other row here. See [Logging](#logging). |
|
|
309
|
+
| `turnStats` | boolean | `false` | Append a one-line cost / duration / cache footer to each finished turn. See [Per-turn stats](#per-turn-stats). |
|
|
310
|
+
| `interactive` | boolean | `false` | **Experimental.** Drive the interactive `claude` TUI (subscription billing) instead of headless `--print`. Requires opencode running under Bun with PTY support; silently falls back to headless otherwise. The tool proxy, `permissionMode` and `/btw` are all unavailable on it, so read [What it does not support](#what-it-does-not-support) before enabling. Env: `CLAUDE_CODE_INTERACTIVE_TRANSPORT=1`. |
|
|
285
311
|
| `interactiveBypass` | boolean | `false` | Deprecated/no-op with `interactive`: Claude Code's TUI shows a manual safety confirmation for `bypassPermissions`, so the plugin intentionally does not pass it. |
|
|
286
312
|
| `interactiveAllowTools` | string[] | `["Bash", "Edit", "Write", "Read", "WebFetch"]` | With `interactive`: built-in tools pre-allowed without prompting (replaces the default list). MCP server wildcards (`mcp__<server>__*`) are always added from the bridged config. |
|
|
287
313
|
| `interactiveSystemPrompt` | boolean | `true` | With `interactive`: append this plugin's CLI/AGENTS/continuation prompt via `--append-system-prompt-file`. The transport intentionally does not forward opencode's own system prompt, because it can trigger Claude Code's third-party-app usage gate on subscription accounts. Set `false` only for diagnostics. |
|
|
288
314
|
|
|
315
|
+
### Environment variables
|
|
316
|
+
|
|
317
|
+
Every variable the plugin itself reads, in one place. Config is read once at opencode startup, so these are the way to change behaviour for a single run without editing `opencode.json`. Claude Code's own variables (`CLAUDE_CODE_DISABLE_THINKING` and friends) are passed through untouched and are listed under [Extended thinking](#extended-thinking).
|
|
318
|
+
|
|
319
|
+
| Variable | Read by | Effect |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| `CLAUDE_CLI_PATH` | provider factory | Fallback `claude` path when `cliPath` is absent. Under opencode the config hook always supplies `cliPath`, so this only applies to direct `createClaudeCode()` use. |
|
|
322
|
+
| `CLAUDE_CODE_COMPACTION_MODEL` | compaction spawn | Model for `/compact`. Wins over the `compactionModel` option. See [Compaction](#compaction). |
|
|
323
|
+
| `CLAUDE_CODE_INTERACTIVE_TRANSPORT` | transport selection | `1` turns on the experimental [interactive transport](#interactive-transport-experimental) for one process, same as `interactive: true`. |
|
|
324
|
+
| `CLAUDE_CODE_INTERACTIVE_BYPASS` | transport selection | Requests `bypassPermissions` in interactive mode. Deliberately ignored, with a warning, for the reason in the `interactiveBypass` row above. |
|
|
325
|
+
| `CLAUDE_CODE_START_WATCHDOG_MS` | start watchdog | Milliseconds a `claude` process may stay completely silent on stdout after a turn is written, or after a proxy tool result should have resumed it, before the plugin acts. First expiry respawns the process and resumes the session; a second ends the turn with an error rather than hanging. Default `90000`; a positive integer is required and anything else falls back to that. Mainly a knob for reproducing the hang. |
|
|
326
|
+
| `OPENCODE_CLAUDE_CODE_LOG_FILE` | logger | `1` writes the log file, `0` forces it off even when `logging.file` is `true`. See [Logging](#logging). |
|
|
327
|
+
| `OPENCODE_CLAUDE_CODE_LOG_DIR` | logger | Directory for the log file, overriding `logging.dir`. |
|
|
328
|
+
| `OPENCODE_CLAUDE_CODE_LOG_LEVEL` | logger | Minimum level to emit, overriding `logging.level`. An unrecognised value falls through to config. |
|
|
329
|
+
| `DEBUG` | logger | `DEBUG=opencode-claude-code` promotes the logger to `mode: "debug"`, echoing every emitted level to opencode's TUI. |
|
|
330
|
+
| `OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP` | startup cleanup | `1` skips the one-time removal of a stale **unscoped** `opencode-claude-code-plugin` install from opencode's plugin cache. That old package is a different artifact that shadows this scoped one when both are present; set this if you are deliberately keeping it. |
|
|
331
|
+
| `OPENCODE_WORKTREE` | MCP bridge | Overrides worktree-root detection, which otherwise walks up from the working directory looking for a `.git` entry. |
|
|
332
|
+
| `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). |
|
|
333
|
+
| `OPENCODE_VERSION` | startup diagnostics | Reported as the opencode version when set, sparing the plugin a `--version` spawn. Diagnostics only. |
|
|
334
|
+
| `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). |
|
|
335
|
+
|
|
336
|
+
The plugin also honours the usual path conventions rather than defining its own: `XDG_CONFIG_HOME` and `XDG_CACHE_HOME` (falling back to `~/.config` and `~/.cache`), `HOME` / `USERPROFILE`, and Claude Code's `CLAUDE_CONFIG_DIR` when the interactive transport needs to find the session transcript. Account providers set `CLAUDE_CONFIG_DIR` themselves for the process they spawn.
|
|
337
|
+
|
|
289
338
|
### Overriding model metadata
|
|
290
339
|
|
|
291
340
|
To rename a model, change a limit, or add a custom one:
|
|
@@ -312,7 +361,7 @@ Anything you supply is merged on top of the defaults; you don't need to redeclar
|
|
|
312
361
|
|
|
313
362
|
## Interactive transport (experimental)
|
|
314
363
|
|
|
315
|
-
By default the plugin spawns `claude --print` (headless). From **June 15, 2026** that usage bills against the separate [Agent SDK credit](#billing
|
|
364
|
+
By default the plugin spawns `claude --print` (headless). From **June 15, 2026** that usage bills against the separate [Agent SDK credit](#billing) on subscription plans. The interactive transport instead drives the real interactive `claude` TUI — which bills as **normal plan usage** — under a native PTY inside opencode's Bun runtime, types your prompt into it, and streams the session transcript (`~/.claude/projects/<cwd>/<session-id>.jsonl`) back through the same pipeline the headless transport uses.
|
|
316
365
|
|
|
317
366
|
```json
|
|
318
367
|
"options": { "interactive": true }
|
|
@@ -333,12 +382,21 @@ Or per-process: `CLAUDE_CODE_INTERACTIVE_TRANSPORT=1`.
|
|
|
333
382
|
|
|
334
383
|
Set `interactiveSystemPrompt: false` only for diagnostics. While disabled, the interactive session will not receive the plugin's CLI context, AGENTS.md guidance, or continuation hints.
|
|
335
384
|
|
|
336
|
-
### What
|
|
385
|
+
### What it does not support
|
|
386
|
+
|
|
387
|
+
This is the part to read before turning it on. Three whole features of this plugin are simply absent on the interactive transport:
|
|
388
|
+
|
|
389
|
+
- **No tool proxy.** The interactive spawn starts no proxy MCP server at all, so `mcp__opencode_proxy__bash`, `edit`, `write`, `webfetch`, `task`, `task_batch`, `question` and `compress` do not exist for that session. Claude uses its own built-in tools directly, which means opencode does not execute them, does not prompt for them, and does not log them. Everything in [Selective tool proxy](#selective-tool-proxy) applies to the headless transport only.
|
|
390
|
+
- **No `permissionMode`.** The interactive spawn never passes your `permissionMode` to the CLI, so `"plan"` and the rest have no effect there. Permission handling is the pre-allow list described below and nothing else.
|
|
391
|
+
- **No [`/btw`](#side-questions-with-btw).** Side questions ride Claude Code's `side_question` control protocol over the headless process's stdio. Asking one in an interactive session returns an error telling you so.
|
|
392
|
+
|
|
393
|
+
### What else is different
|
|
337
394
|
|
|
338
395
|
- **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.
|
|
339
396
|
- **Input is text-only:** images and other non-text blocks are dropped (with a logged warning); tool results are rendered as labeled text.
|
|
340
397
|
- **Output granularity:** text arrives per transcript record, not token-by-token, so it can feel chunkier than headless streaming.
|
|
341
398
|
- **Turn timeout:** a turn that produces no terminal stop within 30 minutes is reported honestly as an error result (visible truncation), not silently ended.
|
|
399
|
+
- **No idle eviction:** `idleProcessTimeoutMs` does not apply to interactive sessions.
|
|
342
400
|
- `/compact` always uses the headless transport regardless of this setting.
|
|
343
401
|
|
|
344
402
|
---
|
|
@@ -431,6 +489,8 @@ It is the one proxy tool opencode never sees. The call is answered inside the pl
|
|
|
431
489
|
|
|
432
490
|
Without it, the appended system prompt tells the model that `compress` is unavailable and to ignore instructions that ask for it, which is the right answer when nothing implements it.
|
|
433
491
|
|
|
492
|
+
The store, the interceptor and the two prompt variants are covered by tests, but the full "model calls compress, the next turn really is a fresh process carrying only the summary" round-trip has not been verified against a live CLI. Treat it as working-but-unproven and check the plugin log the first time you rely on it.
|
|
493
|
+
|
|
434
494
|
Only those seven values are actually proxied; anything else you put in `proxyTools` is ignored. Proxying `Edit` also disables `MultiEdit` — opencode has no batched-edit equivalent, so Claude is forced to fan out into single `Edit` calls that each flow through the permission UI. The `"Question"` proxy is version-gated on opencode's built-in `question` tool: on builds that lack the registry entry the def is silently dropped (a forwarded call would otherwise render as `⚙ invalid`), so add it only on opencode versions that ship the `question` tool.
|
|
435
495
|
|
|
436
496
|
Without `"Task"` in `proxyTools`, Claude's built-in `Agent` tool stays enabled and Claude orchestrates subagents internally with no opencode child-session visibility. To opt out of all proxying, including Task, use an explicit empty list:
|
|
@@ -483,7 +543,7 @@ sqlite3 ~/.local/share/opencode/opencode.db \
|
|
|
483
543
|
|
|
484
544
|
### What you get with proxying on
|
|
485
545
|
|
|
486
|
-
- opencode's **permission prompts** for every Bash/Edit/Write/WebFetch call
|
|
546
|
+
- opencode's **permission prompts** for every Bash/Edit/Write/WebFetch call. The default `--dangerously-skip-permissions` is still passed to `claude`, but it only governs Claude's own built-in tools; a proxied call is executed by opencode and answers to opencode's rules instead. Built-ins that are neither proxied nor listed in `extraDisallowedTools` do run under that flag.
|
|
487
547
|
- opencode's **audit log** captures the calls.
|
|
488
548
|
- Per-tool **policy rules** in opencode apply.
|
|
489
549
|
|
|
@@ -553,6 +613,50 @@ Notes:
|
|
|
553
613
|
|
|
554
614
|
Fully restart opencode after upgrading to load the command and runtime changes. Other providers do not gain Claude's native side-question behavior from this command.
|
|
555
615
|
|
|
616
|
+
## Plugin health with /claude-code-doctor
|
|
617
|
+
|
|
618
|
+
```text
|
|
619
|
+
/claude-code-doctor
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
Prints, in the chat, what the plugin currently thinks is happening. The plugin answers it itself: no model is called, nothing is billed, and the reply reports 0 tokens. It is the thing to paste into a bug report.
|
|
623
|
+
|
|
624
|
+
It carries the startup-diagnostics fields (plugin version, opencode version, `claude` path and version, the working directory and which resolution tier picked it, providers, accounts, `proxyTools`, the on-disk MCP servers, transport, whether an `ANTHROPIC_API_KEY` is present) plus the live runtime state the startup block cannot know:
|
|
625
|
+
|
|
626
|
+
- every live `claude` child, by opencode session id and model, with its pid, whether a turn is in flight, how long it has been up, and the effort it was spawned at,
|
|
627
|
+
- every pending proxy call, with the tool, the call id, how long it has waited, and its deadline,
|
|
628
|
+
- each proxy server's URL with one unauthenticated `initialize` posted to it: `401, good` is the patched behaviour, and anything else is flagged unsafe with the fix (restart every opencode window, since a window opened before 0.13.2 keeps serving an open port). See [Proxy endpoint security](#proxy-endpoint-security).
|
|
629
|
+
|
|
630
|
+
Nothing secret goes in it: not the proxy bearer token, not the value of `ANTHROPIC_API_KEY`, not the system prompt, not a pending call's arguments. A `claude-code-doctor` command you defined yourself is never overwritten. The name has no space in it because opencode reads everything after the first space as the command's arguments. The whole exchange is kept out of any transcript replayed to the CLI, like a `/btw` pair.
|
|
631
|
+
|
|
632
|
+
## Per-turn stats
|
|
633
|
+
|
|
634
|
+
Off by default. With `turnStats: true`:
|
|
635
|
+
|
|
636
|
+
```text
|
|
637
|
+
▌ **stats:** $0.0123 · 4.2 s · 2 CLI turns · in 1.2k · out 812 · cache read 45.1k · cache write 2.0k
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
One line at the end of a finished turn, from the numbers the CLI already reports on its `result`. Notes:
|
|
641
|
+
|
|
642
|
+
- Never on a `/compact` turn (the footer would be appended to what opencode stores as the summary) and never on a turn that ended in error, where the error is the thing to read.
|
|
643
|
+
- It is its own text part led by `▌ **stats:**`, and the plugin strips it again if the conversation is ever replayed into a fresh Claude Code process. The model never reads its own accounting.
|
|
644
|
+
- Token counts are the turn's totals, which is what matches the cost. They are deliberately not the same numbers opencode's context gauge shows, which use the last tool-use iteration so the window is not inflated.
|
|
645
|
+
- The cost is what the CLI reported for the turn, not a billing guarantee.
|
|
646
|
+
|
|
647
|
+
The same numbers are logged at INFO whatever this option is set to, and `total_cost_usd`, `duration_ms`, `duration_api_ms`, `num_turns`, `usage`, `modelUsage` and `permission_denials` always reach `providerMetadata` (denials by tool name and id only, never their inputs).
|
|
648
|
+
|
|
649
|
+
## Things the CLI says that are no longer silent
|
|
650
|
+
|
|
651
|
+
Four Claude Code stream events used to reach nothing but a debug log:
|
|
652
|
+
|
|
653
|
+
- **A rate-limit rejection.** When the CLI reports `status: "rejected"` (or a rejected extra-usage state), the turn now carries a `▌ **rate limit:**` line naming the window, the reason extra usage is unavailable, when it resets, and the four things that can be done about it. Warned once per identity per process. See [Billing](#billing-change-june-15-2026-agent-sdk-credit).
|
|
654
|
+
- **A context compaction Claude Code did on its own.** A `▌ **context compacted:**` note says so, with the before and after token counts, so an answer that suddenly forgets the start of the conversation has a visible cause.
|
|
655
|
+
- **A `result` whose subtype is not `success`** (`error_max_turns`, `error_during_execution`, …). The subtype is named in the transcript and the turn finishes as an error instead of an ordinary reply.
|
|
656
|
+
- **A CLI-executed tool that failed.** Its result is forwarded with the AI SDK's error flag, so opencode renders the row as failed rather than as a success whose output happens to be an error message.
|
|
657
|
+
|
|
658
|
+
At session start the plugin also warns once per process for each MCP server Claude Code could not connect (its tools are simply absent otherwise) and once when the CLI's own `apiKeySource` says an API key is in effect, which is the field that tells you pay-as-you-go billing is happening. See [`ignoreAnthropicApiKey`](#options-reference).
|
|
659
|
+
|
|
556
660
|
## Configuration skill
|
|
557
661
|
|
|
558
662
|
The package includes a `claude-code-plugin` skill so your agent can configure it without asking you to navigate all its options. Ask, for example:
|
|
@@ -652,7 +756,8 @@ Each chat keeps a long-lived `claude` subprocess so the model retains its native
|
|
|
652
756
|
- **Resumed chat after restart** → in-memory state is gone; a new process spawns and the conversation history is summarized and prepended.
|
|
653
757
|
- **Abort (Esc / Ctrl+C)** → the plugin sends the Claude CLI a stream-json `interrupt` control request, so the CLI actually stops generating and running tools instead of finishing the abandoned turn on your bill. The process stays alive for the next message in that chat. If a turn is somehow still running when the next one starts, it is interrupted first (5 s cap). Contributed by [@broskees](https://github.com/broskees).
|
|
654
758
|
- **Idle timeout** → when `idleProcessTimeoutMs` is configured, a completed headless turn arms an eviction timer; reuse cancels it, and eviction preserves the session id for `--resume`.
|
|
655
|
-
- **Cap**: 16 active processes, LRU eviction.
|
|
759
|
+
- **Cap**: 16 active processes, LRU eviction. A process that is mid-turn is never the victim: eviction takes the oldest **idle** one, and when every process is busy it evicts nothing and warns instead, so a running answer is never truncated to make room.
|
|
760
|
+
- **Crash** → if the CLI dies mid-turn (no terminal `result` line), the turn ends with a visible error naming the exit code or signal and the last stderr the CLI wrote, not a silent `stop` that reads as a short but finished answer. An abort you asked for is not reported this way.
|
|
656
761
|
|
|
657
762
|
---
|
|
658
763
|
|
|
@@ -769,7 +874,7 @@ What you see is a **summary** of the model's thinking, not the raw chain-of-thou
|
|
|
769
874
|
|
|
770
875
|
### Reasoning effort
|
|
771
876
|
|
|
772
|
-
Each model exposes `low` / `medium` / `high` / `xhigh` / `max
|
|
877
|
+
Each model exposes five picker variants, `low` / `medium` / `high` / `xhigh` / `max`. An agent's own `reasoningEffort` frontmatter accepts six values: those five plus `minimal`, which maps to the CLI's `low`. The plugin hands the level to the CLI as `CLAUDE_CODE_EFFORT_LEVEL` at spawn, which Claude Code treats as the session-wide override: it beats the `effortLevel` in that account's `settings.json` and a shell export of the same variable. Effort is fixed for the life of a `claude` process, so it is part of the session key. Changing effort retires the previous effort's process and remembered transcript ID before replaying the conversation into a fresh process. Switching back cannot resume stale context; same-effort streaming turns still reuse their process. This reset is scoped to the same directory, model, provider/account, agent, and conversation. If the previous effort still has pending work (including tool results, plan approval, recovery, or `/btw`), the switch is rejected: finish that work at its original effort first. Title, compaction, and `/btw` calls do not trigger effort resets.
|
|
773
878
|
|
|
774
879
|
Earlier versions injected a thinking keyword such as `(ultrathink)` into the user message instead. Claude Code stopped recognising every keyword except `ultrathink`, so that path is gone and nothing is appended to your messages any more. Compaction skips request and agent effort overrides, but still inherits a shell-level `CLAUDE_CODE_EFFORT_LEVEL` when set.
|
|
775
880
|
|
|
@@ -815,14 +920,24 @@ to file only and lets WARN/ERROR bubble in the TUI (they always do).
|
|
|
815
920
|
`mode: "debug"` additionally echoes every emitted level to the TUI (which
|
|
816
921
|
opencode surfaces as warning bubbles).
|
|
817
922
|
|
|
923
|
+
`logging` is an ordinary provider option, so it goes under `provider.claude-code.options` like every other one. Keying it on the package name instead is the common mistake: opencode accepts that config without complaint and the plugin never reads it, so you get no log and no error.
|
|
924
|
+
|
|
818
925
|
**Recommended dev setup** — capture audit trail to disk, keep TUI quiet:
|
|
819
926
|
|
|
820
927
|
```jsonc
|
|
821
|
-
|
|
822
|
-
"
|
|
928
|
+
{
|
|
929
|
+
"provider": {
|
|
930
|
+
"claude-code": {
|
|
931
|
+
"options": {
|
|
932
|
+
"logging": { "file": true }
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
823
936
|
}
|
|
824
937
|
```
|
|
825
938
|
|
|
939
|
+
The snippets below abbreviate to the `logging` value alone; each one belongs at that same path.
|
|
940
|
+
|
|
826
941
|
**Full firehose for deep debugging** (every DEBUG stream event captured):
|
|
827
942
|
|
|
828
943
|
```jsonc
|
|
@@ -881,9 +996,11 @@ grep "plugin ready" ~/.local/share/opencode-claude-code/plugin.log
|
|
|
881
996
|
Reading it:
|
|
882
997
|
|
|
883
998
|
- **`cwd.source`** is which rule picked the working directory Claude will be
|
|
884
|
-
spawned in
|
|
999
|
+
spawned in: `configured` (you pinned `options.cwd`), `process` (normal),
|
|
885
1000
|
`captured` (`process.cwd()` was unusable and opencode's project directory
|
|
886
1001
|
rescued it, the macOS GUI-launch case), or `unresolved` (neither worked).
|
|
1002
|
+
The per-session tier that `opencode serve` uses is resolved per call and so
|
|
1003
|
+
cannot appear here; this line mirrors the synchronous order only.
|
|
887
1004
|
- **`claudeCli.version`** reading `not detected` means the `claude` binary at
|
|
888
1005
|
that path didn't answer `--version`, which also disables version-gated
|
|
889
1006
|
flags like `--thinking-display`.
|
|
@@ -893,6 +1010,10 @@ Reading it:
|
|
|
893
1010
|
opencode still does not hand its version to plugins. It reads `unknown` when
|
|
894
1011
|
opencode is run from source rather than as the packaged binary.
|
|
895
1012
|
|
|
1013
|
+
This block is logged once, to a file that is off by default. For the same
|
|
1014
|
+
fields plus live process and proxy state, without enabling logging, run
|
|
1015
|
+
[`/claude-code-doctor`](#plugin-health-with-claude-code-doctor) in the session.
|
|
1016
|
+
|
|
896
1017
|
### Default behavior (no config, no env)
|
|
897
1018
|
|
|
898
1019
|
Nothing persists; only WARN and ERROR bubble in the TUI. The plugin
|
|
@@ -904,7 +1025,7 @@ plugin internals.
|
|
|
904
1025
|
|
|
905
1026
|
### [opencode-dcp](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning) (Dynamic Context Pruning)
|
|
906
1027
|
|
|
907
|
-
Partial support since v0.5.1. DCP runs in a useful degraded mode: automatic strategies and slash commands work,
|
|
1028
|
+
Partial support since v0.5.1. DCP runs in a useful degraded mode: its automatic strategies and slash commands work, while its own model-facing tools do not reach the model. Model-driven compression is still available, through this plugin's opt-in [`compress` proxy](#context-compression) rather than DCP's tool.
|
|
908
1029
|
|
|
909
1030
|
| DCP feature | Status | Notes |
|
|
910
1031
|
|---|---|---|
|
|
@@ -912,15 +1033,16 @@ Partial support since v0.5.1. DCP runs in a useful degraded mode: automatic stra
|
|
|
912
1033
|
| `experimental.chat.system.transform` (context-limit nudges, iteration reminders) | ✅ Works in headless | Headless spawns forward system-role content via `--append-system-prompt-file`. Interactive mode intentionally omits opencode's forwarded system prompt and keeps only this plugin's CLI/AGENTS/continuation prompt. |
|
|
913
1034
|
| `/dcp compress`, `/dcp sweep`, `/dcp manual`, `/dcp context`, `/dcp stats` slash commands | ✅ Works | Handled by opencode's `command.execute.before` hook, not the model. |
|
|
914
1035
|
| Automatic `deduplication` + `purgeErrors` strategies | ✅ Works | Message-transform only, no model tool calls. |
|
|
915
|
-
|
|
|
1036
|
+
| DCP's own autonomous `compress` / `distill` / `prune` tool calls | ❌ Not supported | DCP registers those as opencode-native tools. Claude CLI only ever sees its own built-ins and MCP-bridged servers, so the model never sees them. |
|
|
1037
|
+
| Model-driven compression through this plugin's `compress` proxy | ⚠️ Opt-in | Add `"Compress"` to `proxyTools` and the plugin exposes `mcp__opencode_proxy__compress`, which gives the model a working way to compress its own context. It is not DCP's tool and does not use DCP's strategies. See [Context compression](#context-compression). |
|
|
916
1038
|
|
|
917
|
-
|
|
1039
|
+
So autonomous compression is available, just not DCP's implementation of it. Two routes: add `"Compress"` to `proxyTools` so the model can compress its own context through this plugin, or leave it off and trigger DCP manually with `/dcp compress` whenever you would have wanted the model to call it. With `"Compress"` absent, the plugin's appended system prompt tells Claude that no such tool exists and to ignore instructions asking for it, which is the correct answer in that case.
|
|
918
1040
|
|
|
919
1041
|
---
|
|
920
1042
|
|
|
921
1043
|
## Known limitations
|
|
922
1044
|
|
|
923
|
-
-
|
|
1045
|
+
- Tool inputs stream as they are constructed (Anthropic's `input_json_delta` is forwarded as `tool-input-delta`), but only for tool calls opencode actually sees. Calls the plugin deliberately does not forward, meaning proxy tools, CLI-internal `WebSearch`, `AskUserQuestion`, `ExitPlanMode`, the todo-ledger `Task*` family and Claude's other internal tools, have their deltas suppressed, because a delta for a tool opencode never saw start renders as a permanently pending `⚙ unknown` row.
|
|
924
1046
|
- Raw chain-of-thought is not available. Claude 4 family models ship summarized thinking only. See [Extended thinking](#extended-thinking) for the full picture.
|
|
925
1047
|
- Recommended Claude Code CLI: **2.1.142+**. Older CLIs work for everything else but skip the `--thinking-display` flag, so Claude Opus 4.7 turns may render empty Thinking rows. If something breaks after a Claude Code update, the CLI version is the first thing to check.
|
|
926
1048
|
- **Foreground Task calls have a 60-minute proxy deadline** (configurable via [`proxyToolTimeoutMs`](#per-tool-proxy-timeouts)). A ceiling covering the longest configured deadline is written into Claude's generated HTTP MCP configuration so long-running opencode subagents are not cut off by Claude's 60-second default. For independent longer work, use `background: true` after enabling opencode's experimental background-subagent flag.
|
|
@@ -960,7 +1082,7 @@ src/
|
|
|
960
1082
|
opencode-types.ts # mirrored opencode types
|
|
961
1083
|
```
|
|
962
1084
|
|
|
963
|
-
For runtime gotchas, the
|
|
1085
|
+
For runtime gotchas, the release flow, and the compatibility audit (last taken against **opencode 1.18.29**), see [`AGENTS.md`](./AGENTS.md).
|
|
964
1086
|
|
|
965
1087
|
## Publishing (maintainers)
|
|
966
1088
|
|
|
@@ -969,7 +1091,7 @@ npm version patch # or minor/major — bumps package.json + creates the tag
|
|
|
969
1091
|
git push origin master --follow-tags
|
|
970
1092
|
```
|
|
971
1093
|
|
|
972
|
-
The GitHub Actions workflow at `.github/workflows/publish.yml` runs `npm publish --access public` on tag push (
|
|
1094
|
+
The GitHub Actions workflow at `.github/workflows/publish.yml` runs `npm publish --access public` on tag push. Since v0.6.2 it authenticates with **npm trusted publishing (OIDC)**, not a token: the job holds `id-token: write`, upgrades npm first because OIDC needs npm 11.5.1 or newer, and passes no `NODE_AUTH_TOKEN`. The trusted publisher is configured on npmjs.com against this repository and the `publish.yml` workflow filename, so a publish that fails on auth means that configuration, not an expired secret. There is no `NPM_TOKEN` in the workflow.
|
|
973
1095
|
|
|
974
1096
|
## Star History
|
|
975
1097
|
|
package/dist/index.d.ts
CHANGED
|
@@ -186,9 +186,9 @@ interface ClaudeCodeConfig {
|
|
|
186
186
|
/**
|
|
187
187
|
* Route `ExitPlanMode` through opencode's native `question` tool so plan
|
|
188
188
|
* approval is a real form instead of a "(yes/no)" line the operator has to
|
|
189
|
-
* answer in prose. Off by default
|
|
190
|
-
*
|
|
191
|
-
*
|
|
189
|
+
* answer in prose. Off by default because it cannot currently fire: headless
|
|
190
|
+
* `--print` is not offered an `ExitPlanMode` tool at all, and this bridge
|
|
191
|
+
* keys on that tool call. See the plan-mode gotcha in AGENTS.md.
|
|
192
192
|
*/
|
|
193
193
|
planModeQuestion?: boolean;
|
|
194
194
|
webSearch?: WebSearchRouting;
|
|
@@ -202,6 +202,8 @@ interface ClaudeCodeConfig {
|
|
|
202
202
|
idleProcessTimeoutMs?: number;
|
|
203
203
|
/** Stage opencode skills as a `--plugin-dir` so Claude's Skill tool can run them. */
|
|
204
204
|
bridgeOpencodeSkills?: boolean;
|
|
205
|
+
/** Append a one-line cost / duration / cache footer to each finished turn. */
|
|
206
|
+
turnStats?: boolean;
|
|
205
207
|
logging?: LoggingConfig;
|
|
206
208
|
}
|
|
207
209
|
interface LoggingConfig {
|
|
@@ -355,11 +357,15 @@ interface ClaudeCodeProviderSettings {
|
|
|
355
357
|
* real form; the answer is fed back to the CLI as the `tool_result` for
|
|
356
358
|
* the original `ExitPlanMode` call, which is what unlocks plan mode.
|
|
357
359
|
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
360
|
+
* Opt-in, and currently dormant. The delivery surface works: opencode's
|
|
361
|
+
* `question` form renders and round-trips (verified 2026-09-06, correcting
|
|
362
|
+
* an earlier claim here that it was broken upstream). What does not work is
|
|
363
|
+
* the trigger: headless `--print` does not offer the model an
|
|
364
|
+
* `ExitPlanMode` tool, measured on CLI 2.1.258, so the bridge has nothing
|
|
365
|
+
* to key on and the text path is what you get. Older opencode builds also
|
|
366
|
+
* have no `question` registry entry, in which case the plugin silently
|
|
367
|
+
* keeps the text path. Re-run the probes in AGENTS.md on a newer CLI before
|
|
368
|
+
* assuming the bridge is reachable.
|
|
363
369
|
*/
|
|
364
370
|
planModeQuestion?: boolean;
|
|
365
371
|
/**
|
|
@@ -390,6 +396,20 @@ interface ClaudeCodeProviderSettings {
|
|
|
390
396
|
* `Unknown skill`. No-op on CLIs without `--plugin-dir`.
|
|
391
397
|
*/
|
|
392
398
|
bridgeOpencodeSkills?: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* Append one compact line to the end of every finished (non-compaction,
|
|
401
|
+
* non-error) turn with what that turn cost: dollars, wall duration, how many
|
|
402
|
+
* internal CLI turns it took, and input / output / cache-read / cache-write
|
|
403
|
+
* tokens. It is rendered as its own text part led by `▌ **stats:**` and is
|
|
404
|
+
* stripped again from any transcript rebuilt for the CLI, so the model never
|
|
405
|
+
* reads its own accounting.
|
|
406
|
+
*
|
|
407
|
+
* Off by default, because a cost line under every reply is a preference.
|
|
408
|
+
* The same numbers are logged at INFO regardless of this setting, and
|
|
409
|
+
* `total_cost_usd`, `duration_ms`, `usage`, `modelUsage` and
|
|
410
|
+
* `permission_denials` always reach `providerMetadata`.
|
|
411
|
+
*/
|
|
412
|
+
turnStats?: boolean;
|
|
393
413
|
/**
|
|
394
414
|
* Routing for Claude's built-in `WebSearch` tool.
|
|
395
415
|
*
|
|
@@ -510,8 +530,22 @@ interface ClaudeStreamMessage {
|
|
|
510
530
|
text?: string;
|
|
511
531
|
}>;
|
|
512
532
|
thinking?: string;
|
|
533
|
+
/** On a `tool_result` block: the CLI-executed tool failed. */
|
|
534
|
+
is_error?: boolean;
|
|
513
535
|
}>;
|
|
514
536
|
};
|
|
537
|
+
apiKeySource?: string;
|
|
538
|
+
permissionMode?: string;
|
|
539
|
+
model?: string;
|
|
540
|
+
claude_code_version?: string;
|
|
541
|
+
tools?: string[];
|
|
542
|
+
mcp_servers?: Array<{
|
|
543
|
+
name?: string;
|
|
544
|
+
status?: string;
|
|
545
|
+
}>;
|
|
546
|
+
compact_metadata?: Record<string, unknown>;
|
|
547
|
+
compactMetadata?: Record<string, unknown>;
|
|
548
|
+
rate_limit_info?: Record<string, unknown>;
|
|
515
549
|
tool?: {
|
|
516
550
|
name?: string;
|
|
517
551
|
id?: string;
|
|
@@ -533,6 +567,24 @@ interface ClaudeStreamMessage {
|
|
|
533
567
|
result?: string;
|
|
534
568
|
is_error?: boolean;
|
|
535
569
|
num_turns?: number;
|
|
570
|
+
stop_reason?: string | null;
|
|
571
|
+
/**
|
|
572
|
+
* Per-model totals on `result`, keyed by model id: `inputTokens`,
|
|
573
|
+
* `outputTokens`, `cacheReadInputTokens`, `cacheCreationInputTokens`,
|
|
574
|
+
* `webSearchRequests`, `costUSD`. All numeric, which is what makes it safe
|
|
575
|
+
* to forward whole into `providerMetadata`.
|
|
576
|
+
*/
|
|
577
|
+
modelUsage?: Record<string, Record<string, number>>;
|
|
578
|
+
/**
|
|
579
|
+
* Tool calls the CLI's permission layer refused during the turn. Each entry
|
|
580
|
+
* also carries a `tool_input` on the wire; it is deliberately not declared
|
|
581
|
+
* here, because it can be a whole file's contents and must not be copied
|
|
582
|
+
* into provider metadata.
|
|
583
|
+
*/
|
|
584
|
+
permission_denials?: Array<{
|
|
585
|
+
tool_name?: string;
|
|
586
|
+
tool_use_id?: string;
|
|
587
|
+
}>;
|
|
536
588
|
usage?: {
|
|
537
589
|
input_tokens?: number;
|
|
538
590
|
output_tokens?: number;
|
|
@@ -768,6 +820,17 @@ declare const DEFAULT_PROXY_TOOL_NAMES: string[];
|
|
|
768
820
|
* so a user-defined command keeps opencode's normal behaviour end to end.
|
|
769
821
|
*/
|
|
770
822
|
declare function registerSideQuestionCommand(config: OpenCodeConfig): boolean;
|
|
823
|
+
/**
|
|
824
|
+
* Registers `/claude-code-doctor` unless the user defined their own command of
|
|
825
|
+
* that name. Unlike `/btw` there is no hook to guard: the command is a plain
|
|
826
|
+
* template and the language model answers the message it produces, so leaving
|
|
827
|
+
* a user definition alone here is the whole guard.
|
|
828
|
+
*
|
|
829
|
+
* The name carries no slash. opencode invokes a command as `/<key>` and takes
|
|
830
|
+
* everything after the first space as `$ARGUMENTS`, so `claude-code doctor`
|
|
831
|
+
* would be the command `claude-code` with the argument `doctor`.
|
|
832
|
+
*/
|
|
833
|
+
declare function registerDoctorCommand(config: OpenCodeConfig): boolean;
|
|
771
834
|
declare function _resetPlanModeWarningForTests(): void;
|
|
772
835
|
declare function warnIfPlanModeCannotExit(permissionMode: string | undefined): void;
|
|
773
836
|
declare function createClaudeCode(settings?: ClaudeCodeProviderSettings): ClaudeCodeProvider;
|
|
@@ -788,4 +851,4 @@ declare const _default: {
|
|
|
788
851
|
server: OpenCodePlugin;
|
|
789
852
|
};
|
|
790
853
|
|
|
791
|
-
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, _resetPlanModeWarningForTests, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, getAgentRegistry, getDefaultSubagentModel, registerSideQuestionCommand, resolveAgentModel, warnIfPlanModeCannotExit };
|
|
854
|
+
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, _resetPlanModeWarningForTests, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, getAgentRegistry, getDefaultSubagentModel, registerDoctorCommand, registerSideQuestionCommand, resolveAgentModel, warnIfPlanModeCannotExit };
|