@khalilgharbaoui/opencode-claude-code-plugin 0.46.0 → 0.46.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/dist/index.js +407 -185
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +3 -1
package/dist/index.js
CHANGED
|
@@ -1625,7 +1625,108 @@ function failoverUntil(resetsAt) {
|
|
|
1625
1625
|
// src/cli-version.ts
|
|
1626
1626
|
import { execFile } from "child_process";
|
|
1627
1627
|
import { promisify } from "util";
|
|
1628
|
+
|
|
1629
|
+
// src/windows-spawn.ts
|
|
1630
|
+
import * as fs from "fs";
|
|
1631
|
+
import * as path2 from "path";
|
|
1632
|
+
var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
1633
|
+
function envValue(env, name) {
|
|
1634
|
+
const direct = env[name];
|
|
1635
|
+
if (direct !== void 0) return direct;
|
|
1636
|
+
const match = Object.keys(env).find((key) => key.toLowerCase() === name.toLowerCase());
|
|
1637
|
+
return match === void 0 ? void 0 : env[match];
|
|
1638
|
+
}
|
|
1639
|
+
function defaultIsFile(candidate) {
|
|
1640
|
+
try {
|
|
1641
|
+
return fs.statSync(candidate).isFile();
|
|
1642
|
+
} catch {
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
function executableExtensions(env = process.env) {
|
|
1647
|
+
const raw = envValue(env, "PATHEXT") ?? DEFAULT_PATHEXT;
|
|
1648
|
+
return raw.split(";").map((ext) => ext.trim().toLowerCase()).filter(Boolean).map((ext) => ext.startsWith(".") ? ext : `.${ext}`);
|
|
1649
|
+
}
|
|
1650
|
+
function isBatchFile(file) {
|
|
1651
|
+
const ext = path2.win32.extname(file).toLowerCase();
|
|
1652
|
+
return ext === ".cmd" || ext === ".bat";
|
|
1653
|
+
}
|
|
1654
|
+
function resolveWindowsCommand(command, deps = {}) {
|
|
1655
|
+
const env = deps.env ?? process.env;
|
|
1656
|
+
const isFile = deps.isFile ?? defaultIsFile;
|
|
1657
|
+
const extensions = executableExtensions(env);
|
|
1658
|
+
const candidatesFor = (base) => {
|
|
1659
|
+
const ext = path2.win32.extname(base).toLowerCase();
|
|
1660
|
+
if (ext && extensions.includes(ext)) return [base];
|
|
1661
|
+
return [...extensions.map((candidate) => base + candidate), base];
|
|
1662
|
+
};
|
|
1663
|
+
if (command.includes("\\") || command.includes("/")) {
|
|
1664
|
+
for (const candidate of candidatesFor(command)) {
|
|
1665
|
+
if (isFile(candidate)) return candidate;
|
|
1666
|
+
}
|
|
1667
|
+
return null;
|
|
1668
|
+
}
|
|
1669
|
+
const pathValue = envValue(env, "PATH") ?? "";
|
|
1670
|
+
for (const entry of pathValue.split(path2.win32.delimiter)) {
|
|
1671
|
+
const directory = entry.trim().replace(/^"(.*)"$/, "$1");
|
|
1672
|
+
if (!directory) continue;
|
|
1673
|
+
for (const candidate of candidatesFor(path2.win32.join(directory, command))) {
|
|
1674
|
+
if (isFile(candidate)) return candidate;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
function quoteBatchArgument(arg) {
|
|
1680
|
+
if (/[\r\n]/.test(arg)) {
|
|
1681
|
+
throw new Error("cmd.exe cannot carry a newline in an argument");
|
|
1682
|
+
}
|
|
1683
|
+
let out = '"';
|
|
1684
|
+
let backslashes = 0;
|
|
1685
|
+
for (const char of arg) {
|
|
1686
|
+
if (char === "\\") {
|
|
1687
|
+
backslashes += 1;
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
if (char === '"') {
|
|
1691
|
+
out += "\\".repeat(backslashes * 2) + '""';
|
|
1692
|
+
backslashes = 0;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
out += "\\".repeat(backslashes) + char;
|
|
1696
|
+
backslashes = 0;
|
|
1697
|
+
}
|
|
1698
|
+
return `${out}${"\\".repeat(backslashes * 2)}"`;
|
|
1699
|
+
}
|
|
1700
|
+
function planCmdInvocation(file, args, env = process.env) {
|
|
1701
|
+
const comspec = envValue(env, "ComSpec") ?? "cmd.exe";
|
|
1702
|
+
const line = [`"${path2.win32.normalize(file)}"`, ...args.map(quoteBatchArgument)];
|
|
1703
|
+
return {
|
|
1704
|
+
file: comspec,
|
|
1705
|
+
args: ["/d", "/s", "/c", `"${line.join(" ")}"`],
|
|
1706
|
+
windowsVerbatimArguments: true
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function planClaudeSpawn(command, args, deps = {}) {
|
|
1710
|
+
const platform2 = deps.platform ?? process.platform;
|
|
1711
|
+
if (platform2 !== "win32") return { file: command, args: [...args] };
|
|
1712
|
+
const env = deps.env ?? process.env;
|
|
1713
|
+
const resolved = resolveWindowsCommand(command, deps) ?? command;
|
|
1714
|
+
if (isBatchFile(resolved)) return planCmdInvocation(resolved, args, env);
|
|
1715
|
+
return { file: resolved, args: [...args] };
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
// src/cli-version.ts
|
|
1628
1719
|
var execFileAsync = promisify(execFile);
|
|
1720
|
+
function probeExec(cliPath, args, options) {
|
|
1721
|
+
const plan = planClaudeSpawn(cliPath, args);
|
|
1722
|
+
return execFileAsync(plan.file, plan.args, {
|
|
1723
|
+
...options,
|
|
1724
|
+
// utf8 is already `execFile`'s default; naming it is what keeps the
|
|
1725
|
+
// promisified overload returning strings rather than Buffers.
|
|
1726
|
+
encoding: "utf8",
|
|
1727
|
+
windowsVerbatimArguments: plan.windowsVerbatimArguments
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1629
1730
|
var cache = /* @__PURE__ */ new Map();
|
|
1630
1731
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
1631
1732
|
var probeTimeoutMs = PROBE_TIMEOUT_MS;
|
|
@@ -1672,7 +1773,7 @@ function detectCliVersion(cliPath) {
|
|
|
1672
1773
|
let deadlineKill = false;
|
|
1673
1774
|
const promise = (async () => {
|
|
1674
1775
|
try {
|
|
1675
|
-
const { stdout } = await
|
|
1776
|
+
const { stdout } = await probeExec(cliPath, ["--version"], {
|
|
1676
1777
|
timeout: probeTimeoutMs
|
|
1677
1778
|
});
|
|
1678
1779
|
const match = /(\d+)\.(\d+)\.(\d+)/.exec(stdout.trim());
|
|
@@ -1769,7 +1870,7 @@ function detectHeadlessSupport(cliPath) {
|
|
|
1769
1870
|
let deadlineKill = false;
|
|
1770
1871
|
const promise = (async () => {
|
|
1771
1872
|
try {
|
|
1772
|
-
const execution =
|
|
1873
|
+
const execution = probeExec(cliPath, [
|
|
1773
1874
|
"--print",
|
|
1774
1875
|
"--input-format",
|
|
1775
1876
|
"stream-json",
|
|
@@ -1805,7 +1906,7 @@ function detectCliSupportsFlag(cliPath, flag) {
|
|
|
1805
1906
|
let deadlineKill = false;
|
|
1806
1907
|
const promise = (async () => {
|
|
1807
1908
|
try {
|
|
1808
|
-
const execution =
|
|
1909
|
+
const execution = probeExec(cliPath, ["--help"], {
|
|
1809
1910
|
timeout: probeTimeoutMs,
|
|
1810
1911
|
killSignal: "SIGKILL",
|
|
1811
1912
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -1838,17 +1939,17 @@ import { unlink } from "fs/promises";
|
|
|
1838
1939
|
|
|
1839
1940
|
// src/proxy-mcp.ts
|
|
1840
1941
|
import { createServer } from "http";
|
|
1841
|
-
import * as
|
|
1842
|
-
import * as
|
|
1942
|
+
import * as fs3 from "fs";
|
|
1943
|
+
import * as path4 from "path";
|
|
1843
1944
|
import * as crypto from "crypto";
|
|
1844
1945
|
import { EventEmitter } from "events";
|
|
1845
1946
|
|
|
1846
1947
|
// src/tmp.ts
|
|
1847
|
-
import * as
|
|
1948
|
+
import * as fs2 from "fs";
|
|
1848
1949
|
import * as os from "os";
|
|
1849
|
-
import * as
|
|
1950
|
+
import * as path3 from "path";
|
|
1850
1951
|
var PLUGIN_TMP_PREFIX = "opencode-claude-code-";
|
|
1851
|
-
var PID_TMP_DIR =
|
|
1952
|
+
var PID_TMP_DIR = path3.join(
|
|
1852
1953
|
os.tmpdir(),
|
|
1853
1954
|
`${PLUGIN_TMP_PREFIX}${process.pid}`
|
|
1854
1955
|
);
|
|
@@ -1861,7 +1962,7 @@ function currentUid() {
|
|
|
1861
1962
|
function untrustworthyReason(dir) {
|
|
1862
1963
|
let stat;
|
|
1863
1964
|
try {
|
|
1864
|
-
stat =
|
|
1965
|
+
stat = fs2.lstatSync(dir);
|
|
1865
1966
|
} catch {
|
|
1866
1967
|
return null;
|
|
1867
1968
|
}
|
|
@@ -1874,16 +1975,16 @@ function untrustworthyReason(dir) {
|
|
|
1874
1975
|
return null;
|
|
1875
1976
|
}
|
|
1876
1977
|
function ensureDir(dir) {
|
|
1877
|
-
|
|
1978
|
+
fs2.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1878
1979
|
try {
|
|
1879
|
-
|
|
1980
|
+
fs2.chmodSync(dir, 448);
|
|
1880
1981
|
} catch {
|
|
1881
1982
|
}
|
|
1882
1983
|
}
|
|
1883
1984
|
function chooseDir() {
|
|
1884
1985
|
const reason = untrustworthyReason(PID_TMP_DIR);
|
|
1885
1986
|
if (!reason) return PID_TMP_DIR;
|
|
1886
|
-
const fallback =
|
|
1987
|
+
const fallback = fs2.mkdtempSync(path3.join(os.tmpdir(), PLUGIN_TMP_PREFIX));
|
|
1887
1988
|
log.warn("refusing the pid-named scratch directory", {
|
|
1888
1989
|
path: PID_TMP_DIR,
|
|
1889
1990
|
reason,
|
|
@@ -1897,7 +1998,7 @@ function registerExitCleanup() {
|
|
|
1897
1998
|
process.on("exit", () => {
|
|
1898
1999
|
for (const dir of createdDirs) {
|
|
1899
2000
|
try {
|
|
1900
|
-
|
|
2001
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
1901
2002
|
} catch {
|
|
1902
2003
|
}
|
|
1903
2004
|
}
|
|
@@ -1918,23 +2019,23 @@ function sweepStalePluginTmpDirs(keep) {
|
|
|
1918
2019
|
const root = os.tmpdir();
|
|
1919
2020
|
let entries;
|
|
1920
2021
|
try {
|
|
1921
|
-
entries =
|
|
2022
|
+
entries = fs2.readdirSync(root);
|
|
1922
2023
|
} catch {
|
|
1923
2024
|
return removed;
|
|
1924
2025
|
}
|
|
1925
2026
|
const uid = currentUid();
|
|
1926
|
-
const keepPath = keep ?
|
|
2027
|
+
const keepPath = keep ? path3.resolve(keep) : null;
|
|
1927
2028
|
for (const entry of entries) {
|
|
1928
2029
|
const match = PID_DIR_NAME.exec(entry);
|
|
1929
2030
|
if (!match) continue;
|
|
1930
2031
|
const pid = Number(match[1]);
|
|
1931
2032
|
if (!Number.isSafeInteger(pid) || pid <= 0) continue;
|
|
1932
2033
|
if (pid === process.pid) continue;
|
|
1933
|
-
const dir =
|
|
1934
|
-
if (keepPath &&
|
|
2034
|
+
const dir = path3.join(root, entry);
|
|
2035
|
+
if (keepPath && path3.resolve(dir) === keepPath) continue;
|
|
1935
2036
|
let stat;
|
|
1936
2037
|
try {
|
|
1937
|
-
stat =
|
|
2038
|
+
stat = fs2.lstatSync(dir);
|
|
1938
2039
|
} catch {
|
|
1939
2040
|
continue;
|
|
1940
2041
|
}
|
|
@@ -1942,7 +2043,7 @@ function sweepStalePluginTmpDirs(keep) {
|
|
|
1942
2043
|
if (uid !== void 0 && stat.uid !== uid) continue;
|
|
1943
2044
|
if (isProcessAlive(pid)) continue;
|
|
1944
2045
|
try {
|
|
1945
|
-
|
|
2046
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
1946
2047
|
removed.push(dir);
|
|
1947
2048
|
} catch (err) {
|
|
1948
2049
|
log.debug("could not remove a stale scratch directory", {
|
|
@@ -2773,11 +2874,11 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2773
2874
|
2
|
|
2774
2875
|
);
|
|
2775
2876
|
const hash = crypto.createHash("sha256").update(body).digest("hex").slice(0, 12);
|
|
2776
|
-
const outPath =
|
|
2877
|
+
const outPath = path4.join(
|
|
2777
2878
|
pluginTmpDir(),
|
|
2778
2879
|
`proxy-${hash}.json`
|
|
2779
2880
|
);
|
|
2780
|
-
|
|
2881
|
+
fs3.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
|
|
2781
2882
|
configFilePath = outPath;
|
|
2782
2883
|
return outPath;
|
|
2783
2884
|
},
|
|
@@ -2794,7 +2895,7 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2794
2895
|
});
|
|
2795
2896
|
if (configFilePath) {
|
|
2796
2897
|
try {
|
|
2797
|
-
|
|
2898
|
+
fs3.unlinkSync(configFilePath);
|
|
2798
2899
|
} catch {
|
|
2799
2900
|
}
|
|
2800
2901
|
configFilePath = null;
|
|
@@ -3363,8 +3464,8 @@ function clearCompression(sessionKey2) {
|
|
|
3363
3464
|
}
|
|
3364
3465
|
|
|
3365
3466
|
// src/mcp-bridge.ts
|
|
3366
|
-
import * as
|
|
3367
|
-
import * as
|
|
3467
|
+
import * as fs4 from "fs";
|
|
3468
|
+
import * as path5 from "path";
|
|
3368
3469
|
import * as os2 from "os";
|
|
3369
3470
|
import * as crypto2 from "crypto";
|
|
3370
3471
|
import {
|
|
@@ -3375,21 +3476,21 @@ var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
|
|
|
3375
3476
|
var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
3376
3477
|
function fileExists(p) {
|
|
3377
3478
|
try {
|
|
3378
|
-
return
|
|
3479
|
+
return fs4.statSync(p).isFile();
|
|
3379
3480
|
} catch {
|
|
3380
3481
|
return false;
|
|
3381
3482
|
}
|
|
3382
3483
|
}
|
|
3383
3484
|
function dirExists(p) {
|
|
3384
3485
|
try {
|
|
3385
|
-
return
|
|
3486
|
+
return fs4.statSync(p).isDirectory();
|
|
3386
3487
|
} catch {
|
|
3387
3488
|
return false;
|
|
3388
3489
|
}
|
|
3389
3490
|
}
|
|
3390
3491
|
function readAndParse(file) {
|
|
3391
3492
|
try {
|
|
3392
|
-
const raw =
|
|
3493
|
+
const raw = fs4.readFileSync(file, "utf8");
|
|
3393
3494
|
const errors = [];
|
|
3394
3495
|
const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
|
|
3395
3496
|
if (errors.length > 0) {
|
|
@@ -3425,14 +3526,14 @@ function deepMerge(target, source) {
|
|
|
3425
3526
|
}
|
|
3426
3527
|
function walkUp(opts) {
|
|
3427
3528
|
const out = [];
|
|
3428
|
-
let current =
|
|
3529
|
+
let current = path5.resolve(opts.start);
|
|
3429
3530
|
while (true) {
|
|
3430
3531
|
for (const target of opts.targets) {
|
|
3431
|
-
const candidate =
|
|
3532
|
+
const candidate = path5.join(current, target);
|
|
3432
3533
|
if (opts.predicate(candidate)) out.push(candidate);
|
|
3433
3534
|
}
|
|
3434
|
-
if (opts.stop && current ===
|
|
3435
|
-
const parent =
|
|
3535
|
+
if (opts.stop && current === path5.resolve(opts.stop)) break;
|
|
3536
|
+
const parent = path5.dirname(current);
|
|
3436
3537
|
if (parent === current) break;
|
|
3437
3538
|
current = parent;
|
|
3438
3539
|
}
|
|
@@ -3440,28 +3541,28 @@ function walkUp(opts) {
|
|
|
3440
3541
|
}
|
|
3441
3542
|
function detectWorktree(cwd) {
|
|
3442
3543
|
const override = process.env.OPENCODE_WORKTREE;
|
|
3443
|
-
if (override) return
|
|
3444
|
-
let current =
|
|
3544
|
+
if (override) return path5.resolve(override);
|
|
3545
|
+
let current = path5.resolve(cwd);
|
|
3445
3546
|
while (true) {
|
|
3446
|
-
const gitPath =
|
|
3547
|
+
const gitPath = path5.join(current, ".git");
|
|
3447
3548
|
try {
|
|
3448
|
-
if (
|
|
3549
|
+
if (fs4.existsSync(gitPath)) return current;
|
|
3449
3550
|
} catch {
|
|
3450
3551
|
}
|
|
3451
|
-
const parent =
|
|
3552
|
+
const parent = path5.dirname(current);
|
|
3452
3553
|
if (parent === current) return void 0;
|
|
3453
3554
|
current = parent;
|
|
3454
3555
|
}
|
|
3455
3556
|
}
|
|
3456
3557
|
function globalConfigDir() {
|
|
3457
|
-
const xdg = process.env.XDG_CONFIG_HOME ??
|
|
3458
|
-
return
|
|
3558
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? path5.join(os2.homedir(), ".config");
|
|
3559
|
+
return path5.join(xdg, "opencode");
|
|
3459
3560
|
}
|
|
3460
3561
|
function loadGlobalConfig(hostApi = "v1") {
|
|
3461
3562
|
const dir = globalConfigDir();
|
|
3462
3563
|
let merged = {};
|
|
3463
3564
|
for (const name of FILE_NAMES.slice().reverse()) {
|
|
3464
|
-
const file =
|
|
3565
|
+
const file = path5.join(dir, name);
|
|
3465
3566
|
if (!fileExists(file)) continue;
|
|
3466
3567
|
const parsed = readAndParse(file);
|
|
3467
3568
|
if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
|
|
@@ -3471,7 +3572,7 @@ function loadGlobalConfig(hostApi = "v1") {
|
|
|
3471
3572
|
function loadProjectFilesInDir(dir, hostApi = "v1") {
|
|
3472
3573
|
let merged = {};
|
|
3473
3574
|
for (const name of PROJECT_FILE_NAMES) {
|
|
3474
|
-
const file =
|
|
3575
|
+
const file = path5.join(dir, name);
|
|
3475
3576
|
if (!fileExists(file)) continue;
|
|
3476
3577
|
const parsed = readAndParse(file);
|
|
3477
3578
|
if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
|
|
@@ -3482,7 +3583,7 @@ function dotOpencodeDirs(cwd, worktree) {
|
|
|
3482
3583
|
const dirs = [];
|
|
3483
3584
|
const seen = /* @__PURE__ */ new Set();
|
|
3484
3585
|
const push = (p) => {
|
|
3485
|
-
const abs =
|
|
3586
|
+
const abs = path5.resolve(p);
|
|
3486
3587
|
if (!seen.has(abs) && dirExists(abs)) {
|
|
3487
3588
|
seen.add(abs);
|
|
3488
3589
|
dirs.push(abs);
|
|
@@ -3498,7 +3599,7 @@ function dotOpencodeDirs(cwd, worktree) {
|
|
|
3498
3599
|
}
|
|
3499
3600
|
const home = os2.homedir();
|
|
3500
3601
|
if (home) {
|
|
3501
|
-
const homeDot =
|
|
3602
|
+
const homeDot = path5.join(home, ".opencode");
|
|
3502
3603
|
if (dirExists(homeDot)) push(homeDot);
|
|
3503
3604
|
}
|
|
3504
3605
|
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
@@ -3645,7 +3746,7 @@ function opencodeConfigLayers(cwd, hostApi = "v1") {
|
|
|
3645
3746
|
const projectDirs = [];
|
|
3646
3747
|
const seenProjectDirs = /* @__PURE__ */ new Set();
|
|
3647
3748
|
for (const f of projectFiles) {
|
|
3648
|
-
const d =
|
|
3749
|
+
const d = path5.dirname(f);
|
|
3649
3750
|
if (!seenProjectDirs.has(d)) {
|
|
3650
3751
|
seenProjectDirs.add(d);
|
|
3651
3752
|
projectDirs.push(d);
|
|
@@ -3712,13 +3813,13 @@ function finishBridge(input) {
|
|
|
3712
3813
|
}
|
|
3713
3814
|
const body = JSON.stringify({ mcpServers: servers }, null, 2);
|
|
3714
3815
|
const bodyDigest = crypto2.createHash("sha256").update(body).digest("hex").slice(0, 12);
|
|
3715
|
-
const outPath =
|
|
3816
|
+
const outPath = path5.join(
|
|
3716
3817
|
pluginTmpDir(),
|
|
3717
3818
|
`mcp-${bodyDigest}.json`
|
|
3718
3819
|
);
|
|
3719
3820
|
try {
|
|
3720
3821
|
if (!fileExists(outPath)) {
|
|
3721
|
-
|
|
3822
|
+
fs4.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
|
|
3722
3823
|
}
|
|
3723
3824
|
} catch (e) {
|
|
3724
3825
|
log.warn("failed to write bridged MCP config", {
|
|
@@ -5308,11 +5409,12 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
5308
5409
|
effort,
|
|
5309
5410
|
promptCacheTtl
|
|
5310
5411
|
});
|
|
5311
|
-
const
|
|
5412
|
+
const plan = planClaudeSpawn(cliPath, cliArgs);
|
|
5413
|
+
const proc = spawn(plan.file, plan.args, {
|
|
5312
5414
|
cwd,
|
|
5313
5415
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5314
5416
|
env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl }),
|
|
5315
|
-
|
|
5417
|
+
windowsVerbatimArguments: plan.windowsVerbatimArguments
|
|
5316
5418
|
});
|
|
5317
5419
|
const lineEmitter = new EventEmitter3();
|
|
5318
5420
|
const ap = {
|
|
@@ -6411,8 +6513,8 @@ function redactLogLine(raw, context) {
|
|
|
6411
6513
|
if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
|
|
6412
6514
|
return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
|
|
6413
6515
|
}
|
|
6414
|
-
function readLogTail(
|
|
6415
|
-
const fd = openSync(
|
|
6516
|
+
function readLogTail(path12, maxBytes = BUNDLE_TAIL_BYTES) {
|
|
6517
|
+
const fd = openSync(path12, "r");
|
|
6416
6518
|
try {
|
|
6417
6519
|
const size = fstatSync(fd).size;
|
|
6418
6520
|
const length = Math.min(size, maxBytes);
|
|
@@ -6562,13 +6664,20 @@ function parsePlanUsage(stdout) {
|
|
|
6562
6664
|
return { status: "failed", error: "no result object in the CLI's reply" };
|
|
6563
6665
|
}
|
|
6564
6666
|
function runCli(cliPath, args, timeoutMs, env) {
|
|
6667
|
+
const plan = planClaudeSpawn(cliPath, args);
|
|
6565
6668
|
return new Promise((resolve6, reject) => {
|
|
6566
6669
|
execFile2(
|
|
6567
|
-
|
|
6568
|
-
args,
|
|
6670
|
+
plan.file,
|
|
6671
|
+
plan.args,
|
|
6569
6672
|
// `killSignal` so a CLI wedged on a hook is actually gone, and a generous
|
|
6570
6673
|
// buffer because the reply is prose of unbounded length.
|
|
6571
|
-
{
|
|
6674
|
+
{
|
|
6675
|
+
timeout: timeoutMs,
|
|
6676
|
+
killSignal: "SIGKILL",
|
|
6677
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
6678
|
+
env,
|
|
6679
|
+
windowsVerbatimArguments: plan.windowsVerbatimArguments
|
|
6680
|
+
},
|
|
6572
6681
|
(error, stdout) => {
|
|
6573
6682
|
if (stdout && stdout.trim()) return resolve6(stdout);
|
|
6574
6683
|
if (error) return reject(error);
|
|
@@ -6607,14 +6716,14 @@ async function fetchPlanUsage(cliPath, options = {}) {
|
|
|
6607
6716
|
}
|
|
6608
6717
|
|
|
6609
6718
|
// src/stale-build.ts
|
|
6610
|
-
import * as
|
|
6611
|
-
import * as
|
|
6719
|
+
import * as fs6 from "fs";
|
|
6720
|
+
import * as path7 from "path";
|
|
6612
6721
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6613
6722
|
|
|
6614
6723
|
// src/startup-diagnostics.ts
|
|
6615
6724
|
import { execFile as execFile3 } from "child_process";
|
|
6616
|
-
import * as
|
|
6617
|
-
import * as
|
|
6725
|
+
import * as fs5 from "fs";
|
|
6726
|
+
import * as path6 from "path";
|
|
6618
6727
|
import { promisify as promisify2 } from "util";
|
|
6619
6728
|
import { fileURLToPath } from "url";
|
|
6620
6729
|
|
|
@@ -6649,8 +6758,8 @@ var cachedPluginVersion;
|
|
|
6649
6758
|
function pluginVersion() {
|
|
6650
6759
|
if (cachedPluginVersion) return cachedPluginVersion;
|
|
6651
6760
|
try {
|
|
6652
|
-
const here =
|
|
6653
|
-
const raw =
|
|
6761
|
+
const here = path6.dirname(fileURLToPath(import.meta.url));
|
|
6762
|
+
const raw = fs5.readFileSync(path6.join(here, "..", "package.json"), "utf8");
|
|
6654
6763
|
const version = JSON.parse(raw).version;
|
|
6655
6764
|
cachedPluginVersion = typeof version === "string" ? version : "unknown";
|
|
6656
6765
|
} catch {
|
|
@@ -6674,7 +6783,7 @@ var opencodeVersionProbe;
|
|
|
6674
6783
|
function detectOpencodeVersion(execPath = process.execPath) {
|
|
6675
6784
|
if (opencodeVersionProbe) return opencodeVersionProbe;
|
|
6676
6785
|
opencodeVersionProbe = (async () => {
|
|
6677
|
-
if (!
|
|
6786
|
+
if (!path6.basename(execPath).toLowerCase().includes("opencode")) {
|
|
6678
6787
|
log.debug("skipping opencode version probe: execPath is not opencode", { execPath });
|
|
6679
6788
|
return void 0;
|
|
6680
6789
|
}
|
|
@@ -6903,10 +7012,10 @@ function defaultEntryPath() {
|
|
|
6903
7012
|
}
|
|
6904
7013
|
function readOnDiskBuild(entryPath) {
|
|
6905
7014
|
try {
|
|
6906
|
-
const raw =
|
|
7015
|
+
const raw = fs6.readFileSync(path7.join(path7.dirname(entryPath), "..", "package.json"), "utf8");
|
|
6907
7016
|
const version = JSON.parse(raw).version;
|
|
6908
7017
|
if (typeof version !== "string" || version.length === 0) return void 0;
|
|
6909
|
-
const stat =
|
|
7018
|
+
const stat = fs6.statSync(entryPath);
|
|
6910
7019
|
return { version, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
6911
7020
|
} catch {
|
|
6912
7021
|
return void 0;
|
|
@@ -6917,7 +7026,7 @@ function recordLoadedBuild() {
|
|
|
6917
7026
|
let mtimeMs;
|
|
6918
7027
|
let size;
|
|
6919
7028
|
try {
|
|
6920
|
-
const stat =
|
|
7029
|
+
const stat = fs6.statSync(entryPath);
|
|
6921
7030
|
mtimeMs = stat.mtimeMs;
|
|
6922
7031
|
size = stat.size;
|
|
6923
7032
|
} catch {
|
|
@@ -7354,7 +7463,7 @@ Could not build the report: ${message}`;
|
|
|
7354
7463
|
|
|
7355
7464
|
// src/agent-models.ts
|
|
7356
7465
|
import { readFile, readdir } from "fs/promises";
|
|
7357
|
-
import
|
|
7466
|
+
import path8 from "path";
|
|
7358
7467
|
|
|
7359
7468
|
// src/models.ts
|
|
7360
7469
|
var PROVIDER_ID = "claude-code";
|
|
@@ -7878,13 +7987,13 @@ async function readAgentMarkdownRecords(directories) {
|
|
|
7878
7987
|
const name = entry.slice(0, -3);
|
|
7879
7988
|
if (records2[name]) continue;
|
|
7880
7989
|
try {
|
|
7881
|
-
const text = await readFile(
|
|
7990
|
+
const text = await readFile(path8.join(directory, entry), "utf8");
|
|
7882
7991
|
records2[name] = parseAgentFrontmatter(text);
|
|
7883
7992
|
const prompt = agentMarkdownBody(text);
|
|
7884
7993
|
if (prompt) records2[name].prompt = prompt;
|
|
7885
7994
|
} catch (err) {
|
|
7886
7995
|
log.debug("failed to read agent markdown", {
|
|
7887
|
-
file:
|
|
7996
|
+
file: path8.join(directory, entry),
|
|
7888
7997
|
error: String(err)
|
|
7889
7998
|
});
|
|
7890
7999
|
}
|
|
@@ -7896,12 +8005,12 @@ function agentDirectories(home, projectDirectory) {
|
|
|
7896
8005
|
const directories = [];
|
|
7897
8006
|
if (projectDirectory) {
|
|
7898
8007
|
for (const name of AGENT_DIR_NAMES) {
|
|
7899
|
-
directories.push(
|
|
8008
|
+
directories.push(path8.join(projectDirectory, ".opencode", name));
|
|
7900
8009
|
}
|
|
7901
8010
|
}
|
|
7902
8011
|
if (home) {
|
|
7903
8012
|
for (const name of AGENT_DIR_NAMES) {
|
|
7904
|
-
directories.push(
|
|
8013
|
+
directories.push(path8.join(home, ".config", "opencode", name));
|
|
7905
8014
|
}
|
|
7906
8015
|
}
|
|
7907
8016
|
return directories;
|
|
@@ -8501,14 +8610,14 @@ function getTrailingUserMessages(prompt, opts = {}) {
|
|
|
8501
8610
|
|
|
8502
8611
|
// src/skill-bridge.ts
|
|
8503
8612
|
import * as crypto3 from "crypto";
|
|
8504
|
-
import * as
|
|
8613
|
+
import * as fs7 from "fs";
|
|
8505
8614
|
import * as os3 from "os";
|
|
8506
|
-
import * as
|
|
8615
|
+
import * as path9 from "path";
|
|
8507
8616
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8508
8617
|
function bundledSkillsDir() {
|
|
8509
8618
|
try {
|
|
8510
8619
|
const here = fileURLToPath3(import.meta.url);
|
|
8511
|
-
const dir =
|
|
8620
|
+
const dir = path9.resolve(path9.dirname(here), "..", "skills");
|
|
8512
8621
|
return dirExists2(dir) ? dir : null;
|
|
8513
8622
|
} catch {
|
|
8514
8623
|
return null;
|
|
@@ -8516,16 +8625,16 @@ function bundledSkillsDir() {
|
|
|
8516
8625
|
}
|
|
8517
8626
|
function dirExists2(p) {
|
|
8518
8627
|
try {
|
|
8519
|
-
return
|
|
8628
|
+
return fs7.statSync(p).isDirectory();
|
|
8520
8629
|
} catch {
|
|
8521
8630
|
return false;
|
|
8522
8631
|
}
|
|
8523
8632
|
}
|
|
8524
8633
|
function realPath(p) {
|
|
8525
8634
|
try {
|
|
8526
|
-
return
|
|
8635
|
+
return fs7.realpathSync(p);
|
|
8527
8636
|
} catch {
|
|
8528
|
-
return
|
|
8637
|
+
return path9.resolve(p);
|
|
8529
8638
|
}
|
|
8530
8639
|
}
|
|
8531
8640
|
function envEnabled(name) {
|
|
@@ -8546,10 +8655,10 @@ function declaredSkillName(source) {
|
|
|
8546
8655
|
return SAFE_SKILL_NAME.test(name) ? name : null;
|
|
8547
8656
|
}
|
|
8548
8657
|
function readSkill(root, entry) {
|
|
8549
|
-
const dir =
|
|
8658
|
+
const dir = path9.join(root, entry);
|
|
8550
8659
|
let source;
|
|
8551
8660
|
try {
|
|
8552
|
-
source =
|
|
8661
|
+
source = fs7.readFileSync(path9.join(dir, "SKILL.md"), "utf8");
|
|
8553
8662
|
} catch {
|
|
8554
8663
|
return null;
|
|
8555
8664
|
}
|
|
@@ -8567,47 +8676,47 @@ function skillRoots(cwd) {
|
|
|
8567
8676
|
const roots = [];
|
|
8568
8677
|
const seen = /* @__PURE__ */ new Set();
|
|
8569
8678
|
const push = (p) => {
|
|
8570
|
-
const abs =
|
|
8679
|
+
const abs = path9.resolve(p);
|
|
8571
8680
|
if (seen.has(abs)) return;
|
|
8572
8681
|
seen.add(abs);
|
|
8573
8682
|
if (dirExists2(abs)) roots.push(abs);
|
|
8574
8683
|
};
|
|
8575
8684
|
const home = os3.homedir();
|
|
8576
|
-
const homeDir = home ?
|
|
8577
|
-
let current =
|
|
8685
|
+
const homeDir = home ? path9.resolve(home) : null;
|
|
8686
|
+
let current = path9.resolve(cwd);
|
|
8578
8687
|
while (true) {
|
|
8579
|
-
push(
|
|
8688
|
+
push(path9.join(current, ".opencode", "skills"));
|
|
8580
8689
|
if (current !== homeDir) {
|
|
8581
|
-
push(
|
|
8582
|
-
push(
|
|
8690
|
+
push(path9.join(current, ".claude", "skills"));
|
|
8691
|
+
push(path9.join(current, ".agents", "skills"));
|
|
8583
8692
|
}
|
|
8584
|
-
const parent =
|
|
8693
|
+
const parent = path9.dirname(current);
|
|
8585
8694
|
if (parent === current) break;
|
|
8586
8695
|
current = parent;
|
|
8587
8696
|
}
|
|
8588
|
-
if (homeDir) push(
|
|
8697
|
+
if (homeDir) push(path9.join(homeDir, ".opencode", "skills"));
|
|
8589
8698
|
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
8590
8699
|
if (envDir) {
|
|
8591
|
-
push(
|
|
8592
|
-
push(
|
|
8700
|
+
push(path9.join(envDir, "skills"));
|
|
8701
|
+
push(path9.join(envDir, "skill"));
|
|
8593
8702
|
}
|
|
8594
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ?
|
|
8703
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path9.join(homeDir, ".config") : null);
|
|
8595
8704
|
if (xdg) {
|
|
8596
|
-
push(
|
|
8597
|
-
push(
|
|
8705
|
+
push(path9.join(xdg, "opencode", "skills"));
|
|
8706
|
+
push(path9.join(xdg, "opencode", "skill"));
|
|
8598
8707
|
}
|
|
8599
8708
|
if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
|
|
8600
8709
|
if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
|
|
8601
|
-
push(
|
|
8710
|
+
push(path9.join(homeDir, ".claude", "skills"));
|
|
8602
8711
|
}
|
|
8603
|
-
push(
|
|
8712
|
+
push(path9.join(homeDir, ".agents", "skills"));
|
|
8604
8713
|
}
|
|
8605
8714
|
return roots;
|
|
8606
8715
|
}
|
|
8607
8716
|
function collectSkills(root, claimed, found) {
|
|
8608
8717
|
let entries;
|
|
8609
8718
|
try {
|
|
8610
|
-
entries =
|
|
8719
|
+
entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
8611
8720
|
} catch {
|
|
8612
8721
|
return;
|
|
8613
8722
|
}
|
|
@@ -8647,23 +8756,23 @@ function discoverBundledSkills() {
|
|
|
8647
8756
|
}
|
|
8648
8757
|
function claudeConfigDir(configDir) {
|
|
8649
8758
|
const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
|
|
8650
|
-
if (explicit) return
|
|
8759
|
+
if (explicit) return path9.resolve(expandHome(explicit));
|
|
8651
8760
|
const home = os3.homedir();
|
|
8652
|
-
return home ?
|
|
8761
|
+
return home ? path9.resolve(home, ".claude") : null;
|
|
8653
8762
|
}
|
|
8654
8763
|
function installedPluginSkillRoots(configDir, cwd) {
|
|
8655
8764
|
if (!configDir) return [];
|
|
8656
8765
|
let parsed;
|
|
8657
8766
|
try {
|
|
8658
8767
|
parsed = JSON.parse(
|
|
8659
|
-
|
|
8768
|
+
fs7.readFileSync(path9.join(configDir, "plugins", "installed_plugins.json"), "utf8")
|
|
8660
8769
|
);
|
|
8661
8770
|
} catch {
|
|
8662
8771
|
return [];
|
|
8663
8772
|
}
|
|
8664
8773
|
const plugins = parsed?.plugins;
|
|
8665
8774
|
if (!plugins || typeof plugins !== "object") return [];
|
|
8666
|
-
const workspace =
|
|
8775
|
+
const workspace = path9.resolve(cwd);
|
|
8667
8776
|
const roots = [];
|
|
8668
8777
|
for (const installs of Object.values(plugins)) {
|
|
8669
8778
|
if (!Array.isArray(installs)) continue;
|
|
@@ -8671,28 +8780,28 @@ function installedPluginSkillRoots(configDir, cwd) {
|
|
|
8671
8780
|
const entry = install;
|
|
8672
8781
|
if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
|
|
8673
8782
|
if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
|
|
8674
|
-
roots.push(
|
|
8783
|
+
roots.push(path9.join(entry.installPath, "skills"));
|
|
8675
8784
|
}
|
|
8676
8785
|
}
|
|
8677
8786
|
return roots;
|
|
8678
8787
|
}
|
|
8679
8788
|
function coversWorkspace(projectPath, workspace) {
|
|
8680
8789
|
if (typeof projectPath !== "string" || !projectPath) return false;
|
|
8681
|
-
const root =
|
|
8790
|
+
const root = path9.resolve(projectPath);
|
|
8682
8791
|
if (root === workspace) return true;
|
|
8683
|
-
const rel =
|
|
8684
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
8792
|
+
const rel = path9.relative(root, workspace);
|
|
8793
|
+
return rel !== "" && !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
8685
8794
|
}
|
|
8686
8795
|
function discoverNativeClaudeSkills(opts) {
|
|
8687
8796
|
const found = [];
|
|
8688
8797
|
const scanned = /* @__PURE__ */ new Set();
|
|
8689
8798
|
const scan = (root, scope) => {
|
|
8690
|
-
const abs =
|
|
8799
|
+
const abs = path9.resolve(root);
|
|
8691
8800
|
if (scanned.has(abs)) return;
|
|
8692
8801
|
scanned.add(abs);
|
|
8693
8802
|
let entries;
|
|
8694
8803
|
try {
|
|
8695
|
-
entries =
|
|
8804
|
+
entries = fs7.readdirSync(abs, { withFileTypes: true });
|
|
8696
8805
|
} catch {
|
|
8697
8806
|
return;
|
|
8698
8807
|
}
|
|
@@ -8704,13 +8813,13 @@ function discoverNativeClaudeSkills(opts) {
|
|
|
8704
8813
|
}
|
|
8705
8814
|
};
|
|
8706
8815
|
const configDir = claudeConfigDir(opts.configDir);
|
|
8707
|
-
if (configDir) scan(
|
|
8816
|
+
if (configDir) scan(path9.join(configDir, "skills"), "user");
|
|
8708
8817
|
const home = os3.homedir();
|
|
8709
|
-
const homeDir = home ?
|
|
8710
|
-
let current =
|
|
8818
|
+
const homeDir = home ? path9.resolve(home) : null;
|
|
8819
|
+
let current = path9.resolve(opts.cwd);
|
|
8711
8820
|
while (true) {
|
|
8712
|
-
if (current !== homeDir) scan(
|
|
8713
|
-
const parent =
|
|
8821
|
+
if (current !== homeDir) scan(path9.join(current, ".claude", "skills"), "project");
|
|
8822
|
+
const parent = path9.dirname(current);
|
|
8714
8823
|
if (parent === current) break;
|
|
8715
8824
|
current = parent;
|
|
8716
8825
|
}
|
|
@@ -8744,10 +8853,10 @@ function dropNativelyLoadedSkills(skills, native) {
|
|
|
8744
8853
|
}
|
|
8745
8854
|
function readBundledSkillInfos() {
|
|
8746
8855
|
return discoverBundledSkills().flatMap((skill) => {
|
|
8747
|
-
const file =
|
|
8856
|
+
const file = path9.join(skill.dir, "SKILL.md");
|
|
8748
8857
|
let text;
|
|
8749
8858
|
try {
|
|
8750
|
-
text =
|
|
8859
|
+
text = fs7.readFileSync(file, "utf8");
|
|
8751
8860
|
} catch {
|
|
8752
8861
|
return [];
|
|
8753
8862
|
}
|
|
@@ -8771,15 +8880,15 @@ function readBundledSkillInfos() {
|
|
|
8771
8880
|
}
|
|
8772
8881
|
function linkSkill(source, target) {
|
|
8773
8882
|
try {
|
|
8774
|
-
|
|
8883
|
+
fs7.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
|
|
8775
8884
|
return;
|
|
8776
8885
|
} catch {
|
|
8777
|
-
|
|
8886
|
+
fs7.cpSync(source, target, { recursive: true, dereference: true });
|
|
8778
8887
|
}
|
|
8779
8888
|
}
|
|
8780
8889
|
function fileExists2(p) {
|
|
8781
8890
|
try {
|
|
8782
|
-
return
|
|
8891
|
+
return fs7.statSync(p).isFile();
|
|
8783
8892
|
} catch {
|
|
8784
8893
|
return false;
|
|
8785
8894
|
}
|
|
@@ -8788,14 +8897,14 @@ function buildSkillPluginDir(skills) {
|
|
|
8788
8897
|
if (skills.length === 0) return null;
|
|
8789
8898
|
const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
|
|
8790
8899
|
const hash = crypto3.createHash("sha256").update(fingerprint).digest("hex").slice(0, 12);
|
|
8791
|
-
const root =
|
|
8792
|
-
const manifest =
|
|
8900
|
+
const root = path9.join(pluginTmpDir(), `skills-${hash}`);
|
|
8901
|
+
const manifest = path9.join(root, ".claude-plugin", "plugin.json");
|
|
8793
8902
|
if (fileExists2(manifest)) return root;
|
|
8794
8903
|
try {
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8904
|
+
fs7.rmSync(root, { recursive: true, force: true });
|
|
8905
|
+
fs7.mkdirSync(path9.join(root, ".claude-plugin"), { recursive: true });
|
|
8906
|
+
fs7.mkdirSync(path9.join(root, "skills"), { recursive: true });
|
|
8907
|
+
fs7.writeFileSync(
|
|
8799
8908
|
manifest,
|
|
8800
8909
|
JSON.stringify(
|
|
8801
8910
|
{
|
|
@@ -8808,7 +8917,7 @@ function buildSkillPluginDir(skills) {
|
|
|
8808
8917
|
{ encoding: "utf8", mode: 384 }
|
|
8809
8918
|
);
|
|
8810
8919
|
for (const skill of skills) {
|
|
8811
|
-
linkSkill(skill.dir,
|
|
8920
|
+
linkSkill(skill.dir, path9.join(root, "skills", skill.name));
|
|
8812
8921
|
}
|
|
8813
8922
|
} catch (err) {
|
|
8814
8923
|
log.warn("failed to stage opencode skill plugin dir", {
|
|
@@ -8824,7 +8933,7 @@ function registerBundledSkillPath(config) {
|
|
|
8824
8933
|
if (!dir) return false;
|
|
8825
8934
|
config.skills ??= {};
|
|
8826
8935
|
const paths = config.skills.paths ??= [];
|
|
8827
|
-
if (paths.some((p) =>
|
|
8936
|
+
if (paths.some((p) => path9.resolve(p) === dir)) return false;
|
|
8828
8937
|
paths.push(dir);
|
|
8829
8938
|
return true;
|
|
8830
8939
|
}
|
|
@@ -8878,12 +8987,13 @@ import { join as join10 } from "path";
|
|
|
8878
8987
|
|
|
8879
8988
|
// src/claude-session-bun.ts
|
|
8880
8989
|
import * as os4 from "os";
|
|
8881
|
-
import * as
|
|
8882
|
-
import * as
|
|
8990
|
+
import * as fs8 from "fs";
|
|
8991
|
+
import * as path10 from "path";
|
|
8883
8992
|
import { execFileSync } from "child_process";
|
|
8884
8993
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8994
|
+
import { StringDecoder } from "string_decoder";
|
|
8885
8995
|
function resolveClaude(cmd = "claude") {
|
|
8886
|
-
if (
|
|
8996
|
+
if (path10.isAbsolute(cmd) && fs8.existsSync(cmd)) return cmd;
|
|
8887
8997
|
const viaBun = Bun.which(cmd);
|
|
8888
8998
|
if (viaBun) return viaBun;
|
|
8889
8999
|
const isWin = os4.platform() === "win32";
|
|
@@ -8892,24 +9002,30 @@ function resolveClaude(cmd = "claude") {
|
|
|
8892
9002
|
encoding: "utf8",
|
|
8893
9003
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8894
9004
|
});
|
|
8895
|
-
const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) =>
|
|
9005
|
+
const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs8.existsSync(p));
|
|
8896
9006
|
if (first) return first;
|
|
8897
9007
|
} catch {
|
|
8898
9008
|
}
|
|
8899
9009
|
throw new Error(`Could not resolve command on PATH: ${cmd}`);
|
|
8900
9010
|
}
|
|
8901
9011
|
function encodeCwd(cwd) {
|
|
8902
|
-
const resolved =
|
|
9012
|
+
const resolved = path10.resolve(cwd);
|
|
8903
9013
|
let real = resolved;
|
|
8904
9014
|
try {
|
|
8905
|
-
real =
|
|
9015
|
+
real = fs8.realpathSync.native(resolved);
|
|
8906
9016
|
} catch {
|
|
8907
9017
|
}
|
|
8908
9018
|
return real.replace(/[^a-zA-Z0-9]/g, "-");
|
|
8909
9019
|
}
|
|
8910
9020
|
var bunPtySpawner = (argv, opts) => {
|
|
8911
9021
|
const [command, ...args] = argv;
|
|
8912
|
-
|
|
9022
|
+
const resolved = resolveClaude(command ?? "claude");
|
|
9023
|
+
if (os4.platform() === "win32" && isBatchFile(resolved)) {
|
|
9024
|
+
throw new Error(
|
|
9025
|
+
`The interactive transport cannot run a Windows batch shim (${resolved}). Point \`cliPath\` at claude.exe, or use the default headless transport.`
|
|
9026
|
+
);
|
|
9027
|
+
}
|
|
9028
|
+
return Bun.spawn([resolved, ...args], {
|
|
8913
9029
|
cwd: opts.cwd,
|
|
8914
9030
|
env: opts.env,
|
|
8915
9031
|
terminal: {
|
|
@@ -9124,6 +9240,123 @@ function isInterruptRecord(rec) {
|
|
|
9124
9240
|
function isTurnDurationRecord(rec) {
|
|
9125
9241
|
return rec?.type === "system" && rec.subtype === "turn_duration";
|
|
9126
9242
|
}
|
|
9243
|
+
var TRANSCRIPT_CHUNK_BYTES = 1 << 20;
|
|
9244
|
+
var TranscriptCursor = class {
|
|
9245
|
+
/** The path is asked for on every poll rather than kept, because it is a
|
|
9246
|
+
* field of the session and a test may repoint it; a path that changes is
|
|
9247
|
+
* a different file, which the `dev:ino` check already starts over on. */
|
|
9248
|
+
constructor(resolvePath) {
|
|
9249
|
+
this.resolvePath = resolvePath;
|
|
9250
|
+
}
|
|
9251
|
+
resolvePath;
|
|
9252
|
+
/** Bytes of the current file already read. */
|
|
9253
|
+
offset = 0;
|
|
9254
|
+
/** `dev:ino` of the file those bytes came from, null before the first
|
|
9255
|
+
* successful stat. A different one is a new file, not growth. */
|
|
9256
|
+
fileId = null;
|
|
9257
|
+
/** Holds back an incomplete trailing UTF-8 sequence between two reads. */
|
|
9258
|
+
decoder = new StringDecoder("utf8");
|
|
9259
|
+
/** What was read after the last newline: a record the CLI is part way
|
|
9260
|
+
* through writing, completed by a later read. */
|
|
9261
|
+
partial = "";
|
|
9262
|
+
/** Complete lines seen since byte 0 of the current file. */
|
|
9263
|
+
seen = 0;
|
|
9264
|
+
/** Complete lines handed on: the line-index cursor. */
|
|
9265
|
+
taken = 0;
|
|
9266
|
+
/** Complete lines read and not handed on yet, oldest first. */
|
|
9267
|
+
queue = [];
|
|
9268
|
+
stats = { polls: 0, reads: 0, bytes: 0 };
|
|
9269
|
+
/** Complete lines in the file now. */
|
|
9270
|
+
lineCount() {
|
|
9271
|
+
this.poll(true);
|
|
9272
|
+
return this.seen;
|
|
9273
|
+
}
|
|
9274
|
+
/** The complete lines not handed on yet, oldest first. They stay queued
|
|
9275
|
+
* until `take` says how many of them were used. */
|
|
9276
|
+
pending() {
|
|
9277
|
+
this.poll(true);
|
|
9278
|
+
return this.queue;
|
|
9279
|
+
}
|
|
9280
|
+
/** Whether the file holds a complete line this cursor has not handed on. */
|
|
9281
|
+
hasUnread() {
|
|
9282
|
+
return this.pending().length > 0;
|
|
9283
|
+
}
|
|
9284
|
+
/** Mark the first `count` pending lines as handed on. */
|
|
9285
|
+
take(count) {
|
|
9286
|
+
const used = Math.min(count, this.queue.length);
|
|
9287
|
+
if (used <= 0) return;
|
|
9288
|
+
this.queue.splice(0, used);
|
|
9289
|
+
this.taken += used;
|
|
9290
|
+
}
|
|
9291
|
+
/** Hand on nothing of what is in the file now: the records written before a
|
|
9292
|
+
* turn started belong to the turn before it. */
|
|
9293
|
+
skipToEnd() {
|
|
9294
|
+
this.poll(false);
|
|
9295
|
+
this.taken = Math.max(this.taken, this.seen);
|
|
9296
|
+
this.queue.length = 0;
|
|
9297
|
+
}
|
|
9298
|
+
/** Read whatever was appended since the last look. */
|
|
9299
|
+
poll(collect) {
|
|
9300
|
+
this.stats.polls++;
|
|
9301
|
+
const filePath = this.resolvePath();
|
|
9302
|
+
let stat;
|
|
9303
|
+
try {
|
|
9304
|
+
stat = fs8.statSync(filePath);
|
|
9305
|
+
} catch {
|
|
9306
|
+
return;
|
|
9307
|
+
}
|
|
9308
|
+
const fileId = `${stat.dev}:${stat.ino}`;
|
|
9309
|
+
if (this.fileId !== null && (fileId !== this.fileId || stat.size < this.offset)) {
|
|
9310
|
+
this.offset = 0;
|
|
9311
|
+
this.seen = 0;
|
|
9312
|
+
this.partial = "";
|
|
9313
|
+
this.decoder = new StringDecoder("utf8");
|
|
9314
|
+
this.queue.length = 0;
|
|
9315
|
+
}
|
|
9316
|
+
this.fileId = fileId;
|
|
9317
|
+
if (stat.size <= this.offset) return;
|
|
9318
|
+
let fd = null;
|
|
9319
|
+
try {
|
|
9320
|
+
fd = fs8.openSync(filePath, "r");
|
|
9321
|
+
const buffer = Buffer.allocUnsafe(
|
|
9322
|
+
Math.min(TRANSCRIPT_CHUNK_BYTES, stat.size - this.offset)
|
|
9323
|
+
);
|
|
9324
|
+
while (this.offset < stat.size) {
|
|
9325
|
+
const want = Math.min(buffer.length, stat.size - this.offset);
|
|
9326
|
+
const read = fs8.readSync(fd, buffer, 0, want, this.offset);
|
|
9327
|
+
this.stats.reads++;
|
|
9328
|
+
if (read <= 0) break;
|
|
9329
|
+
this.offset += read;
|
|
9330
|
+
this.stats.bytes += read;
|
|
9331
|
+
this.ingest(this.decoder.write(buffer.subarray(0, read)), collect);
|
|
9332
|
+
}
|
|
9333
|
+
} catch {
|
|
9334
|
+
} finally {
|
|
9335
|
+
if (fd !== null) {
|
|
9336
|
+
try {
|
|
9337
|
+
fs8.closeSync(fd);
|
|
9338
|
+
} catch {
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
}
|
|
9342
|
+
}
|
|
9343
|
+
/** Split decoded text into complete lines, keeping the unterminated tail. */
|
|
9344
|
+
ingest(text, collect) {
|
|
9345
|
+
if (!text) return;
|
|
9346
|
+
this.partial += text;
|
|
9347
|
+
let start = 0;
|
|
9348
|
+
for (; ; ) {
|
|
9349
|
+
const newline = this.partial.indexOf("\n", start);
|
|
9350
|
+
if (newline === -1) break;
|
|
9351
|
+
if (collect && this.seen >= this.taken) {
|
|
9352
|
+
this.queue.push(this.partial.slice(start, newline));
|
|
9353
|
+
}
|
|
9354
|
+
this.seen++;
|
|
9355
|
+
start = newline + 1;
|
|
9356
|
+
}
|
|
9357
|
+
if (start > 0) this.partial = this.partial.slice(start);
|
|
9358
|
+
}
|
|
9359
|
+
};
|
|
9127
9360
|
var normalizedPrompt = (text) => text.replace(/\s+/g, " ").trim();
|
|
9128
9361
|
function isOwnPromptRecord(rec, normalized) {
|
|
9129
9362
|
const content = rec?.type === "user" ? rec.message?.content : void 0;
|
|
@@ -9131,12 +9364,12 @@ function isOwnPromptRecord(rec, normalized) {
|
|
|
9131
9364
|
}
|
|
9132
9365
|
function resolveConfigDir(configDir) {
|
|
9133
9366
|
const value = configDir ?? process.env.CLAUDE_CONFIG_DIR;
|
|
9134
|
-
if (!value) return
|
|
9367
|
+
if (!value) return path10.join(os4.homedir(), ".claude");
|
|
9135
9368
|
if (value === "~") return os4.homedir();
|
|
9136
9369
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
9137
|
-
return
|
|
9370
|
+
return path10.join(os4.homedir(), value.slice(2));
|
|
9138
9371
|
}
|
|
9139
|
-
return
|
|
9372
|
+
return path10.resolve(value);
|
|
9140
9373
|
}
|
|
9141
9374
|
function fatalScreenMessage(kind, configDir, cwd) {
|
|
9142
9375
|
if (kind === "trust") {
|
|
@@ -9155,8 +9388,8 @@ var ClaudeSession = class {
|
|
|
9155
9388
|
/** The newest terminal output, ANSI included, for diagnostics. */
|
|
9156
9389
|
raw = "";
|
|
9157
9390
|
proc = null;
|
|
9158
|
-
|
|
9159
|
-
|
|
9391
|
+
/** The transcript, read forwards only; its `taken` is the old line cursor. */
|
|
9392
|
+
transcript;
|
|
9160
9393
|
lastDataAt = 0;
|
|
9161
9394
|
exited = false;
|
|
9162
9395
|
exitCode = null;
|
|
@@ -9200,7 +9433,7 @@ var ClaudeSession = class {
|
|
|
9200
9433
|
abandonTurn = false;
|
|
9201
9434
|
o;
|
|
9202
9435
|
constructor(opts = {}) {
|
|
9203
|
-
this.cwd =
|
|
9436
|
+
this.cwd = path10.resolve(opts.cwd ?? process.cwd());
|
|
9204
9437
|
this.configDir = resolveConfigDir(opts.configDir);
|
|
9205
9438
|
this.explicitConfigDir = opts.configDir ? this.configDir : void 0;
|
|
9206
9439
|
this.signal = opts.signal;
|
|
@@ -9212,12 +9445,13 @@ var ClaudeSession = class {
|
|
|
9212
9445
|
this.onExit = opts.onExit;
|
|
9213
9446
|
this.spawnPty = opts.spawnPty ?? bunPtySpawner;
|
|
9214
9447
|
this.sessionId = opts.resumeSessionId ?? randomUUID4();
|
|
9215
|
-
this.jsonlPath =
|
|
9448
|
+
this.jsonlPath = path10.join(
|
|
9216
9449
|
this.configDir,
|
|
9217
9450
|
"projects",
|
|
9218
9451
|
encodeCwd(this.cwd),
|
|
9219
9452
|
`${this.sessionId}.jsonl`
|
|
9220
9453
|
);
|
|
9454
|
+
this.transcript = new TranscriptCursor(() => this.jsonlPath);
|
|
9221
9455
|
this.o = {
|
|
9222
9456
|
cwd: this.cwd,
|
|
9223
9457
|
cliPath: opts.cliPath,
|
|
@@ -9322,7 +9556,12 @@ var ClaudeSession = class {
|
|
|
9322
9556
|
};
|
|
9323
9557
|
proc.exited.then((code) => markExited(typeof code === "number" ? code : null)).catch(() => markExited(null));
|
|
9324
9558
|
await this.waitForBoot();
|
|
9325
|
-
this.
|
|
9559
|
+
this.transcript.skipToEnd();
|
|
9560
|
+
}
|
|
9561
|
+
/** How much this session has read off the transcript. Read-only, and the
|
|
9562
|
+
* one thing `scripts/bench-transcript-poll.ts` measures. */
|
|
9563
|
+
get transcriptStats() {
|
|
9564
|
+
return this.transcript.stats;
|
|
9326
9565
|
}
|
|
9327
9566
|
onData(chunk) {
|
|
9328
9567
|
this.lastDataAt = Date.now();
|
|
@@ -9469,22 +9708,10 @@ var ClaudeSession = class {
|
|
|
9469
9708
|
while (Date.now() < until) {
|
|
9470
9709
|
await delay(80);
|
|
9471
9710
|
if (this.aborted || this.exited) return;
|
|
9472
|
-
if (this.
|
|
9711
|
+
if (this.transcript.hasUnread()) return;
|
|
9473
9712
|
}
|
|
9474
9713
|
}
|
|
9475
9714
|
}
|
|
9476
|
-
readRawLines() {
|
|
9477
|
-
try {
|
|
9478
|
-
return fs7.readFileSync(this.jsonlPath, "utf8").split("\n");
|
|
9479
|
-
} catch {
|
|
9480
|
-
return [];
|
|
9481
|
-
}
|
|
9482
|
-
}
|
|
9483
|
-
/** Count of complete lines (split('\n') minus the trailing/partial element). */
|
|
9484
|
-
lineCount() {
|
|
9485
|
-
const lines = this.readRawLines();
|
|
9486
|
-
return lines.length > 0 ? lines.length - 1 : 0;
|
|
9487
|
-
}
|
|
9488
9715
|
rawTail(max = 600) {
|
|
9489
9716
|
const clean = stripTerminal(this.raw).replace(/\s+/g, " ").trim();
|
|
9490
9717
|
return clean.length > max ? clean.slice(-max) : clean;
|
|
@@ -9517,7 +9744,7 @@ var ClaudeSession = class {
|
|
|
9517
9744
|
this.turn = new Promise((resolve6) => settle = resolve6);
|
|
9518
9745
|
this.turnDenied = [];
|
|
9519
9746
|
this.abandonTurn = false;
|
|
9520
|
-
this.
|
|
9747
|
+
this.transcript.skipToEnd();
|
|
9521
9748
|
this.screen = "";
|
|
9522
9749
|
try {
|
|
9523
9750
|
if (this.o.bracketedPaste) {
|
|
@@ -9582,30 +9809,26 @@ var ClaudeSession = class {
|
|
|
9582
9809
|
/**
|
|
9583
9810
|
* Read every complete transcript record past the cursor into the running
|
|
9584
9811
|
* turn. Returns whether anything new was read. The one place a record is
|
|
9585
|
-
* interpreted, shared by the poll loop and `flushTranscript
|
|
9812
|
+
* interpreted, shared by the poll loop and `flushTranscript`, and the only
|
|
9813
|
+
* one that consumes what `TranscriptCursor` has queued.
|
|
9586
9814
|
*/
|
|
9587
9815
|
scanTranscript() {
|
|
9588
9816
|
const scan = this.scan;
|
|
9589
9817
|
if (!scan || scan.end) return false;
|
|
9590
|
-
const lines = this.
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
for (let i =
|
|
9818
|
+
const lines = this.transcript.pending().slice();
|
|
9819
|
+
if (lines.length === 0) return false;
|
|
9820
|
+
let used = 0;
|
|
9821
|
+
for (let i = 0; i < lines.length && !scan.end; i++) {
|
|
9594
9822
|
const s = lines[i];
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
continue;
|
|
9598
|
-
}
|
|
9823
|
+
used = i + 1;
|
|
9824
|
+
if (!s || !s.trim()) continue;
|
|
9599
9825
|
let rec = null;
|
|
9600
9826
|
try {
|
|
9601
9827
|
rec = JSON.parse(s);
|
|
9602
9828
|
} catch {
|
|
9603
9829
|
}
|
|
9604
9830
|
if (scan.awaitingOwnPrompt !== null) {
|
|
9605
|
-
if (!isOwnPromptRecord(rec, scan.awaitingOwnPrompt))
|
|
9606
|
-
this.cursor = i + 1;
|
|
9607
|
-
continue;
|
|
9608
|
-
}
|
|
9831
|
+
if (!isOwnPromptRecord(rec, scan.awaitingOwnPrompt)) continue;
|
|
9609
9832
|
scan.awaitingOwnPrompt = null;
|
|
9610
9833
|
}
|
|
9611
9834
|
if (rec) this.notePlanApproval(rec);
|
|
@@ -9625,9 +9848,8 @@ var ClaudeSession = class {
|
|
|
9625
9848
|
scan.end = scan.stopReason ? "stop" : "ended";
|
|
9626
9849
|
}
|
|
9627
9850
|
}
|
|
9628
|
-
this.cursor = i + 1;
|
|
9629
9851
|
}
|
|
9630
|
-
|
|
9852
|
+
this.transcript.take(used);
|
|
9631
9853
|
scan.lastBeat = Date.now();
|
|
9632
9854
|
scan.stopSeenAt = Date.now();
|
|
9633
9855
|
return true;
|
|
@@ -10416,13 +10638,13 @@ function spawnInteractiveProcess(opts) {
|
|
|
10416
10638
|
}
|
|
10417
10639
|
|
|
10418
10640
|
// src/prompts.ts
|
|
10419
|
-
import { readFileSync as
|
|
10641
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
10420
10642
|
import { homedir as homedir8 } from "os";
|
|
10421
10643
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
10422
10644
|
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
10423
|
-
function readPromptFileIfPresent(
|
|
10645
|
+
function readPromptFileIfPresent(path12) {
|
|
10424
10646
|
try {
|
|
10425
|
-
const content =
|
|
10647
|
+
const content = readFileSync6(path12, "utf8").trim();
|
|
10426
10648
|
return content || void 0;
|
|
10427
10649
|
} catch {
|
|
10428
10650
|
return void 0;
|
|
@@ -10527,9 +10749,9 @@ function extractSystemMessages(prompt) {
|
|
|
10527
10749
|
}
|
|
10528
10750
|
var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
|
|
10529
10751
|
function userAuthoredInstructions(forwarded, opts = {}) {
|
|
10530
|
-
const readFile2 = opts.readFile ?? ((
|
|
10752
|
+
const readFile2 = opts.readFile ?? ((path12) => {
|
|
10531
10753
|
try {
|
|
10532
|
-
return
|
|
10754
|
+
return readFileSync6(path12, "utf8");
|
|
10533
10755
|
} catch {
|
|
10534
10756
|
return null;
|
|
10535
10757
|
}
|
|
@@ -10541,16 +10763,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
|
|
|
10541
10763
|
}
|
|
10542
10764
|
const seen = /* @__PURE__ */ new Set();
|
|
10543
10765
|
for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
|
|
10544
|
-
const
|
|
10545
|
-
if (seen.has(
|
|
10546
|
-
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(
|
|
10547
|
-
seen.add(
|
|
10548
|
-
const file = readFile2(
|
|
10766
|
+
const path12 = match[1].trim();
|
|
10767
|
+
if (seen.has(path12) || !isAbsolute3(path12)) continue;
|
|
10768
|
+
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path12))) continue;
|
|
10769
|
+
seen.add(path12);
|
|
10770
|
+
const file = readFile2(path12);
|
|
10549
10771
|
if (!file?.trim()) continue;
|
|
10550
10772
|
const start = match.index + match[0].length + 1;
|
|
10551
10773
|
const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
|
|
10552
10774
|
if (content === null) continue;
|
|
10553
|
-
out.push({ text: `Instructions from: ${
|
|
10775
|
+
out.push({ text: `Instructions from: ${path12}
|
|
10554
10776
|
${content}`, content });
|
|
10555
10777
|
}
|
|
10556
10778
|
return out;
|
|
@@ -10587,10 +10809,10 @@ ${options.compressionSummary.trim()}`
|
|
|
10587
10809
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
10588
10810
|
const content = parts.join("\n\n");
|
|
10589
10811
|
if (!content) return void 0;
|
|
10590
|
-
const
|
|
10812
|
+
const path12 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID6()}.md`);
|
|
10591
10813
|
try {
|
|
10592
|
-
writeFileSync6(
|
|
10593
|
-
return
|
|
10814
|
+
writeFileSync6(path12, content, { encoding: "utf8", mode: 384 });
|
|
10815
|
+
return path12;
|
|
10594
10816
|
} catch (err) {
|
|
10595
10817
|
log.warn("failed to write system prompt file", { error: String(err) });
|
|
10596
10818
|
return void 0;
|
|
@@ -14908,7 +15130,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14908
15130
|
import {
|
|
14909
15131
|
existsSync as existsSync5,
|
|
14910
15132
|
mkdirSync as mkdirSync5,
|
|
14911
|
-
readFileSync as
|
|
15133
|
+
readFileSync as readFileSync7,
|
|
14912
15134
|
realpathSync as realpathSync4,
|
|
14913
15135
|
rmSync as rmSync3,
|
|
14914
15136
|
writeFileSync as writeFileSync7
|
|
@@ -14935,7 +15157,7 @@ function userIntendsToUseUnscoped() {
|
|
|
14935
15157
|
const cfg = userOpencodeJsonPath();
|
|
14936
15158
|
if (!existsSync5(cfg)) return false;
|
|
14937
15159
|
try {
|
|
14938
|
-
const json = JSON.parse(
|
|
15160
|
+
const json = JSON.parse(readFileSync7(cfg, "utf8"));
|
|
14939
15161
|
const plugins = json.plugin;
|
|
14940
15162
|
if (!Array.isArray(plugins)) return false;
|
|
14941
15163
|
return plugins.some(
|
|
@@ -14956,7 +15178,7 @@ function ourLoadedDir() {
|
|
|
14956
15178
|
function ourVersion(dir) {
|
|
14957
15179
|
if (!dir) return null;
|
|
14958
15180
|
try {
|
|
14959
|
-
const pkg = JSON.parse(
|
|
15181
|
+
const pkg = JSON.parse(readFileSync7(join13(dir, "package.json"), "utf8"));
|
|
14960
15182
|
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
14961
15183
|
} catch {
|
|
14962
15184
|
return null;
|
|
@@ -14968,7 +15190,7 @@ function markerPath() {
|
|
|
14968
15190
|
}
|
|
14969
15191
|
function markerVersion() {
|
|
14970
15192
|
try {
|
|
14971
|
-
const marker = JSON.parse(
|
|
15193
|
+
const marker = JSON.parse(readFileSync7(markerPath(), "utf8"));
|
|
14972
15194
|
return typeof marker.version === "string" ? marker.version : null;
|
|
14973
15195
|
} catch {
|
|
14974
15196
|
return null;
|
|
@@ -15025,7 +15247,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15025
15247
|
if (!existsSync5(pkgJsonPath)) return;
|
|
15026
15248
|
let pkg = {};
|
|
15027
15249
|
try {
|
|
15028
|
-
pkg = JSON.parse(
|
|
15250
|
+
pkg = JSON.parse(readFileSync7(pkgJsonPath, "utf8"));
|
|
15029
15251
|
} catch {
|
|
15030
15252
|
return;
|
|
15031
15253
|
}
|
|
@@ -15044,7 +15266,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15044
15266
|
const cachePkgJson = join13(cacheRoot, "package.json");
|
|
15045
15267
|
if (!existsSync5(cachePkgJson)) return;
|
|
15046
15268
|
try {
|
|
15047
|
-
const cfg = JSON.parse(
|
|
15269
|
+
const cfg = JSON.parse(readFileSync7(cachePkgJson, "utf8"));
|
|
15048
15270
|
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
15049
15271
|
delete cfg.dependencies[STALE_PACKAGE_NAME];
|
|
15050
15272
|
writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
|
|
@@ -15124,7 +15346,7 @@ function registerOpencodeLogSink(client) {
|
|
|
15124
15346
|
}
|
|
15125
15347
|
|
|
15126
15348
|
// src/v2-client.ts
|
|
15127
|
-
import
|
|
15349
|
+
import path11 from "path";
|
|
15128
15350
|
function formatAgentTypeList(agents) {
|
|
15129
15351
|
const lines = agents.flatMap((agent) => {
|
|
15130
15352
|
if (agent.hidden === true || agent.mode === "primary") return [];
|
|
@@ -15153,7 +15375,7 @@ function toV1Session(info) {
|
|
|
15153
15375
|
if (!info || typeof info !== "object") return void 0;
|
|
15154
15376
|
const session = info;
|
|
15155
15377
|
const root = session.location?.directory;
|
|
15156
|
-
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ?
|
|
15378
|
+
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path11.resolve(root, session.subpath) : root : void 0;
|
|
15157
15379
|
return { ...info, directory, parentID: session.parentID };
|
|
15158
15380
|
}
|
|
15159
15381
|
function toV1Messages(messages) {
|