@khalilgharbaoui/opencode-claude-code-plugin 0.26.2 → 0.27.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 CHANGED
@@ -250,6 +250,7 @@ What a pick does, in full:
250
250
  - **The conversation is replayed, not resumed.** Claude transcripts live under each account's own `CLAUDE_CONFIG_DIR`, so `--resume` cannot cross accounts. The plugin starts a fresh Claude session on the target and replays the thread from opencode's history, then tells it to carry on. That costs input tokens on the new account, and anything the CLI held but opencode did not is gone.
251
251
  - **Per-profile MCP servers do not come along.** A server configured only in the limited account's Claude profile is simply absent on the target.
252
252
  - **`stop`, dismissing the form, or any answer that is not one of the offered accounts** ends the turn exactly the way the rate-limit error ends it today.
253
+ - **An account that cannot serve at all gets the same form.** When Claude Code reports that an account's login expired (`authentication_failed`), or that it is on hold, unverified or has a billing problem, the plugin writes a note naming the account and what to do, and offers the switch if another account is configured. For an expired login the note gives the exact command, for example `CLAUDE_CONFIG_DIR=~/.claude-work claude auth login`. The switch lasts until opencode restarts, so restart after logging in again to move back. With a single account you get the note alone.
253
254
 
254
255
  Only two things open the form: a `rate_limit_event` the CLI marked `rejected`, and the two known account-limit error texts (`Third-party apps now draw from your extra usage…`, `You've hit your individual spend limit`). A generic 4xx, a timeout or a bad flag never does, deliberately: a transient failure must not quietly move where your usage is billed.
255
256
 
@@ -365,7 +366,8 @@ model: claude-code-work/claude-opus-5@work
365
366
  | `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). |
366
367
  | `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). |
367
368
  | `idleProcessTimeoutMs` | number | – | Kill a retained headless Claude worker after this many idle milliseconds following a completed turn. The timer starts when a turn finishes, a new turn cancels it, a worker that is mid-turn when it fires is left alone and re-timed, and the session id is preserved for `--resume`. Values above Node's maximum timer delay (`2147483647`) are ignored. Omit or set `0` to retain workers until LRU eviction (16 processes). Interactive transport is excluded. Contributed by [@bernardofortes](https://github.com/bernardofortes). |
368
- | `bridgeOpencodeSkills` | boolean | `false` | Expose your opencode skills to Claude's native `Skill` tool. Off by default because every bridged skill is also in the system prompt opencode forwards, so a large set is paid for twice per turn; the bundled configuration skill is staged either way. See [Skill bridge](#skill-bridge). Written by [@broskees](https://github.com/broskees). |
369
+ | `bridgeOpencodeSkills` | boolean | `false` | Expose your opencode skills to Claude's native `Skill` tool, from every root opencode itself reads. Off by default because every bridged skill is also in the system prompt opencode forwards, so a large set is paid for twice per turn; the bundled configuration skill is staged either way. See [Skill bridge](#skill-bridge). Written by [@broskees](https://github.com/broskees). |
370
+ | `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, so one skill does not reach the model twice. Matched by resolved path, by identical `SKILL.md`, or by name. `false` bridges everything and reinstates the duplicates. See [Skills Claude already has](#skills-claude-already-has). |
369
371
  | `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). |
370
372
  | `turnStats` | boolean | `false` | Append a one-line cost / duration / cache footer to each finished turn. See [Per-turn stats](#per-turn-stats). |
371
373
  | `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`. |
@@ -777,6 +779,7 @@ Four Claude Code stream events used to reach nothing but a debug log:
777
779
 
778
780
  - **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).
779
781
  - **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.
782
+ - **A conversation Claude Code cleared.** Sending `/clear` as a message, or a plan-mode exit that clears context, makes Claude Code start a fresh conversation while opencode still shows the old messages. A `▌ **claude code reset:**` note says so. The plugin deliberately does not replay the earlier messages, since that would undo the clear. Start a new opencode session if you want the two to match.
780
783
  - **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.
781
784
  - **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.
782
785
 
@@ -798,7 +801,7 @@ No separate skill installation or copying is needed. It ships with each package
798
801
 
799
802
  ## Skill bridge
800
803
 
801
- opencode and Claude Code use the same on-disk skill format, a `<name>/SKILL.md` whose frontmatter carries `name` and `description`, but they read from different directories. opencode looks in `.opencode/skills/` and `~/.config/opencode/skills/`; the Claude CLI looks in `~/.claude/skills/` and its own plugins. So opencode advertises your skills in the system prompt it forwards, the model calls `Skill("browser-automation")`, and Claude answers `Unknown skill`.
804
+ opencode and Claude Code use the same on-disk skill format, a `<name>/SKILL.md` whose frontmatter carries `name` and `description`, but they read from overlapping, not identical, directories. opencode looks in `.opencode/skills/`, `~/.config/opencode/skills/`, `~/.agents/skills/` and more; the Claude CLI looks in `~/.claude/skills/`, the project's `.claude/skills/` and its own plugins. Where they differ, opencode advertises a skill in the system prompt it forwards, the model calls `Skill("browser-automation")`, and Claude answers `Unknown skill`. Where they overlap, the same skill reaches one session twice.
802
805
 
