@khalilgharbaoui/opencode-claude-code-plugin 0.54.2 → 0.56.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 +9 -1
- package/dist/index.d.ts +159 -10
- package/dist/index.js +2177 -603
- package/dist/index.js.map +1 -1
- package/dist/tui.js +206 -8
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +105 -9
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
[](./LICENSE)
|
|
19
19
|
[](https://www.buymeacoffee.com/khalilgharbaoui)
|
|
20
20
|
|
|
21
|
+
<a href="https://www.buymeacoffee.com/khalilgharbaoui"><img src="site/public/buy-me-a-coffee.png" alt="Buy me a coffee" width="214" height="60"></a>
|
|
22
|
+
|
|
21
23
|
**Claude Code is the provider.** This opencode plugin runs Anthropic's Claude models through the official **Claude Code CLI** (`claude`) as a subprocess instead of calling the HTTP API. opencode inherits whatever that CLI is logged in as (a Claude subscription, an API key, Bedrock or Vertex), gets Claude's own tools, MCP servers and skills, and keeps running the tools that touch your machine itself, behind its own permission prompts. One package serves opencode 1.x and 2.x.
|
|
22
24
|
|
|
23
25
|
- **Your CLI's login, untouched.** The plugin never reads, stores or replays a token. Lifting the OAuth session out of the official client is what proxy-style plugins do, and Anthropic disallowed that for third-party Claude use in February 2026. This plugin structurally cannot do it.
|
|
@@ -106,10 +108,16 @@ This README used to hold all of the above. Release notes and bookmarks point at
|
|
|
106
108
|
|
|
107
109
|
Made and maintained by [Khalil Gharbaoui (@khalilgharbaoui)](https://github.com/khalilgharbaoui). This plugin absorbs work from its forks directly, cherry-picked with the original authorship preserved or reimplemented with the author named in the commit. The people behind the features you are using, and what each built, are on the [Credits](./docs/credits.md) page; `git log --author` on this repo shows the preserved authorship.
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
## Support
|
|
112
|
+
|
|
113
|
+
This plugin is maintained, not a side project that gets abandoned when the next Claude Code or opencode release breaks something. Keeping it working means tracking CLI flag changes, opencode plugin API changes, and new model IDs as they ship.
|
|
114
|
+
|
|
115
|
+
If the plugin saves you time, you can keep it maintained here:
|
|
110
116
|
|
|
111
117
|
<a href="https://www.buymeacoffee.com/khalilgharbaoui"><img src="site/public/buy-me-a-coffee.png" alt="Buy me a coffee" width="214" height="60"></a>
|
|
112
118
|
|
|
119
|
+
One-off or recurring, both help. Bug reports and PRs are welcome too; see [`AGENTS.md`](./AGENTS.md) for the contribution rules.
|
|
120
|
+
|
|
113
121
|
## License
|
|
114
122
|
|
|
115
123
|
MIT. See [LICENSE](./LICENSE). The first version was written by Émilien ([@unixfox](https://github.com/unixfox)); everything since by [Khalil Gharbaoui](https://github.com/khalilgharbaoui) and the people in [Credits](./docs/credits.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -425,6 +425,8 @@ interface ClaudeCodeConfig {
|
|
|
425
425
|
proxyOpencodeTools?: string[];
|
|
426
426
|
stripContextReminders?: boolean;
|
|
427
427
|
extraDisallowedTools?: string[];
|
|
428
|
+
/** See `ClaudeCodeProviderSettings.mapReadRules`. Default true. */
|
|
429
|
+
mapReadRules?: boolean;
|
|
428
430
|
proxyToolTimeoutMs?: Record<string, number>;
|
|
429
431
|
/**
|
|
430
432
|
* Route `ExitPlanMode` through opencode's native `question` tool so plan
|
|
@@ -458,6 +460,16 @@ interface ClaudeCodeConfig {
|
|
|
458
460
|
resumeAcrossModelChanges?: boolean;
|
|
459
461
|
/** Carry a conversation's Claude transcript over on an account switch. Default true. */
|
|
460
462
|
crossAccountResume?: boolean;
|
|
463
|
+
/** See `ClaudeCodeProviderSettings.maxBudgetUsd`. Resolved; unset is off. */
|
|
464
|
+
maxBudgetUsd?: number;
|
|
465
|
+
/** See `ClaudeCodeProviderSettings.budgetWarnAt`. Resolved percentages. */
|
|
466
|
+
budgetWarnAt?: number[];
|
|
467
|
+
/** See `ClaudeCodeProviderSettings.maxBackgroundSubagents`. Resolved; unset is off. */
|
|
468
|
+
maxBackgroundSubagents?: number;
|
|
469
|
+
/** Resolved `usageWarnThresholds`, in percent; empty for no notes. */
|
|
470
|
+
usageWarnThresholds?: number[];
|
|
471
|
+
/** Resolved `usageSwitchAt`, in percent, or null for never. */
|
|
472
|
+
usageSwitchAt?: number | null;
|
|
461
473
|
logging?: LoggingConfig;
|
|
462
474
|
}
|
|
463
475
|
interface LoggingConfig {
|
|
@@ -490,12 +502,13 @@ interface LoggingConfig {
|
|
|
490
502
|
}
|
|
491
503
|
type WebSearchRouting = "claude" | "disabled" | (string & {});
|
|
492
504
|
/**
|
|
493
|
-
* What happens when the account a conversation
|
|
494
|
-
* `"
|
|
495
|
-
*
|
|
496
|
-
*
|
|
505
|
+
* What happens when the account a conversation is running on is out of usage
|
|
506
|
+
* or cannot serve. `"off"` (the default, also what unset means) ends the turn
|
|
507
|
+
* on one `▌ **usage limit:**` note; `"ask"` ends it on a form listing the
|
|
508
|
+
* other configured accounts and continues on the pick; `"auto"` moves the same
|
|
509
|
+
* request to the next eligible configured account without asking.
|
|
497
510
|
*/
|
|
498
|
-
type AccountFailoverMode = "ask" | "off";
|
|
511
|
+
type AccountFailoverMode = "ask" | "off" | "auto";
|
|
499
512
|
/** `subagentDispatch`. Unset behaves as `"off"`; see the option's own note. */
|
|
500
513
|
type SubagentDispatchMode = "ask" | "off";
|
|
501
514
|
interface ClaudeCodeProviderSettings {
|
|
@@ -539,11 +552,13 @@ interface ClaudeCodeProviderSettings {
|
|
|
539
552
|
*/
|
|
540
553
|
accountInProcess?: boolean;
|
|
541
554
|
/**
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
555
|
+
* What a usage limit or an account that cannot serve does to the turn.
|
|
556
|
+
* Unset or `"off"` (the default) ends it on one `▌ **usage limit:**` note.
|
|
557
|
+
* `"ask"` shows a form listing the other configured accounts and continues
|
|
558
|
+
* on the pick. `"auto"` continues the same request on the next eligible
|
|
559
|
+
* configured account, in configured order, without asking. Both only do
|
|
560
|
+
* anything when more than one account is configured. See
|
|
561
|
+
* docs/configuration/accounts.md.
|
|
547
562
|
*/
|
|
548
563
|
accountFailover?: AccountFailoverMode;
|
|
549
564
|
/**
|
|
@@ -618,6 +633,43 @@ interface ClaudeCodeProviderSettings {
|
|
|
618
633
|
* the cache they wrote. See `resolveAgentCacheTtl` in `src/agent-models.ts`.
|
|
619
634
|
*/
|
|
620
635
|
defaultSubagentCacheTtl?: string;
|
|
636
|
+
/**
|
|
637
|
+
* `--max-turns` for discovered `mode: subagent` agents that declare no
|
|
638
|
+
* `maxTurns` of their own. Counted by the CLI per user message, so a reused
|
|
639
|
+
* process is not used up by it (measured on 2.1.293, h #g245). Unset (the
|
|
640
|
+
* default) passes nothing. See `resolveAgentCliCaps` in `src/agent-models.ts`.
|
|
641
|
+
*/
|
|
642
|
+
defaultSubagentMaxTurns?: number;
|
|
643
|
+
/**
|
|
644
|
+
* `--max-budget-usd` for discovered `mode: subagent` agents that declare no
|
|
645
|
+
* `maxBudgetUsd` of their own. The CLI counts it over the process's whole
|
|
646
|
+
* life, which is why it is a subagent-only cap (h #g245). Unset (the
|
|
647
|
+
* default) passes nothing.
|
|
648
|
+
*/
|
|
649
|
+
defaultSubagentMaxBudgetUsd?: number;
|
|
650
|
+
/**
|
|
651
|
+
* A dollar cap on one opencode conversation: its own turns plus every
|
|
652
|
+
* subagent it dispatched, summed from the CLI's own `total_cost_usd`. Unset
|
|
653
|
+
* (the default) is no cap and no bookkeeping. Notes at `budgetWarnAt`, and
|
|
654
|
+
* once the sum reaches the cap the next turn ends before anything is spawned,
|
|
655
|
+
* with one `▌ **budget:**` note and an error finish. Compaction is exempt.
|
|
656
|
+
* In memory only: see `src/budget-ledger.ts`.
|
|
657
|
+
*/
|
|
658
|
+
maxBudgetUsd?: number;
|
|
659
|
+
/**
|
|
660
|
+
* Percentages of `maxBudgetUsd` at which one `▌ **budget:**` note is
|
|
661
|
+
* written, each once per conversation. Default `[50, 80]`; `[]` keeps the
|
|
662
|
+
* stop and drops the notes.
|
|
663
|
+
*/
|
|
664
|
+
budgetWarnAt?: number[];
|
|
665
|
+
/**
|
|
666
|
+
* How many background subagents may run at once, counted from the
|
|
667
|
+
* dispatches this opencode process saw opencode accept. A `background: true`
|
|
668
|
+
* `task` or `task_batch` that would go over it is refused as a whole with a
|
|
669
|
+
* tool result naming the cap and the running count. Foreground dispatch is
|
|
670
|
+
* never capped. Unset (the default) is no cap.
|
|
671
|
+
*/
|
|
672
|
+
maxBackgroundSubagents?: number;
|
|
621
673
|
/**
|
|
622
674
|
* Models to try, in order, when the model a turn would have run on is
|
|
623
675
|
* refused. The default for every agent that declares no `fallbackModels` of
|
|
@@ -778,6 +830,34 @@ interface ClaudeCodeProviderSettings {
|
|
|
778
830
|
* the model has to work without it.
|
|
779
831
|
*/
|
|
780
832
|
extraDisallowedTools?: string[];
|
|
833
|
+
/**
|
|
834
|
+
* Translate opencode's own `read` permission DENY rules into Claude Code
|
|
835
|
+
* deny rules for the Read the CLI runs itself. On by default: it closes a
|
|
836
|
+
* hole operators reasonably assume is already closed.
|
|
837
|
+
*
|
|
838
|
+
* `Read`, and whatever other file readers a CLI ships, are not proxied by
|
|
839
|
+
* default (`proxyTools` is Bash, Edit, Write, WebFetch, Task), so without
|
|
840
|
+
* this a file opencode would refuse to read for its own `read` tool is read
|
|
841
|
+
* by Claude with no prompt. Measured on 2026-10-16 with opencode 1.19.0 and
|
|
842
|
+
* Claude Code 2.1.293: "read .env with your Read tool" returned the planted
|
|
843
|
+
* secret.
|
|
844
|
+
*
|
|
845
|
+
* What is mapped, and what is not:
|
|
846
|
+
* - a `deny` becomes `--disallowedTools "Read(<pattern>)"`, which covers
|
|
847
|
+
* every file-reading tool the CLI has (its own rule validator says
|
|
848
|
+
* Read rules are the only file rules matched);
|
|
849
|
+
* - an `ask` is NOT turned into a deny. A headless spawn runs with the
|
|
850
|
+
* skip flag and has nobody to ask, and opencode's own `*.env` default
|
|
851
|
+
* is an `ask`, so denying it would refuse reads the operator expects to
|
|
852
|
+
* approve. `/claude-code-doctor` lists them as not enforced;
|
|
853
|
+
* - `glob`, `grep`, `list` and `external_directory` rules are not mapped:
|
|
854
|
+
* Claude Code's deny beats its allow, so "outside the working directory"
|
|
855
|
+
* cannot be said without denying the working directory too. Use
|
|
856
|
+
* `permissionPreset: "read-only"` (which sends `--restricted`) for that.
|
|
857
|
+
*
|
|
858
|
+
* Set `false` to send nothing and keep the CLI's own posture.
|
|
859
|
+
*/
|
|
860
|
+
mapReadRules?: boolean;
|
|
781
861
|
/**
|
|
782
862
|
* Per-tool proxy call timeouts in milliseconds, keyed by the proxy tool
|
|
783
863
|
* name (`bash`, `edit`, `write`, `webfetch`, `task`, `question` —
|
|
@@ -987,6 +1067,23 @@ interface ClaudeCodeProviderSettings {
|
|
|
987
1067
|
* verified live. Set `false` to keep the replay.
|
|
988
1068
|
*/
|
|
989
1069
|
crossAccountResume?: boolean;
|
|
1070
|
+
/**
|
|
1071
|
+
* Usage levels, in percent of a window, at which a served turn ends with one
|
|
1072
|
+
* `▌ **usage:**` note naming the account, the window, how much is used and
|
|
1073
|
+
* when it resets. Default `[50, 75, 90, 95]`; `[]` turns the notes off. Each
|
|
1074
|
+
* threshold is said once per account per window, and again only after the
|
|
1075
|
+
* window resets. Read from Claude Code's own `rate_limit_event`, so the
|
|
1076
|
+
* headless transport only. See docs/configuration/accounts.md.
|
|
1077
|
+
*/
|
|
1078
|
+
usageWarnThresholds?: number[];
|
|
1079
|
+
/**
|
|
1080
|
+
* The usage level, in percent, at which work should leave this account
|
|
1081
|
+
* BEFORE it hits the wall. Default `90`; `false` or `0` never. With
|
|
1082
|
+
* `accountFailover: "auto"` and another eligible account, new turns move to
|
|
1083
|
+
* it until the window resets; otherwise the note names the other accounts to
|
|
1084
|
+
* pick a model from. The turn that crossed is never changed: it was served.
|
|
1085
|
+
*/
|
|
1086
|
+
usageSwitchAt?: number | false;
|
|
990
1087
|
/**
|
|
991
1088
|
* Routing for Claude's built-in `WebSearch` tool.
|
|
992
1089
|
*
|
|
@@ -1400,6 +1497,22 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1400
1497
|
* `⚙ invalid` and wedge the turn, so the plugin keeps the text path.
|
|
1401
1498
|
*/
|
|
1402
1499
|
private resolvePlanModeQuestion;
|
|
1500
|
+
/**
|
|
1501
|
+
* The ledger this turn keeps under `maxBudgetUsd` (h #g245), or null for
|
|
1502
|
+
* none. The parent lookup runs once per session per process and only with a
|
|
1503
|
+
* cap set, so a default install never pays for it. A lookup that cannot
|
|
1504
|
+
* answer reads as "no parent", which makes the session its own conversation:
|
|
1505
|
+
* its spend is still counted, against its own budget.
|
|
1506
|
+
*/
|
|
1507
|
+
private resolveTurnBudget;
|
|
1508
|
+
/**
|
|
1509
|
+
* A turn the session budget stopped before anything was spawned: one
|
|
1510
|
+
* `▌ **budget:**` note (when the turn may write one) and an error finish,
|
|
1511
|
+
* the shape a usage limit has had since h #g214. A child session gets an
|
|
1512
|
+
* `error` part instead of the note, so the parent's `task` result says why
|
|
1513
|
+
* without the note becoming the subagent's answer.
|
|
1514
|
+
*/
|
|
1515
|
+
private budgetStopStream;
|
|
1403
1516
|
/**
|
|
1404
1517
|
* A dispatch form, as a turn: one synthetic `question` tool call and a
|
|
1405
1518
|
* `tool-calls` finish, which is how the operator's answer comes back on the
|
|
@@ -1489,6 +1602,31 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1489
1602
|
* because which method opencode used is not in them.
|
|
1490
1603
|
*/
|
|
1491
1604
|
private streamTurn;
|
|
1605
|
+
/**
|
|
1606
|
+
* Run the turn, moving the same request to the next eligible configured
|
|
1607
|
+
* account when the one it ran on is out of usage or cannot serve
|
|
1608
|
+
* (`accountFailover: "auto"`). The single wiring point for the automatic
|
|
1609
|
+
* switch, and outside `runModelChain` on purpose: an account switch starts
|
|
1610
|
+
* that account's own model chain from the top.
|
|
1611
|
+
*
|
|
1612
|
+
* Anything but `"auto"` is `runModelChain` and one `await`, so `"off"` and
|
|
1613
|
+
* `"ask"` are exactly what they were. With `"auto"`, the attempt that failed
|
|
1614
|
+
* decides the switch itself (`completeResult` sets `run.switchTo` and closes
|
|
1615
|
+
* its stream without a `finish`), and the next attempt's prologue makes the
|
|
1616
|
+
* switch the form's pick would have made. This only splices the attempts
|
|
1617
|
+
* into one caller turn: one `stream-start`, and the `finish` of whichever
|
|
1618
|
+
* attempt did not switch.
|
|
1619
|
+
*
|
|
1620
|
+
* The bound is `run.tried`, as it is `tried` for the model chain: every
|
|
1621
|
+
* account spawned or switched to goes in, and none in it is picked again.
|
|
1622
|
+
*
|
|
1623
|
+
* Cancelling the returned stream is a stop, for every attempt: this loop is
|
|
1624
|
+
* the plugin starting more work on the caller's behalf, so a caller that
|
|
1625
|
+
* has gone away must reach all of it. Each attempt gets a signal that the
|
|
1626
|
+
* caller's own `abortSignal` and a `cancel()` both abort, and the loop
|
|
1627
|
+
* starts no further account once either has happened.
|
|
1628
|
+
*/
|
|
1629
|
+
private runAccountFailover;
|
|
1492
1630
|
/**
|
|
1493
1631
|
* Run the turn, moving to the next model in the fallback chain if the one
|
|
1494
1632
|
* it started on is refused. The single wiring point for `src/model-fallback.ts`.
|
|
@@ -1585,6 +1723,17 @@ type AgentRecord = {
|
|
|
1585
1723
|
* and not the CLI's per-agent `experimental.cacheTtl`.
|
|
1586
1724
|
*/
|
|
1587
1725
|
cacheTtl?: string;
|
|
1726
|
+
/**
|
|
1727
|
+
* `--max-turns` for this agent's own `claude` process. Raw as declared (a
|
|
1728
|
+
* number in opencode.json, a string in frontmatter); `resolveAgentCliCaps`
|
|
1729
|
+
* checks it.
|
|
1730
|
+
*/
|
|
1731
|
+
maxTurns?: number | string;
|
|
1732
|
+
/**
|
|
1733
|
+
* `--max-budget-usd` for this agent's own `claude` process. Subagents only;
|
|
1734
|
+
* see `resolveAgentCliCaps` for why.
|
|
1735
|
+
*/
|
|
1736
|
+
maxBudgetUsd?: number | string;
|
|
1588
1737
|
/**
|
|
1589
1738
|
* Models to try, in order, when the one this agent would have run is
|
|
1590
1739
|
* refused. Same account throughout; see `src/model-fallback.ts`.
|