@khalilgharbaoui/opencode-claude-code-plugin 0.60.1 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.60.1",
3
+ "version": "0.61.0",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "homepage": "https://opencode-claude-code-plugin.dev/",
6
6
  "funding": {
@@ -155,9 +155,10 @@ Defaults below describe normal headless opencode use when the key is absent.
155
155
  | `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. |
156
156
  | `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. |
157
157
  | `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. |
158
- | `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. |
158
+ | `enableTodoTools` | boolean | `false` | Set `CLAUDE_CODE_ENABLE_TODO_TOOLS=1` on every `claude` spawn (both transports, never compaction), so the CLI offers `TaskCreate`/`TaskUpdate`/`TaskGet`/`TaskList` on every model. Claude Code 2.1.280 to 2.1.293 offers them on its own only to Claude 3.x, Opus 4.0-4.7, Sonnet 4.0-4.6 and Haiku 4.5; on Opus 4.8, Opus 5/5.5, Sonnet 5/5.5, Fable and Haiku 5.5 no todos reach opencode's `todowrite` (or the sidebar's todo fraction) without it. Fills a gap only: a `CLAUDE_CODE_ENABLE_TODO_TOOLS` already in opencode's environment, `0` included, is passed through and decides. Off by default because the CLI turned the tools off for those models deliberately. Doctor row: `enableTodoTools`, with a note when the environment overrides it. Spawn log: `enableTodoTools: true` on `spawning new claude process`. Subagents still need `permission.todowrite: "allow"`. |
159
+ | `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`. The cost is THIS turn's: the CLI reports the Claude session's running total, and the footer prints the difference from that session's previous total (the INFO line carries both, `costUsd` and `turnCostUsd`). The first turn of a session resumed after an opencode restart, or of a fork, prints no cost, because `--resume` restores the running total and there is nothing to subtract; the next turn has it again. Not a billing guarantee. |
159
160
  | `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, an account-failover switch, and a `claude` whose `--help` does not advertise `--fork-session`. Recording costs nothing while the option is off. |
160
- | `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. |
161
+ | `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. opencode-dcp's `<dcp-message-id>` references (and its `@12@` compact form) are not part of the digest: dcp renumbers them on every opencode restart, so after an undo the same message came back under another number and the conversation was refused as `conversation-changed`. Records carry a digest `version`; one without it (written by an older plugin) is compared the old way. 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. |
161
162
  | `resumeAcrossModelChanges` | boolean | `true` | When the operator changes a conversation's model or reasoning effort mid-conversation, continue the same Claude session (`--resume <id> --model <new>`) instead of replaying the thread as text. Both are in the session key, so changing either used to start a fresh Claude conversation (issue #91); neither needs one, because `--model` and `CLAUDE_CODE_EFFORT_LEVEL` are spawn-time and the CLI applies either to a resumed transcript (measured on 2.1.288). The session is MOVED off the old key, so one Claude conversation has exactly one owner. Refused on every term `resumeAfterRestart` is refused on (another account, a transcript that is gone, a conversation that changed) plus two more: a sibling whose transcript may still be written to, and anything that is not the same cwd, request scope, opencode session, agent and prompt-cache TTL. Not on compaction, not on an account-failover switch. It reads the record `resumeAfterRestart` writes, so `resumeAfterRestart: false` turns it off too. Log: `continuing this conversation's claude session under the new model or effort` (NOTICE). |
