@khalilgharbaoui/opencode-claude-code-plugin 0.45.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.45.0",
3
+ "version": "0.46.1",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "homepage": "https://opencode-claude-code-plugin.dev/",
6
6
  "funding": {
@@ -146,6 +146,7 @@ Defaults below describe normal headless opencode use when the key is absent.
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, 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
+ | `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). |
149
150
  | `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
151
  | `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
152
  | `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. |
@@ -320,7 +321,9 @@ through the TUI's own input queue, as on headless. A turn ends on its stop reaso
320
321
  `turn_duration`, or, as a safeguard for a CLI that writes neither, after 20 s of total
321
322
  silence once it has its reply (reported as ended without a stop reason). It reads
322
323
  completed transcript blocks, not token
323
- deltas. It currently re-reads the transcript while polling. Normal interactive turns
324
+ deltas. A poll reads only the bytes appended since the last one, so a poll that finds
325
+ nothing new costs one `stat` and no read at all, however long the conversation is.
326
+ Normal interactive turns
324
327
  do not forward opencode's system prompt (on the TUI, Claude Code bills a request
325
328
  carrying it as a third-party app's from extra usage; headless does not). The
326
329
  account-switch form, the fallback chain, `forkSessions`, the usage-limit and
@@ -381,10 +384,30 @@ resend your message, or wait for the window to reset.
381
384
  and the `▌ **rate limit:**` paragraph the plugin used to write is log-only now, so a
382
385
  limited turn carries exactly one block.
383
386
  - Written on a child session too (a subagent that died on a limit is what the parent's
384
- `task` result should say), but never on a compaction turn, whose text becomes the
385
- stored summary. How the turn finishes is unchanged.
386
- - `permission_denials`, `turnStats` and the finish reason are untouched: the note
387
- replaces text, not control flow.
387
+ `task` result should say), but never on a compaction turn, which fails outright
388
+ instead (see "A failed compaction turn" below).
389
+ - The turn **finishes as an error** (`finishReason` `{unified:"error", raw:"usage_limit"}`),
390
+ the way the account-block note's turn always has: nothing was served, so filing it as
391
+ a reply hid the failure from opencode. There is no `error` stream part outside
392
+ compaction, so the note stays the single account of what happened.
393
+ - `permission_denials` and `turnStats` are untouched; `turnStats` was already suppressed
394
+ on a failed turn, so a limited turn never carried a stats line anyway.
395
+
396
+ ### A failed compaction turn
397
+
398
+ A compaction turn's text is what opencode stores as the summary, so a compaction whose
399
+ `result` is `is_error: true` (or carries a failing subtype) writes **no text at all**:
400
+ no note, no CLI prose, nothing. It ends on an `error` stream part naming the cause and
401
+ the CLI's own sentence, plus a finish of `{unified:"error", raw:<cause>}` where the
402
+ cause is `usage_limit`, an account-block kind such as `authentication_failed`, or the
403
+ failing subtype. opencode then marks the compaction failed and keeps the conversation
404
+ it already had.
405
+
406
+ Before this, a usage-limited compaction returned the CLI's sentence as a normal answer
407
+ and opencode stored it as the summary, so every later session that could not resume its
408
+ Claude transcript began from that sentence with the earlier messages gone (issue #90).
409
+ There is no option for this and nothing to turn off; the operator retries the
410
+ compaction once the account can serve.
388
411
 
389
412
  When a user says the limit message is unhelpful, check the plugin version before
390
413
  anything else: this note is recent and a long-lived opencode window may predate it.