803
806
  By default the plugin discovers your opencode skills, stages a throwaway Claude Code plugin directory that links them, and passes it as `claude --plugin-dir`. They register natively, prefixed with the plugin name:
804
807
 
@@ -809,9 +812,33 @@ opencode-skills:rtk
809
812
 
810
813
  Claude can invoke them with the Skill tool or as `/opencode-skills:<name>`. `--plugin-dir` is scoped to the spawned session, so nothing is written into your `~/.claude`.
811
814
 
812
- Discovery order, first match wins: `.opencode/skills/` walking up from the working directory, then `~/.opencode/skills/`, then `$OPENCODE_CONFIG_DIR/skills/`, then `~/.config/opencode/skills/`. A project skill shadows a global one of the same name. If the skill set is unchanged the staged directory is reused between spawns.
815
+ Discovery covers every root opencode itself reads, first match wins:
813
816
 
814
- The bridge is **off by default**: every bridged skill's name and description is also in the system prompt opencode already forwards, so a large skill set is paid for twice on every turn. Set `bridgeOpencodeSkills: true` when the model tries `Skill("<name>")` for a skill opencode advertises and gets `Unknown skill`; the bundled configuration skill is staged either way. When on, the bridge applies to the headless, interactive and direct `doGenerate` spawns alike, never to compaction, and it is skipped on a Claude CLI without `--plugin-dir` (the plugin probes `claude --help` and logs a notice).
817
+ 1. Walking up from the working directory: `.opencode/skills/`, `.claude/skills/`, `.agents/skills/` at each level.
818
+ 2. `~/.opencode/skills/`.
819
+ 3. `$OPENCODE_CONFIG_DIR/skills/` and `.../skill/`.
820
+ 4. `~/.config/opencode/skills/` and `.../skill/` (or `$XDG_CONFIG_HOME`).
821
+ 5. `~/.claude/skills/` and `~/.agents/skills/`.
822
+
823
+ A project skill shadows a global one of the same name, and an opencode-managed copy shadows an external one. Step 5 is opencode's own "external" scan and honours its `OPENCODE_DISABLE_EXTERNAL_SKILLS` and `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` variables. A skill is known by the `name:` its `SKILL.md` frontmatter declares, falling back to the directory name, which is what opencode advertises. If the skill set is unchanged the staged directory is reused between spawns.
824
+
825
+ ### Skills Claude already has
826
+
827
+ Those roots overlap Claude Code's own, which reads `$CLAUDE_CONFIG_DIR/skills/` (`~/.claude/skills/` by default), the project's `.claude/skills/`, and the `skills/` folder of every installed plugin. Without care one skill reaches a single session twice, costing prompt tokens on every turn and leaving it ambiguous which copy answers.
828
+
829
+ So `bridgeSkipNativeSkills` (**on by default**) leaves a skill unbridged when Claude already loads it. A skill counts as already loaded when:
830
+
831
+ - it is literally the same directory, symlinks resolved;
832
+ - its `SKILL.md` is byte-identical to a native one, wherever that one lives (this is the case for a skill installed as a Claude plugin *and* symlinked into `~/.agents/skills`);
833
+ - a **different** skill of the same name is registered under user or project scope. Plugin skills are namespaced `<plugin>:<name>` and so never take a bridged name, only duplicate its content.
834
+
835
+ Only that last case changes which copy answers `Skill("<name>")`, so it is logged at WARN naming both paths; the others are logged at INFO. Set `bridgeSkipNativeSkills: false` to bridge everything regardless and get the duplicates back.
836
+
837
+ One limitation worth knowing: the plugin scan reads `installed_plugins.json` and does not check whether that plugin is actually enabled, so a skill from a disabled plugin can be treated as native. If a skill disappears, grep `plugin.log` for `skills claude code already loads`: one line names both paths and the reason.
838
+
839
+ ### Enabling it
840
+
841
+ The bridge itself is **off by default**: every bridged skill's name and description is also in the system prompt opencode already forwards, so a large skill set is paid for twice on every turn. Set `bridgeOpencodeSkills: true` when the model tries `Skill("<name>")` for a skill opencode advertises and gets `Unknown skill`; the bundled configuration skill is staged either way. When on, the bridge applies to the headless, interactive and direct `doGenerate` spawns alike, never to compaction, and it is skipped on a Claude CLI without `--plugin-dir` (the plugin probes `claude --help` and logs a notice).
815
842
 