162
163
  | `crossAccountResume` | boolean | `true` | On any account switch (a by-hand pick of another account's model in the same session, the switch form, and the switch back when the override expires), and only between accounts in the same `accountGroups` group, continue the same Claude conversation on the other account instead of replaying the thread as text. An account is a `CLAUDE_CONFIG_DIR` and a conversation is one `projects/<encoded cwd>/<id>.jsonl` inside it, so the switch copies that file into the target account's own projects dir (0600, dir 0700) and keeps the session id for the `--resume` both transports already build. Measured on CLI 2.1.288: a copied transcript resumes under another config dir with its context intact, the CLI resolves a session by FILENAME (not the `sessionId` in the records), it appends to the copy it found rather than to the cwd's path, the source comes out byte-identical, and no record holds a user, org or account id. Never overwrites: an older copy already at the target path (what a switch back finds) makes the carry take a fresh session id, and the live copy is the longest one. Refuses on nothing to carry, a non-file at the target path, or a failed copy, and each refusal falls back to the replay. Not on compaction, and inert with one account. Log: `carried this conversation's claude transcript to the other account` or `replaying this conversation as text on the other account` with a reason (both NOTICE). What is NOT verified live is a different Anthropic login answering another's conversation; `false` keeps the replay. |
163
164
  | `usageWarnThresholds` | number[] | `[50, 75, 90, 95]` | Percent levels of a usage window at which a SERVED turn ends with one `▌ **usage:**` note (its own text part, stripped from rebuilt transcripts): account, window, utilization, reset in local time. Read from every headless `rate_limit_event` (not only a rejected one), whose `utilization` is a FRACTION (`0.9` is 90%) and is ABSENT on an `allowed` event with headroom, which is unknown, never zero, and crosses nothing. Readings persist per account per window in `$XDG_STATE_HOME/opencode-claude-code-plugin/account-usage.json` (`0600`, names, numbers and times only), and so does the dedup: each threshold is said once per account per window across reused processes, sessions and restarts, again only after the window's reset. A crossing in a `/compact` turn or a child session is not said there and is said by the next ordinary turn on the account. Never on `doGenerate`, never turns a served turn into a failure. `[]` turns the notes off but keeps the readings. The single-account name is `default`. Interactive transport: no event, so only a limit reply is recorded (`limited`). Log: `claude account usage crossed a warning threshold` (NOTICE). |
@@ -229,7 +230,7 @@ their secret values. Arbitrary MCP `{env:NAME}` placeholders are outside this li
229
230
  | `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. |
230
231
  | `CLAUDE_CODE_INTERACTIVE_BYPASS` | Deprecated no-op, like `interactiveBypass`. |
231
232
  | `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS` | Set to `0` for the interactive TUI unless the operator set it, so a proxied call past 120 s stays in the foreground as on headless. |
232
- | `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. |
233
+ | `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. A process that has not sent its first `system`/`init` yet gets at least its `MCP_TIMEOUT` plus 30 s (150 s by default), since the CLI sends `init` only after its MCP servers connect or time out; the log's `beforeInit` says which delay fired. Keep within timer range; do not lower for routine config checks. |
233
234
  | `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. |
234
235
  | `OPENCODE_CLAUDE_CODE_LOG_FILE` | Overrides `logging.file`: trimmed `0/false/no/off` are false; any other nonempty value is true; empty falls back to config. Prefer `1` or `0`. |
235
236
  | `OPENCODE_CLAUDE_CODE_LOG_DIR` | Overrides `logging.dir`. |
@@ -516,6 +517,15 @@ Tell the user what a pick actually does before recommending one:
516
517
  the copy is refused does the conversation get replayed into a fresh session on the
517
518
  target account, which costs input tokens there and loses anything the CLI held but
518
519
  opencode did not.
520
+ - The turn on the picked account starts with one `▌ **account failover:**` line that says
521
+ which way the conversation arrived: "continues where it stopped" (carried) or
522
+ "replayed into a fresh Claude session there" (not), and names an account block's
523
+ reason instead of "is out of usage".
524
+ - After an earlier pick already moved the conversation (`work` to `personal`), a limit on
525
+ `personal` makes the form name `personal`, not `work`, and offer neither; the
526
+ remembered limit, the usage-limit note and an account-block note's login command are
527
+ `personal`'s too. A pick is still stored under the provider's own account, which is
528
+ where every turn resolves its account from, so the conversation moves on from there.
519
529
  - MCP servers configured only in the limited account's Claude profile will be **missing**
520
530
  on the target account.
521
531
  - `stop`, dismissing the form, or answering with anything that is not one of the offered
@@ -587,7 +597,8 @@ What to tell a user who asks for it, or asks why billing moved:
587
597
  through the same override. Never compaction (it fails outright) and never a title
588
598
  request (no spawn). An available account beats the `fallbackModels` chain, as the form
589
599
  does. Stopping the turn, or cancelling the stream it returned, stops the attempt in
590
- flight and starts no further account.
600
+ flight and starts no further account. (A cancel is a stop in every mode, `"off"` and
601
+ `"ask"` included.)
591
602
  - Verified with a scripted `claude` only; the switch path itself is the one measured
592
603
  live for the form.
593
604
 
@@ -676,7 +687,8 @@ both come out of the same usage limit; leave a long-lived main session at the de
676
687
 
677
688
  Only grant `permission.task` for approved target agents if delegation is wanted.
678
689
  `permission.todowrite: "allow"` is needed for subagent todos; opencode otherwise denies
679
- them by default. Ask before broadening permissions. Use the singular `agent` config
690
+ them by default. On models newer than the 4.x line the CLI offers no todo tool at all
691
+ unless `enableTodoTools` is on. Ask before broadening permissions. Use the singular `agent` config
680
692
  object for inline definitions, with `forceModel`/`reasoningEffort` under `options` if
681
693
  the opencode schema requires it. Markdown fallback reads top-level scalar fields only.
682
694
 
@@ -1464,6 +1476,7 @@ Prefer the doctor: it needs no logging change and no restart.
1464
1476
  | A question form never renders and the turn hangs | Blocking notification/tool hooks, or a pending request not reaching the visible session | Check `GET /question` on the same server/workspace: absent means investigate pre-tool hooks or replacement tools; present means inspect session ownership, permission priority and event delivery. On the maintainer's Mac, awaiting `alerter` dismissal in `tool.execute.before` blocked the tool itself. Native providers load global plugins too. The separate detach/reattach issue #36604 remains open; #36603 closed unmerged. |
1465
1477
  | No thinking summary | CLI version, explicit disable/summary env, or no thinking text emitted | Check version and nonsecret flag presence; do not override deliberate user suppression |
1466
1478
  | `⚙ invalid` rows for `todowrite` inside a subagent | Subagent lacks `permission.todowrite: "allow"` | Grant it on the agent definition with approval |
1479
+ | No todos at all, no todo fraction in the sidebar | Claude Code offers `TaskCreate`/`TaskUpdate`/`TaskGet`/`TaskList` only to Claude 3.x, Opus 4.0-4.7, Sonnet 4.0-4.6 and Haiku 4.5 (2.1.293); Opus 4.8, Opus 5/5.5, Sonnet 5/5.5, Fable and Haiku 5.5 get none | Set `enableTodoTools: true` (or export `CLAUDE_CODE_ENABLE_TODO_TOOLS=1` before starting opencode; spawns inherit it, and a value set there wins); measured on Haiku 5.5, opencode's todo list fills |
1467
1480
  | Other `⚙ invalid` or `⚙ unknown` tool rows | A Claude tool the plugin does not map for this version | Note plugin version, CLI version and the tool name; upgrade or report |
1468
1481
  | `AGENTS.md` appears twice in Claude's system prompt | Plugin older than 0.16.0 | Upgrade |
1469
1482
  | "What does the plugin actually think is going on?" | Startup diagnostics go to a log that is off by default | Run `/claude-code-doctor` in the session; paste that instead of the log |