@khalilgharbaoui/opencode-claude-code-plugin 0.55.0 → 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 CHANGED
@@ -18,6 +18,8 @@
18
18
  [![License](https://img.shields.io/github/license/khalilgharbaoui/opencode-claude-code-plugin?style=flat-square&label=license&labelColor=15181E&color=FFC46B)](./LICENSE)
19
19
  [![Buy me a coffee](https://img.shields.io/badge/support-buy%20me%20a%20coffee-FFC46B?style=flat-square&labelColor=15181E&logo=buymeacoffee&logoColor=FFC46B)](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
- Free and MIT-licensed. If the plugin saves you time, you can buy [its maintainer](https://github.com/khalilgharbaoui) a coffee:
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 {
@@ -621,6 +633,43 @@ interface ClaudeCodeProviderSettings {
621
633
  * the cache they wrote. See `resolveAgentCacheTtl` in `src/agent-models.ts`.
622
634
  */
623
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;
624
673
  /**
625
674
  * Models to try, in order, when the model a turn would have run on is
626
675
  * refused. The default for every agent that declares no `fallbackModels` of
@@ -781,6 +830,34 @@ interface ClaudeCodeProviderSettings {
781
830
  * the model has to work without it.
782
831
  */
783
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;
784
861
  /**
785
862
  * Per-tool proxy call timeouts in milliseconds, keyed by the proxy tool
786
863
  * name (`bash`, `edit`, `write`, `webfetch`, `task`, `question` —
@@ -990,6 +1067,23 @@ interface ClaudeCodeProviderSettings {
990
1067
  * verified live. Set `false` to keep the replay.
991
1068
  */
992
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;
993
1087
  /**
994
1088
  * Routing for Claude's built-in `WebSearch` tool.
995
1089
  *
@@ -1403,6 +1497,22 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
1403
1497
  * `⚙ invalid` and wedge the turn, so the plugin keeps the text path.
1404
1498
  */
1405
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;
1406
1516
  /**
1407
1517
  * A dispatch form, as a turn: one synthetic `question` tool call and a
1408
1518
  * `tool-calls` finish, which is how the operator's answer comes back on the
@@ -1613,6 +1723,17 @@ type AgentRecord = {
1613
1723
  * and not the CLI's per-agent `experimental.cacheTtl`.
1614
1724
  */
1615
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;
1616
1737
  /**
1617
1738
  * Models to try, in order, when the one this agent would have run is
1618
1739
  * refused. Same account throughout; see `src/model-fallback.ts`.