816
843
  This bridge was written by [@broskees](https://github.com/broskees) (Joseph Roberts) on his fork and absorbed here with credit; see [Credits](#credits).
817
844
 
package/dist/index.d.ts CHANGED
@@ -407,6 +407,8 @@ interface ClaudeCodeConfig {
407
407
  idleProcessTimeoutMs?: number;
408
408
  /** Stage opencode skills as a `--plugin-dir` so Claude's Skill tool can run them. */
409
409
  bridgeOpencodeSkills?: boolean;
410
+ /** Leave out the skills the Claude session already loads natively. Default true. */
411
+ bridgeSkipNativeSkills?: boolean;
410
412
  /** Append a one-line cost / duration / cache footer to each finished turn. */
411
413
  turnStats?: boolean;
412
414
  logging?: LoggingConfig;
@@ -662,17 +664,41 @@ interface ClaudeCodeProviderSettings {
662
664
  */
663
665
  idleProcessTimeoutMs?: number;
664
666
  /**
665
- * Expose your opencode skills (`.opencode/skills`, `~/.config/opencode/skills`)
666
- * to Claude Code's native Skill tool by staging them as a session-scoped
667
- * `--plugin-dir`, so a `Skill("<name>")` call for a skill opencode advertises
668
- * does not fail with `Unknown skill`. Off by default: every bridged skill
669
- * is also listed in the system prompt opencode forwards, so a large skill
670
- * set costs prompt tokens twice per turn. When on it applies to the
671
- * headless, interactive and direct `doGenerate` spawns alike; compaction
672
- * never loads it, and the bundled configuration skill is staged either way.
673
- * No-op on CLIs without `--plugin-dir`.
667
+ * Expose your opencode skills to Claude Code's native Skill tool by staging
668
+ * them as a session-scoped `--plugin-dir`, so a `Skill("<name>")` call for a
669
+ * skill opencode advertises does not fail with `Unknown skill`. Covers every
670
+ * root opencode itself reads: project `.opencode/`, `.claude/` and `.agents/`
671
+ * walking up from the workspace, the opencode config dirs, and the global
672
+ * `~/.claude/skills` and `~/.agents/skills` (those last two behind the same
673
+ * `OPENCODE_DISABLE_EXTERNAL_SKILLS` / `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS`
674
+ * switches opencode honours).
675
+ *
676
+ * Off by default: every bridged skill is also listed in the system prompt
677
+ * opencode forwards, so a large skill set costs prompt tokens twice per turn.
678
+ * When on it applies to the headless, interactive and direct `doGenerate`
679
+ * spawns alike; compaction never loads it, and the bundled configuration
680
+ * skill is staged either way. No-op on CLIs without `--plugin-dir`.
674
681
  */
675
682
  bridgeOpencodeSkills?: boolean;
683
+ /**
684
+ * Leave a skill unbridged when the Claude Code session already loads it:
685
+ * from `<CLAUDE_CONFIG_DIR>/skills` (`~/.claude/skills` by default), from the
686
+ * project's own `.claude/skills`, or from an installed plugin's `skills/`.
687
+ * On by default, because those roots overlap opencode's and the duplicate
688
+ * costs prompt tokens on every turn for nothing.
689
+ *
690
+ * A skill is treated as already loaded when it is literally the same
691
+ * directory (symlinks resolved), when its SKILL.md is byte-identical to a
692
+ * native one, or when a *different* skill of the same name is registered
693
+ * under user or project scope. That last case is the only one that changes
694
+ * behaviour, because `Skill("<name>")` then answers from Claude's copy
695
+ * rather than opencode's, so it is logged at WARN naming both paths. Plugin
696
+ * skills are namespaced `<plugin>:<name>` and so only ever match by content.
697
+ *
698
+ * Set `false` to bridge everything regardless, which restores the pre-0.25
699
+ * behaviour of advertising a shared skill twice.
700
+ */
701
+ bridgeSkipNativeSkills?: boolean;
676
702
  /**
677
703
  * Append one compact line to the end of every finished (non-compaction,
678
704
  * non-error) turn with what that turn cost: dollars, wall duration, how many
@@ -804,6 +830,14 @@ interface ClaudeStreamMessage {
804
830
  agent_id?: string;
805
831
  description?: string;
806
832
  };
833
+ /**
834
+ * On an `assistant` message the CLI synthesised to report a failure: the
835
+ * kind of failure (`authentication_failed`, `billing_error`, ...). Read by
836
+ * `accountBlockKind`; schema confirmed on Claude Code 2.1.280.
837
+ */
838
+ error?: string;
839
+ /** On a `conversation_reset`: the conversation Claude Code started. */
840
+ new_conversation_id?: string;
807
841
  message?: {
808
842
  role?: string;
809
843
  model?: string;
@@ -987,6 +1021,13 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
987
1021
  * spawn block resolves anything: `userMsg` is built well ahead of it.
988
1022
  */
989
1023
  private stripContextRemindersEnabled;
1024
+ /**
1025
+ * Arguments the skill bridge needs beyond `cwd` / `cliPath`: which
1026
+ * `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether
1027
+ * to drop the ones it already loads. A failover moves the spawn to another
1028
+ * account, and therefore to that account's config dir.
1029
+ */
1030
+ private skillBridgeSpawn;
990
1031
  /** Share one lazy registry request within a turn without making it stale. */
991
1032
  private createLiveToolInfoLoader;
992
1033
  /**