@khalilgharbaoui/opencode-claude-code-plugin 0.17.0 → 0.18.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/README.md +30 -6
- package/dist/index.d.ts +7 -1
- package/dist/index.js +79 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/skills/claude-code-plugin/SKILL.md +448 -0
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ model: claude-code-appical/claude-opus-5@appical
|
|
|
267
267
|
| `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). |
|
|
268
268
|
| `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). |
|
|
269
269
|
| `proxyToolTimeoutMs` | `Record<string, number>` | – | Per-tool proxy call deadline in ms, keyed by proxy tool name (`bash`, `task`, …). Defaults: 10 min flat, `task` → 60 min. For `bash`, the call's own `input.timeout` is honoured on top (`max(resolved, input.timeout)`). See [Selective tool proxy](#selective-tool-proxy). |
|
|
270
|
-
| `planModeQuestion` | boolean | `false` | Route `ExitPlanMode` approval through opencode's native `question` tool instead of a text "(yes/no)" prompt.
|
|
270
|
+
| `planModeQuestion` | boolean | `false` | Route `ExitPlanMode` approval through opencode's native `question` tool instead of a text "(yes/no)" prompt. Opt-in; verify the form works in your installation first. See [Plan mode](#plan-mode). |
|
|
271
271
|
| `controlRequestBehavior` | `allow` \| `deny` | `allow` | Default response when `skipPermissions: false` and Claude sends a `can_use_tool` control request. |
|
|
272
272
|
| `controlRequestToolBehaviors` | `Record<string, "allow" \| "deny">` | – | Per-tool override for `can_use_tool`. Example: `{ "Bash": "deny", "Read": "allow" }`. |
|
|
273
273
|
| `controlRequestDenyMessage` | string | built-in message | Message returned to Claude on a deny. |
|
|
@@ -553,6 +553,20 @@ Notes:
|
|
|
553
553
|
|
|
554
554
|
Fully restart opencode after upgrading to load the command and runtime changes. Other providers do not gain Claude's native side-question behavior from this command.
|
|
555
555
|
|
|
556
|
+
## Configuration skill
|
|
557
|
+
|
|
558
|
+
The package includes a `claude-code-plugin` skill so your agent can configure it without asking you to navigate all its options. Ask, for example:
|
|
559
|
+
|
|
560
|
+
```text
|
|
561
|
+
Use the claude-code-plugin skill to configure a work account and idle worker cleanup.
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
It covers accounts, models and agent effort, proxy tools, permissions, MCP/skill bridging, timeouts, logging, upgrades and troubleshooting. It directs the agent to preserve JSONC comments, change only requested settings, validate the result, protect credentials and ask before paid probes or broader permissions.
|
|
565
|
+
|
|
566
|
+
The plugin registers the bundled directory with opencode's `skills.paths`, making it available to other providers too on supporting opencode versions. For ordinary headless Claude turns it also loads through Claude's native Skill tool as `opencode-skills:claude-code-plugin`, even when `bridgeOpencodeSkills` is off. This requires CLI `--plugin-dir` support; interactive transport, compaction and direct `doGenerate` calls do not load the native bridge.
|
|
567
|
+
|
|
568
|
+
No separate skill installation or copying is needed. It ships with each package version, so upgrading updates the reference. Fully restart opencode to load it. `test-configure-skill.ts` checks coverage of provider/logging options, model ids, proxy tools and environment variables; maintainers must update behavior and default guidance in the same change as the implementation.
|
|
569
|
+
|
|
556
570
|
## Skill bridge
|
|
557
571
|
|
|
558
572
|
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`.
|
|
@@ -568,7 +582,7 @@ Claude can invoke them with the Skill tool or as `/opencode-skills:<name>`. `--p
|
|
|
568
582
|
|
|
569
583
|
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.
|
|
570
584
|
|
|
571
|
-
|
|
585
|
+
Bridging **your own skills is off by default** here, unlike on the fork it came from: every bridged skill is also listed in the system prompt opencode already forwards, so a large skill set is paid for twice on every turn. Turn it on when you see `Unknown skill`. The bundled configuration skill is loaded independently of this opt-in. The native bridge is not used for compaction or interactive transport, or on a Claude CLI without `--plugin-dir` (the plugin probes `claude --help` and logs a notice).
|
|
572
586
|
|
|
573
587
|
This bridge was written by [@broskees](https://github.com/broskees) (Joseph Roberts) on his fork and absorbed here with credit; see [Credits](#credits).
|
|
574
588
|
|
|
@@ -646,6 +660,10 @@ Each chat keeps a long-lived `claude` subprocess so the model retains its native
|
|
|
646
660
|
|
|
647
661
|
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.
|
|
648
662
|
|
|
663
|
+
> **Plan mode never permits edits, and you do not have to configure anything for that.** The CLI lets `--dangerously-skip-permissions` override `--permission-mode plan` outright, and `skipPermissions` defaults to `true`, so until this was fixed anyone asking for plan mode silently got full write access (measured on CLI 2.1.258: the run wrote a file on request without a prompt). The plugin now drops the skip flag whenever `permissionMode` is `"plan"`; every other mode governs prompting, which is what that flag is for, so those still pass it.
|
|
664
|
+
>
|
|
665
|
+
> Two things to know. Nothing releases plan mode mid-session: headless Claude Code is not offered an `ExitPlanMode` tool, so approving a plan in chat does not unlock writes, and leaving plan mode means changing the config and restarting opencode. The plugin warns about this once at startup. And the CLI still writes its own plan document under `~/.claude*/plans/`, which is its own feature and outside your workspace; your files and commands are untouched.
|
|
666
|
+
|
|
649
667
|
By default that prompt is text: the plan is rendered as markdown, followed by `**Do you want to proceed with this plan?** (yes/no)`, and you answer in your next message.
|
|
650
668
|
|
|
651
669
|
### Approval as a real form (`planModeQuestion`, opt-in)
|
|
@@ -661,7 +679,7 @@ Set `planModeQuestion: true` to route the approval through opencode's native `qu
|
|
|
661
679
|
|
|
662
680
|
The plan is still rendered, but the turn then ends on `tool-calls` and opencode runs its own `question` tool, so approval is a form rather than prose. Your answer is fed back to the CLI as the `tool_result` for the original `ExitPlanMode` call, which is what actually unlocks plan mode on the Claude side. A "yes" typed as ordinary text never does that. Anything other than picking `yes` (including custom text) comes back as rejection feedback the model is told to act on.
|
|
663
681
|
|
|
664
|
-
> **
|
|
682
|
+
> **This cannot currently fire on the default headless transport, so leaving it off costs you nothing.** The form it delivers through works (see [AskUserQuestion](#askuserquestion)), but headless `--print` does not offer the model an `ExitPlanMode` tool at all on CLI 2.1.258, and the bridge keys on that tool call. Measured three ways: asked directly for its tool list in plan mode, the CLI returned `Agent, Bash, Edit, ListAgents, Read, ReportFindings, ScheduleWakeup, Skill, ToolSearch, Workflow, Write` and nothing else; asked to do work it said "I'm unable to exit plan mode from within the tool set available to me"; and a full probe through this plugin with `planModeQuestion: true` produced no `ExitPlanMode` anywhere in `plugin.log` while the model asked for approval in prose. The name is still known to the CLI (`--disallowedTools ExitPlanMode` validates silently, where a bogus name warns), so this reads as headless dormancy rather than removal, the same shape as the [`AskUserQuestion` fallback](#askuserquestion). The text path below is what you actually get, and it works. Re-run those probes on a newer CLI before assuming the bridge is reachable. On opencode builds with no `question` registry entry the plugin silently keeps the text path (look for `plan-mode question gate` in the log).
|
|
665
683
|
|
|
666
684
|
Approval bridge contributed by [@CollieIsCute](https://github.com/CollieIsCute).
|
|
667
685
|
|
|
@@ -671,11 +689,17 @@ Approval bridge contributed by [@CollieIsCute](https://github.com/CollieIsCute).
|
|
|
671
689
|
|
|
672
690
|
opencode ships a built-in `question` tool (`packages/opencode/src/tool/question.ts`) that renders a real TUI form with options and a custom-answer field — near-identical to Claude Code's `AskUserQuestion` (`multiSelect` → `multiple`). The plugin can route `AskUserQuestion` through it so the prompt becomes an actual form instead of plain text. Two modes:
|
|
673
691
|
|
|
674
|
-
### With `"Question"` in `proxyTools` (
|
|
692
|
+
### With `"Question"` in `proxyTools` (opt-in)
|
|
693
|
+
|
|
694
|
+
> **Correction, September 6, 2026: this is no longer blocked, and earlier releases of this README were wrong about why.** The missing form was attributed to an upstream TUI regression. The real cause was local: a notification plugin awaited macOS `alerter` dismissal inside `tool.execute.before`, so the question tool never started. Native providers load that same global plugin, which is why their identical failure did not isolate the TUI. With the hook made non-blocking, the form renders, and the full path through this plugin is verified: on plugin 0.18.0 / Claude Code 2.1.258 / opencode 1.18.29, Claude called `mcp__opencode_proxy__question`, the request appeared in `GET /question`, the reply completed the tool, and Claude's answer contained a token it could only have read from the tool result. Confirmed in a real terminal too: with `"Question"` enabled and opencode relaunched, the proxied call rendered as a TUI form and the clicked answers came back into the turn.
|
|
695
|
+
>
|
|
696
|
+
> `"Question"` is still opt-in, because turning it on disables Claude's own `AskUserQuestion` (see the fallback below) and that trade should be deliberate. If your form does not render, see [question troubleshooting](#question-troubleshooting) before assuming an upstream bug.
|
|
697
|
+
|
|
698
|
+
#### Question troubleshooting
|
|
675
699
|
|
|
676
|
-
|
|
700
|
+
For a stalled call, inspect `GET /question` on the same opencode server and workspace. If no request exists, check awaited `tool.execute.before` hooks and custom tools replacing `question`, especially notification plugins: a hook opencode waits on runs *before* the tool, so the request cannot exist yet. If a request exists but no form appears, check session ownership, pending permissions, and event delivery. The separate detach/reattach issue [anomalyco/opencode#36604](https://github.com/anomalyco/opencode/issues/36604) remains open; [PR #36603](https://github.com/anomalyco/opencode/pull/36603) is closed without merging. Do not infer a universal platform or version failure from either symptom.
|
|
677
701
|
|
|
678
|
-
Add `"Question"` to `proxyTools
|
|
702
|
+
Add `"Question"` to `proxyTools`. Claude's built-in `AskUserQuestion` is disabled via `--disallowedTools`, and the plugin exposes `mcp__opencode_proxy__question` in its place. A primary agent needs no permission entry (verified on opencode 1.18.29 with no `permission` block at all); if a subagent's form is refused, grant it `permission.question: "allow"` on that agent, the same way [subagent todos](#subagent-todos) need `todowrite`. The model calls the proxy, opencode renders the form, and the operator's answers come back as arrays of selected labels. On builds that lack the `question` registry entry the def is silently dropped at spawn (version gate), and the deny/markdown fallback below applies instead.
|
|
679
703
|
|
|
680
704
|
`proxyTools` replaces the default list rather than adding to it, so repeat the defaults you still want:
|
|
681
705
|
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,10 @@ type OpenCodeConfig = {
|
|
|
78
78
|
models?: Record<string, unknown>;
|
|
79
79
|
}>;
|
|
80
80
|
agent?: Record<string, Record<string, unknown>>;
|
|
81
|
+
skills?: {
|
|
82
|
+
paths?: string[];
|
|
83
|
+
urls?: string[];
|
|
84
|
+
};
|
|
81
85
|
};
|
|
82
86
|
/**
|
|
83
87
|
* Bus events surface to plugins. Shape mirrors what opencode core publishes
|
|
@@ -764,6 +768,8 @@ declare const DEFAULT_PROXY_TOOL_NAMES: string[];
|
|
|
764
768
|
* so a user-defined command keeps opencode's normal behaviour end to end.
|
|
765
769
|
*/
|
|
766
770
|
declare function registerSideQuestionCommand(config: OpenCodeConfig): boolean;
|
|
771
|
+
declare function _resetPlanModeWarningForTests(): void;
|
|
772
|
+
declare function warnIfPlanModeCannotExit(permissionMode: string | undefined): void;
|
|
767
773
|
declare function createClaudeCode(settings?: ClaudeCodeProviderSettings): ClaudeCodeProvider;
|
|
768
774
|
/**
|
|
769
775
|
* Build models in OpenCode's config schema format (flat properties like
|
|
@@ -782,4 +788,4 @@ declare const _default: {
|
|
|
782
788
|
server: OpenCodePlugin;
|
|
783
789
|
};
|
|
784
790
|
|
|
785
|
-
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, getAgentRegistry, getDefaultSubagentModel, registerSideQuestionCommand, resolveAgentModel };
|
|
791
|
+
export { type AgentRecord, type ClaudeCodeConfig, ClaudeCodeLanguageModel, type ClaudeCodeProvider, type ClaudeCodeProviderSettings, type ClaudeStreamMessage, DEFAULT_PROXY_TOOL_NAMES, type OpenCodeHooks, type OpenCodeModel, type OpenCodePlugin, _resetPlanModeWarningForTests, bridgeOpencodeMcp, claudeCodeProviders, configModelsForProvider, createClaudeCode, _default as default, defaultModels, getAgentRegistry, getDefaultSubagentModel, registerSideQuestionCommand, resolveAgentModel, warnIfPlanModeCannotExit };
|
package/dist/index.js
CHANGED
|
@@ -447,10 +447,13 @@ function detectCliSupportsFlag(cliPath, flag) {
|
|
|
447
447
|
if (cached) return cached;
|
|
448
448
|
const promise = (async () => {
|
|
449
449
|
try {
|
|
450
|
-
const
|
|
450
|
+
const execution = execFileAsync(cliPath, ["--help"], {
|
|
451
451
|
timeout: 5e3,
|
|
452
|
+
killSignal: "SIGKILL",
|
|
452
453
|
maxBuffer: 4 * 1024 * 1024
|
|
453
454
|
});
|
|
455
|
+
execution.child.stdin?.end();
|
|
456
|
+
const { stdout } = await execution;
|
|
454
457
|
return stdout.includes(flag);
|
|
455
458
|
} catch (err) {
|
|
456
459
|
log.warn("failed to probe claude cli flag support", {
|
|
@@ -2252,7 +2255,7 @@ function buildCliArgs(opts) {
|
|
|
2252
2255
|
if (fastMode && cliSupportsFastMode(cliVersion ?? null)) {
|
|
2253
2256
|
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
2254
2257
|
}
|
|
2255
|
-
if (skipPermissions) {
|
|
2258
|
+
if (skipPermissions && permissionMode !== "plan") {
|
|
2256
2259
|
args.push("--dangerously-skip-permissions");
|
|
2257
2260
|
}
|
|
2258
2261
|
return args;
|
|
@@ -3302,7 +3305,17 @@ import * as crypto2 from "crypto";
|
|
|
3302
3305
|
import * as fs3 from "fs";
|
|
3303
3306
|
import * as os2 from "os";
|
|
3304
3307
|
import * as path4 from "path";
|
|
3308
|
+
import { fileURLToPath } from "url";
|
|
3305
3309
|
var SKILL_PLUGIN_NAME = "opencode-skills";
|
|
3310
|
+
function bundledSkillsDir() {
|
|
3311
|
+
try {
|
|
3312
|
+
const here = fileURLToPath(import.meta.url);
|
|
3313
|
+
const dir = path4.resolve(path4.dirname(here), "..", "skills");
|
|
3314
|
+
return dirExists(dir) ? dir : null;
|
|
3315
|
+
} catch {
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3306
3319
|
function dirExists(p) {
|
|
3307
3320
|
try {
|
|
3308
3321
|
return fs3.statSync(p).isDirectory();
|
|
@@ -3341,28 +3354,46 @@ function skillRoots(cwd) {
|
|
|
3341
3354
|
if (xdg) push(path4.join(xdg, "opencode", "skills"));
|
|
3342
3355
|
return roots;
|
|
3343
3356
|
}
|
|
3357
|
+
function collectSkills(root, claimed, found) {
|
|
3358
|
+
let entries;
|
|
3359
|
+
try {
|
|
3360
|
+
entries = fs3.readdirSync(root, { withFileTypes: true });
|
|
3361
|
+
} catch {
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
for (const entry of entries) {
|
|
3365
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
3366
|
+
const name = entry.name;
|
|
3367
|
+
if (name.startsWith(".")) continue;
|
|
3368
|
+
if (claimed.has(name)) continue;
|
|
3369
|
+
const dir = path4.join(root, name);
|
|
3370
|
+
if (!fileExists(path4.join(dir, "SKILL.md"))) continue;
|
|
3371
|
+
claimed.add(name);
|
|
3372
|
+
found.push({ name, dir });
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
var byName = (a, b) => a.name.localeCompare(b.name);
|
|
3344
3376
|
function discoverOpencodeSkills(cwd) {
|
|
3345
3377
|
const found = [];
|
|
3346
3378
|
const claimed = /* @__PURE__ */ new Set();
|
|
3347
|
-
for (const root of skillRoots(cwd))
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
return found.sort((a, b) => a.name.localeCompare(b.name));
|
|
3379
|
+
for (const root of skillRoots(cwd)) collectSkills(root, claimed, found);
|
|
3380
|
+
return found.sort(byName);
|
|
3381
|
+
}
|
|
3382
|
+
function discoverBundledSkills() {
|
|
3383
|
+
const root = bundledSkillsDir();
|
|
3384
|
+
if (!root) return [];
|
|
3385
|
+
const found = [];
|
|
3386
|
+
collectSkills(root, /* @__PURE__ */ new Set(), found);
|
|
3387
|
+
return found.sort(byName);
|
|
3388
|
+
}
|
|
3389
|
+
function registerBundledSkillPath(config) {
|
|
3390
|
+
const dir = bundledSkillsDir();
|
|
3391
|
+
if (!dir) return false;
|
|
3392
|
+
config.skills ??= {};
|
|
3393
|
+
const paths = config.skills.paths ??= [];
|
|
3394
|
+
if (paths.some((p) => path4.resolve(p) === dir)) return false;
|
|
3395
|
+
paths.push(dir);
|
|
3396
|
+
return true;
|
|
3366
3397
|
}
|
|
3367
3398
|
function linkSkill(source, target) {
|
|
3368
3399
|
try {
|
|
@@ -3408,8 +3439,10 @@ function buildSkillPluginDir(skills) {
|
|
|
3408
3439
|
return root;
|
|
3409
3440
|
}
|
|
3410
3441
|
async function resolveSkillPluginDirs(opts) {
|
|
3411
|
-
|
|
3412
|
-
const
|
|
3442
|
+
const user = opts.enabled ? discoverOpencodeSkills(opts.cwd) : [];
|
|
3443
|
+
const claimed = new Set(user.map((s) => s.name));
|
|
3444
|
+
const bundled = discoverBundledSkills().filter((s) => !claimed.has(s.name));
|
|
3445
|
+
const skills = [...user, ...bundled].sort(byName);
|
|
3413
3446
|
if (skills.length === 0) return [];
|
|
3414
3447
|
const supported = await detectCliSupportsFlag(opts.cliPath, "--plugin-dir");
|
|
3415
3448
|
if (!supported) {
|
|
@@ -3424,6 +3457,7 @@ async function resolveSkillPluginDirs(opts) {
|
|
|
3424
3457
|
log.info("bridged opencode skills into claude", {
|
|
3425
3458
|
count: skills.length,
|
|
3426
3459
|
names: skills.map((s) => s.name),
|
|
3460
|
+
bundled: bundled.map((s) => s.name),
|
|
3427
3461
|
pluginDir: dir
|
|
3428
3462
|
});
|
|
3429
3463
|
return [dir];
|
|
@@ -7670,7 +7704,7 @@ import {
|
|
|
7670
7704
|
} from "fs";
|
|
7671
7705
|
import { homedir as homedir6 } from "os";
|
|
7672
7706
|
import { join as join8, resolve as resolve4 } from "path";
|
|
7673
|
-
import { fileURLToPath } from "url";
|
|
7707
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7674
7708
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
7675
7709
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
7676
7710
|
var alreadyRan = false;
|
|
@@ -7702,7 +7736,7 @@ function userIntendsToUseUnscoped() {
|
|
|
7702
7736
|
}
|
|
7703
7737
|
function ourLoadedDir() {
|
|
7704
7738
|
try {
|
|
7705
|
-
const filePath =
|
|
7739
|
+
const filePath = fileURLToPath2(import.meta.url);
|
|
7706
7740
|
return realpathSync(resolve4(filePath, "..", ".."));
|
|
7707
7741
|
} catch {
|
|
7708
7742
|
return null;
|
|
@@ -7776,12 +7810,12 @@ import { execFile as execFile2 } from "child_process";
|
|
|
7776
7810
|
import * as fs6 from "fs";
|
|
7777
7811
|
import * as path8 from "path";
|
|
7778
7812
|
import { promisify as promisify2 } from "util";
|
|
7779
|
-
import { fileURLToPath as
|
|
7813
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7780
7814
|
var cachedPluginVersion;
|
|
7781
7815
|
function pluginVersion() {
|
|
7782
7816
|
if (cachedPluginVersion) return cachedPluginVersion;
|
|
7783
7817
|
try {
|
|
7784
|
-
const here = path8.dirname(
|
|
7818
|
+
const here = path8.dirname(fileURLToPath3(import.meta.url));
|
|
7785
7819
|
const raw = fs6.readFileSync(path8.join(here, "..", "package.json"), "utf8");
|
|
7786
7820
|
const version = JSON.parse(raw).version;
|
|
7787
7821
|
cachedPluginVersion = typeof version === "string" ? version : "unknown";
|
|
@@ -7905,6 +7939,7 @@ function pickOpencodeDirectory(input) {
|
|
|
7905
7939
|
return void 0;
|
|
7906
7940
|
}
|
|
7907
7941
|
var warnedAnthropicApiKey = false;
|
|
7942
|
+
var warnedPlanModeNoExit = false;
|
|
7908
7943
|
var DEFAULT_PROXY_TOOL_NAMES = [
|
|
7909
7944
|
"Bash",
|
|
7910
7945
|
"Edit",
|
|
@@ -7936,6 +7971,18 @@ function warnIfAnthropicApiKey(ignore) {
|
|
|
7936
7971
|
);
|
|
7937
7972
|
}
|
|
7938
7973
|
}
|
|
7974
|
+
function _resetPlanModeWarningForTests() {
|
|
7975
|
+
warnedPlanModeNoExit = false;
|
|
7976
|
+
}
|
|
7977
|
+
function warnIfPlanModeCannotExit(permissionMode) {
|
|
7978
|
+
if (permissionMode !== "plan") return;
|
|
7979
|
+
if (warnedPlanModeNoExit) return;
|
|
7980
|
+
warnedPlanModeNoExit = true;
|
|
7981
|
+
log.warn(
|
|
7982
|
+
'permissionMode "plan" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.',
|
|
7983
|
+
{ permissionMode, measuredOn: "claude-code 2.1.258" }
|
|
7984
|
+
);
|
|
7985
|
+
}
|
|
7939
7986
|
function createClaudeCode(settings = {}) {
|
|
7940
7987
|
if (settings.logging) {
|
|
7941
7988
|
configureLogger({
|
|
@@ -7946,6 +7993,7 @@ function createClaudeCode(settings = {}) {
|
|
|
7946
7993
|
});
|
|
7947
7994
|
}
|
|
7948
7995
|
warnIfAnthropicApiKey(settings.ignoreAnthropicApiKey);
|
|
7996
|
+
warnIfPlanModeCannotExit(settings.permissionMode);
|
|
7949
7997
|
const cliPath = settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? "claude";
|
|
7950
7998
|
const providerName = settings.providerID ?? settings.name ?? "claude-code";
|
|
7951
7999
|
const proxyTools = settings.proxyTools ?? [...DEFAULT_PROXY_TOOL_NAMES];
|
|
@@ -8177,6 +8225,7 @@ var server = async (input) => {
|
|
|
8177
8225
|
return {
|
|
8178
8226
|
config: async (config) => {
|
|
8179
8227
|
if (registerSideQuestionCommand(config)) ownsSideQuestionCommand = true;
|
|
8228
|
+
registerBundledSkillPath(config);
|
|
8180
8229
|
config.provider ??= {};
|
|
8181
8230
|
await buildAgentRegistry(config);
|
|
8182
8231
|
const expanded = await expandAccountProviders(config);
|
|
@@ -8252,6 +8301,7 @@ var index_default = {
|
|
|
8252
8301
|
export {
|
|
8253
8302
|
ClaudeCodeLanguageModel,
|
|
8254
8303
|
DEFAULT_PROXY_TOOL_NAMES,
|
|
8304
|
+
_resetPlanModeWarningForTests,
|
|
8255
8305
|
bridgeOpencodeMcp,
|
|
8256
8306
|
claudeCodeProviders,
|
|
8257
8307
|
configModelsForProvider,
|
|
@@ -8261,6 +8311,7 @@ export {
|
|
|
8261
8311
|
getAgentRegistry,
|
|
8262
8312
|
getDefaultSubagentModel,
|
|
8263
8313
|
registerSideQuestionCommand,
|
|
8264
|
-
resolveAgentModel
|
|
8314
|
+
resolveAgentModel,
|
|
8315
|
+
warnIfPlanModeCannotExit
|
|
8265
8316
|
};
|
|
8266
8317
|
//# sourceMappingURL=index.js.map
|