@khalilgharbaoui/opencode-claude-code-plugin 0.27.1 → 0.28.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 +81 -2
- package/dist/index.d.ts +120 -66
- package/dist/index.js +1163 -778
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/claude-code-plugin/SKILL.md +30 -3
package/README.md
CHANGED
|
@@ -345,6 +345,7 @@ model: claude-code-work/claude-opus-5@work
|
|
|
345
345
|
| `cwd` | string | see description | Working directory for the spawned CLI. Resolved **lazily per request**, first match winning: this explicit value, then the opencode session's own `directory` (so `opencode serve` and the web UI spawn in the right project even though one server handles many), then `process.cwd()` when it is a real directory, then the project directory captured at plugin init (this rescues macOS GUI launches, where `process.cwd()` is `/`), and finally `process.cwd()` regardless. [Startup diagnostics](#startup-diagnostics) reports which tier won. Session tier contributed by [@galvani](https://github.com/galvani). |
|
|
346
346
|
| `skipPermissions` | boolean | `true` | Pass `--dangerously-skip-permissions` to `claude`. It is still passed when `proxyTools` is set: proxied calls go through opencode's permission system regardless, but unproxied CLI built-ins do not. The one case where the flag is dropped is `permissionMode: "plan"`, because the CLI lets the skip flag override plan mode outright. See [Plan mode](#plan-mode). |
|
|
347
347
|
| `permissionMode` | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` | – | Forwarded to headless `claude --permission-mode`. `"plan"` also suppresses `--dangerously-skip-permissions` (see the row above). Not version-gated, so check that your installed CLI accepts the value. The [interactive transport](#interactive-transport-experimental) does not forward it. |
|
|
348
|
+
| `permissionPreset` | `"read-only"` | – | A named permission posture, so you set one option instead of combining five and getting one wrong. Opt-in: unset is exactly today's behaviour. `"read-only"` replaces `skipPermissions`, `permissionMode`, `controlRequestBehavior` and `controlRequestToolBehaviors`, and filters the write and command tools out of `proxyTools`. See [Read-only mode](#read-only-mode). |
|
|
348
349
|
| `defaultSubagentModel` | string | – | Model that plugin-discovered `mode: subagent` agents run on when their own definition pins nothing. The caller's account is kept; only the model name changes. An agent's own `forceModel` wins over it, and an unknown id is refused rather than spawned. Unset means no implicit override at all. See [Subagents: your account, their model](#subagents-your-account-their-model). |
|
|
349
350
|
| `proxyTools` | string[] | `["Bash", "Edit", "Write", "WebFetch", "Task"]` | Claude built-in tools to route through opencode's executor + permission UI. Opt-in extras: `"Question"`, `"Compress"`. See [Selective tool proxy](#selective-tool-proxy). |
|
|
350
351
|
| `extraDisallowedTools` | string[] | – | Extra Claude built-ins to switch off with `--disallowedTools`, on top of what `proxyTools` implies. Claude's names, e.g. `["NotebookEdit"]`. See [Closing a tool with no proxy](#closing-a-tool-with-no-proxy). |
|
|
@@ -362,7 +363,7 @@ model: claude-code-work/claude-opus-5@work
|
|
|
362
363
|
| `stripContextReminders` | boolean | `false` | Remove opencode-dcp's `<dcp-system-reminder>` blocks from message text when no `compress` tool is proxied, so an order the model cannot follow stops being re-sent with every message that carries it. Inert as soon as `compress` is reachable. See [Trimming unsatisfiable context reminders](#trimming-unsatisfiable-context-reminders). |
|
|
363
364
|
| `webSearch` | `"claude"` \| `"disabled"` \| `<tool>` | `"claude"` | Routing for Claude's built-in `WebSearch`. See [WebSearch routing](#websearch-routing). |
|
|
364
365
|
| `multiStepContinuation` | boolean | `true` | Append a system-prompt hint nudging Claude to chain tool calls within one turn instead of pausing between subtasks. Each opencode turn boundary requires the user to manually press "continue", so for multi-step tasks this reduces friction. Set `false` to disable. |
|
|
365
|
-
| `autoContinueIncompleteTurns` | boolean \| `"smart"` | `"smart"` | Smartly continue incomplete Claude CLI results inside the same opencode turn. Reduces manual "continue" presses when Claude ends after reasoning/tool activity without a useful final answer. Set `false` to disable. |
|
|
366
|
+
| `autoContinueIncompleteTurns` | boolean \| `"smart"` | `"smart"` | Smartly continue incomplete Claude CLI results inside the same opencode turn. Reduces manual "continue" presses when Claude ends after reasoning/tool activity without a useful final answer. Also gates the `▌ **no reply:**` note on a turn that ends with no text and no tool call. Set `false` to disable both. |
|
|
366
367
|
| `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). |
|
|
367
368
|
| `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). |
|
|
368
369
|
| `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). |
|
|
@@ -391,7 +392,9 @@ Every variable the plugin itself reads, in one place. Config is read once at ope
|
|
|
391
392
|
| `OPENCODE_CLAUDE_CODE_LOG_DIR` | logger | Directory for the log file, overriding `logging.dir`. |
|
|
392
393
|
| `OPENCODE_CLAUDE_CODE_LOG_LEVEL` | logger | Minimum level to emit, overriding `logging.level`. An unrecognised value falls through to config. |
|
|
393
394
|
| `DEBUG` | logger | `DEBUG=opencode-claude-code` promotes the logger to `mode: "debug"`, echoing every emitted level to opencode's TUI. |
|
|
394
|
-
| `OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP` | startup cleanup | `1` skips the
|
|
395
|
+
| `OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP` | startup cleanup | `1` skips the removal of a stale **unscoped** `opencode-claude-code-plugin` install from opencode's plugin cache. That old package is a different artifact that shadows this scoped one when both are present; set this if you are deliberately keeping it. |
|
|
396
|
+
| `OPENCODE_CLAUDE_CODE_PLUGIN_FORCE_CLEANUP` | startup cleanup | `1` runs that cleanup even when the marker at `$XDG_STATE_HOME/opencode-claude-code-plugin/cleanup-stale.json` (default `~/.local/state/...`) records that this plugin version already swept. Without it the cleanup walks opencode's plugin cache once per installed version rather than on every launch. |
|
|
397
|
+
| `OPENCODE_CLAUDE_CODE_NO_TMP_SWEEP` | scratch directory | `1` skips the sweep of `<tmpdir>/opencode-claude-code-<pid>` directories left behind by plugin processes that were killed. See [Scratch files on disk](#scratch-files-on-disk). |
|
|
395
398
|
| `OPENCODE_WORKTREE` | MCP bridge | Overrides worktree-root detection, which otherwise walks up from the working directory looking for a `.git` entry. |
|
|
396
399
|
| `OPENCODE_CONFIG` / `OPENCODE_CONFIG_DIR` | config discovery | Where the plugin looks for your opencode config when bridging MCP and skills. See [Discovery order](#discovery-order-highest-to-lowest-priority). |
|
|
397
400
|
| `OPENCODE_VERSION` | startup diagnostics | Reported as the opencode version when set, sparing the plugin a `--version` spawn. Diagnostics only. |
|
|
@@ -528,6 +531,23 @@ A patched process answers `401`. A `200` is a pre-0.13.2 process still running,
|
|
|
528
531
|
|
|
529
532
|
Nothing to configure. If proxied tools ever stop working after a Claude Code upgrade, check the plugin log for `proxy-mcp rejected a request`, which names which guard failed.
|
|
530
533
|
|
|
534
|
+
### Scratch files on disk
|
|
535
|
+
|
|
536
|
+
Everything the plugin writes for the Claude CLI to read goes into one per-process directory, `<tmpdir>/opencode-claude-code-<pid>`, created `0700`:
|
|
537
|
+
|
|
538
|
+
| File | Mode | Holds |
|
|
539
|
+
| --- | --- | --- |
|
|
540
|
+
| `mcp-<hash>.json` | `0600` | the bridged MCP config, including any `{env:VAR}` values substituted from your environment |
|
|
541
|
+
| `proxy-<hash>.json` | `0600` | the proxy endpoint's bearer token |
|
|
542
|
+
| `opencode-cc-sys-<uuid>.md` | `0600` | the full appended system prompt, forwarded opencode instructions and AGENTS.md included |
|
|
543
|
+
| `skills-<hash>/` | inherits | staged skill plugin dirs for the skill bridge; the `0700` parent is what keeps them private |
|
|
544
|
+
|
|
545
|
+
On a shared host the OS tmpdir is world-writable and the pid name is guessable, so the plugin refuses a path that already exists but is a symlink, is not a directory, or is not owned by you: it falls back to a fresh `mkdtemp` name and logs a warning naming both paths.
|
|
546
|
+
|
|
547
|
+
The directory is removed on normal exit. `SIGKILL` skips that, so on first use each run the plugin also sweeps `<tmpdir>/opencode-claude-code-<pid>` directories whose pid is no longer running and which you own. Anything else, another user's directory, a live process's, a symlink, a name that is not exactly that pattern, is left alone. Set `OPENCODE_CLAUDE_CODE_NO_TMP_SWEEP=1` to turn the sweep off.
|
|
548
|
+
|
|
549
|
+
**Windows is not hardened here.** Both spawn sites pass `shell: true` on `win32`, so the CLI argument list goes through `cmd.exe` unquoted. Treat Windows as unsupported until that is fixed; see the note in `docs/agents-history.md`.
|
|
550
|
+
|
|
531
551
|
### Closing a tool with no proxy
|
|
532
552
|
|
|
533
553
|
`proxyTools` only reaches built-ins the plugin can replace. A built-in with no opencode equivalent, `NotebookEdit` today and whatever Claude Code ships next, stays enabled and unmediated no matter what you put in that list. `extraDisallowedTools` names them directly:
|
|
@@ -782,6 +802,7 @@ Four Claude Code stream events used to reach nothing but a debug log:
|
|
|
782
802
|
- **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.
|
|
783
803
|
- **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.
|
|
784
804
|
- **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.
|
|
805
|
+
- **A turn that finished cleanly without saying anything.** No text, no tool call, no error: opencode files it as an ordinary reply, so what you get is a blank message with nothing to distinguish it from a crash. A `▌ **no reply:**` note now says which of the two shapes it was, thinking-with-no-answer or nothing at all, and that nothing failed and nothing is pending. It is its own text part and is stripped from any transcript rebuilt for the CLI. Never on a compaction turn, a failed turn, an aborted one, or one that ended on a question, and suppressed entirely by `"autoContinueIncompleteTurns": false`. There is deliberately no automatic retry: measured across the whole retained plugin log, every finished turn carried between 940 and 5,080 characters of reply and none was silent.
|
|
785
806
|
|
|
786
807
|
At session start the plugin also warns once per process for each MCP server Claude Code could not connect (its tools are simply absent otherwise) and once when the CLI's own `apiKeySource` says an API key is in effect, which is the field that tells you pay-as-you-go billing is happening. See [`ignoreAnthropicApiKey`](#options-reference).
|
|
787
808
|
|
|
@@ -915,6 +936,64 @@ Each chat keeps a long-lived `claude` subprocess so the model retains its native
|
|
|
915
936
|
|
|
916
937
|
---
|
|
917
938
|
|
|
939
|
+
## Read-only mode
|
|
940
|
+
|
|
941
|
+
```json
|
|
942
|
+
"options": {
|
|
943
|
+
"permissionPreset": "read-only"
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
That is the whole configuration. The turn can read your code and search the
|
|
948
|
+
web, and it cannot write a file, run a command, or execute code.
|
|
949
|
+
|
|
950
|
+
Presets exist because read-only was previously a combination you had to get
|
|
951
|
+
exactly right. `permissionMode: "plan"` alone does not do it, and neither does
|
|
952
|
+
any single Claude Code flag, because this plugin puts a second execution path
|
|
953
|
+
next to the CLI's own tools: `proxyTools` defaults to `Bash`, `Edit`, `Write`,
|
|
954
|
+
`WebFetch` and `Task`, and each of those is an MCP tool the CLI calls and
|
|
955
|
+
**opencode** executes. No CLI flag reaches them. So the preset works at three
|
|
956
|
+
layers:
|
|
957
|
+
|
|
958
|
+
| Layer | What read-only does | Why it is needed |
|
|
959
|
+
| --- | --- | --- |
|
|
960
|
+
| Claude CLI tools | `--restricted` (CLI 2.1.258+) | Removes Bash, the REPL and the other code-running built-ins, removes WebFetch, confines the file tools to the working directories, and refuses bypass |
|
|
961
|
+
| Claude CLI tools, older CLIs | `--disallowedTools Bash Write Edit NotebookEdit REPL JavaScript WebFetch` | `--restricted` is version-gated; these names are not |
|
|
962
|
+
| The opencode proxy | `bash`, `write`, `edit`, `webfetch`, `task` and `task_batch` are dropped from `proxyTools` | These run in opencode, so the CLI flags above never see them |
|
|
963
|
+
| Everything else | `--permission-prompts none` (CLI 2.1.263+) and `controlRequestBehavior: "deny"` | A bridged MCP tool or a read outside the working directory is neither of the above |
|
|
964
|
+
|
|
965
|
+
`skipPermissions` is forced to `false`, and that is not a style choice:
|
|
966
|
+
`--restricted --dangerously-skip-permissions` is a startup error on CLI 2.1.280
|
|
967
|
+
(`Error: bypassPermissions not supported in restricted mode`), so a spawn
|
|
968
|
+
carrying both would not run at all.
|
|
969
|
+
|
|
970
|
+
**The preset replaces rather than merges.** Setting `skipPermissions`,
|
|
971
|
+
`permissionMode`, `controlRequestBehavior` or `controlRequestToolBehaviors`
|
|
972
|
+
next to it has no effect; each dropped value is logged at NOTICE at startup so
|
|
973
|
+
you can see it happen. `proxyTools` is the exception: it is filtered, so a list
|
|
974
|
+
naming `Question` keeps it. An unrecognised preset name applies **nothing** and
|
|
975
|
+
logs a WARN, rather than guessing at what you meant.
|
|
976
|
+
|
|
977
|
+
**What stops working.** Reads are fine (`Read`, `Grep`, `Glob`, `WebSearch`),
|
|
978
|
+
but anything that would raise a permission prompt is denied, and that includes
|
|
979
|
+
bridged MCP tools and the `question` proxy. Claude's own `AskUserQuestion`
|
|
980
|
+
still renders its stop-and-wait markdown, so the model can still ask you
|
|
981
|
+
things. If you need one specific tool allowed, do not use the preset: set the
|
|
982
|
+
underlying options yourself.
|
|
983
|
+
|
|
984
|
+
**On an older CLI** the preset still holds through `--disallowedTools` plus the
|
|
985
|
+
plugin's own denial of every permission request, and it warns naming what is
|
|
986
|
+
missing. Below 2.1.258 you lose the cwd confinement on reads; below 2.1.263 the
|
|
987
|
+
denial happens in the plugin rather than in the CLI, one layer instead of two.
|
|
988
|
+
|
|
989
|
+
Measured end to end on CLI 2.1.280 and `claude-haiku-4-5`: a turn under the
|
|
990
|
+
preset asked to write a file and run a command did neither, the file was never
|
|
991
|
+
created, and the CLI's own `permission_denials` recorded the single blocked
|
|
992
|
+
`Write` with no Bash attempt at all, because there was no Bash tool to attempt
|
|
993
|
+
with.
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
918
997
|
## Plan mode
|
|
919
998
|
|
|
920
999
|
Set `permissionMode: "plan"` to forward `--permission-mode plan` to Claude. The plugin handles `ExitPlanMode` specially — instead of forwarding it as a tool call, it converts it to a confirmation prompt that flows through opencode normally.
|
package/dist/index.d.ts
CHANGED
|
@@ -376,7 +376,15 @@ interface ClaudeCodeConfig {
|
|
|
376
376
|
accountFailover?: AccountFailoverMode;
|
|
377
377
|
providerID?: string;
|
|
378
378
|
skipPermissions?: boolean;
|
|
379
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Widened past `PermissionMode` because a resolved `permissionPreset` puts
|
|
381
|
+
* its own internal token here (`READ_ONLY_PERMISSION_MODE`), which
|
|
382
|
+
* `buildCliArgs` translates to CLI flags. The operator-facing option on
|
|
383
|
+
* `ClaudeCodeProviderSettings` stays a plain `PermissionMode`.
|
|
384
|
+
*/
|
|
385
|
+
permissionMode?: EffectivePermissionMode;
|
|
386
|
+
/** The preset this config was resolved from, for diagnostics. */
|
|
387
|
+
permissionPreset?: PermissionPreset;
|
|
380
388
|
mcpConfig?: string | string[];
|
|
381
389
|
strictMcpConfig?: boolean;
|
|
382
390
|
bridgeOpencodeMcp?: boolean;
|
|
@@ -492,6 +500,23 @@ interface ClaudeCodeProviderSettings {
|
|
|
492
500
|
defaultSubagentModel?: string;
|
|
493
501
|
skipPermissions?: boolean;
|
|
494
502
|
permissionMode?: PermissionMode;
|
|
503
|
+
/**
|
|
504
|
+
* Apply a named permission preset instead of hand-combining
|
|
505
|
+
* `permissionMode`, `skipPermissions`, `proxyTools`,
|
|
506
|
+
* `extraDisallowedTools` and `controlRequestBehavior`.
|
|
507
|
+
*
|
|
508
|
+
* `"read-only"` makes the turn unable to change anything: no
|
|
509
|
+
* `--dangerously-skip-permissions` (the CLI refuses bypass under
|
|
510
|
+
* `--restricted` outright), `--restricted` so the CLI has no Bash, REPL or
|
|
511
|
+
* WebFetch at all, the mutating built-ins on `--disallowedTools` for CLIs
|
|
512
|
+
* too old for that flag, the write and command tools removed from the
|
|
513
|
+
* opencode proxy, and every remaining permission request denied.
|
|
514
|
+
*
|
|
515
|
+
* A preset overrides those five options rather than merging with them, so
|
|
516
|
+
* one setting decides the posture; the plugin warns about each value it
|
|
517
|
+
* drops. Unset (the default) changes nothing.
|
|
518
|
+
*/
|
|
519
|
+
permissionPreset?: PermissionPreset;
|
|
495
520
|
mcpConfig?: string | string[];
|
|
496
521
|
strictMcpConfig?: boolean;
|
|
497
522
|
/**
|
|
@@ -806,6 +831,25 @@ interface ClaudeCodeProviderSettings {
|
|
|
806
831
|
logging?: LoggingConfig;
|
|
807
832
|
}
|
|
808
833
|
type PermissionMode = "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
|
|
834
|
+
/**
|
|
835
|
+
* A named bundle of permission settings, so a safety posture is one option
|
|
836
|
+
* instead of a hand-rolled combination of `permissionMode`, `skipPermissions`,
|
|
837
|
+
* `proxyTools`, `extraDisallowedTools` and `controlRequestBehavior`. Opt-in:
|
|
838
|
+
* unset means today's behaviour, unchanged.
|
|
839
|
+
*
|
|
840
|
+
* `read-only` is the only preset so far. See `src/permission-presets.ts` for
|
|
841
|
+
* what it resolves to and why each part is needed.
|
|
842
|
+
*/
|
|
843
|
+
type PermissionPreset = "read-only";
|
|
844
|
+
/**
|
|
845
|
+
* The `permissionMode` a resolved preset puts on `ClaudeCodeConfig`.
|
|
846
|
+
* Plugin-internal, never a value the operator sets: `buildCliArgs` translates
|
|
847
|
+
* `"read-only"` into `--restricted` (plus `--permission-prompts none` where
|
|
848
|
+
* the CLI has it) rather than passing it to `--permission-mode`, which would
|
|
849
|
+
* reject it.
|
|
850
|
+
*/
|
|
851
|
+
declare const READ_ONLY_PERMISSION_MODE = "read-only";
|
|
852
|
+
type EffectivePermissionMode = PermissionMode | typeof READ_ONLY_PERMISSION_MODE;
|
|
809
853
|
type ControlRequestBehavior = "allow" | "deny";
|
|
810
854
|
/**
|
|
811
855
|
* Claude CLI stream-json message types.
|
|
@@ -937,6 +981,61 @@ interface ClaudeStreamMessage {
|
|
|
937
981
|
index?: number;
|
|
938
982
|
}
|
|
939
983
|
|
|
984
|
+
/**
|
|
985
|
+
* Named permission presets: one option that decides a safety posture, instead
|
|
986
|
+
* of the operator hand-combining `permissionMode`, `skipPermissions`,
|
|
987
|
+
* `proxyTools`, `extraDisallowedTools` and `controlRequestBehavior` and
|
|
988
|
+
* getting one of them wrong.
|
|
989
|
+
*
|
|
990
|
+
* Opt-in. No preset resolves to exactly today's behaviour, byte for byte.
|
|
991
|
+
*
|
|
992
|
+
* ## Why `read-only` is not just a CLI flag
|
|
993
|
+
*
|
|
994
|
+
* Claude Code 2.1.248 added `--restricted`, which removes the built-in tools
|
|
995
|
+
* that run commands or code plus WebFetch, confines the file tools to the
|
|
996
|
+
* working directories, and refuses bypassPermissions. That covers the CLI's
|
|
997
|
+
* own tools and nothing else, and this plugin's whole point is that it puts a
|
|
998
|
+
* second execution path next to them: `proxyTools` defaults to `Bash`, `Edit`,
|
|
999
|
+
* `Write`, `WebFetch` and `Task`, each an MCP tool the CLI calls and opencode
|
|
1000
|
+
* executes. `--restricted` never sees those. So the preset has to work at
|
|
1001
|
+
* three layers at once:
|
|
1002
|
+
*
|
|
1003
|
+
* 1. the CLI's own tools (`--restricted`, plus `--disallowedTools` for the
|
|
1004
|
+
* same names so a CLI too old for `--restricted` still refuses them),
|
|
1005
|
+
* 2. the opencode proxy (the mutating defs are dropped before the proxy
|
|
1006
|
+
* server is built, so they are never offered),
|
|
1007
|
+
* 3. everything left that would prompt (denied, because a bridged MCP
|
|
1008
|
+
* server or a file tool reaching outside the cwd is neither of the
|
|
1009
|
+
* above).
|
|
1010
|
+
*
|
|
1011
|
+
* ## Measured, on the CLI this was written against (2.1.280)
|
|
1012
|
+
*
|
|
1013
|
+
* `--restricted --dangerously-skip-permissions` is a hard startup error,
|
|
1014
|
+
* `Error: bypassPermissions not supported in restricted mode`, so dropping
|
|
1015
|
+
* the skip flag is a correctness requirement and not hygiene. In the binary,
|
|
1016
|
+
* `restrictedMode` is a `bypassImmune` circuit breaker, which is the same
|
|
1017
|
+
* fact from the other side. A `--restricted` run asked to write a file and
|
|
1018
|
+
* run a command reported one `permission_denials` entry (`Write`) and no Bash
|
|
1019
|
+
* attempt at all: the shell tool was not in the set to begin with.
|
|
1020
|
+
*/
|
|
1021
|
+
|
|
1022
|
+
/** The options a preset decides, resolved. */
|
|
1023
|
+
interface ResolvedPermissionPreset {
|
|
1024
|
+
preset: PermissionPreset;
|
|
1025
|
+
permissionMode: EffectivePermissionMode;
|
|
1026
|
+
skipPermissions: boolean;
|
|
1027
|
+
proxyTools: string[];
|
|
1028
|
+
extraDisallowedTools: string[];
|
|
1029
|
+
controlRequestBehavior: ControlRequestBehavior;
|
|
1030
|
+
/** Always undefined under a preset: see `overridden` below. */
|
|
1031
|
+
controlRequestToolBehaviors: undefined;
|
|
1032
|
+
/**
|
|
1033
|
+
* Operator-set values the preset replaced, as `option: reason` lines, so
|
|
1034
|
+
* the caller can warn once per option rather than silently winning.
|
|
1035
|
+
*/
|
|
1036
|
+
overridden: string[];
|
|
1037
|
+
}
|
|
1038
|
+
|
|
940
1039
|
interface DiagnosticsProviderEntry {
|
|
941
1040
|
name?: string;
|
|
942
1041
|
options?: Record<string, unknown>;
|
|
@@ -953,67 +1052,21 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
953
1052
|
private toFinishReason;
|
|
954
1053
|
/**
|
|
955
1054
|
* Whether this call only names the session, which gets the synthetic stub
|
|
956
|
-
* rather than a `claude` spawn.
|
|
957
|
-
* tools, and that is the whole test there. opencode 2 sends its tool set
|
|
958
|
-
* along with it (measured on 2.0.11: `scope: "tools"`, agent `title`), so
|
|
959
|
-
* every new V2 session paid for a second `claude` process just to title
|
|
960
|
-
* itself; for a V2 model the request kind, carried as the `title` agent,
|
|
961
|
-
* decides instead.
|
|
1055
|
+
* rather than a `claude` spawn. See `isTitleRequest` in title.ts.
|
|
962
1056
|
*/
|
|
963
1057
|
private isTitleRequest;
|
|
964
1058
|
private requestScope;
|
|
965
1059
|
/**
|
|
966
1060
|
* Build the combined `--mcp-config` list and return both the list and the
|
|
967
|
-
* hash of the bridged opencode MCP block
|
|
968
|
-
*
|
|
969
|
-
* and respawn the underlying claude process.
|
|
970
|
-
*
|
|
971
|
-
* `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When
|
|
972
|
-
* provided it overlays opencode's UI-toggled state on top of disk config
|
|
973
|
-
* so `/mcps` toggles propagate without a config file write.
|
|
1061
|
+
* hash of the bridged opencode MCP block. See `effectiveMcpConfig` in
|
|
1062
|
+
* spawn-planning.ts.
|
|
974
1063
|
*/
|
|
975
1064
|
private effectiveMcpConfig;
|
|
976
1065
|
/** Resolve ProxyToolDef[] for the configured proxyTools names. */
|
|
977
1066
|
private resolvedProxyTools;
|
|
978
|
-
/**
|
|
979
|
-
* Resolve ProxyToolDef[] for opencode's MCP-backed tools so they go
|
|
980
|
-
* through the in-process proxy instead of being bridged into Claude CLI's
|
|
981
|
-
* `--mcp-config`. Routing through the proxy keeps a single execution site
|
|
982
|
-
* (opencode), so the call is permission-prompted and rendered as an
|
|
983
|
-
* opencode tool call.
|
|
984
|
-
*
|
|
985
|
-
* Opt-in (`proxyOpencodeMcpTools: true`) and off by default. It used to
|
|
986
|
-
* default to true while finding nothing, because it discovered tools via
|
|
987
|
-
* `client.tool.list()`, which enumerates opencode's `ToolRegistry` and not
|
|
988
|
-
* the MCP tools merged into the model's tool set afterwards. Discovery now
|
|
989
|
-
* reads that merged set, the `tools` array opencode passes `doStream`, so
|
|
990
|
-
* the option does what it says. Turning it on by default at the same time
|
|
991
|
-
* would have silently moved every existing user's MCP traffic off the
|
|
992
|
-
* working direct bridge, so the default went to false instead: today's
|
|
993
|
-
* behaviour is preserved exactly and crossing over is the operator's call.
|
|
994
|
-
*
|
|
995
|
-
* Returns null when the feature is off or nothing matched, which leaves
|
|
996
|
-
* every server on the direct bridge.
|
|
997
|
-
*/
|
|
1067
|
+
/** Resolve ProxyToolDef[] for opencode's MCP-backed tools. */
|
|
998
1068
|
private resolvedProxyMcpTools;
|
|
999
|
-
/**
|
|
1000
|
-
* Live tool info derived from a single `client.tool.list()` fetch:
|
|
1001
|
-
*
|
|
1002
|
-
* - `taskDescription`: opencode's `task` tool description exactly as the
|
|
1003
|
-
* registry renders it for native models, including the "Available
|
|
1004
|
-
* agent types" list. Overlaid onto the static `task` proxy def so
|
|
1005
|
-
* Claude sees the same subagent catalog native models see, instead
|
|
1006
|
-
* of hunting through config files.
|
|
1007
|
-
* - `questionDescription` / `hasQuestion`: opencode's `question` tool
|
|
1008
|
-
* description and whether the registry has the entry at all. Older
|
|
1009
|
-
* builds lack it, in which case a `mcp__opencode_proxy__question`
|
|
1010
|
-
* call resolves to `⚙ invalid`; the version gate drops the def.
|
|
1011
|
-
*
|
|
1012
|
-
* Returns undefined/false when the SDK client is unavailable (direct
|
|
1013
|
-
* AI-SDK use, tests) so the static defs stand. `resolved` distinguishes
|
|
1014
|
-
* "the registry answered and has no `question` entry" from "nobody
|
|
1015
|
-
* answered": only the former is a real version-gate signal.
|
|
1016
|
-
*/
|
|
1069
|
+
/** One `client.tool.list()` fetch, shaped for this turn's gates. */
|
|
1017
1070
|
private fetchLiveToolInfo;
|
|
1018
1071
|
/**
|
|
1019
1072
|
* Whether dcp-style context reminders should be stripped from this turn's
|
|
@@ -1024,8 +1077,7 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1024
1077
|
/**
|
|
1025
1078
|
* Arguments the skill bridge needs beyond `cwd` / `cliPath`: which
|
|
1026
1079
|
* `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether
|
|
1027
|
-
* to drop the ones it already loads.
|
|
1028
|
-
* account, and therefore to that account's config dir.
|
|
1080
|
+
* to drop the ones it already loads.
|
|
1029
1081
|
*/
|
|
1030
1082
|
private skillBridgeSpawn;
|
|
1031
1083
|
/** Share one lazy registry request within a turn without making it stale. */
|
|
@@ -1042,17 +1094,6 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1042
1094
|
* The process lifecycle owns the server lifecycle via session-manager.
|
|
1043
1095
|
*/
|
|
1044
1096
|
private ensureProxyServer;
|
|
1045
|
-
private extractPendingProxyResult;
|
|
1046
|
-
/**
|
|
1047
|
-
* The result opencode produced for a pending proxy call, if the prompt
|
|
1048
|
-
* carries it. For `task_batch` that means every child's result gathered
|
|
1049
|
-
* back onto the parent: opencode runs the children in one step and hands
|
|
1050
|
-
* all their results to the next call together, so a partial set is not
|
|
1051
|
-
* expected. If it ever happens the batch still resolves, with the gap
|
|
1052
|
-
* named in the text, because leaving the parent pending would send this
|
|
1053
|
-
* turn down the fresh-envelope path and reject the call as orphaned.
|
|
1054
|
-
*/
|
|
1055
|
-
private extractPendingProxyResultForCall;
|
|
1056
1097
|
/**
|
|
1057
1098
|
* Resolve the session affinity token for this LLM call. Delegates to the
|
|
1058
1099
|
* exported `resolveSessionAffinity` helper so the logic is unit-testable.
|
|
@@ -1197,6 +1238,19 @@ declare function registerSideQuestionCommand(config: OpenCodeConfig): boolean;
|
|
|
1197
1238
|
declare function registerDoctorCommand(config: OpenCodeConfig): boolean;
|
|
1198
1239
|
declare function _resetPlanModeWarningForTests(): void;
|
|
1199
1240
|
declare function warnIfPlanModeCannotExit(permissionMode: string | undefined): void;
|
|
1241
|
+
/**
|
|
1242
|
+
* Resolve `permissionPreset` for this provider, reporting what it replaced.
|
|
1243
|
+
*
|
|
1244
|
+
* The result is applied over the operator's settings by `createClaudeCode`,
|
|
1245
|
+
* which is the one funnel both opencode majors reach the language model
|
|
1246
|
+
* through, so a preset needs wiring in exactly one place.
|
|
1247
|
+
*
|
|
1248
|
+
* Every override is logged at NOTICE, and an unrecognised preset name is a
|
|
1249
|
+
* WARN plus no preset at all. A safety option must never be approximated: a
|
|
1250
|
+
* typo'd `"readonly"` silently running at full permissions is worse than one
|
|
1251
|
+
* that says so.
|
|
1252
|
+
*/
|
|
1253
|
+
declare function applyPermissionPreset(settings: ClaudeCodeProviderSettings, defaultProxyTools: readonly string[]): ResolvedPermissionPreset | null;
|
|
1200
1254
|
declare function createClaudeCode(settings?: ClaudeCodeProviderSettings): ClaudeCodeProvider;
|
|
1201
1255
|
/**
|
|
1202
1256
|
* Build models in OpenCode's config schema format (flat properties like
|
|
@@ -1236,4 +1290,4 @@ declare const _default: {
|
|
|
1236
1290
|
setup: (ctx: V2Context) => Promise<V2Cleanup>;
|
|
1237
1291
|
};
|
|
1238
1292
|
|
|
1239
|
-
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, _resetPlanModeWarningForTests, bridgeOpencodeMcp, buildAgentRegistry, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, extractDeletedSessionId, getAgentRegistry, getDefaultSubagentModel, registerDoctorCommand, registerSideQuestionCommand, resolveAgentModel, warnIfPlanModeCannotExit };
|
|
1293
|
+
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, _resetPlanModeWarningForTests, applyPermissionPreset, bridgeOpencodeMcp, buildAgentRegistry, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, extractDeletedSessionId, getAgentRegistry, getDefaultSubagentModel, registerDoctorCommand, registerSideQuestionCommand, resolveAgentModel, warnIfPlanModeCannotExit };
|