@sideboard-ai/core 0.1.62 → 0.1.67
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/{agents-KF3J3C53.js → agents-OTBZVTZO.js} +27 -7
- package/dist/{agents-QLSFIQAO.js → agents-TS3EI2YI.js} +27 -7
- package/dist/{app-settings-LTSFG7QJ.js → app-settings-GJVZGU3M.js} +13 -3
- package/dist/{app-settings-HK4KAM4R.js → app-settings-ONKMFSEJ.js} +13 -3
- package/dist/{chunk-UGXSSBFJ.js → chunk-4BUCGNZS.js} +1 -1
- package/dist/chunk-4UIYN56C.js +173 -0
- package/dist/{chunk-NVU7EC2E.js → chunk-57WWNMFP.js} +2 -2
- package/dist/{chunk-A6FOKNOU.js → chunk-5HTTCYWA.js} +2 -2
- package/dist/{chunk-DXTGDXO2.js → chunk-7ZOQY4A7.js} +31 -6
- package/dist/{chunk-ZF5YJCXO.js → chunk-B4PRRKMA.js} +284 -93
- package/dist/{chunk-DDN3UF4R.js → chunk-BPOTVI5J.js} +115 -3
- package/dist/{chunk-MBAL5FE6.js → chunk-DB3I75FI.js} +4 -4
- package/dist/chunk-FGDJOJ2L.js +176 -0
- package/dist/{chunk-2FEA4LDA.js → chunk-JWZNK3CQ.js} +31 -6
- package/dist/{chunk-2MCNURQV.js → chunk-K7LM4CLD.js} +279 -93
- package/dist/{chunk-3KKNTHUC.js → chunk-LW5CMQGZ.js} +1 -1
- package/dist/{chunk-JB2H7E7V.js → chunk-M5V4QIWF.js} +1 -1
- package/dist/{chunk-6CUJQKTJ.js → chunk-MHR4OGZ4.js} +115 -3
- package/dist/{chunk-46KMKHSD.js → chunk-WAFIF22N.js} +2 -2
- package/dist/{chunk-27MAWNKY.js → chunk-ZRFAZVGL.js} +2 -2
- package/dist/{connected-teams-MKIYKOYA.js → connected-teams-OMDHTL6A.js} +2 -2
- package/dist/{connected-teams-RSL4VB3X.js → connected-teams-RUNMOCG5.js} +2 -2
- package/dist/{coordinator-prompt-N2R6FGZK.js → coordinator-prompt-3BSMKZL4.js} +4 -4
- package/dist/{coordinator-prompt-LKG3SHMI.js → coordinator-prompt-6HMTOX4G.js} +4 -4
- package/dist/{global-workspace-LALWQWRJ.js → global-workspace-QS7A23CB.js} +5 -6
- package/dist/{global-workspace-YOAUQCMA.js → global-workspace-YZ2NJVLL.js} +5 -6
- package/dist/index.cjs +1345 -933
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +750 -715
- package/dist/mcp/run-stdio.cjs +1293 -977
- package/dist/mcp/run-stdio.js +691 -748
- package/dist/{run-GPSVV5OH.js → run-JLD2Y52J.js} +1 -1
- package/dist/{run-HZTBWGQR.js → run-Y2MI3EVV.js} +1 -1
- package/dist/{workspaces-E75POJXP.js → workspaces-AIUYUXB7.js} +6 -7
- package/dist/{workspaces-VCAIHVIE.js → workspaces-EBFW7I7S.js} +6 -7
- package/dist/{worktree-MV4WRW6G.js → worktree-7NBPIVFZ.js} +2 -2
- package/dist/{worktree-LZEGVQSN.js → worktree-WMDJW2BL.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-73AT5K4A.js +0 -104
- package/dist/chunk-GI7E5733.js +0 -30
- package/dist/chunk-RIWUU7FO.js +0 -107
- package/dist/chunk-S2LL5HA4.js +0 -33
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1507,18 +1507,37 @@ var init_gh_errors = __esm({
|
|
|
1507
1507
|
});
|
|
1508
1508
|
|
|
1509
1509
|
// src/agents/path.ts
|
|
1510
|
+
function prependPathDir(env, dir) {
|
|
1511
|
+
if (!dir || !(0, import_node_fs4.existsSync)(dir)) return;
|
|
1512
|
+
const current = env.PATH ?? "";
|
|
1513
|
+
const parts = current.split(import_node_path4.delimiter).filter(Boolean);
|
|
1514
|
+
if (parts.includes(dir)) {
|
|
1515
|
+
env.PATH = current;
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
env.PATH = [dir, ...parts].join(import_node_path4.delimiter);
|
|
1519
|
+
}
|
|
1520
|
+
function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os3.homedir)()) {
|
|
1521
|
+
return (0, import_node_path4.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
|
|
1522
|
+
}
|
|
1523
|
+
function isConductorBundledCli(filePath) {
|
|
1524
|
+
const p = (filePath ?? "").replace(/\\/g, "/");
|
|
1525
|
+
return p.includes("/com.conductor.app/bin/") || p.endsWith("/com.conductor.app/bin");
|
|
1526
|
+
}
|
|
1510
1527
|
function ensureAgentPath(env = process.env) {
|
|
1511
1528
|
const home = env.HOME || env.USERPROFILE || (0, import_node_os3.homedir)();
|
|
1512
1529
|
const current = env.PATH ?? "";
|
|
1513
1530
|
const parts = current.split(import_node_path4.delimiter).filter(Boolean);
|
|
1514
1531
|
const seen = new Set(parts);
|
|
1515
1532
|
const extras = [
|
|
1516
|
-
...EXTRA_BIN_DIRS.map((rel) =>
|
|
1533
|
+
...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path4.join)(home, rel)),
|
|
1517
1534
|
"/opt/homebrew/bin",
|
|
1518
|
-
"/usr/local/bin"
|
|
1535
|
+
"/usr/local/bin",
|
|
1536
|
+
// Keep after Homebrew/npm so a user-installed CLI still wins.
|
|
1537
|
+
conductorBundledBinDir(home)
|
|
1519
1538
|
];
|
|
1520
1539
|
for (const dir of extras.reverse()) {
|
|
1521
|
-
if (!dir || seen.has(dir)) continue;
|
|
1540
|
+
if (!dir || seen.has(dir) || !(0, import_node_fs4.existsSync)(dir)) continue;
|
|
1522
1541
|
parts.unshift(dir);
|
|
1523
1542
|
seen.add(dir);
|
|
1524
1543
|
}
|
|
@@ -1526,10 +1545,50 @@ function ensureAgentPath(env = process.env) {
|
|
|
1526
1545
|
env.PATH = next;
|
|
1527
1546
|
return next;
|
|
1528
1547
|
}
|
|
1529
|
-
|
|
1548
|
+
function enrichPathWithNpmGlobalBin(env = process.env) {
|
|
1549
|
+
ensureAgentPath(env);
|
|
1550
|
+
try {
|
|
1551
|
+
const prefix = (0, import_node_child_process.execFileSync)("npm", ["prefix", "-g"], {
|
|
1552
|
+
encoding: "utf8",
|
|
1553
|
+
env: { ...process.env, ...env },
|
|
1554
|
+
timeout: 8e3,
|
|
1555
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1556
|
+
}).trim().split(/\r?\n/).find(Boolean);
|
|
1557
|
+
if (prefix) {
|
|
1558
|
+
const binDir = process.platform === "win32" ? prefix : (0, import_node_path4.join)(prefix, "bin");
|
|
1559
|
+
prependPathDir(env, binDir);
|
|
1560
|
+
}
|
|
1561
|
+
} catch {
|
|
1562
|
+
}
|
|
1563
|
+
return env.PATH ?? "";
|
|
1564
|
+
}
|
|
1565
|
+
function posixShellSingleQuote(value) {
|
|
1566
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1567
|
+
}
|
|
1568
|
+
function resolveCommandBinarySync(command, whichPath) {
|
|
1569
|
+
const trimmed = command.trim();
|
|
1570
|
+
if (!trimmed) return trimmed;
|
|
1571
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
1572
|
+
if (!match) return trimmed;
|
|
1573
|
+
const bin = match[1];
|
|
1574
|
+
const rest = match[2] ?? "";
|
|
1575
|
+
if (bin.includes("/") || bin.includes("\\")) return trimmed;
|
|
1576
|
+
const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
|
|
1577
|
+
if (!abs) return trimmed;
|
|
1578
|
+
return `${abs}${rest}`;
|
|
1579
|
+
}
|
|
1580
|
+
function withExportedPath(command, pathValue) {
|
|
1581
|
+
const trimmed = command.trim();
|
|
1582
|
+
if (!trimmed) return trimmed;
|
|
1583
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
1584
|
+
return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
|
|
1585
|
+
}
|
|
1586
|
+
var import_node_fs4, import_node_child_process, import_node_os3, import_node_path4, EXTRA_BIN_DIRS;
|
|
1530
1587
|
var init_path = __esm({
|
|
1531
1588
|
"src/agents/path.ts"() {
|
|
1532
1589
|
"use strict";
|
|
1590
|
+
import_node_fs4 = require("fs");
|
|
1591
|
+
import_node_child_process = require("child_process");
|
|
1533
1592
|
import_node_os3 = require("os");
|
|
1534
1593
|
import_node_path4 = require("path");
|
|
1535
1594
|
EXTRA_BIN_DIRS = [
|
|
@@ -1537,6 +1596,10 @@ var init_path = __esm({
|
|
|
1537
1596
|
".cargo/bin",
|
|
1538
1597
|
".nvm/current/bin",
|
|
1539
1598
|
".asdf/shims",
|
|
1599
|
+
".volta/bin",
|
|
1600
|
+
".npm-global/bin",
|
|
1601
|
+
// fnm default alias (common when shell init is skipped)
|
|
1602
|
+
".local/share/fnm/aliases/default/bin",
|
|
1540
1603
|
// pnpm / npm global bins (where `@brightsy/cli` typically lands)
|
|
1541
1604
|
"Library/pnpm",
|
|
1542
1605
|
".pnpm"
|
|
@@ -2596,7 +2659,7 @@ function isLocalPrFetchBranch(ref) {
|
|
|
2596
2659
|
async function createThreadWorktree(opts) {
|
|
2597
2660
|
let branchName = `thread/${opts.slug}`;
|
|
2598
2661
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2599
|
-
if ((0,
|
|
2662
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2600
2663
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2601
2664
|
}
|
|
2602
2665
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2666,7 +2729,7 @@ async function createExistingBranchWorktree(opts) {
|
|
|
2666
2729
|
const branchName = opts.branchName.trim();
|
|
2667
2730
|
if (!branchName) throw new Error("branch name required");
|
|
2668
2731
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2669
|
-
if ((0,
|
|
2732
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2670
2733
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2671
2734
|
}
|
|
2672
2735
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2927,9 +2990,9 @@ function sameRepoPath(a, b) {
|
|
|
2927
2990
|
}
|
|
2928
2991
|
function listLocalThreadBranchSlugs(repoPath) {
|
|
2929
2992
|
const refsDir = (0, import_node_path5.join)(repoPath, ".git", "refs", "heads", "thread");
|
|
2930
|
-
if (!(0,
|
|
2993
|
+
if (!(0, import_node_fs5.existsSync)(refsDir)) return [];
|
|
2931
2994
|
try {
|
|
2932
|
-
return (0,
|
|
2995
|
+
return (0, import_node_fs5.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
|
|
2933
2996
|
} catch {
|
|
2934
2997
|
return [];
|
|
2935
2998
|
}
|
|
@@ -2937,8 +3000,8 @@ function listLocalThreadBranchSlugs(repoPath) {
|
|
|
2937
3000
|
function collectTakenTeamSlugs(repoPath) {
|
|
2938
3001
|
const taken = /* @__PURE__ */ new Set();
|
|
2939
3002
|
const root = worktreesRoot(repoPath);
|
|
2940
|
-
if ((0,
|
|
2941
|
-
for (const entry of (0,
|
|
3003
|
+
if ((0, import_node_fs5.existsSync)(root)) {
|
|
3004
|
+
for (const entry of (0, import_node_fs5.readdirSync)(root, { withFileTypes: true })) {
|
|
2942
3005
|
if (entry.isDirectory() && entry.name !== ".DS_Store") {
|
|
2943
3006
|
taken.add(normalizeTakenSlug(entry.name));
|
|
2944
3007
|
}
|
|
@@ -2960,16 +3023,16 @@ function allocateTeamSlug(repoPath) {
|
|
|
2960
3023
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
2961
3024
|
const team = allocateTeamName(taken);
|
|
2962
3025
|
const path = (0, import_node_path5.join)(worktreesRoot(repoPath), team.slug);
|
|
2963
|
-
if (!(0,
|
|
3026
|
+
if (!(0, import_node_fs5.existsSync)(path)) return team;
|
|
2964
3027
|
taken.add(team.slug);
|
|
2965
3028
|
}
|
|
2966
3029
|
throw new Error("No available soccer team worktree directories left");
|
|
2967
3030
|
}
|
|
2968
|
-
var
|
|
3031
|
+
var import_node_fs5, import_node_path5;
|
|
2969
3032
|
var init_worktree = __esm({
|
|
2970
3033
|
"src/git/worktree.ts"() {
|
|
2971
3034
|
"use strict";
|
|
2972
|
-
|
|
3035
|
+
import_node_fs5 = require("fs");
|
|
2973
3036
|
import_node_path5 = require("path");
|
|
2974
3037
|
init_paths();
|
|
2975
3038
|
init_thread_store();
|
|
@@ -2997,6 +3060,7 @@ __export(app_settings_exports, {
|
|
|
2997
3060
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
2998
3061
|
brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
|
|
2999
3062
|
brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
|
|
3063
|
+
brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
|
|
3000
3064
|
caffeinateWhileCloudConnectEnabled: () => caffeinateWhileCloudConnectEnabled,
|
|
3001
3065
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
3002
3066
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
@@ -3015,17 +3079,21 @@ __export(app_settings_exports, {
|
|
|
3015
3079
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
3016
3080
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
3017
3081
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
3082
|
+
resolveAgentExecutable: () => resolveAgentExecutable,
|
|
3018
3083
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
3019
3084
|
resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
|
|
3020
3085
|
resolveNewThreadOptions: () => resolveNewThreadOptions,
|
|
3021
3086
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
3022
3087
|
saveAppSettings: () => saveAppSettings,
|
|
3023
3088
|
updateAdvancedSettings: () => updateAdvancedSettings,
|
|
3089
|
+
updateAgentExecutable: () => updateAgentExecutable,
|
|
3024
3090
|
updateAppEnvironment: () => updateAppEnvironment,
|
|
3025
3091
|
updateBrightsySettings: () => updateBrightsySettings,
|
|
3026
3092
|
updateClaudeSettings: () => updateClaudeSettings,
|
|
3093
|
+
updateCodexSettings: () => updateCodexSettings,
|
|
3027
3094
|
updateDefaultsSettings: () => updateDefaultsSettings,
|
|
3028
|
-
updateIntegrationsSettings: () => updateIntegrationsSettings
|
|
3095
|
+
updateIntegrationsSettings: () => updateIntegrationsSettings,
|
|
3096
|
+
updateOpencodeSettings: () => updateOpencodeSettings
|
|
3029
3097
|
});
|
|
3030
3098
|
function appSettingsPath() {
|
|
3031
3099
|
return (0, import_node_path6.join)(appDataDir(), "settings.json");
|
|
@@ -3046,16 +3114,33 @@ function normalizeClaude(raw) {
|
|
|
3046
3114
|
}
|
|
3047
3115
|
return out;
|
|
3048
3116
|
}
|
|
3117
|
+
function normalizeCliExecutable(raw) {
|
|
3118
|
+
if (!raw || typeof raw !== "object") return {};
|
|
3119
|
+
const source = raw;
|
|
3120
|
+
const out = {};
|
|
3121
|
+
if (typeof source.executablePath === "string") {
|
|
3122
|
+
const path = source.executablePath.trim();
|
|
3123
|
+
if (path) out.executablePath = path;
|
|
3124
|
+
}
|
|
3125
|
+
return out;
|
|
3126
|
+
}
|
|
3049
3127
|
function normalizeBrightsy(raw) {
|
|
3050
3128
|
if (!raw || typeof raw !== "object") return {};
|
|
3051
3129
|
const source = raw;
|
|
3052
3130
|
const out = {};
|
|
3131
|
+
if (typeof source.executablePath === "string") {
|
|
3132
|
+
const path = source.executablePath.trim();
|
|
3133
|
+
if (path) out.executablePath = path;
|
|
3134
|
+
}
|
|
3053
3135
|
if (typeof source.cloudConnectEnabled === "boolean") {
|
|
3054
3136
|
out.cloudConnectEnabled = source.cloudConnectEnabled;
|
|
3055
3137
|
}
|
|
3056
3138
|
if (typeof source.cloudConnectAgent === "string" && CLOUD_CONNECT_AGENTS.has(source.cloudConnectAgent)) {
|
|
3057
3139
|
out.cloudConnectAgent = source.cloudConnectAgent;
|
|
3058
3140
|
}
|
|
3141
|
+
if (typeof source.injectWorktreeMcp === "boolean") {
|
|
3142
|
+
out.injectWorktreeMcp = source.injectWorktreeMcp;
|
|
3143
|
+
}
|
|
3059
3144
|
return out;
|
|
3060
3145
|
}
|
|
3061
3146
|
function normalizeIntegrations(raw) {
|
|
@@ -3141,6 +3226,8 @@ function normalizeAdvanced(raw) {
|
|
|
3141
3226
|
function normalizeSettings(raw) {
|
|
3142
3227
|
const env = {};
|
|
3143
3228
|
let claude = {};
|
|
3229
|
+
let codex = {};
|
|
3230
|
+
let opencode = {};
|
|
3144
3231
|
let brightsy = {};
|
|
3145
3232
|
let integrations = {};
|
|
3146
3233
|
let defaults = {};
|
|
@@ -3159,6 +3246,12 @@ function normalizeSettings(raw) {
|
|
|
3159
3246
|
if ("claude" in raw) {
|
|
3160
3247
|
claude = normalizeClaude(raw.claude);
|
|
3161
3248
|
}
|
|
3249
|
+
if ("codex" in raw) {
|
|
3250
|
+
codex = normalizeCliExecutable(raw.codex);
|
|
3251
|
+
}
|
|
3252
|
+
if ("opencode" in raw) {
|
|
3253
|
+
opencode = normalizeCliExecutable(raw.opencode);
|
|
3254
|
+
}
|
|
3162
3255
|
if ("brightsy" in raw) {
|
|
3163
3256
|
brightsy = normalizeBrightsy(raw.brightsy);
|
|
3164
3257
|
}
|
|
@@ -3174,15 +3267,24 @@ function normalizeSettings(raw) {
|
|
|
3174
3267
|
advanced = normalizeAdvanced(raw.advanced);
|
|
3175
3268
|
}
|
|
3176
3269
|
}
|
|
3177
|
-
return {
|
|
3270
|
+
return {
|
|
3271
|
+
environment: env,
|
|
3272
|
+
claude,
|
|
3273
|
+
codex,
|
|
3274
|
+
opencode,
|
|
3275
|
+
brightsy,
|
|
3276
|
+
integrations,
|
|
3277
|
+
defaults,
|
|
3278
|
+
advanced
|
|
3279
|
+
};
|
|
3178
3280
|
}
|
|
3179
3281
|
function loadAppSettings() {
|
|
3180
3282
|
const path = appSettingsPath();
|
|
3181
|
-
if (!(0,
|
|
3283
|
+
if (!(0, import_node_fs6.existsSync)(path)) {
|
|
3182
3284
|
return { ...EMPTY_SETTINGS };
|
|
3183
3285
|
}
|
|
3184
3286
|
try {
|
|
3185
|
-
const parsed = JSON.parse((0,
|
|
3287
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
|
|
3186
3288
|
return normalizeSettings(parsed);
|
|
3187
3289
|
} catch {
|
|
3188
3290
|
return { ...EMPTY_SETTINGS };
|
|
@@ -3190,8 +3292,8 @@ function loadAppSettings() {
|
|
|
3190
3292
|
}
|
|
3191
3293
|
function saveAppSettings(settings) {
|
|
3192
3294
|
const normalized = normalizeSettings(settings);
|
|
3193
|
-
(0,
|
|
3194
|
-
(0,
|
|
3295
|
+
(0, import_node_fs6.mkdirSync)(appDataDir(), { recursive: true });
|
|
3296
|
+
(0, import_node_fs6.writeFileSync)(appSettingsPath(), `${JSON.stringify(normalized, null, 2)}
|
|
3195
3297
|
`, "utf8");
|
|
3196
3298
|
return normalized;
|
|
3197
3299
|
}
|
|
@@ -3227,6 +3329,13 @@ function updateClaudeSettings(patch) {
|
|
|
3227
3329
|
function updateBrightsySettings(patch) {
|
|
3228
3330
|
const current = loadAppSettings();
|
|
3229
3331
|
const brightsy = { ...current.brightsy };
|
|
3332
|
+
if ("executablePath" in patch) {
|
|
3333
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3334
|
+
delete brightsy.executablePath;
|
|
3335
|
+
} else {
|
|
3336
|
+
brightsy.executablePath = patch.executablePath.trim();
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3230
3339
|
if (typeof patch.cloudConnectEnabled === "boolean") {
|
|
3231
3340
|
brightsy.cloudConnectEnabled = patch.cloudConnectEnabled;
|
|
3232
3341
|
}
|
|
@@ -3237,8 +3346,35 @@ function updateBrightsySettings(patch) {
|
|
|
3237
3346
|
brightsy.cloudConnectAgent = patch.cloudConnectAgent;
|
|
3238
3347
|
}
|
|
3239
3348
|
}
|
|
3349
|
+
if (typeof patch.injectWorktreeMcp === "boolean") {
|
|
3350
|
+
brightsy.injectWorktreeMcp = patch.injectWorktreeMcp;
|
|
3351
|
+
}
|
|
3240
3352
|
return saveAppSettings({ ...current, brightsy });
|
|
3241
3353
|
}
|
|
3354
|
+
function updateCodexSettings(patch) {
|
|
3355
|
+
const current = loadAppSettings();
|
|
3356
|
+
const codex = { ...current.codex };
|
|
3357
|
+
if ("executablePath" in patch) {
|
|
3358
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3359
|
+
delete codex.executablePath;
|
|
3360
|
+
} else {
|
|
3361
|
+
codex.executablePath = patch.executablePath.trim();
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
return saveAppSettings({ ...current, codex });
|
|
3365
|
+
}
|
|
3366
|
+
function updateOpencodeSettings(patch) {
|
|
3367
|
+
const current = loadAppSettings();
|
|
3368
|
+
const opencode = { ...current.opencode };
|
|
3369
|
+
if ("executablePath" in patch) {
|
|
3370
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3371
|
+
delete opencode.executablePath;
|
|
3372
|
+
} else {
|
|
3373
|
+
opencode.executablePath = patch.executablePath.trim();
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
return saveAppSettings({ ...current, opencode });
|
|
3377
|
+
}
|
|
3242
3378
|
function updateIntegrationsSettings(patch) {
|
|
3243
3379
|
const current = loadAppSettings();
|
|
3244
3380
|
const integrations = { ...current.integrations };
|
|
@@ -3346,6 +3482,9 @@ function getLinearApiKey(settings = loadAppSettings()) {
|
|
|
3346
3482
|
function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
|
|
3347
3483
|
return Boolean(settings.brightsy.cloudConnectEnabled);
|
|
3348
3484
|
}
|
|
3485
|
+
function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
|
|
3486
|
+
return Boolean(settings.brightsy.injectWorktreeMcp);
|
|
3487
|
+
}
|
|
3349
3488
|
function brightsyCloudConnectAgent(settings = loadAppSettings()) {
|
|
3350
3489
|
const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
|
|
3351
3490
|
const preferred = getDefaultAgent(settings);
|
|
@@ -3440,8 +3579,36 @@ function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
|
3440
3579
|
return 3;
|
|
3441
3580
|
}
|
|
3442
3581
|
function resolveClaudeExecutable(settings = loadAppSettings()) {
|
|
3443
|
-
|
|
3444
|
-
|
|
3582
|
+
return resolveAgentExecutable("claude", settings);
|
|
3583
|
+
}
|
|
3584
|
+
function resolveAgentExecutable(agent, settings = loadAppSettings()) {
|
|
3585
|
+
const fallback = DEFAULT_CLI_BIN[agent];
|
|
3586
|
+
if (agent === "claude") {
|
|
3587
|
+
const override2 = settings.claude.executablePath?.trim();
|
|
3588
|
+
return override2 || fallback;
|
|
3589
|
+
}
|
|
3590
|
+
if (agent === "codex") {
|
|
3591
|
+
const override2 = settings.codex.executablePath?.trim();
|
|
3592
|
+
return override2 || fallback;
|
|
3593
|
+
}
|
|
3594
|
+
if (agent === "opencode") {
|
|
3595
|
+
const override2 = settings.opencode.executablePath?.trim();
|
|
3596
|
+
return override2 || fallback;
|
|
3597
|
+
}
|
|
3598
|
+
const override = settings.brightsy.executablePath?.trim();
|
|
3599
|
+
return override || fallback;
|
|
3600
|
+
}
|
|
3601
|
+
function updateAgentExecutable(agent, executablePath) {
|
|
3602
|
+
if (agent === "claude") {
|
|
3603
|
+
return updateClaudeSettings({ executablePath });
|
|
3604
|
+
}
|
|
3605
|
+
if (agent === "codex") {
|
|
3606
|
+
return updateCodexSettings({ executablePath });
|
|
3607
|
+
}
|
|
3608
|
+
if (agent === "opencode") {
|
|
3609
|
+
return updateOpencodeSettings({ executablePath });
|
|
3610
|
+
}
|
|
3611
|
+
return updateBrightsySettings({ executablePath });
|
|
3445
3612
|
}
|
|
3446
3613
|
function claudeChromeEnabled(settings = loadAppSettings()) {
|
|
3447
3614
|
return Boolean(settings.claude.chromeEnabled);
|
|
@@ -3469,11 +3636,11 @@ function childEnvWithAppSettings(extra) {
|
|
|
3469
3636
|
function harnessEnvKey(harness) {
|
|
3470
3637
|
return HARNESS_ENV_KEYS[harness];
|
|
3471
3638
|
}
|
|
3472
|
-
var
|
|
3639
|
+
var import_node_fs6, import_node_os4, import_node_path6, HARNESS_ENV_KEYS, DEFAULT_AGENTS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
|
|
3473
3640
|
var init_app_settings = __esm({
|
|
3474
3641
|
"src/store/app-settings.ts"() {
|
|
3475
3642
|
"use strict";
|
|
3476
|
-
|
|
3643
|
+
import_node_fs6 = require("fs");
|
|
3477
3644
|
import_node_os4 = require("os");
|
|
3478
3645
|
import_node_path6 = require("path");
|
|
3479
3646
|
init_thinking_effort();
|
|
@@ -3502,11 +3669,19 @@ var init_app_settings = __esm({
|
|
|
3502
3669
|
EMPTY_SETTINGS = {
|
|
3503
3670
|
environment: {},
|
|
3504
3671
|
claude: {},
|
|
3672
|
+
codex: {},
|
|
3673
|
+
opencode: {},
|
|
3505
3674
|
brightsy: {},
|
|
3506
3675
|
integrations: {},
|
|
3507
3676
|
defaults: {},
|
|
3508
3677
|
advanced: {}
|
|
3509
3678
|
};
|
|
3679
|
+
DEFAULT_CLI_BIN = {
|
|
3680
|
+
claude: "claude",
|
|
3681
|
+
codex: "codex",
|
|
3682
|
+
opencode: "opencode",
|
|
3683
|
+
brightsy: "brightsy"
|
|
3684
|
+
};
|
|
3510
3685
|
}
|
|
3511
3686
|
});
|
|
3512
3687
|
|
|
@@ -3632,7 +3807,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
3632
3807
|
function ensureGlobalCoordinatorCwd(opts) {
|
|
3633
3808
|
const dir = globalAgentCwd();
|
|
3634
3809
|
try {
|
|
3635
|
-
(0,
|
|
3810
|
+
(0, import_node_fs7.mkdirSync)(dir, { recursive: true });
|
|
3636
3811
|
} catch {
|
|
3637
3812
|
return dir;
|
|
3638
3813
|
}
|
|
@@ -3640,7 +3815,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3640
3815
|
let orchId = opts?.orchestratorThreadId?.trim() || "";
|
|
3641
3816
|
if (!orchId) {
|
|
3642
3817
|
try {
|
|
3643
|
-
const existing = (0,
|
|
3818
|
+
const existing = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
|
|
3644
3819
|
const m = existing.match(
|
|
3645
3820
|
/YOUR orchestration thread id is `([0-9a-f-]{36})`/i
|
|
3646
3821
|
);
|
|
@@ -3682,9 +3857,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3682
3857
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
3683
3858
|
].join("\n");
|
|
3684
3859
|
try {
|
|
3685
|
-
(0,
|
|
3860
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
|
|
3686
3861
|
`, "utf8");
|
|
3687
|
-
(0,
|
|
3862
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
|
|
3688
3863
|
`, "utf8");
|
|
3689
3864
|
} catch {
|
|
3690
3865
|
}
|
|
@@ -3717,11 +3892,11 @@ function coordinatorSystemPrompt(opts) {
|
|
|
3717
3892
|
formatWorkspaceInventory(opts.workspaces)
|
|
3718
3893
|
].join("\n");
|
|
3719
3894
|
}
|
|
3720
|
-
var
|
|
3895
|
+
var import_node_fs7, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
|
|
3721
3896
|
var init_coordinator_prompt = __esm({
|
|
3722
3897
|
"src/orchestrator/coordinator-prompt.ts"() {
|
|
3723
3898
|
"use strict";
|
|
3724
|
-
|
|
3899
|
+
import_node_fs7 = require("fs");
|
|
3725
3900
|
import_node_path7 = require("path");
|
|
3726
3901
|
init_worktree();
|
|
3727
3902
|
init_app_settings();
|
|
@@ -3947,26 +4122,26 @@ function brightsyConfigPath() {
|
|
|
3947
4122
|
}
|
|
3948
4123
|
function loadBrightsyConfig() {
|
|
3949
4124
|
const path = brightsyConfigPath();
|
|
3950
|
-
if (!(0,
|
|
4125
|
+
if (!(0, import_node_fs8.existsSync)(path)) {
|
|
3951
4126
|
throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
|
|
3952
4127
|
}
|
|
3953
|
-
const raw = JSON.parse((0,
|
|
4128
|
+
const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
|
|
3954
4129
|
if (!raw.access_token || !raw.account_id) {
|
|
3955
4130
|
throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
|
|
3956
4131
|
}
|
|
3957
4132
|
return raw;
|
|
3958
4133
|
}
|
|
3959
4134
|
function saveBrightsyConfig(cfg) {
|
|
3960
|
-
(0,
|
|
4135
|
+
(0, import_node_fs8.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
|
|
3961
4136
|
`, {
|
|
3962
4137
|
mode: 384
|
|
3963
4138
|
});
|
|
3964
4139
|
}
|
|
3965
|
-
var
|
|
4140
|
+
var import_node_fs8, import_node_os5, import_node_path8;
|
|
3966
4141
|
var init_config = __esm({
|
|
3967
4142
|
"src/brightsy/config.ts"() {
|
|
3968
4143
|
"use strict";
|
|
3969
|
-
|
|
4144
|
+
import_node_fs8 = require("fs");
|
|
3970
4145
|
import_node_os5 = require("os");
|
|
3971
4146
|
import_node_path8 = require("path");
|
|
3972
4147
|
}
|
|
@@ -3987,18 +4162,18 @@ function storePath() {
|
|
|
3987
4162
|
}
|
|
3988
4163
|
function readStore() {
|
|
3989
4164
|
const path = storePath();
|
|
3990
|
-
if (!(0,
|
|
4165
|
+
if (!(0, import_node_fs9.existsSync)(path)) return [];
|
|
3991
4166
|
try {
|
|
3992
|
-
const parsed = JSON.parse((0,
|
|
4167
|
+
const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
|
|
3993
4168
|
return Array.isArray(parsed.teams) ? parsed.teams : [];
|
|
3994
4169
|
} catch {
|
|
3995
4170
|
return [];
|
|
3996
4171
|
}
|
|
3997
4172
|
}
|
|
3998
4173
|
function writeStore(teams) {
|
|
3999
|
-
(0,
|
|
4174
|
+
(0, import_node_fs9.mkdirSync)(appDataDir(), { recursive: true });
|
|
4000
4175
|
const path = storePath();
|
|
4001
|
-
(0,
|
|
4176
|
+
(0, import_node_fs9.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
|
|
4002
4177
|
`, {
|
|
4003
4178
|
mode: 384
|
|
4004
4179
|
});
|
|
@@ -4110,11 +4285,11 @@ function brightsyMcpServerName(slug) {
|
|
|
4110
4285
|
const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
|
|
4111
4286
|
return `brightsy_${cleaned || "team"}`;
|
|
4112
4287
|
}
|
|
4113
|
-
var
|
|
4288
|
+
var import_node_fs9, import_node_path9;
|
|
4114
4289
|
var init_connected_teams = __esm({
|
|
4115
4290
|
"src/brightsy/connected-teams.ts"() {
|
|
4116
4291
|
"use strict";
|
|
4117
|
-
|
|
4292
|
+
import_node_fs9 = require("fs");
|
|
4118
4293
|
import_node_path9 = require("path");
|
|
4119
4294
|
init_paths();
|
|
4120
4295
|
init_accounts();
|
|
@@ -4162,6 +4337,11 @@ function normalizeTurnInput(input) {
|
|
|
4162
4337
|
if (typeof input === "string") return { prompt: input };
|
|
4163
4338
|
return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
|
|
4164
4339
|
}
|
|
4340
|
+
function dropCachedPrefixOnResume(input, sessionId) {
|
|
4341
|
+
const turn = normalizeTurnInput(input);
|
|
4342
|
+
if (sessionId) return { prompt: turn.prompt };
|
|
4343
|
+
return turn;
|
|
4344
|
+
}
|
|
4165
4345
|
function flattenTurnInput(input) {
|
|
4166
4346
|
const { cachedPrefix, prompt } = normalizeTurnInput(input);
|
|
4167
4347
|
if (!cachedPrefix) return prompt;
|
|
@@ -4306,9 +4486,13 @@ async function fetchTeamChatTargets(team) {
|
|
|
4306
4486
|
};
|
|
4307
4487
|
}
|
|
4308
4488
|
async function listBrightsyChatTargetsViaCli() {
|
|
4309
|
-
const listed = await run(
|
|
4310
|
-
|
|
4311
|
-
|
|
4489
|
+
const listed = await run(
|
|
4490
|
+
resolveAgentExecutable("brightsy"),
|
|
4491
|
+
["chat", "--list-targets", "--json"],
|
|
4492
|
+
{
|
|
4493
|
+
reject: false
|
|
4494
|
+
}
|
|
4495
|
+
);
|
|
4312
4496
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
4313
4497
|
throw new Error(
|
|
4314
4498
|
listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
|
|
@@ -4392,13 +4576,15 @@ async function syncCliForTarget(accountId) {
|
|
|
4392
4576
|
}
|
|
4393
4577
|
applyConnectedTeamToCli(team);
|
|
4394
4578
|
}
|
|
4395
|
-
var brightsyAdapter;
|
|
4579
|
+
var import_node_fs10, brightsyAdapter;
|
|
4396
4580
|
var init_brightsy = __esm({
|
|
4397
4581
|
"src/agents/brightsy.ts"() {
|
|
4398
4582
|
"use strict";
|
|
4583
|
+
import_node_fs10 = require("fs");
|
|
4399
4584
|
init_run();
|
|
4400
4585
|
init_connected_teams();
|
|
4401
4586
|
init_config();
|
|
4587
|
+
init_app_settings();
|
|
4402
4588
|
init_brightsy_targets();
|
|
4403
4589
|
init_error_detail();
|
|
4404
4590
|
init_turn_input();
|
|
@@ -4406,18 +4592,32 @@ var init_brightsy = __esm({
|
|
|
4406
4592
|
brightsyAdapter = {
|
|
4407
4593
|
kind: "brightsy",
|
|
4408
4594
|
async detect() {
|
|
4409
|
-
const
|
|
4410
|
-
if (
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4595
|
+
const brightsy = resolveAgentExecutable("brightsy");
|
|
4596
|
+
if (brightsy !== "brightsy") {
|
|
4597
|
+
if (!(0, import_node_fs10.existsSync)(brightsy)) {
|
|
4598
|
+
return {
|
|
4599
|
+
agent: "brightsy",
|
|
4600
|
+
installed: false,
|
|
4601
|
+
authenticated: false,
|
|
4602
|
+
linearMcp: false,
|
|
4603
|
+
warnings: [],
|
|
4604
|
+
reason: `Brightsy executable not found: ${brightsy}`
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
} else {
|
|
4608
|
+
const which = await run("which", ["brightsy"], { reject: false });
|
|
4609
|
+
if (which.exitCode !== 0) {
|
|
4610
|
+
return {
|
|
4611
|
+
agent: "brightsy",
|
|
4612
|
+
installed: false,
|
|
4613
|
+
authenticated: false,
|
|
4614
|
+
linearMcp: false,
|
|
4615
|
+
warnings: [],
|
|
4616
|
+
reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4419
4619
|
}
|
|
4420
|
-
const who = await run(
|
|
4620
|
+
const who = await run(brightsy, ["whoami"], { reject: false });
|
|
4421
4621
|
const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
|
|
4422
4622
|
return {
|
|
4423
4623
|
agent: "brightsy",
|
|
@@ -4442,7 +4642,7 @@ var init_brightsy = __esm({
|
|
|
4442
4642
|
target.id
|
|
4443
4643
|
];
|
|
4444
4644
|
return {
|
|
4445
|
-
file: "brightsy",
|
|
4645
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4446
4646
|
args,
|
|
4447
4647
|
cwd: thread.worktreePath,
|
|
4448
4648
|
// Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
|
|
@@ -4459,7 +4659,11 @@ var init_brightsy = __esm({
|
|
|
4459
4659
|
const target = decodeBrightsyTarget(thread.model);
|
|
4460
4660
|
await syncCliForTarget(target.accountId);
|
|
4461
4661
|
const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
|
|
4462
|
-
return {
|
|
4662
|
+
return {
|
|
4663
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4664
|
+
args,
|
|
4665
|
+
cwd: thread.worktreePath
|
|
4666
|
+
};
|
|
4463
4667
|
}
|
|
4464
4668
|
};
|
|
4465
4669
|
}
|
|
@@ -4504,6 +4708,18 @@ var init_claude_mcp = __esm({
|
|
|
4504
4708
|
}
|
|
4505
4709
|
});
|
|
4506
4710
|
|
|
4711
|
+
// src/mcp/profile.ts
|
|
4712
|
+
function sideboardMcpProfile(env = process.env) {
|
|
4713
|
+
return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
|
|
4714
|
+
}
|
|
4715
|
+
var SIDEBOARD_MCP_PROFILE_ENV;
|
|
4716
|
+
var init_profile = __esm({
|
|
4717
|
+
"src/mcp/profile.ts"() {
|
|
4718
|
+
"use strict";
|
|
4719
|
+
SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
|
|
4720
|
+
}
|
|
4721
|
+
});
|
|
4722
|
+
|
|
4507
4723
|
// src/agents/node-launch.ts
|
|
4508
4724
|
function isAsarPath(filePath) {
|
|
4509
4725
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
@@ -4551,6 +4767,38 @@ function isBrightsyConnected() {
|
|
|
4551
4767
|
return false;
|
|
4552
4768
|
}
|
|
4553
4769
|
}
|
|
4770
|
+
function promptMentionsBrightsy(text) {
|
|
4771
|
+
return BRIGHTSY_WORD.test(text ?? "");
|
|
4772
|
+
}
|
|
4773
|
+
function isBrightsyMcpToolName(name) {
|
|
4774
|
+
const n = name.toLowerCase();
|
|
4775
|
+
return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
|
|
4776
|
+
}
|
|
4777
|
+
function toolInputUsesBrightsyDatasource(input) {
|
|
4778
|
+
const ds = input?.datasource;
|
|
4779
|
+
return typeof ds === "string" && ds.toLowerCase() === "brightsy";
|
|
4780
|
+
}
|
|
4781
|
+
function messageUsedBrightsyMcp(msg) {
|
|
4782
|
+
if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
|
|
4783
|
+
for (const part of msg.parts ?? []) {
|
|
4784
|
+
if (part.type !== "tool") continue;
|
|
4785
|
+
if (isBrightsyMcpToolName(part.name)) return true;
|
|
4786
|
+
if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
|
|
4787
|
+
return true;
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
return false;
|
|
4791
|
+
}
|
|
4792
|
+
function threadRequestsBrightsyMcp(thread) {
|
|
4793
|
+
return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
|
|
4794
|
+
}
|
|
4795
|
+
function shouldInjectBrightsyMcp(thread, opts) {
|
|
4796
|
+
if (!(opts?.connected ?? isBrightsyConnected())) return false;
|
|
4797
|
+
if (opts?.orchestrator) return true;
|
|
4798
|
+
const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
|
|
4799
|
+
if (alwaysOn) return true;
|
|
4800
|
+
return threadRequestsBrightsyMcp(thread);
|
|
4801
|
+
}
|
|
4554
4802
|
function mcpLaunch(cmd, name, env) {
|
|
4555
4803
|
if (cmd === "brightsy-mcp") {
|
|
4556
4804
|
return { name, command: "brightsy-mcp", ...env ? { env } : {} };
|
|
@@ -4596,7 +4844,7 @@ function corePackageDir() {
|
|
|
4596
4844
|
}
|
|
4597
4845
|
function findSideboardMcpJsEntry() {
|
|
4598
4846
|
const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
|
|
4599
|
-
if (override && (0,
|
|
4847
|
+
if (override && (0, import_node_fs11.existsSync)(override)) return override;
|
|
4600
4848
|
let dir = corePackageDir();
|
|
4601
4849
|
for (let i = 0; i < 10; i++) {
|
|
4602
4850
|
const candidates = [
|
|
@@ -4609,7 +4857,7 @@ function findSideboardMcpJsEntry() {
|
|
|
4609
4857
|
(0, import_node_path10.join)(dir, "cli/dist/index.js")
|
|
4610
4858
|
];
|
|
4611
4859
|
for (const p of candidates) {
|
|
4612
|
-
if ((0,
|
|
4860
|
+
if ((0, import_node_fs11.existsSync)(p)) return p;
|
|
4613
4861
|
}
|
|
4614
4862
|
const parent = (0, import_node_path10.dirname)(dir);
|
|
4615
4863
|
if (parent === dir) break;
|
|
@@ -4640,11 +4888,13 @@ async function buildInjectedMcpServers(opts) {
|
|
|
4640
4888
|
const servers = [];
|
|
4641
4889
|
if (opts.includeSideboard) {
|
|
4642
4890
|
const sideboard = await resolveSideboardMcpServer();
|
|
4891
|
+
const orchId = opts.orchestratorThreadId?.trim();
|
|
4892
|
+
const profile = orchId ? "orchestration" : "worktree";
|
|
4643
4893
|
sideboard.env = {
|
|
4644
4894
|
...sideboard.env ?? {},
|
|
4645
|
-
SIDEBOARD_APP_DATA: appDataDir()
|
|
4895
|
+
SIDEBOARD_APP_DATA: appDataDir(),
|
|
4896
|
+
[SIDEBOARD_MCP_PROFILE_ENV]: profile
|
|
4646
4897
|
};
|
|
4647
|
-
const orchId = opts.orchestratorThreadId?.trim();
|
|
4648
4898
|
if (orchId) {
|
|
4649
4899
|
sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
|
|
4650
4900
|
}
|
|
@@ -4719,16 +4969,16 @@ function writeMcpServersConfig(servers) {
|
|
|
4719
4969
|
...s.env ? { env: s.env } : {}
|
|
4720
4970
|
};
|
|
4721
4971
|
}
|
|
4722
|
-
const dir = (0,
|
|
4972
|
+
const dir = (0, import_node_fs11.mkdtempSync)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
|
|
4723
4973
|
const cfgPath = (0, import_node_path10.join)(dir, "mcp.json");
|
|
4724
|
-
(0,
|
|
4974
|
+
(0, import_node_fs11.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
4725
4975
|
return cfgPath;
|
|
4726
4976
|
}
|
|
4727
|
-
var
|
|
4977
|
+
var import_node_fs11, import_node_module, import_node_os6, import_node_path10, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
|
|
4728
4978
|
var init_injected_mcp = __esm({
|
|
4729
4979
|
"src/agents/injected-mcp.ts"() {
|
|
4730
4980
|
"use strict";
|
|
4731
|
-
|
|
4981
|
+
import_node_fs11 = require("fs");
|
|
4732
4982
|
import_node_module = require("module");
|
|
4733
4983
|
import_node_os6 = require("os");
|
|
4734
4984
|
import_node_path10 = require("path");
|
|
@@ -4736,6 +4986,8 @@ var init_injected_mcp = __esm({
|
|
|
4736
4986
|
init_run();
|
|
4737
4987
|
init_config();
|
|
4738
4988
|
init_connected_teams();
|
|
4989
|
+
init_profile();
|
|
4990
|
+
init_app_settings();
|
|
4739
4991
|
init_paths();
|
|
4740
4992
|
init_node_launch();
|
|
4741
4993
|
import_meta = {};
|
|
@@ -4751,6 +5003,7 @@ var init_injected_mcp = __esm({
|
|
|
4751
5003
|
"mcp__sideboard__present_plan"
|
|
4752
5004
|
];
|
|
4753
5005
|
brightsyMcpCommandCache = null;
|
|
5006
|
+
BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
|
|
4754
5007
|
}
|
|
4755
5008
|
});
|
|
4756
5009
|
|
|
@@ -4905,11 +5158,11 @@ function parseIssuesJson(raw) {
|
|
|
4905
5158
|
}
|
|
4906
5159
|
return [];
|
|
4907
5160
|
}
|
|
4908
|
-
var
|
|
5161
|
+
var import_node_fs12, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
|
|
4909
5162
|
var init_claude = __esm({
|
|
4910
5163
|
"src/agents/claude.ts"() {
|
|
4911
5164
|
"use strict";
|
|
4912
|
-
|
|
5165
|
+
import_node_fs12 = require("fs");
|
|
4913
5166
|
init_run();
|
|
4914
5167
|
init_app_settings();
|
|
4915
5168
|
init_claude_mcp();
|
|
@@ -4938,7 +5191,7 @@ var init_claude = __esm({
|
|
|
4938
5191
|
async detect() {
|
|
4939
5192
|
const claude = resolveClaudeExecutable();
|
|
4940
5193
|
if (claude !== "claude") {
|
|
4941
|
-
if (!(0,
|
|
5194
|
+
if (!(0, import_node_fs12.existsSync)(claude)) {
|
|
4942
5195
|
return {
|
|
4943
5196
|
agent: "claude",
|
|
4944
5197
|
installed: false,
|
|
@@ -4977,9 +5230,8 @@ var init_claude = __esm({
|
|
|
4977
5230
|
},
|
|
4978
5231
|
async buildTurn(thread, input) {
|
|
4979
5232
|
const claude = resolveClaudeExecutable();
|
|
4980
|
-
const turn = normalizeTurnInput(input);
|
|
4981
5233
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
4982
|
-
const effective = sessionId
|
|
5234
|
+
const effective = dropCachedPrefixOnResume(input, sessionId);
|
|
4983
5235
|
const promptText = flattenTurnInput(effective);
|
|
4984
5236
|
const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
|
|
4985
5237
|
if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
|
|
@@ -4992,7 +5244,9 @@ var init_claude = __esm({
|
|
|
4992
5244
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
4993
5245
|
const injectedServers = await buildInjectedMcpServers({
|
|
4994
5246
|
includeSideboard: true,
|
|
4995
|
-
includeBrightsy:
|
|
5247
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5248
|
+
orchestrator: isOrchestrator
|
|
5249
|
+
}),
|
|
4996
5250
|
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
4997
5251
|
});
|
|
4998
5252
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
@@ -5166,9 +5420,14 @@ async function listCodexModels() {
|
|
|
5166
5420
|
if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
|
|
5167
5421
|
return cachedCodexModels.models;
|
|
5168
5422
|
}
|
|
5169
|
-
const
|
|
5170
|
-
if (
|
|
5171
|
-
|
|
5423
|
+
const codex = resolveAgentExecutable("codex");
|
|
5424
|
+
if (codex === "codex") {
|
|
5425
|
+
const which = await run("which", ["codex"], { reject: false });
|
|
5426
|
+
if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
|
|
5427
|
+
} else if (!(0, import_node_fs13.existsSync)(codex)) {
|
|
5428
|
+
return FALLBACK_CODEX_MODELS;
|
|
5429
|
+
}
|
|
5430
|
+
const listed = await run(codex, ["debug", "models"], { reject: false });
|
|
5172
5431
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5173
5432
|
return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
|
|
5174
5433
|
}
|
|
@@ -5207,8 +5466,8 @@ function codexConfigHasNetworkAccess() {
|
|
|
5207
5466
|
(0, import_node_path11.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
|
|
5208
5467
|
];
|
|
5209
5468
|
for (const path of candidates) {
|
|
5210
|
-
if (!(0,
|
|
5211
|
-
const text = (0,
|
|
5469
|
+
if (!(0, import_node_fs13.existsSync)(path)) continue;
|
|
5470
|
+
const text = (0, import_node_fs13.readFileSync)(path, "utf8");
|
|
5212
5471
|
if (/network_access\s*=\s*true/.test(text)) return true;
|
|
5213
5472
|
}
|
|
5214
5473
|
return false;
|
|
@@ -5241,21 +5500,23 @@ function asRecord(value) {
|
|
|
5241
5500
|
}
|
|
5242
5501
|
function codexLooksAuthenticated() {
|
|
5243
5502
|
const authPath = (0, import_node_path11.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
|
|
5244
|
-
if (!(0,
|
|
5503
|
+
if (!(0, import_node_fs13.existsSync)(authPath)) return false;
|
|
5245
5504
|
try {
|
|
5246
|
-
return (0,
|
|
5505
|
+
return (0, import_node_fs13.statSync)(authPath).size > 2;
|
|
5247
5506
|
} catch {
|
|
5248
5507
|
return false;
|
|
5249
5508
|
}
|
|
5250
5509
|
}
|
|
5251
|
-
var
|
|
5510
|
+
var import_node_fs13, import_node_os7, import_node_path11, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
|
|
5252
5511
|
var init_codex = __esm({
|
|
5253
5512
|
"src/agents/codex.ts"() {
|
|
5254
5513
|
"use strict";
|
|
5255
|
-
|
|
5514
|
+
import_node_fs13 = require("fs");
|
|
5256
5515
|
import_node_os7 = require("os");
|
|
5257
5516
|
import_node_path11 = require("path");
|
|
5258
5517
|
init_run();
|
|
5518
|
+
init_app_settings();
|
|
5519
|
+
init_global_workspace();
|
|
5259
5520
|
init_error_detail();
|
|
5260
5521
|
init_injected_mcp();
|
|
5261
5522
|
init_turn_input();
|
|
@@ -5273,16 +5534,30 @@ var init_codex = __esm({
|
|
|
5273
5534
|
codexAdapter = {
|
|
5274
5535
|
kind: "codex",
|
|
5275
5536
|
async detect() {
|
|
5276
|
-
const
|
|
5277
|
-
if (
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5537
|
+
const codex = resolveAgentExecutable("codex");
|
|
5538
|
+
if (codex !== "codex") {
|
|
5539
|
+
if (!(0, import_node_fs13.existsSync)(codex)) {
|
|
5540
|
+
return {
|
|
5541
|
+
agent: "codex",
|
|
5542
|
+
installed: false,
|
|
5543
|
+
authenticated: false,
|
|
5544
|
+
linearMcp: false,
|
|
5545
|
+
warnings: [],
|
|
5546
|
+
reason: `Codex executable not found: ${codex}`
|
|
5547
|
+
};
|
|
5548
|
+
}
|
|
5549
|
+
} else {
|
|
5550
|
+
const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
|
|
5551
|
+
if (which.exitCode !== 0) {
|
|
5552
|
+
return {
|
|
5553
|
+
agent: "codex",
|
|
5554
|
+
installed: false,
|
|
5555
|
+
authenticated: false,
|
|
5556
|
+
linearMcp: false,
|
|
5557
|
+
warnings: [],
|
|
5558
|
+
reason: "codex CLI not found on PATH"
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
5286
5561
|
}
|
|
5287
5562
|
const authenticated = codexLooksAuthenticated();
|
|
5288
5563
|
const linearMcp = false;
|
|
@@ -5302,22 +5577,24 @@ var init_codex = __esm({
|
|
|
5302
5577
|
};
|
|
5303
5578
|
},
|
|
5304
5579
|
async buildTurn(thread, input) {
|
|
5305
|
-
const prompt = flattenTurnInput(input);
|
|
5306
5580
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5581
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5307
5582
|
const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
|
|
5308
5583
|
const promptArg = useStdin ? "-" : prompt;
|
|
5309
5584
|
if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
|
|
5310
|
-
const { cachedPrefix } = normalizeTurnInput(input);
|
|
5311
5585
|
console.error(
|
|
5312
|
-
`[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${
|
|
5586
|
+
`[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${prompt.includes("Current request:")}`
|
|
5313
5587
|
);
|
|
5314
5588
|
}
|
|
5315
5589
|
const mode = permissionMode(thread);
|
|
5316
5590
|
const model = thread.model?.trim();
|
|
5591
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5317
5592
|
const injected = await buildInjectedMcpServers({
|
|
5318
5593
|
includeSideboard: true,
|
|
5319
|
-
includeBrightsy:
|
|
5320
|
-
|
|
5594
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5595
|
+
orchestrator: isOrchestrator
|
|
5596
|
+
}),
|
|
5597
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5321
5598
|
});
|
|
5322
5599
|
const mcpOverrides = toCodexMcpConfigArgs(injected);
|
|
5323
5600
|
const execOpts = [
|
|
@@ -5337,7 +5614,7 @@ var init_codex = __esm({
|
|
|
5337
5614
|
];
|
|
5338
5615
|
const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
|
|
5339
5616
|
return {
|
|
5340
|
-
file: "codex",
|
|
5617
|
+
file: resolveAgentExecutable("codex"),
|
|
5341
5618
|
args,
|
|
5342
5619
|
cwd: thread.worktreePath,
|
|
5343
5620
|
stdin: useStdin ? `${prompt}
|
|
@@ -5433,16 +5710,17 @@ var init_codex = __esm({
|
|
|
5433
5710
|
return cached;
|
|
5434
5711
|
},
|
|
5435
5712
|
async buildAttach(thread) {
|
|
5713
|
+
const codex = resolveAgentExecutable("codex");
|
|
5436
5714
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5437
5715
|
if (sessionId) {
|
|
5438
5716
|
return {
|
|
5439
|
-
file:
|
|
5717
|
+
file: codex,
|
|
5440
5718
|
args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
|
|
5441
5719
|
cwd: thread.worktreePath
|
|
5442
5720
|
};
|
|
5443
5721
|
}
|
|
5444
5722
|
return {
|
|
5445
|
-
file:
|
|
5723
|
+
file: codex,
|
|
5446
5724
|
args: ["--cd", thread.worktreePath],
|
|
5447
5725
|
cwd: thread.worktreePath
|
|
5448
5726
|
};
|
|
@@ -5450,7 +5728,7 @@ var init_codex = __esm({
|
|
|
5450
5728
|
async listLinearIssues(_repoPath) {
|
|
5451
5729
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
5452
5730
|
const { stdout, exitCode } = await run(
|
|
5453
|
-
"codex",
|
|
5731
|
+
resolveAgentExecutable("codex"),
|
|
5454
5732
|
[
|
|
5455
5733
|
"exec",
|
|
5456
5734
|
"--json",
|
|
@@ -5713,21 +5991,22 @@ function cursorRunnerPath() {
|
|
|
5713
5991
|
(0, import_node_path12.join)(root, "src", "agents", "cursor-runner.ts")
|
|
5714
5992
|
];
|
|
5715
5993
|
for (const candidate of candidates) {
|
|
5716
|
-
if ((0,
|
|
5994
|
+
if ((0, import_node_fs14.existsSync)(candidate)) return candidate;
|
|
5717
5995
|
}
|
|
5718
5996
|
return candidates[0];
|
|
5719
5997
|
}
|
|
5720
|
-
var
|
|
5998
|
+
var import_node_fs14, import_node_module2, import_node_path12, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
|
|
5721
5999
|
var init_cursor = __esm({
|
|
5722
6000
|
"src/agents/cursor.ts"() {
|
|
5723
6001
|
"use strict";
|
|
5724
|
-
|
|
6002
|
+
import_node_fs14 = require("fs");
|
|
5725
6003
|
import_node_module2 = require("module");
|
|
5726
6004
|
import_node_path12 = require("path");
|
|
5727
6005
|
import_node_url2 = require("url");
|
|
5728
6006
|
import_sdk = require("@cursor/sdk");
|
|
5729
6007
|
init_run();
|
|
5730
6008
|
init_app_settings();
|
|
6009
|
+
init_global_workspace();
|
|
5731
6010
|
init_cursor_events();
|
|
5732
6011
|
init_injected_mcp();
|
|
5733
6012
|
init_node_launch();
|
|
@@ -5764,13 +6043,16 @@ var init_cursor = __esm({
|
|
|
5764
6043
|
};
|
|
5765
6044
|
},
|
|
5766
6045
|
async buildTurn(thread, input) {
|
|
5767
|
-
const prompt = flattenTurnInput(input);
|
|
5768
6046
|
const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6047
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
|
|
5769
6048
|
const apiKey = resolveCursorApiKey() || void 0;
|
|
6049
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5770
6050
|
const injected = await buildInjectedMcpServers({
|
|
5771
6051
|
includeSideboard: true,
|
|
5772
|
-
includeBrightsy:
|
|
5773
|
-
|
|
6052
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6053
|
+
orchestrator: isOrchestrator
|
|
6054
|
+
}),
|
|
6055
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5774
6056
|
});
|
|
5775
6057
|
const mcpServers = toCursorMcpServers(injected);
|
|
5776
6058
|
const req = {
|
|
@@ -5841,9 +6123,14 @@ async function listOpencodeModels() {
|
|
|
5841
6123
|
if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
|
|
5842
6124
|
return cachedOpencodeModels.models;
|
|
5843
6125
|
}
|
|
5844
|
-
const
|
|
5845
|
-
if (
|
|
5846
|
-
|
|
6126
|
+
const opencode = resolveAgentExecutable("opencode");
|
|
6127
|
+
if (opencode === "opencode") {
|
|
6128
|
+
const which = await run("which", ["opencode"], { reject: false });
|
|
6129
|
+
if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
|
|
6130
|
+
} else if (!(0, import_node_fs15.existsSync)(opencode)) {
|
|
6131
|
+
return FALLBACK_OPENCODE_MODELS;
|
|
6132
|
+
}
|
|
6133
|
+
const listed = await run(opencode, ["models"], { reject: false });
|
|
5847
6134
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5848
6135
|
return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
|
|
5849
6136
|
}
|
|
@@ -5870,11 +6157,14 @@ function usageFromOpencode(tokens) {
|
|
|
5870
6157
|
cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
|
|
5871
6158
|
};
|
|
5872
6159
|
}
|
|
5873
|
-
var FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
6160
|
+
var import_node_fs15, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
5874
6161
|
var init_opencode = __esm({
|
|
5875
6162
|
"src/agents/opencode.ts"() {
|
|
5876
6163
|
"use strict";
|
|
6164
|
+
import_node_fs15 = require("fs");
|
|
5877
6165
|
init_run();
|
|
6166
|
+
init_app_settings();
|
|
6167
|
+
init_global_workspace();
|
|
5878
6168
|
init_error_detail();
|
|
5879
6169
|
init_injected_mcp();
|
|
5880
6170
|
init_turn_input();
|
|
@@ -5895,20 +6185,34 @@ var init_opencode = __esm({
|
|
|
5895
6185
|
opencodeAdapter = {
|
|
5896
6186
|
kind: "opencode",
|
|
5897
6187
|
async detect() {
|
|
5898
|
-
const
|
|
5899
|
-
if (
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
6188
|
+
const opencode = resolveAgentExecutable("opencode");
|
|
6189
|
+
if (opencode !== "opencode") {
|
|
6190
|
+
if (!(0, import_node_fs15.existsSync)(opencode)) {
|
|
6191
|
+
return {
|
|
6192
|
+
agent: "opencode",
|
|
6193
|
+
installed: false,
|
|
6194
|
+
authenticated: false,
|
|
6195
|
+
linearMcp: false,
|
|
6196
|
+
warnings: [],
|
|
6197
|
+
reason: `OpenCode executable not found: ${opencode}`
|
|
6198
|
+
};
|
|
6199
|
+
}
|
|
6200
|
+
} else {
|
|
6201
|
+
const which = await run("which", ["opencode"], { reject: false });
|
|
6202
|
+
if (which.exitCode !== 0) {
|
|
6203
|
+
return {
|
|
6204
|
+
agent: "opencode",
|
|
6205
|
+
installed: false,
|
|
6206
|
+
authenticated: false,
|
|
6207
|
+
linearMcp: false,
|
|
6208
|
+
warnings: [],
|
|
6209
|
+
reason: "opencode CLI not found on PATH"
|
|
6210
|
+
};
|
|
6211
|
+
}
|
|
5908
6212
|
}
|
|
5909
|
-
const auth = await run(
|
|
6213
|
+
const auth = await run(opencode, ["auth", "list"], { reject: false });
|
|
5910
6214
|
const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
|
|
5911
|
-
const mcp = await run(
|
|
6215
|
+
const mcp = await run(opencode, ["mcp", "list"], { reject: false });
|
|
5912
6216
|
const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
|
|
5913
6217
|
return {
|
|
5914
6218
|
agent: "opencode",
|
|
@@ -5920,8 +6224,8 @@ var init_opencode = __esm({
|
|
|
5920
6224
|
};
|
|
5921
6225
|
},
|
|
5922
6226
|
async buildTurn(thread, input) {
|
|
5923
|
-
const prompt = flattenTurnInput(input);
|
|
5924
6227
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6228
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5925
6229
|
const mode = permissionMode(thread);
|
|
5926
6230
|
const model = thread.model?.trim();
|
|
5927
6231
|
const args = [
|
|
@@ -5937,14 +6241,17 @@ var init_opencode = __esm({
|
|
|
5937
6241
|
if (model) {
|
|
5938
6242
|
args.push("--model", model);
|
|
5939
6243
|
}
|
|
6244
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5940
6245
|
const injected = await buildInjectedMcpServers({
|
|
5941
6246
|
includeSideboard: true,
|
|
5942
|
-
includeBrightsy:
|
|
5943
|
-
|
|
6247
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6248
|
+
orchestrator: isOrchestrator
|
|
6249
|
+
}),
|
|
6250
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5944
6251
|
});
|
|
5945
6252
|
const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
|
|
5946
6253
|
return {
|
|
5947
|
-
file: "opencode",
|
|
6254
|
+
file: resolveAgentExecutable("opencode"),
|
|
5948
6255
|
args,
|
|
5949
6256
|
cwd: thread.worktreePath,
|
|
5950
6257
|
// `opencode run` treats non-TTY stdin as the message body when no positional
|
|
@@ -6020,7 +6327,7 @@ var init_opencode = __esm({
|
|
|
6020
6327
|
async resolveSessionId(worktreePath, cached) {
|
|
6021
6328
|
const cachedId = cached?.trim() || null;
|
|
6022
6329
|
const listed = await run(
|
|
6023
|
-
"opencode",
|
|
6330
|
+
resolveAgentExecutable("opencode"),
|
|
6024
6331
|
["session", "list", "--format", "json"],
|
|
6025
6332
|
{ cwd: worktreePath, reject: false }
|
|
6026
6333
|
);
|
|
@@ -6051,7 +6358,7 @@ var init_opencode = __esm({
|
|
|
6051
6358
|
const args = ["--dir", thread.worktreePath];
|
|
6052
6359
|
if (sessionId) args.push("--session", sessionId);
|
|
6053
6360
|
return {
|
|
6054
|
-
file: "opencode",
|
|
6361
|
+
file: resolveAgentExecutable("opencode"),
|
|
6055
6362
|
args,
|
|
6056
6363
|
cwd: thread.worktreePath,
|
|
6057
6364
|
env: {
|
|
@@ -6062,7 +6369,7 @@ var init_opencode = __esm({
|
|
|
6062
6369
|
async listLinearIssues(_repoPath) {
|
|
6063
6370
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
6064
6371
|
const { stdout, exitCode } = await run(
|
|
6065
|
-
"opencode",
|
|
6372
|
+
resolveAgentExecutable("opencode"),
|
|
6066
6373
|
["run", prompt, "--format", "json"],
|
|
6067
6374
|
{
|
|
6068
6375
|
reject: false,
|
|
@@ -6295,9 +6602,30 @@ function getAgentSetupInfo(agent) {
|
|
|
6295
6602
|
function listAgentSetupInfo() {
|
|
6296
6603
|
return Object.values(SETUP);
|
|
6297
6604
|
}
|
|
6298
|
-
|
|
6299
|
-
|
|
6605
|
+
function resolveLoginCommand(agent) {
|
|
6606
|
+
const info = getAgentSetupInfo(agent);
|
|
6607
|
+
if (!info.loginCommand) return null;
|
|
6608
|
+
if (!CLI_LOGIN_AGENTS.has(agent)) return info.loginCommand;
|
|
6609
|
+
const exe = resolveAgentExecutable(agent);
|
|
6610
|
+
return info.loginCommand.replace(/^\S+/, exe);
|
|
6611
|
+
}
|
|
6612
|
+
async function prepareTerminalCommand(command) {
|
|
6613
|
+
enrichPathWithNpmGlobalBin();
|
|
6300
6614
|
const trimmed = command.trim();
|
|
6615
|
+
if (!trimmed) return trimmed;
|
|
6616
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
6617
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
6618
|
+
const bin = match?.[1];
|
|
6619
|
+
let whichPath = null;
|
|
6620
|
+
if (bin && !bin.includes("/") && !bin.includes("\\")) {
|
|
6621
|
+
const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
|
|
6622
|
+
whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
|
|
6623
|
+
}
|
|
6624
|
+
const resolved = resolveCommandBinarySync(trimmed, whichPath);
|
|
6625
|
+
return withExportedPath(resolved, process.env.PATH ?? "");
|
|
6626
|
+
}
|
|
6627
|
+
async function openInSystemTerminal(command) {
|
|
6628
|
+
const trimmed = (await prepareTerminalCommand(command)).trim();
|
|
6301
6629
|
if (!trimmed) throw new Error("Command is empty");
|
|
6302
6630
|
if (process.platform === "darwin") {
|
|
6303
6631
|
const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -6338,6 +6666,11 @@ async function openInSystemTerminal(command) {
|
|
|
6338
6666
|
`No terminal found to run: ${trimmed}. Install gnome-terminal (or similar), or run the command manually.`
|
|
6339
6667
|
);
|
|
6340
6668
|
}
|
|
6669
|
+
async function whichCli(bin) {
|
|
6670
|
+
const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
|
|
6671
|
+
if (which.exitCode !== 0) return null;
|
|
6672
|
+
return which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null;
|
|
6673
|
+
}
|
|
6341
6674
|
async function installAgent(agent) {
|
|
6342
6675
|
const info = getAgentSetupInfo(agent);
|
|
6343
6676
|
if (info.kind === "bundled-sdk" || info.kind === "api-key") {
|
|
@@ -6358,7 +6691,18 @@ async function installAgent(agent) {
|
|
|
6358
6691
|
message: `Opened Terminal to run: ${info.installCommand}`
|
|
6359
6692
|
};
|
|
6360
6693
|
}
|
|
6361
|
-
|
|
6694
|
+
enrichPathWithNpmGlobalBin();
|
|
6695
|
+
const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
|
|
6696
|
+
if (cliBin) {
|
|
6697
|
+
const existing = await whichCli(cliBin);
|
|
6698
|
+
if (existing) {
|
|
6699
|
+
return {
|
|
6700
|
+
ok: true,
|
|
6701
|
+
command: existing,
|
|
6702
|
+
message: isConductorBundledCli(existing) ? `Using Conductor\u2019s ${cliBin} at ${existing}. No extra install \u2014 Log in only if auth is missing.` : `Already on PATH: ${existing}`
|
|
6703
|
+
};
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6362
6706
|
const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
|
|
6363
6707
|
const ok = result.exitCode === 0;
|
|
6364
6708
|
if (!ok) {
|
|
@@ -6384,36 +6728,44 @@ async function installAgent(agent) {
|
|
|
6384
6728
|
};
|
|
6385
6729
|
}
|
|
6386
6730
|
}
|
|
6731
|
+
enrichPathWithNpmGlobalBin();
|
|
6732
|
+
let binPath = null;
|
|
6733
|
+
if (cliBin) {
|
|
6734
|
+
binPath = await whichCli(cliBin);
|
|
6735
|
+
}
|
|
6387
6736
|
return {
|
|
6388
6737
|
ok: true,
|
|
6389
6738
|
command: info.installCommand,
|
|
6390
6739
|
exitCode: 0,
|
|
6391
6740
|
stdout: result.stdout,
|
|
6392
6741
|
stderr: result.stderr,
|
|
6393
|
-
message: `Installed ${info.npmPackage}`
|
|
6742
|
+
message: binPath ? `Installed ${info.npmPackage} \u2192 ${binPath}` : `Installed ${info.npmPackage} (CLI not yet on PATH \u2014 Log in still exports Electron\u2019s PATH into Terminal)`
|
|
6394
6743
|
};
|
|
6395
6744
|
}
|
|
6396
6745
|
async function loginAgent(agent) {
|
|
6397
6746
|
const info = getAgentSetupInfo(agent);
|
|
6398
|
-
|
|
6747
|
+
const loginCommand = resolveLoginCommand(agent);
|
|
6748
|
+
if (!loginCommand) {
|
|
6399
6749
|
return {
|
|
6400
6750
|
ok: true,
|
|
6401
6751
|
message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
|
|
6402
6752
|
};
|
|
6403
6753
|
}
|
|
6404
|
-
await
|
|
6754
|
+
const prepared = await prepareTerminalCommand(loginCommand);
|
|
6755
|
+
await openInSystemTerminal(prepared);
|
|
6405
6756
|
return {
|
|
6406
6757
|
ok: true,
|
|
6407
6758
|
openedTerminal: true,
|
|
6408
|
-
command:
|
|
6409
|
-
message: `Opened Terminal to run: ${
|
|
6759
|
+
command: prepared,
|
|
6760
|
+
message: `Opened Terminal to run: ${prepared}`
|
|
6410
6761
|
};
|
|
6411
6762
|
}
|
|
6412
|
-
var SETUP;
|
|
6763
|
+
var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
|
|
6413
6764
|
var init_install = __esm({
|
|
6414
6765
|
"src/agents/install.ts"() {
|
|
6415
6766
|
"use strict";
|
|
6416
6767
|
init_run();
|
|
6768
|
+
init_app_settings();
|
|
6417
6769
|
init_path();
|
|
6418
6770
|
SETUP = {
|
|
6419
6771
|
claude: {
|
|
@@ -6462,6 +6814,18 @@ var init_install = __esm({
|
|
|
6462
6814
|
npmPackage: "@brightsy/cli"
|
|
6463
6815
|
}
|
|
6464
6816
|
};
|
|
6817
|
+
CLI_BIN = {
|
|
6818
|
+
claude: "claude",
|
|
6819
|
+
codex: "codex",
|
|
6820
|
+
opencode: "opencode",
|
|
6821
|
+
brightsy: "brightsy"
|
|
6822
|
+
};
|
|
6823
|
+
CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
|
|
6824
|
+
"claude",
|
|
6825
|
+
"codex",
|
|
6826
|
+
"opencode",
|
|
6827
|
+
"brightsy"
|
|
6828
|
+
]);
|
|
6465
6829
|
}
|
|
6466
6830
|
});
|
|
6467
6831
|
|
|
@@ -6477,14 +6841,17 @@ __export(agents_exports, {
|
|
|
6477
6841
|
claudeAdapter: () => claudeAdapter,
|
|
6478
6842
|
codexAdapter: () => codexAdapter,
|
|
6479
6843
|
coerceOrchestratorAgent: () => coerceOrchestratorAgent,
|
|
6844
|
+
conductorBundledBinDir: () => conductorBundledBinDir,
|
|
6480
6845
|
cursorAdapter: () => cursorAdapter,
|
|
6481
6846
|
cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
|
|
6482
6847
|
decodeBrightsyTarget: () => decodeBrightsyTarget,
|
|
6483
6848
|
encodeBrightsyTarget: () => encodeBrightsyTarget,
|
|
6849
|
+
enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
|
|
6484
6850
|
ensureAgentPath: () => ensureAgentPath,
|
|
6485
6851
|
getAdapter: () => getAdapter,
|
|
6486
6852
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
6487
6853
|
installAgent: () => installAgent,
|
|
6854
|
+
isConductorBundledCli: () => isConductorBundledCli,
|
|
6488
6855
|
isCursorAutoModel: () => isCursorAutoModel,
|
|
6489
6856
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6490
6857
|
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
@@ -6500,8 +6867,13 @@ __export(agents_exports, {
|
|
|
6500
6867
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6501
6868
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6502
6869
|
permissionMode: () => permissionMode,
|
|
6870
|
+
posixShellSingleQuote: () => posixShellSingleQuote,
|
|
6871
|
+
prepareTerminalCommand: () => prepareTerminalCommand,
|
|
6872
|
+
resolveCommandBinarySync: () => resolveCommandBinarySync,
|
|
6503
6873
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
6504
|
-
|
|
6874
|
+
resolveLoginCommand: () => resolveLoginCommand,
|
|
6875
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6876
|
+
withExportedPath: () => withExportedPath
|
|
6505
6877
|
});
|
|
6506
6878
|
function getAdapter(kind) {
|
|
6507
6879
|
return adapters[kind];
|
|
@@ -6559,31 +6931,31 @@ function removedWorkspacesFile() {
|
|
|
6559
6931
|
}
|
|
6560
6932
|
function readAll() {
|
|
6561
6933
|
const path = workspacesFile();
|
|
6562
|
-
if (!(0,
|
|
6934
|
+
if (!(0, import_node_fs19.existsSync)(path)) return [];
|
|
6563
6935
|
try {
|
|
6564
|
-
const raw = JSON.parse((0,
|
|
6936
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6565
6937
|
return Array.isArray(raw) ? raw : [];
|
|
6566
6938
|
} catch {
|
|
6567
6939
|
return [];
|
|
6568
6940
|
}
|
|
6569
6941
|
}
|
|
6570
6942
|
function writeAll(list) {
|
|
6571
|
-
(0,
|
|
6572
|
-
(0,
|
|
6943
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6944
|
+
(0, import_node_fs19.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
|
|
6573
6945
|
}
|
|
6574
6946
|
function readRemoved() {
|
|
6575
6947
|
const path = removedWorkspacesFile();
|
|
6576
|
-
if (!(0,
|
|
6948
|
+
if (!(0, import_node_fs19.existsSync)(path)) return /* @__PURE__ */ new Set();
|
|
6577
6949
|
try {
|
|
6578
|
-
const raw = JSON.parse((0,
|
|
6950
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6579
6951
|
return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
|
|
6580
6952
|
} catch {
|
|
6581
6953
|
return /* @__PURE__ */ new Set();
|
|
6582
6954
|
}
|
|
6583
6955
|
}
|
|
6584
6956
|
function writeRemoved(paths) {
|
|
6585
|
-
(0,
|
|
6586
|
-
(0,
|
|
6957
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6958
|
+
(0, import_node_fs19.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
|
|
6587
6959
|
}
|
|
6588
6960
|
function rememberRemoved(repoPath) {
|
|
6589
6961
|
const next = readRemoved();
|
|
@@ -6606,7 +6978,7 @@ function listWorkspaces() {
|
|
|
6606
6978
|
async function addWorkspace(repoPath) {
|
|
6607
6979
|
const root = await resolveRepoRoot(repoPath);
|
|
6608
6980
|
if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
|
|
6609
|
-
if (!(0,
|
|
6981
|
+
if (!(0, import_node_fs19.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
|
|
6610
6982
|
forgetRemoved(root);
|
|
6611
6983
|
await ensureGhPreferOrigin(root);
|
|
6612
6984
|
const current = readAll();
|
|
@@ -6636,7 +7008,7 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6636
7008
|
if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
|
|
6637
7009
|
continue;
|
|
6638
7010
|
}
|
|
6639
|
-
if (!(0,
|
|
7011
|
+
if (!(0, import_node_fs19.existsSync)(path)) continue;
|
|
6640
7012
|
const ws = {
|
|
6641
7013
|
path,
|
|
6642
7014
|
name: (0, import_node_path16.basename)(path),
|
|
@@ -6649,11 +7021,11 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6649
7021
|
if (dirty) writeAll(next);
|
|
6650
7022
|
return next.sort((a, b) => a.name.localeCompare(b.name));
|
|
6651
7023
|
}
|
|
6652
|
-
var
|
|
7024
|
+
var import_node_fs19, import_node_path16;
|
|
6653
7025
|
var init_workspaces = __esm({
|
|
6654
7026
|
"src/store/workspaces.ts"() {
|
|
6655
7027
|
"use strict";
|
|
6656
|
-
|
|
7028
|
+
import_node_fs19 = require("fs");
|
|
6657
7029
|
import_node_path16 = require("path");
|
|
6658
7030
|
init_paths();
|
|
6659
7031
|
init_global_workspace();
|
|
@@ -6732,17 +7104,17 @@ __export(plan_file_exports, {
|
|
|
6732
7104
|
});
|
|
6733
7105
|
function ensureAttachmentsGitignore2(worktreePath) {
|
|
6734
7106
|
const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
6735
|
-
if ((0,
|
|
6736
|
-
(0,
|
|
6737
|
-
(0,
|
|
7107
|
+
if ((0, import_node_fs29.existsSync)(gitignoreAbs)) return;
|
|
7108
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
|
|
7109
|
+
(0, import_node_fs29.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
6738
7110
|
}
|
|
6739
7111
|
function planFileAbs(worktreePath) {
|
|
6740
7112
|
return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
|
|
6741
7113
|
}
|
|
6742
7114
|
function readTextIfPresent2(abs) {
|
|
6743
|
-
if (!(0,
|
|
7115
|
+
if (!(0, import_node_fs29.existsSync)(abs)) return null;
|
|
6744
7116
|
try {
|
|
6745
|
-
const content = (0,
|
|
7117
|
+
const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
|
|
6746
7118
|
return content.trim() ? content : null;
|
|
6747
7119
|
} catch {
|
|
6748
7120
|
return null;
|
|
@@ -6754,16 +7126,16 @@ function readPlanFile(worktreePath) {
|
|
|
6754
7126
|
function writePlanFile(worktreePath, content) {
|
|
6755
7127
|
ensureAttachmentsGitignore2(worktreePath);
|
|
6756
7128
|
const abs = planFileAbs(worktreePath);
|
|
6757
|
-
(0,
|
|
7129
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
|
|
6758
7130
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
6759
|
-
(0,
|
|
7131
|
+
(0, import_node_fs29.writeFileSync)(abs, body, "utf8");
|
|
6760
7132
|
return PLAN_FILE_REL;
|
|
6761
7133
|
}
|
|
6762
|
-
var
|
|
7134
|
+
var import_node_fs29, import_node_path24;
|
|
6763
7135
|
var init_plan_file = __esm({
|
|
6764
7136
|
"src/plan/plan-file.ts"() {
|
|
6765
7137
|
"use strict";
|
|
6766
|
-
|
|
7138
|
+
import_node_fs29 = require("fs");
|
|
6767
7139
|
import_node_path24 = require("path");
|
|
6768
7140
|
init_workspace_scratch();
|
|
6769
7141
|
init_plan_present();
|
|
@@ -6780,9 +7152,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6780
7152
|
const agentId = opts.agentId.trim();
|
|
6781
7153
|
if (!agentId) return null;
|
|
6782
7154
|
const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
6783
|
-
if (!(0,
|
|
7155
|
+
if (!(0, import_node_fs30.existsSync)(runsPath)) return null;
|
|
6784
7156
|
try {
|
|
6785
|
-
const lines = (0,
|
|
7157
|
+
const lines = (0, import_node_fs30.readFileSync)(runsPath, "utf8").split("\n");
|
|
6786
7158
|
let best = null;
|
|
6787
7159
|
for (const line of lines) {
|
|
6788
7160
|
const trimmed = line.trim();
|
|
@@ -6808,11 +7180,11 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6808
7180
|
return null;
|
|
6809
7181
|
}
|
|
6810
7182
|
}
|
|
6811
|
-
var
|
|
7183
|
+
var import_node_fs30, import_node_path25;
|
|
6812
7184
|
var init_cursor_recover = __esm({
|
|
6813
7185
|
"src/agents/cursor-recover.ts"() {
|
|
6814
7186
|
"use strict";
|
|
6815
|
-
|
|
7187
|
+
import_node_fs30 = require("fs");
|
|
6816
7188
|
import_node_path25 = require("path");
|
|
6817
7189
|
init_paths();
|
|
6818
7190
|
}
|
|
@@ -6826,7 +7198,7 @@ var import_node_path26 = require("path");
|
|
|
6826
7198
|
|
|
6827
7199
|
// src/orchestrator/orchestrator.ts
|
|
6828
7200
|
var import_node_events = require("events");
|
|
6829
|
-
var
|
|
7201
|
+
var import_node_fs31 = require("fs");
|
|
6830
7202
|
init_error_detail();
|
|
6831
7203
|
|
|
6832
7204
|
// src/agents/spawn.ts
|
|
@@ -7216,7 +7588,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
7216
7588
|
}
|
|
7217
7589
|
|
|
7218
7590
|
// src/hook/conductor.ts
|
|
7219
|
-
var
|
|
7591
|
+
var import_node_fs16 = require("fs");
|
|
7220
7592
|
var import_node_net = require("net");
|
|
7221
7593
|
var import_node_path13 = require("path");
|
|
7222
7594
|
var import_execa3 = require("execa");
|
|
@@ -7229,8 +7601,8 @@ function matchSimpleGlob(pattern, name) {
|
|
|
7229
7601
|
}
|
|
7230
7602
|
function readWorktreeInclude(repoPath) {
|
|
7231
7603
|
const path = (0, import_node_path13.join)(repoPath, ".worktreeinclude");
|
|
7232
|
-
if (!(0,
|
|
7233
|
-
return (0,
|
|
7604
|
+
if (!(0, import_node_fs16.existsSync)(path)) return [];
|
|
7605
|
+
return (0, import_node_fs16.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
7234
7606
|
}
|
|
7235
7607
|
function resolveFilesToCopy(repoPath) {
|
|
7236
7608
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -7240,7 +7612,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7240
7612
|
if (settings?.fileIncludeGlobs?.length) {
|
|
7241
7613
|
const matched = [];
|
|
7242
7614
|
try {
|
|
7243
|
-
for (const entry of (0,
|
|
7615
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7244
7616
|
if (!entry.isFile()) continue;
|
|
7245
7617
|
for (const glob of settings.fileIncludeGlobs) {
|
|
7246
7618
|
if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path13.basename)(glob), entry.name)) {
|
|
@@ -7255,7 +7627,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7255
7627
|
}
|
|
7256
7628
|
const defaults = [];
|
|
7257
7629
|
try {
|
|
7258
|
-
for (const entry of (0,
|
|
7630
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7259
7631
|
if (entry.isFile() && entry.name.startsWith(".env")) {
|
|
7260
7632
|
defaults.push(entry.name);
|
|
7261
7633
|
}
|
|
@@ -7270,10 +7642,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
7270
7642
|
const copied = [];
|
|
7271
7643
|
for (const rel of patterns) {
|
|
7272
7644
|
const src = (0, import_node_path13.join)(repoPath, rel);
|
|
7273
|
-
if (!(0,
|
|
7645
|
+
if (!(0, import_node_fs16.existsSync)(src)) continue;
|
|
7274
7646
|
const dest = (0, import_node_path13.join)(worktreePath, rel);
|
|
7275
|
-
(0,
|
|
7276
|
-
(0,
|
|
7647
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path13.dirname)(dest), { recursive: true });
|
|
7648
|
+
(0, import_node_fs16.copyFileSync)(src, dest);
|
|
7277
7649
|
copied.push(rel);
|
|
7278
7650
|
}
|
|
7279
7651
|
return copied;
|
|
@@ -7526,15 +7898,15 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
7526
7898
|
}
|
|
7527
7899
|
|
|
7528
7900
|
// src/hook/cursor-worktrees.ts
|
|
7529
|
-
var
|
|
7901
|
+
var import_node_fs17 = require("fs");
|
|
7530
7902
|
var import_node_path14 = require("path");
|
|
7531
7903
|
var import_execa4 = require("execa");
|
|
7532
7904
|
var import_node_readline3 = require("readline");
|
|
7533
7905
|
function loadCursorWorktreesJson(rootPath) {
|
|
7534
7906
|
const path = (0, import_node_path14.join)(rootPath, ".cursor", "worktrees.json");
|
|
7535
|
-
if (!(0,
|
|
7907
|
+
if (!(0, import_node_fs17.existsSync)(path)) return null;
|
|
7536
7908
|
try {
|
|
7537
|
-
return JSON.parse((0,
|
|
7909
|
+
return JSON.parse((0, import_node_fs17.readFileSync)(path, "utf8"));
|
|
7538
7910
|
} catch {
|
|
7539
7911
|
return null;
|
|
7540
7912
|
}
|
|
@@ -7592,7 +7964,7 @@ async function runCursorWorktreeSetup(repoPath, worktreePath, onLine) {
|
|
|
7592
7964
|
}
|
|
7593
7965
|
|
|
7594
7966
|
// src/git/orphan-cleanup.ts
|
|
7595
|
-
var
|
|
7967
|
+
var import_node_fs18 = require("fs");
|
|
7596
7968
|
var import_node_path15 = require("path");
|
|
7597
7969
|
init_worktree();
|
|
7598
7970
|
init_thread_store();
|
|
@@ -7608,9 +7980,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7608
7980
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
7609
7981
|
);
|
|
7610
7982
|
const homeRoot = sideboardWorkspacesDir();
|
|
7611
|
-
if ((0,
|
|
7983
|
+
if ((0, import_node_fs18.existsSync)(homeRoot)) {
|
|
7612
7984
|
try {
|
|
7613
|
-
for (const entry of (0,
|
|
7985
|
+
for (const entry of (0, import_node_fs18.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
7614
7986
|
if (!entry.isDirectory()) continue;
|
|
7615
7987
|
void entry;
|
|
7616
7988
|
}
|
|
@@ -7620,7 +7992,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7620
7992
|
const orphans = [];
|
|
7621
7993
|
const seen = /* @__PURE__ */ new Set();
|
|
7622
7994
|
for (const repoPath of repos) {
|
|
7623
|
-
if (!repoPath || !(0,
|
|
7995
|
+
if (!repoPath || !(0, import_node_fs18.existsSync)(repoPath)) continue;
|
|
7624
7996
|
try {
|
|
7625
7997
|
const wts = await listWorktrees(repoPath);
|
|
7626
7998
|
for (const wt of wts) {
|
|
@@ -7631,7 +8003,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7631
8003
|
seen.add(path);
|
|
7632
8004
|
let mtimeMs = 0;
|
|
7633
8005
|
try {
|
|
7634
|
-
mtimeMs = (0,
|
|
8006
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7635
8007
|
} catch {
|
|
7636
8008
|
mtimeMs = 0;
|
|
7637
8009
|
}
|
|
@@ -7641,16 +8013,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7641
8013
|
}
|
|
7642
8014
|
try {
|
|
7643
8015
|
const root = worktreesRoot(repoPath);
|
|
7644
|
-
if ((0,
|
|
7645
|
-
for (const entry of (0,
|
|
8016
|
+
if ((0, import_node_fs18.existsSync)(root)) {
|
|
8017
|
+
for (const entry of (0, import_node_fs18.readdirSync)(root, { withFileTypes: true })) {
|
|
7646
8018
|
if (!entry.isDirectory()) continue;
|
|
7647
8019
|
const path = (0, import_node_path15.join)(root, entry.name).replace(/\/$/, "");
|
|
7648
8020
|
if (known.has(path) || seen.has(path)) continue;
|
|
7649
|
-
if (!(0,
|
|
8021
|
+
if (!(0, import_node_fs18.existsSync)((0, import_node_path15.join)(path, ".git"))) continue;
|
|
7650
8022
|
seen.add(path);
|
|
7651
8023
|
let mtimeMs = 0;
|
|
7652
8024
|
try {
|
|
7653
|
-
mtimeMs = (0,
|
|
8025
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7654
8026
|
} catch {
|
|
7655
8027
|
mtimeMs = Date.now();
|
|
7656
8028
|
}
|
|
@@ -7787,7 +8159,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
7787
8159
|
}
|
|
7788
8160
|
|
|
7789
8161
|
// src/git/clone-repo.ts
|
|
7790
|
-
var
|
|
8162
|
+
var import_node_fs20 = require("fs");
|
|
7791
8163
|
var import_node_path17 = require("path");
|
|
7792
8164
|
var import_execa6 = require("execa");
|
|
7793
8165
|
init_paths();
|
|
@@ -7803,7 +8175,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7803
8175
|
}
|
|
7804
8176
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
7805
8177
|
const dest = (0, import_node_path17.join)(sideboardReposDir(), name);
|
|
7806
|
-
if ((0,
|
|
8178
|
+
if ((0, import_node_fs20.existsSync)(dest)) {
|
|
7807
8179
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
7808
8180
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
7809
8181
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -7823,13 +8195,14 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7823
8195
|
init_thread_store();
|
|
7824
8196
|
|
|
7825
8197
|
// src/threads/create.ts
|
|
7826
|
-
var
|
|
8198
|
+
var import_node_fs21 = require("fs");
|
|
7827
8199
|
|
|
7828
8200
|
// src/detect/detect.ts
|
|
7829
8201
|
init_agents();
|
|
7830
8202
|
var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
|
|
7831
8203
|
async function requireAgent(agent, opts) {
|
|
7832
8204
|
ensureAgentPath();
|
|
8205
|
+
enrichPathWithNpmGlobalBin();
|
|
7833
8206
|
const status = await Promise.race([
|
|
7834
8207
|
getAdapter(agent).detect(),
|
|
7835
8208
|
new Promise((_, reject) => {
|
|
@@ -7867,7 +8240,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
7867
8240
|
});
|
|
7868
8241
|
await requireAgent(resolved.agent);
|
|
7869
8242
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
7870
|
-
if (!(0,
|
|
8243
|
+
if (!(0, import_node_fs21.existsSync)(repoPath)) {
|
|
7871
8244
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
7872
8245
|
}
|
|
7873
8246
|
let sourceRef = input.sourceRef;
|
|
@@ -8330,7 +8703,7 @@ function forkChatTab(input) {
|
|
|
8330
8703
|
|
|
8331
8704
|
// src/review/request-review.ts
|
|
8332
8705
|
var import_node_crypto3 = require("crypto");
|
|
8333
|
-
var
|
|
8706
|
+
var import_node_fs22 = require("fs");
|
|
8334
8707
|
var import_node_path18 = require("path");
|
|
8335
8708
|
init_global_workspace();
|
|
8336
8709
|
init_thread_store();
|
|
@@ -8475,9 +8848,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
8475
8848
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
8476
8849
|
}
|
|
8477
8850
|
function readTextIfPresent(abs) {
|
|
8478
|
-
if (!(0,
|
|
8851
|
+
if (!(0, import_node_fs22.existsSync)(abs)) return null;
|
|
8479
8852
|
try {
|
|
8480
|
-
const content = (0,
|
|
8853
|
+
const content = (0, import_node_fs22.readFileSync)(abs, "utf8");
|
|
8481
8854
|
return content.trim() ? content : null;
|
|
8482
8855
|
} catch {
|
|
8483
8856
|
return null;
|
|
@@ -8485,9 +8858,9 @@ function readTextIfPresent(abs) {
|
|
|
8485
8858
|
}
|
|
8486
8859
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
8487
8860
|
const gitignoreAbs = (0, import_node_path18.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
8488
|
-
if ((0,
|
|
8489
|
-
(0,
|
|
8490
|
-
(0,
|
|
8861
|
+
if ((0, import_node_fs22.existsSync)(gitignoreAbs)) return;
|
|
8862
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path18.dirname)(gitignoreAbs), { recursive: true });
|
|
8863
|
+
(0, import_node_fs22.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
8491
8864
|
}
|
|
8492
8865
|
function resolveReviewGuidelines(worktreePath) {
|
|
8493
8866
|
const repoAbs = (0, import_node_path18.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -8521,8 +8894,8 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
8521
8894
|
};
|
|
8522
8895
|
}
|
|
8523
8896
|
ensureAttachmentsGitignore(worktreePath);
|
|
8524
|
-
(0,
|
|
8525
|
-
(0,
|
|
8897
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path18.dirname)(localAbs), { recursive: true });
|
|
8898
|
+
(0, import_node_fs22.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
8526
8899
|
return {
|
|
8527
8900
|
path: REVIEW_REQUEST_PATH,
|
|
8528
8901
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -8721,8 +9094,8 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
8721
9094
|
}
|
|
8722
9095
|
|
|
8723
9096
|
// src/threads/adopt.ts
|
|
8724
|
-
var
|
|
8725
|
-
var
|
|
9097
|
+
var import_node_child_process2 = require("child_process");
|
|
9098
|
+
var import_node_fs23 = require("fs");
|
|
8726
9099
|
var import_node_os8 = require("os");
|
|
8727
9100
|
var import_node_path19 = require("path");
|
|
8728
9101
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -8747,21 +9120,21 @@ function mapAgentType(raw) {
|
|
|
8747
9120
|
return null;
|
|
8748
9121
|
}
|
|
8749
9122
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
8750
|
-
if (!workspacePath || !(0,
|
|
9123
|
+
if (!workspacePath || !(0, import_node_fs23.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
8751
9124
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
8752
9125
|
let best = null;
|
|
8753
9126
|
let hashes;
|
|
8754
9127
|
try {
|
|
8755
|
-
hashes = (0,
|
|
9128
|
+
hashes = (0, import_node_fs23.readdirSync)(CURSOR_SDK_STORE);
|
|
8756
9129
|
} catch {
|
|
8757
9130
|
return null;
|
|
8758
9131
|
}
|
|
8759
9132
|
for (const hash of hashes) {
|
|
8760
9133
|
const agentsFile = (0, import_node_path19.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
8761
|
-
if (!(0,
|
|
9134
|
+
if (!(0, import_node_fs23.existsSync)(agentsFile)) continue;
|
|
8762
9135
|
let text;
|
|
8763
9136
|
try {
|
|
8764
|
-
text = (0,
|
|
9137
|
+
text = (0, import_node_fs23.readFileSync)(agentsFile, "utf8");
|
|
8765
9138
|
} catch {
|
|
8766
9139
|
continue;
|
|
8767
9140
|
}
|
|
@@ -8785,7 +9158,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
8785
9158
|
return best?.agentId ?? null;
|
|
8786
9159
|
}
|
|
8787
9160
|
async function adoptThread(input) {
|
|
8788
|
-
if (!(0,
|
|
9161
|
+
if (!(0, import_node_fs23.existsSync)(input.worktreePath)) {
|
|
8789
9162
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
8790
9163
|
}
|
|
8791
9164
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -8809,18 +9182,18 @@ async function adoptThread(input) {
|
|
|
8809
9182
|
return thread;
|
|
8810
9183
|
}
|
|
8811
9184
|
function listConductorWorkspaces() {
|
|
8812
|
-
if (!(0,
|
|
9185
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8813
9186
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8814
9187
|
}
|
|
8815
|
-
const tmp = (0,
|
|
9188
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8816
9189
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8817
9190
|
try {
|
|
8818
|
-
(0,
|
|
9191
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8819
9192
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8820
9193
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8821
|
-
if ((0,
|
|
9194
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8822
9195
|
try {
|
|
8823
|
-
(0,
|
|
9196
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8824
9197
|
} catch {
|
|
8825
9198
|
}
|
|
8826
9199
|
}
|
|
@@ -8896,22 +9269,22 @@ function listConductorWorkspaces() {
|
|
|
8896
9269
|
db.close();
|
|
8897
9270
|
}
|
|
8898
9271
|
} finally {
|
|
8899
|
-
(0,
|
|
9272
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8900
9273
|
}
|
|
8901
9274
|
}
|
|
8902
9275
|
function importConductorWorkspace(workspaceId) {
|
|
8903
|
-
if (!(0,
|
|
9276
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8904
9277
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8905
9278
|
}
|
|
8906
|
-
const tmp = (0,
|
|
9279
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8907
9280
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8908
9281
|
try {
|
|
8909
|
-
(0,
|
|
9282
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8910
9283
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8911
9284
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8912
|
-
if ((0,
|
|
9285
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8913
9286
|
try {
|
|
8914
|
-
(0,
|
|
9287
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8915
9288
|
} catch {
|
|
8916
9289
|
}
|
|
8917
9290
|
}
|
|
@@ -8929,7 +9302,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8929
9302
|
).get(workspaceId);
|
|
8930
9303
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
8931
9304
|
const worktreePath = String(row.workspacePath);
|
|
8932
|
-
if (!(0,
|
|
9305
|
+
if (!(0, import_node_fs23.existsSync)(worktreePath)) {
|
|
8933
9306
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
8934
9307
|
}
|
|
8935
9308
|
let sessionId = null;
|
|
@@ -8969,7 +9342,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8969
9342
|
}
|
|
8970
9343
|
let repoPath = worktreePath;
|
|
8971
9344
|
try {
|
|
8972
|
-
repoPath = (0,
|
|
9345
|
+
repoPath = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
8973
9346
|
cwd: worktreePath,
|
|
8974
9347
|
encoding: "utf8"
|
|
8975
9348
|
}).trim();
|
|
@@ -8992,7 +9365,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8992
9365
|
db.close();
|
|
8993
9366
|
}
|
|
8994
9367
|
} finally {
|
|
8995
|
-
(0,
|
|
9368
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8996
9369
|
}
|
|
8997
9370
|
}
|
|
8998
9371
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -9000,7 +9373,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
9000
9373
|
}
|
|
9001
9374
|
|
|
9002
9375
|
// src/threads/stack-layers.ts
|
|
9003
|
-
var
|
|
9376
|
+
var import_node_fs24 = require("fs");
|
|
9004
9377
|
init_run();
|
|
9005
9378
|
init_stack();
|
|
9006
9379
|
init_worktree();
|
|
@@ -9068,7 +9441,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
9068
9441
|
let createdWorktree = false;
|
|
9069
9442
|
const trees = await listWorktrees(repoPath);
|
|
9070
9443
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
9071
|
-
if (checkedOut?.path && (0,
|
|
9444
|
+
if (checkedOut?.path && (0, import_node_fs24.existsSync)(checkedOut.path)) {
|
|
9072
9445
|
if (input.reuseExistingWorktree !== false) {
|
|
9073
9446
|
worktreePath = checkedOut.path;
|
|
9074
9447
|
} else {
|
|
@@ -9210,7 +9583,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
9210
9583
|
async function createPrStack(input, onSetupLine) {
|
|
9211
9584
|
await requireAgent(input.agent);
|
|
9212
9585
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9213
|
-
if (!(0,
|
|
9586
|
+
if (!(0, import_node_fs24.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
9214
9587
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
9215
9588
|
const status = await detectGhStack(repoPath);
|
|
9216
9589
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -9277,7 +9650,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9277
9650
|
}
|
|
9278
9651
|
}
|
|
9279
9652
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
9280
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
9653
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs24.existsSync)(bootstrap.worktreePath)) {
|
|
9281
9654
|
try {
|
|
9282
9655
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
9283
9656
|
deleteBranch: bootstrap.branchName
|
|
@@ -9292,12 +9665,12 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9292
9665
|
init_worktree();
|
|
9293
9666
|
|
|
9294
9667
|
// src/diff/diff.ts
|
|
9295
|
-
var
|
|
9668
|
+
var import_node_fs25 = require("fs");
|
|
9296
9669
|
var import_node_path20 = require("path");
|
|
9297
9670
|
init_run();
|
|
9298
9671
|
init_worktree();
|
|
9299
9672
|
async function inspectGitWorktree(worktreePath) {
|
|
9300
|
-
if (!worktreePath || !(0,
|
|
9673
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) return "missing_worktree";
|
|
9301
9674
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
9302
9675
|
reject: false
|
|
9303
9676
|
});
|
|
@@ -9305,7 +9678,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
9305
9678
|
return "ok";
|
|
9306
9679
|
}
|
|
9307
9680
|
async function initializeGitRepository(worktreePath) {
|
|
9308
|
-
if (!worktreePath || !(0,
|
|
9681
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) {
|
|
9309
9682
|
throw new Error("Worktree not found");
|
|
9310
9683
|
}
|
|
9311
9684
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -9743,7 +10116,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9743
10116
|
assertSafeRelativePath(relativePath);
|
|
9744
10117
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
9745
10118
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9746
|
-
const st = (0,
|
|
10119
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9747
10120
|
if (!st.isFile()) {
|
|
9748
10121
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9749
10122
|
}
|
|
@@ -9752,7 +10125,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9752
10125
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
9753
10126
|
);
|
|
9754
10127
|
}
|
|
9755
|
-
const buf = (0,
|
|
10128
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9756
10129
|
return {
|
|
9757
10130
|
path: relativePath,
|
|
9758
10131
|
contentBase64: buf.toString("base64"),
|
|
@@ -9763,11 +10136,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
9763
10136
|
assertSafeRelativePath(relativePath);
|
|
9764
10137
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
9765
10138
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9766
|
-
const st = (0,
|
|
10139
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9767
10140
|
if (!st.isFile()) {
|
|
9768
10141
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9769
10142
|
}
|
|
9770
|
-
const buf = (0,
|
|
10143
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9771
10144
|
if (isImageRelativePath(relativePath)) {
|
|
9772
10145
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
9773
10146
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -9811,8 +10184,8 @@ function assertSafeRelativePath(relativePath) {
|
|
|
9811
10184
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
9812
10185
|
assertSafeRelativePath(relativePath);
|
|
9813
10186
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9814
|
-
(0,
|
|
9815
|
-
(0,
|
|
10187
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path20.dirname)(abs), { recursive: true });
|
|
10188
|
+
(0, import_node_fs25.writeFileSync)(abs, content, "utf8");
|
|
9816
10189
|
return { path: relativePath };
|
|
9817
10190
|
}
|
|
9818
10191
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -9991,7 +10364,7 @@ async function confirmLand(thread, opts) {
|
|
|
9991
10364
|
}
|
|
9992
10365
|
|
|
9993
10366
|
// src/skills/discover.ts
|
|
9994
|
-
var
|
|
10367
|
+
var import_node_fs26 = require("fs");
|
|
9995
10368
|
var import_node_os9 = require("os");
|
|
9996
10369
|
var import_node_path21 = require("path");
|
|
9997
10370
|
function toCommand(name) {
|
|
@@ -10025,7 +10398,7 @@ function parseFrontmatter(content) {
|
|
|
10025
10398
|
}
|
|
10026
10399
|
function readSkill(skillMd, source) {
|
|
10027
10400
|
try {
|
|
10028
|
-
const content = (0,
|
|
10401
|
+
const content = (0, import_node_fs26.readFileSync)(skillMd, "utf8");
|
|
10029
10402
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
10030
10403
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
10031
10404
|
const name = fmName || dirName;
|
|
@@ -10044,19 +10417,19 @@ function readSkill(skillMd, source) {
|
|
|
10044
10417
|
}
|
|
10045
10418
|
}
|
|
10046
10419
|
function scanSkillsDir(dir, source, out) {
|
|
10047
|
-
if (!(0,
|
|
10420
|
+
if (!(0, import_node_fs26.existsSync)(dir)) return;
|
|
10048
10421
|
let entries;
|
|
10049
10422
|
try {
|
|
10050
|
-
entries = (0,
|
|
10423
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10051
10424
|
} catch {
|
|
10052
10425
|
return;
|
|
10053
10426
|
}
|
|
10054
10427
|
for (const entry of entries) {
|
|
10055
10428
|
if (entry.startsWith(".")) continue;
|
|
10056
10429
|
const skillMd = (0, import_node_path21.join)(dir, entry, "SKILL.md");
|
|
10057
|
-
if (!(0,
|
|
10430
|
+
if (!(0, import_node_fs26.existsSync)(skillMd)) continue;
|
|
10058
10431
|
try {
|
|
10059
|
-
if (!(0,
|
|
10432
|
+
if (!(0, import_node_fs26.statSync)(skillMd).isFile()) continue;
|
|
10060
10433
|
} catch {
|
|
10061
10434
|
continue;
|
|
10062
10435
|
}
|
|
@@ -10065,12 +10438,12 @@ function scanSkillsDir(dir, source, out) {
|
|
|
10065
10438
|
}
|
|
10066
10439
|
}
|
|
10067
10440
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
10068
|
-
if (!(0,
|
|
10441
|
+
if (!(0, import_node_fs26.existsSync)(pluginsRoot)) return;
|
|
10069
10442
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
10070
10443
|
if (depth > 7) return;
|
|
10071
10444
|
let entries;
|
|
10072
10445
|
try {
|
|
10073
|
-
entries = (0,
|
|
10446
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10074
10447
|
} catch {
|
|
10075
10448
|
return;
|
|
10076
10449
|
}
|
|
@@ -10082,7 +10455,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
10082
10455
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
10083
10456
|
const full = (0, import_node_path21.join)(dir, entry);
|
|
10084
10457
|
try {
|
|
10085
|
-
if (!(0,
|
|
10458
|
+
if (!(0, import_node_fs26.statSync)(full).isDirectory()) continue;
|
|
10086
10459
|
} catch {
|
|
10087
10460
|
continue;
|
|
10088
10461
|
}
|
|
@@ -10122,7 +10495,7 @@ function discoverSkills(worktreePath) {
|
|
|
10122
10495
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
10123
10496
|
}
|
|
10124
10497
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
10125
|
-
const raw = (0,
|
|
10498
|
+
const raw = (0, import_node_fs26.readFileSync)(skillPath, "utf8");
|
|
10126
10499
|
if (raw.startsWith("---")) {
|
|
10127
10500
|
const end = raw.indexOf("\n---", 3);
|
|
10128
10501
|
if (end >= 0) {
|
|
@@ -10215,7 +10588,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
10215
10588
|
}
|
|
10216
10589
|
|
|
10217
10590
|
// src/composer/stage-files.ts
|
|
10218
|
-
var
|
|
10591
|
+
var import_node_fs27 = require("fs");
|
|
10219
10592
|
var import_node_path22 = require("path");
|
|
10220
10593
|
var import_node_crypto5 = require("crypto");
|
|
10221
10594
|
init_workspace_scratch();
|
|
@@ -10253,21 +10626,21 @@ function imageMimeType(filePath) {
|
|
|
10253
10626
|
}
|
|
10254
10627
|
function ensureAttachmentsDir(worktreePath) {
|
|
10255
10628
|
const dir = (0, import_node_path22.join)(worktreePath, ATTACHMENTS_DIR);
|
|
10256
|
-
(0,
|
|
10629
|
+
(0, import_node_fs27.mkdirSync)(dir, { recursive: true });
|
|
10257
10630
|
const gi = (0, import_node_path22.join)(dir, ".gitignore");
|
|
10258
|
-
if (!(0,
|
|
10259
|
-
(0,
|
|
10631
|
+
if (!(0, import_node_fs27.existsSync)(gi)) {
|
|
10632
|
+
(0, import_node_fs27.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
10260
10633
|
}
|
|
10261
10634
|
return dir;
|
|
10262
10635
|
}
|
|
10263
10636
|
function uniqueAttachmentName(dir, originalName) {
|
|
10264
10637
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
10265
|
-
if (!(0,
|
|
10638
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, safe))) return safe;
|
|
10266
10639
|
const ext = (0, import_node_path22.extname)(safe);
|
|
10267
10640
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
10268
10641
|
for (let i = 1; i < 1e4; i++) {
|
|
10269
10642
|
const candidate = `${stem}-${i}${ext}`;
|
|
10270
|
-
if (!(0,
|
|
10643
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
|
|
10271
10644
|
}
|
|
10272
10645
|
return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
|
|
10273
10646
|
}
|
|
@@ -10325,13 +10698,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
10325
10698
|
for (const abs of absolutePaths) {
|
|
10326
10699
|
const originalName = (0, import_node_path22.basename)(abs);
|
|
10327
10700
|
try {
|
|
10328
|
-
const st = (0,
|
|
10701
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10329
10702
|
if (!st.isFile()) continue;
|
|
10330
10703
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10331
10704
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10332
|
-
(0,
|
|
10705
|
+
(0, import_node_fs27.copyFileSync)(abs, destAbs);
|
|
10333
10706
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10334
|
-
const buf = (0,
|
|
10707
|
+
const buf = (0, import_node_fs27.readFileSync)(destAbs);
|
|
10335
10708
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10336
10709
|
} catch (err) {
|
|
10337
10710
|
out.push({
|
|
@@ -10354,7 +10727,7 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
10354
10727
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
10355
10728
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10356
10729
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10357
|
-
(0,
|
|
10730
|
+
(0, import_node_fs27.writeFileSync)(destAbs, buf);
|
|
10358
10731
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10359
10732
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
10360
10733
|
} catch (err) {
|
|
@@ -10383,9 +10756,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10383
10756
|
const name = (0, import_node_path22.basename)(rel);
|
|
10384
10757
|
try {
|
|
10385
10758
|
const abs = (0, import_node_path22.join)(worktreePath, rel);
|
|
10386
|
-
const st = (0,
|
|
10759
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10387
10760
|
if (!st.isFile()) continue;
|
|
10388
|
-
const buf = (0,
|
|
10761
|
+
const buf = (0, import_node_fs27.readFileSync)(abs);
|
|
10389
10762
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10390
10763
|
} catch (err) {
|
|
10391
10764
|
out.push({
|
|
@@ -10400,7 +10773,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10400
10773
|
}
|
|
10401
10774
|
|
|
10402
10775
|
// src/agents/instructions.ts
|
|
10403
|
-
var
|
|
10776
|
+
var import_node_fs28 = require("fs");
|
|
10404
10777
|
var import_node_path23 = require("path");
|
|
10405
10778
|
init_worktree_labels();
|
|
10406
10779
|
function normPath2(p) {
|
|
@@ -10500,6 +10873,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
10500
10873
|
);
|
|
10501
10874
|
return lines.join("\n");
|
|
10502
10875
|
}
|
|
10876
|
+
function formatWorktreeReminder() {
|
|
10877
|
+
return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout.";
|
|
10878
|
+
}
|
|
10503
10879
|
function formatArtifactDirective() {
|
|
10504
10880
|
return [
|
|
10505
10881
|
"Sideboard side column (desktop UI):",
|
|
@@ -10520,60 +10896,6 @@ function formatArtifactDirective() {
|
|
|
10520
10896
|
"Never say artifacts, CMS UI, or the Files column are unavailable. Prefer present_schema for list/edit/publish; present_files for storage UI; html fences for standalone pages."
|
|
10521
10897
|
].join("\n");
|
|
10522
10898
|
}
|
|
10523
|
-
var FILES_BY_AGENT = {
|
|
10524
|
-
claude: [
|
|
10525
|
-
"CLAUDE.md",
|
|
10526
|
-
".claude/CLAUDE.md",
|
|
10527
|
-
"CLAUDE.local.md",
|
|
10528
|
-
"AGENTS.md",
|
|
10529
|
-
"AGENT.md"
|
|
10530
|
-
],
|
|
10531
|
-
codex: ["AGENTS.md", "AGENT.md", ".codex/AGENTS.md", "CLAUDE.md"],
|
|
10532
|
-
opencode: ["AGENTS.md", "AGENT.md", "OPENCODE.md", "CLAUDE.md"],
|
|
10533
|
-
brightsy: ["AGENTS.md", "AGENT.md", "CLAUDE.md"],
|
|
10534
|
-
cursor: ["AGENTS.md", "AGENT.md", ".cursor/rules", "CLAUDE.md"]
|
|
10535
|
-
};
|
|
10536
|
-
var MAX_CHARS_PER_FILE = 48e3;
|
|
10537
|
-
function loadAgentInstructions(worktreePath, agent) {
|
|
10538
|
-
const candidates = FILES_BY_AGENT[agent] ?? FILES_BY_AGENT.claude;
|
|
10539
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10540
|
-
const out = [];
|
|
10541
|
-
for (const rel of candidates) {
|
|
10542
|
-
if (seen.has(rel)) continue;
|
|
10543
|
-
const abs = (0, import_node_path23.join)(worktreePath, rel);
|
|
10544
|
-
if (!(0, import_node_fs25.existsSync)(abs)) continue;
|
|
10545
|
-
try {
|
|
10546
|
-
if (!(0, import_node_fs25.statSync)(abs).isFile()) continue;
|
|
10547
|
-
let content = (0, import_node_fs25.readFileSync)(abs, "utf8");
|
|
10548
|
-
if (!content.trim()) continue;
|
|
10549
|
-
if (content.length > MAX_CHARS_PER_FILE) {
|
|
10550
|
-
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
10551
|
-
|
|
10552
|
-
\u2026(truncated)`;
|
|
10553
|
-
}
|
|
10554
|
-
seen.add(rel);
|
|
10555
|
-
out.push({ relativePath: rel, content });
|
|
10556
|
-
} catch {
|
|
10557
|
-
}
|
|
10558
|
-
}
|
|
10559
|
-
return out;
|
|
10560
|
-
}
|
|
10561
|
-
function formatAgentInstructions(files) {
|
|
10562
|
-
if (files.length === 0) return null;
|
|
10563
|
-
const parts = [
|
|
10564
|
-
"Project agent instructions (from the worktree \u2014 follow these):",
|
|
10565
|
-
""
|
|
10566
|
-
];
|
|
10567
|
-
for (const f of files) {
|
|
10568
|
-
parts.push(`## ${f.relativePath}`);
|
|
10569
|
-
parts.push("");
|
|
10570
|
-
parts.push(f.content.trim());
|
|
10571
|
-
parts.push("");
|
|
10572
|
-
parts.push("---");
|
|
10573
|
-
parts.push("");
|
|
10574
|
-
}
|
|
10575
|
-
return parts.join("\n").trimEnd();
|
|
10576
|
-
}
|
|
10577
10899
|
|
|
10578
10900
|
// src/orchestrator/orchestrator.ts
|
|
10579
10901
|
init_types();
|
|
@@ -10698,7 +11020,7 @@ var Orchestrator = class {
|
|
|
10698
11020
|
}
|
|
10699
11021
|
continue;
|
|
10700
11022
|
}
|
|
10701
|
-
if (!(0,
|
|
11023
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
10702
11024
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10703
11025
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10704
11026
|
continue;
|
|
@@ -11115,13 +11437,14 @@ var Orchestrator = class {
|
|
|
11115
11437
|
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
11116
11438
|
"Do not say artifacts/CMS UI are unavailable."
|
|
11117
11439
|
].join(" ") : null;
|
|
11440
|
+
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
11118
11441
|
const agentPrompt = [
|
|
11119
11442
|
thread.planMode ? PLAN_MODE_INSTRUCTION : null,
|
|
11120
11443
|
orchestrationReminder,
|
|
11444
|
+
worktreeReminder,
|
|
11121
11445
|
artifactReminder,
|
|
11122
11446
|
expandedPrompt
|
|
11123
11447
|
].filter(Boolean).join("\n\n");
|
|
11124
|
-
const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
|
|
11125
11448
|
if (thread.attachments.length > 0) {
|
|
11126
11449
|
updateThread(threadId, { attachments: [] });
|
|
11127
11450
|
}
|
|
@@ -11149,7 +11472,6 @@ var Orchestrator = class {
|
|
|
11149
11472
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
11150
11473
|
customPrompt: settings?.prompts?.renameBranch
|
|
11151
11474
|
}) : null;
|
|
11152
|
-
const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
|
|
11153
11475
|
let seed = null;
|
|
11154
11476
|
if (!fresh.sessionId) {
|
|
11155
11477
|
const prior = fresh.messages.slice(0, -1);
|
|
@@ -11170,12 +11492,12 @@ var Orchestrator = class {
|
|
|
11170
11492
|
});
|
|
11171
11493
|
}
|
|
11172
11494
|
}
|
|
11173
|
-
const cachedPrefix = [
|
|
11495
|
+
const cachedPrefix = fresh.sessionId ? "" : [
|
|
11174
11496
|
coordinatorDirective,
|
|
11175
11497
|
worktreeDirective,
|
|
11176
11498
|
artifactDirective,
|
|
11177
11499
|
renameBranchDirective,
|
|
11178
|
-
|
|
11500
|
+
seed
|
|
11179
11501
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11180
11502
|
try {
|
|
11181
11503
|
const stderrTail = [];
|
|
@@ -11262,15 +11584,11 @@ var Orchestrator = class {
|
|
|
11262
11584
|
const retryThread = this.requireThread(threadId);
|
|
11263
11585
|
const prior = retryThread.messages.slice(0, -1);
|
|
11264
11586
|
const retrySeed = buildSessionSeed(prior);
|
|
11265
|
-
const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
|
|
11266
|
-
loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
|
|
11267
|
-
);
|
|
11268
11587
|
const retryPrefix = [
|
|
11269
11588
|
coordinatorDirective,
|
|
11270
11589
|
worktreeDirective,
|
|
11271
11590
|
artifactDirective,
|
|
11272
11591
|
renameBranchDirective,
|
|
11273
|
-
retryInstructions,
|
|
11274
11592
|
retrySeed
|
|
11275
11593
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11276
11594
|
const retryHandle = await spawnAgentTurn(
|
|
@@ -12105,7 +12423,7 @@ var Orchestrator = class {
|
|
|
12105
12423
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
12106
12424
|
return restored2;
|
|
12107
12425
|
}
|
|
12108
|
-
if (!(0,
|
|
12426
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
12109
12427
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
12110
12428
|
const { execa: execa7 } = await import("execa");
|
|
12111
12429
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -12314,6 +12632,7 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
12314
12632
|
}
|
|
12315
12633
|
|
|
12316
12634
|
// src/mcp/server.ts
|
|
12635
|
+
init_profile();
|
|
12317
12636
|
var MAX_ORCH_THREADS = 5;
|
|
12318
12637
|
var CREATE_THREAD_TIMEOUT_MS = 9e4;
|
|
12319
12638
|
function withTimeout(promise, ms, label) {
|
|
@@ -12427,14 +12746,13 @@ async function startMcpServer() {
|
|
|
12427
12746
|
),
|
|
12428
12747
|
artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
12429
12748
|
},
|
|
12430
|
-
async ({ title, type,
|
|
12749
|
+
async ({ title, type, artifact_id }) => {
|
|
12431
12750
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
12432
12751
|
const payload = {
|
|
12433
12752
|
ok: true,
|
|
12434
12753
|
artifact_id: id,
|
|
12435
12754
|
title,
|
|
12436
12755
|
type,
|
|
12437
|
-
content,
|
|
12438
12756
|
message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
12439
12757
|
};
|
|
12440
12758
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12487,7 +12805,6 @@ async function startMcpServer() {
|
|
|
12487
12805
|
ok: true,
|
|
12488
12806
|
path,
|
|
12489
12807
|
title: title?.trim() || "Plan",
|
|
12490
|
-
content,
|
|
12491
12808
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
12492
12809
|
};
|
|
12493
12810
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12517,9 +12834,6 @@ async function startMcpServer() {
|
|
|
12517
12834
|
datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
|
|
12518
12835
|
resource_id: args.resource_id,
|
|
12519
12836
|
record_id: args.record_id,
|
|
12520
|
-
resource: args.resource,
|
|
12521
|
-
record: args.record,
|
|
12522
|
-
records: args.records,
|
|
12523
12837
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
12524
12838
|
};
|
|
12525
12839
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12547,739 +12861,741 @@ async function startMcpServer() {
|
|
|
12547
12861
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
12548
12862
|
}
|
|
12549
12863
|
);
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
if (
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12864
|
+
if (sideboardMcpProfile() !== "worktree") {
|
|
12865
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
12866
|
+
const accountDefaults = resolveThreadDefaults2();
|
|
12867
|
+
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
12868
|
+
server.tool(
|
|
12869
|
+
"create_thread",
|
|
12870
|
+
`Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces. From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Then use send_to_thread to chat.`,
|
|
12871
|
+
{
|
|
12872
|
+
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
|
|
12873
|
+
sourceRef: import_zod.z.string(),
|
|
12874
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
12875
|
+
model: import_zod.z.string().nullable().optional().describe(
|
|
12876
|
+
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
12877
|
+
),
|
|
12878
|
+
repoPath: import_zod.z.string(),
|
|
12879
|
+
title: import_zod.z.string().optional(),
|
|
12880
|
+
parentThreadId: import_zod.z.string().optional().describe(
|
|
12881
|
+
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
12882
|
+
)
|
|
12883
|
+
},
|
|
12884
|
+
async (args) => {
|
|
12885
|
+
const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
|
|
12886
|
+
let parentId = args.parentThreadId?.trim() || "";
|
|
12887
|
+
let parent = parentId ? orch.getThread(parentId) : null;
|
|
12888
|
+
let parentCorrectedFrom;
|
|
12889
|
+
if (envParentId) {
|
|
12890
|
+
const envParent = orch.getThread(envParentId);
|
|
12891
|
+
if (envParent) {
|
|
12892
|
+
if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
|
|
12893
|
+
else if (!parentId) parentCorrectedFrom = void 0;
|
|
12894
|
+
parentId = envParentId;
|
|
12895
|
+
parent = envParent;
|
|
12896
|
+
}
|
|
12581
12897
|
}
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12898
|
+
if (parentId && !parent) {
|
|
12899
|
+
parentCorrectedFrom = parentId;
|
|
12900
|
+
parentId = "";
|
|
12901
|
+
parent = null;
|
|
12902
|
+
}
|
|
12903
|
+
if (parentId) {
|
|
12904
|
+
const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
|
|
12905
|
+
if (children.length >= MAX_ORCH_THREADS) {
|
|
12906
|
+
return {
|
|
12907
|
+
content: [
|
|
12908
|
+
{
|
|
12909
|
+
type: "text",
|
|
12910
|
+
text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
|
|
12911
|
+
}
|
|
12912
|
+
],
|
|
12913
|
+
isError: true
|
|
12914
|
+
};
|
|
12915
|
+
}
|
|
12916
|
+
}
|
|
12917
|
+
let agentArg = args.agent;
|
|
12918
|
+
let agentCoercedFrom;
|
|
12919
|
+
const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
|
|
12920
|
+
if (resolvedProbe === "codex") {
|
|
12921
|
+
agentCoercedFrom = agentArg ?? "codex";
|
|
12922
|
+
const accountAgent = resolveNewThreadOptions2({}).agent;
|
|
12923
|
+
agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
|
|
12924
|
+
}
|
|
12925
|
+
const opts = resolveNewThreadOptions2({
|
|
12926
|
+
agent: agentArg,
|
|
12927
|
+
model: args.model
|
|
12928
|
+
});
|
|
12929
|
+
try {
|
|
12930
|
+
const thread = await withTimeout(
|
|
12931
|
+
orch.createThread({
|
|
12932
|
+
sourceType: args.sourceType,
|
|
12933
|
+
sourceRef: args.sourceRef,
|
|
12934
|
+
agent: opts.agent,
|
|
12935
|
+
model: opts.model,
|
|
12936
|
+
effort: opts.effort,
|
|
12937
|
+
fast: opts.fast,
|
|
12938
|
+
repoPath: args.repoPath,
|
|
12939
|
+
title: args.title,
|
|
12940
|
+
parentThreadId: parentId || null
|
|
12941
|
+
}),
|
|
12942
|
+
CREATE_THREAD_TIMEOUT_MS,
|
|
12943
|
+
"create_thread"
|
|
12944
|
+
);
|
|
12945
|
+
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
12591
12946
|
return {
|
|
12592
12947
|
content: [
|
|
12593
12948
|
{
|
|
12594
12949
|
type: "text",
|
|
12595
|
-
text:
|
|
12950
|
+
text: JSON.stringify({
|
|
12951
|
+
id: thread.id,
|
|
12952
|
+
title: thread.title,
|
|
12953
|
+
branchName: thread.branchName,
|
|
12954
|
+
worktreePath: thread.worktreePath,
|
|
12955
|
+
agent: thread.agent,
|
|
12956
|
+
model: thread.model,
|
|
12957
|
+
status: thread.status,
|
|
12958
|
+
link: `sideboard://thread/${thread.id}`,
|
|
12959
|
+
parentThreadId: thread.parentThreadId,
|
|
12960
|
+
...agentCoercedFrom ? {
|
|
12961
|
+
agentCoercedFrom,
|
|
12962
|
+
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
12963
|
+
} : {},
|
|
12964
|
+
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
12965
|
+
})
|
|
12966
|
+
}
|
|
12967
|
+
]
|
|
12968
|
+
};
|
|
12969
|
+
} catch (err) {
|
|
12970
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12971
|
+
return {
|
|
12972
|
+
content: [
|
|
12973
|
+
{
|
|
12974
|
+
type: "text",
|
|
12975
|
+
text: `create_thread failed: ${message}`
|
|
12596
12976
|
}
|
|
12597
12977
|
],
|
|
12598
12978
|
isError: true
|
|
12599
12979
|
};
|
|
12600
12980
|
}
|
|
12601
12981
|
}
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
agent: opts.agent,
|
|
12620
|
-
model: opts.model,
|
|
12621
|
-
effort: opts.effort,
|
|
12622
|
-
fast: opts.fast,
|
|
12623
|
-
repoPath: args.repoPath,
|
|
12624
|
-
title: args.title,
|
|
12625
|
-
parentThreadId: parentId || null
|
|
12626
|
-
}),
|
|
12627
|
-
CREATE_THREAD_TIMEOUT_MS,
|
|
12628
|
-
"create_thread"
|
|
12629
|
-
);
|
|
12630
|
-
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
12982
|
+
);
|
|
12983
|
+
server.tool(
|
|
12984
|
+
"send_to_thread",
|
|
12985
|
+
"Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.",
|
|
12986
|
+
{
|
|
12987
|
+
ref: import_zod.z.string(),
|
|
12988
|
+
prompt: import_zod.z.string(),
|
|
12989
|
+
force_stop: import_zod.z.boolean().optional()
|
|
12990
|
+
},
|
|
12991
|
+
async ({ ref, prompt, force_stop }) => {
|
|
12992
|
+
if (force_stop) {
|
|
12993
|
+
const existing = orch.getThread(ref);
|
|
12994
|
+
if (existing) {
|
|
12995
|
+
orch.stop(ref, { clearQueue: true });
|
|
12996
|
+
}
|
|
12997
|
+
}
|
|
12998
|
+
const thread = await orch.send(ref, prompt);
|
|
12631
12999
|
return {
|
|
12632
13000
|
content: [
|
|
12633
13001
|
{
|
|
12634
13002
|
type: "text",
|
|
12635
13003
|
text: JSON.stringify({
|
|
12636
13004
|
id: thread.id,
|
|
12637
|
-
title: thread.title,
|
|
12638
|
-
branchName: thread.branchName,
|
|
12639
|
-
worktreePath: thread.worktreePath,
|
|
12640
|
-
agent: thread.agent,
|
|
12641
|
-
model: thread.model,
|
|
12642
13005
|
status: thread.status,
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
...agentCoercedFrom ? {
|
|
12646
|
-
agentCoercedFrom,
|
|
12647
|
-
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
12648
|
-
} : {},
|
|
12649
|
-
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
13006
|
+
queueLength: thread.queue.length,
|
|
13007
|
+
forceStopped: Boolean(force_stop)
|
|
12650
13008
|
})
|
|
12651
13009
|
}
|
|
12652
13010
|
]
|
|
12653
13011
|
};
|
|
12654
|
-
}
|
|
12655
|
-
|
|
13012
|
+
}
|
|
13013
|
+
);
|
|
13014
|
+
server.tool(
|
|
13015
|
+
"wait_for_turn",
|
|
13016
|
+
"Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
|
|
13017
|
+
{
|
|
13018
|
+
ref: import_zod.z.string(),
|
|
13019
|
+
timeoutMs: import_zod.z.number().optional()
|
|
13020
|
+
},
|
|
13021
|
+
async ({ ref, timeoutMs }) => {
|
|
13022
|
+
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
13023
|
+
const result = orch.getTurnResult(thread.id);
|
|
12656
13024
|
return {
|
|
12657
13025
|
content: [
|
|
12658
13026
|
{
|
|
12659
13027
|
type: "text",
|
|
12660
|
-
text:
|
|
13028
|
+
text: JSON.stringify({
|
|
13029
|
+
id: thread.id,
|
|
13030
|
+
status: result.status,
|
|
13031
|
+
text: result.text
|
|
13032
|
+
})
|
|
12661
13033
|
}
|
|
12662
|
-
]
|
|
12663
|
-
isError: true
|
|
13034
|
+
]
|
|
12664
13035
|
};
|
|
12665
13036
|
}
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
force_stop: import_zod.z.boolean().optional()
|
|
12675
|
-
},
|
|
12676
|
-
async ({ ref, prompt, force_stop }) => {
|
|
12677
|
-
if (force_stop) {
|
|
12678
|
-
const existing = orch.getThread(ref);
|
|
12679
|
-
if (existing) {
|
|
12680
|
-
orch.stop(ref, { clearQueue: true });
|
|
12681
|
-
}
|
|
13037
|
+
);
|
|
13038
|
+
server.tool(
|
|
13039
|
+
"get_turn_result",
|
|
13040
|
+
"Final assistant message only (not full transcript)",
|
|
13041
|
+
{ ref: import_zod.z.string() },
|
|
13042
|
+
async ({ ref }) => {
|
|
13043
|
+
const result = orch.getTurnResult(ref);
|
|
13044
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
12682
13045
|
}
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
{
|
|
12703
|
-
ref: import_zod.z.string(),
|
|
12704
|
-
timeoutMs: import_zod.z.number().optional()
|
|
12705
|
-
},
|
|
12706
|
-
async ({ ref, timeoutMs }) => {
|
|
12707
|
-
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
12708
|
-
const result = orch.getTurnResult(thread.id);
|
|
12709
|
-
return {
|
|
12710
|
-
content: [
|
|
12711
|
-
{
|
|
12712
|
-
type: "text",
|
|
12713
|
-
text: JSON.stringify({
|
|
12714
|
-
id: thread.id,
|
|
12715
|
-
status: result.status,
|
|
12716
|
-
text: result.text
|
|
12717
|
-
})
|
|
12718
|
-
}
|
|
12719
|
-
]
|
|
12720
|
-
};
|
|
12721
|
-
}
|
|
12722
|
-
);
|
|
12723
|
-
server.tool(
|
|
12724
|
-
"get_turn_result",
|
|
12725
|
-
"Final assistant message only (not full transcript)",
|
|
12726
|
-
{ ref: import_zod.z.string() },
|
|
12727
|
-
async ({ ref }) => {
|
|
12728
|
-
const result = orch.getTurnResult(ref);
|
|
12729
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
12730
|
-
}
|
|
12731
|
-
);
|
|
12732
|
-
server.tool(
|
|
12733
|
-
"stop_thread",
|
|
12734
|
-
"Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true.",
|
|
12735
|
-
{
|
|
12736
|
-
ref: import_zod.z.string(),
|
|
12737
|
-
force: import_zod.z.boolean().optional()
|
|
12738
|
-
},
|
|
12739
|
-
async ({ ref, force }) => {
|
|
12740
|
-
const t = orch.getThread(ref);
|
|
12741
|
-
if (!t) {
|
|
13046
|
+
);
|
|
13047
|
+
server.tool(
|
|
13048
|
+
"stop_thread",
|
|
13049
|
+
"Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true.",
|
|
13050
|
+
{
|
|
13051
|
+
ref: import_zod.z.string(),
|
|
13052
|
+
force: import_zod.z.boolean().optional()
|
|
13053
|
+
},
|
|
13054
|
+
async ({ ref, force }) => {
|
|
13055
|
+
const t = orch.getThread(ref);
|
|
13056
|
+
if (!t) {
|
|
13057
|
+
return {
|
|
13058
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13059
|
+
isError: true
|
|
13060
|
+
};
|
|
13061
|
+
}
|
|
13062
|
+
const clearQueue = force !== false;
|
|
13063
|
+
const hadQueued = t.queue.length > 0;
|
|
13064
|
+
const stopped = orch.stop(ref, { clearQueue });
|
|
12742
13065
|
return {
|
|
12743
|
-
content: [
|
|
12744
|
-
|
|
13066
|
+
content: [
|
|
13067
|
+
{
|
|
13068
|
+
type: "text",
|
|
13069
|
+
text: JSON.stringify({
|
|
13070
|
+
id: stopped.id,
|
|
13071
|
+
status: stopped.status,
|
|
13072
|
+
clearedQueue: clearQueue && hadQueued
|
|
13073
|
+
})
|
|
13074
|
+
}
|
|
13075
|
+
]
|
|
12745
13076
|
};
|
|
12746
13077
|
}
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
async ({ ref }) => {
|
|
12769
|
-
const t = orch.getThread(ref);
|
|
12770
|
-
if (!t) {
|
|
13078
|
+
);
|
|
13079
|
+
server.tool(
|
|
13080
|
+
"archive_thread",
|
|
13081
|
+
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Cannot archive the cloud coordinator \u2014 use the Sideboard UI for that.",
|
|
13082
|
+
{ ref: import_zod.z.string() },
|
|
13083
|
+
async ({ ref }) => {
|
|
13084
|
+
const t = orch.getThread(ref);
|
|
13085
|
+
if (!t) {
|
|
13086
|
+
return {
|
|
13087
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13088
|
+
isError: true
|
|
13089
|
+
};
|
|
13090
|
+
}
|
|
13091
|
+
const blocked = mcpArchiveBlockedReason(t);
|
|
13092
|
+
if (blocked) {
|
|
13093
|
+
return {
|
|
13094
|
+
content: [{ type: "text", text: blocked }],
|
|
13095
|
+
isError: true
|
|
13096
|
+
};
|
|
13097
|
+
}
|
|
13098
|
+
const archived = await orch.archive(ref);
|
|
12771
13099
|
return {
|
|
12772
|
-
content: [
|
|
12773
|
-
|
|
13100
|
+
content: [
|
|
13101
|
+
{
|
|
13102
|
+
type: "text",
|
|
13103
|
+
text: JSON.stringify({
|
|
13104
|
+
id: archived.id,
|
|
13105
|
+
status: archived.status
|
|
13106
|
+
})
|
|
13107
|
+
}
|
|
13108
|
+
]
|
|
12774
13109
|
};
|
|
12775
13110
|
}
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
13111
|
+
);
|
|
13112
|
+
server.tool(
|
|
13113
|
+
"restore_thread",
|
|
13114
|
+
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
13115
|
+
{ ref: import_zod.z.string() },
|
|
13116
|
+
async ({ ref }) => {
|
|
13117
|
+
try {
|
|
13118
|
+
const restored = await orch.restore(ref);
|
|
13119
|
+
return {
|
|
13120
|
+
content: [
|
|
13121
|
+
{
|
|
13122
|
+
type: "text",
|
|
13123
|
+
text: JSON.stringify({
|
|
13124
|
+
id: restored.id,
|
|
13125
|
+
status: restored.status,
|
|
13126
|
+
worktreePath: restored.worktreePath
|
|
13127
|
+
})
|
|
13128
|
+
}
|
|
13129
|
+
]
|
|
13130
|
+
};
|
|
13131
|
+
} catch (err) {
|
|
13132
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13133
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13134
|
+
}
|
|
12782
13135
|
}
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
]
|
|
12794
|
-
};
|
|
12795
|
-
}
|
|
12796
|
-
);
|
|
12797
|
-
server.tool(
|
|
12798
|
-
"restore_thread",
|
|
12799
|
-
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
12800
|
-
{ ref: import_zod.z.string() },
|
|
12801
|
-
async ({ ref }) => {
|
|
12802
|
-
try {
|
|
12803
|
-
const restored = await orch.restore(ref);
|
|
13136
|
+
);
|
|
13137
|
+
server.tool(
|
|
13138
|
+
"get_diff",
|
|
13139
|
+
"Compact diff summary (capped hunks, paginated)",
|
|
13140
|
+
{
|
|
13141
|
+
ref: import_zod.z.string(),
|
|
13142
|
+
maxFiles: import_zod.z.number().optional()
|
|
13143
|
+
},
|
|
13144
|
+
async ({ ref, maxFiles }) => {
|
|
13145
|
+
const summary = await orch.diffSummary(ref);
|
|
12804
13146
|
return {
|
|
12805
13147
|
content: [
|
|
12806
13148
|
{
|
|
12807
13149
|
type: "text",
|
|
12808
13150
|
text: JSON.stringify({
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
worktreePath: restored.worktreePath
|
|
13151
|
+
...summary,
|
|
13152
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
12812
13153
|
})
|
|
12813
13154
|
}
|
|
12814
13155
|
]
|
|
12815
13156
|
};
|
|
12816
|
-
} catch (err) {
|
|
12817
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12818
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12819
13157
|
}
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
13158
|
+
);
|
|
13159
|
+
server.tool(
|
|
13160
|
+
"request_review",
|
|
13161
|
+
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
|
|
13162
|
+
{ ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
|
|
13163
|
+
async ({ ref }) => {
|
|
13164
|
+
try {
|
|
13165
|
+
const tab = await orch.requestReview(ref);
|
|
13166
|
+
const from = orch.getThread(ref);
|
|
13167
|
+
return {
|
|
13168
|
+
content: [
|
|
13169
|
+
{
|
|
13170
|
+
type: "text",
|
|
13171
|
+
text: JSON.stringify({
|
|
13172
|
+
id: tab.id,
|
|
13173
|
+
title: tab.title,
|
|
13174
|
+
status: tab.status,
|
|
13175
|
+
fromThreadId: from?.id ?? ref,
|
|
13176
|
+
link: `sideboard://thread/${tab.id}`
|
|
13177
|
+
})
|
|
13178
|
+
}
|
|
13179
|
+
]
|
|
13180
|
+
};
|
|
13181
|
+
} catch (err) {
|
|
13182
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13183
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13184
|
+
}
|
|
13185
|
+
}
|
|
13186
|
+
);
|
|
13187
|
+
const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
13188
|
+
server.tool(
|
|
13189
|
+
"list_models",
|
|
13190
|
+
"List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
|
|
13191
|
+
{
|
|
13192
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
13193
|
+
},
|
|
13194
|
+
async ({ agent }) => {
|
|
13195
|
+
try {
|
|
13196
|
+
const catalogs = await listModelsForAgent(agent);
|
|
13197
|
+
return {
|
|
13198
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
13199
|
+
};
|
|
13200
|
+
} catch (err) {
|
|
13201
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13202
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13203
|
+
}
|
|
13204
|
+
}
|
|
13205
|
+
);
|
|
13206
|
+
server.tool(
|
|
13207
|
+
"fork_worktree",
|
|
13208
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
13209
|
+
{
|
|
13210
|
+
ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
|
|
13211
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
13212
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
13213
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
13214
|
+
title: import_zod.z.string().optional()
|
|
13215
|
+
},
|
|
13216
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
13217
|
+
try {
|
|
13218
|
+
const source = orch.getThread(ref);
|
|
13219
|
+
if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
|
|
13220
|
+
const thread = await orch.forkThreadWorktree({
|
|
13221
|
+
threadId: ref,
|
|
13222
|
+
throughIndex: through_index,
|
|
13223
|
+
agent,
|
|
13224
|
+
model,
|
|
13225
|
+
title
|
|
13226
|
+
});
|
|
13227
|
+
return {
|
|
13228
|
+
content: [
|
|
13229
|
+
{
|
|
13230
|
+
type: "text",
|
|
13231
|
+
text: JSON.stringify({
|
|
13232
|
+
id: thread.id,
|
|
13233
|
+
title: thread.title,
|
|
13234
|
+
status: thread.status,
|
|
13235
|
+
agent: thread.agent,
|
|
13236
|
+
model: thread.model,
|
|
13237
|
+
branchName: thread.branchName,
|
|
13238
|
+
worktreePath: thread.worktreePath,
|
|
13239
|
+
fromThreadId: source?.id ?? ref,
|
|
13240
|
+
link: `sideboard://thread/${thread.id}`
|
|
13241
|
+
})
|
|
13242
|
+
}
|
|
13243
|
+
]
|
|
13244
|
+
};
|
|
13245
|
+
} catch (err) {
|
|
13246
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13247
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13248
|
+
}
|
|
13249
|
+
}
|
|
13250
|
+
);
|
|
13251
|
+
server.tool(
|
|
13252
|
+
"fork_chat",
|
|
13253
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Remote coordinators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
13254
|
+
{
|
|
13255
|
+
ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
13256
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
13257
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
13258
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
13259
|
+
title: import_zod.z.string().optional()
|
|
13260
|
+
},
|
|
13261
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
13262
|
+
try {
|
|
13263
|
+
const source = orch.getThread(ref);
|
|
13264
|
+
if (!source) {
|
|
13265
|
+
return {
|
|
13266
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13267
|
+
isError: true
|
|
13268
|
+
};
|
|
12839
13269
|
}
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
13270
|
+
const tab = orch.forkChatTab({
|
|
13271
|
+
threadId: source.id,
|
|
13272
|
+
throughIndex: through_index,
|
|
13273
|
+
agent,
|
|
13274
|
+
model,
|
|
13275
|
+
title
|
|
13276
|
+
});
|
|
13277
|
+
return {
|
|
13278
|
+
content: [
|
|
13279
|
+
{
|
|
13280
|
+
type: "text",
|
|
13281
|
+
text: JSON.stringify({
|
|
13282
|
+
id: tab.id,
|
|
13283
|
+
title: tab.title,
|
|
13284
|
+
status: tab.status,
|
|
13285
|
+
agent: tab.agent,
|
|
13286
|
+
model: tab.model,
|
|
13287
|
+
sourceType: tab.sourceType,
|
|
13288
|
+
worktreePath: tab.worktreePath,
|
|
13289
|
+
fromThreadId: source.id,
|
|
13290
|
+
link: `sideboard://thread/${tab.id}`
|
|
13291
|
+
})
|
|
13292
|
+
}
|
|
13293
|
+
]
|
|
13294
|
+
};
|
|
13295
|
+
} catch (err) {
|
|
13296
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13297
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13298
|
+
}
|
|
13299
|
+
}
|
|
13300
|
+
);
|
|
13301
|
+
server.tool(
|
|
13302
|
+
"run_dev_script",
|
|
13303
|
+
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
13304
|
+
{
|
|
13305
|
+
ref: import_zod.z.string(),
|
|
13306
|
+
name: import_zod.z.string().optional()
|
|
13307
|
+
},
|
|
13308
|
+
async ({ ref, name }) => {
|
|
13309
|
+
const result = await orch.startDev(ref, name);
|
|
12852
13310
|
return {
|
|
12853
13311
|
content: [
|
|
12854
13312
|
{
|
|
12855
13313
|
type: "text",
|
|
12856
13314
|
text: JSON.stringify({
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
link: `sideboard://thread/${tab.id}`
|
|
13315
|
+
port: result.port,
|
|
13316
|
+
scriptName: result.scriptName,
|
|
13317
|
+
ports: result.ports,
|
|
13318
|
+
url: `http://localhost:${result.port}`
|
|
12862
13319
|
})
|
|
12863
13320
|
}
|
|
12864
13321
|
]
|
|
12865
13322
|
};
|
|
12866
|
-
} catch (err) {
|
|
12867
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12868
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12869
13323
|
}
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
},
|
|
12879
|
-
async ({ agent }) => {
|
|
12880
|
-
try {
|
|
12881
|
-
const catalogs = await listModelsForAgent(agent);
|
|
13324
|
+
);
|
|
13325
|
+
server.tool(
|
|
13326
|
+
"list_run_scripts",
|
|
13327
|
+
"List named run scripts available for a thread",
|
|
13328
|
+
{ ref: import_zod.z.string() },
|
|
13329
|
+
async ({ ref }) => {
|
|
13330
|
+
const scripts = orch.listThreadRunScripts(ref);
|
|
13331
|
+
const active = orch.getActiveRuns(ref);
|
|
12882
13332
|
return {
|
|
12883
|
-
content: [
|
|
13333
|
+
content: [
|
|
13334
|
+
{
|
|
13335
|
+
type: "text",
|
|
13336
|
+
text: JSON.stringify({ scripts, active }, null, 2)
|
|
13337
|
+
}
|
|
13338
|
+
]
|
|
12884
13339
|
};
|
|
12885
|
-
} catch (err) {
|
|
12886
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12887
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12888
13340
|
}
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
13341
|
+
);
|
|
13342
|
+
server.tool(
|
|
13343
|
+
"stop_dev_script",
|
|
13344
|
+
"Stop a running script for a thread (all scripts if name omitted)",
|
|
13345
|
+
{
|
|
13346
|
+
ref: import_zod.z.string(),
|
|
13347
|
+
name: import_zod.z.string().optional()
|
|
13348
|
+
},
|
|
13349
|
+
async ({ ref, name }) => {
|
|
13350
|
+
orch.stopDev(ref, name);
|
|
13351
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
13352
|
+
}
|
|
13353
|
+
);
|
|
13354
|
+
server.tool(
|
|
13355
|
+
"run_setup",
|
|
13356
|
+
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
13357
|
+
{ ref: import_zod.z.string() },
|
|
13358
|
+
async ({ ref }) => {
|
|
13359
|
+
const result = await orch.runSetup(ref);
|
|
13360
|
+
return {
|
|
13361
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
13362
|
+
};
|
|
13363
|
+
}
|
|
13364
|
+
);
|
|
13365
|
+
server.tool(
|
|
13366
|
+
"add_workspace",
|
|
13367
|
+
"Register a git repo as a Sideboard workspace",
|
|
13368
|
+
{ repoPath: import_zod.z.string() },
|
|
13369
|
+
async ({ repoPath }) => {
|
|
13370
|
+
const ws = await orch.addWorkspace(repoPath);
|
|
13371
|
+
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
13372
|
+
}
|
|
13373
|
+
);
|
|
13374
|
+
server.tool(
|
|
13375
|
+
"remove_workspace",
|
|
13376
|
+
"Unregister a Sideboard workspace (does not archive threads)",
|
|
13377
|
+
{ repoPath: import_zod.z.string() },
|
|
13378
|
+
async ({ repoPath }) => {
|
|
13379
|
+
orch.removeWorkspace(repoPath);
|
|
13380
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
13381
|
+
}
|
|
13382
|
+
);
|
|
13383
|
+
server.tool(
|
|
13384
|
+
"fanout",
|
|
13385
|
+
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
13386
|
+
{
|
|
13387
|
+
prompt: import_zod.z.string(),
|
|
13388
|
+
agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
13389
|
+
repoPath: import_zod.z.string(),
|
|
13390
|
+
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
13391
|
+
sourceRef: import_zod.z.string().optional(),
|
|
13392
|
+
title: import_zod.z.string().optional()
|
|
13393
|
+
},
|
|
13394
|
+
async (args) => {
|
|
13395
|
+
const threads = await orch.bestOfN(args);
|
|
13396
|
+
return {
|
|
13397
|
+
content: [
|
|
13398
|
+
{
|
|
13399
|
+
type: "text",
|
|
13400
|
+
text: JSON.stringify(
|
|
13401
|
+
threads.map((t) => ({
|
|
13402
|
+
id: t.id,
|
|
13403
|
+
agent: t.agent,
|
|
13404
|
+
branchName: t.branchName,
|
|
13405
|
+
worktreePath: t.worktreePath
|
|
13406
|
+
})),
|
|
13407
|
+
null,
|
|
13408
|
+
2
|
|
13409
|
+
)
|
|
13410
|
+
}
|
|
13411
|
+
]
|
|
13412
|
+
};
|
|
13413
|
+
}
|
|
13414
|
+
);
|
|
13415
|
+
server.tool(
|
|
13416
|
+
"list_branches",
|
|
13417
|
+
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
13418
|
+
{
|
|
13419
|
+
repoPath: import_zod.z.string(),
|
|
13420
|
+
unmergedOnly: import_zod.z.boolean().optional()
|
|
13421
|
+
},
|
|
13422
|
+
async ({ repoPath, unmergedOnly }) => {
|
|
13423
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13424
|
+
const branches = await listBranches(root, {
|
|
13425
|
+
unmergedOnly: unmergedOnly !== false
|
|
12911
13426
|
});
|
|
12912
13427
|
return {
|
|
12913
13428
|
content: [
|
|
12914
13429
|
{
|
|
12915
13430
|
type: "text",
|
|
12916
|
-
text:
|
|
12917
|
-
id: thread.id,
|
|
12918
|
-
title: thread.title,
|
|
12919
|
-
status: thread.status,
|
|
12920
|
-
agent: thread.agent,
|
|
12921
|
-
model: thread.model,
|
|
12922
|
-
branchName: thread.branchName,
|
|
12923
|
-
worktreePath: thread.worktreePath,
|
|
12924
|
-
fromThreadId: source?.id ?? ref,
|
|
12925
|
-
link: `sideboard://thread/${thread.id}`
|
|
12926
|
-
})
|
|
13431
|
+
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
12927
13432
|
}
|
|
12928
13433
|
]
|
|
12929
13434
|
};
|
|
12930
|
-
} catch (err) {
|
|
12931
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12932
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12933
13435
|
}
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
13436
|
+
);
|
|
13437
|
+
server.tool(
|
|
13438
|
+
"list_prs",
|
|
13439
|
+
"List open GitHub PRs for a registered workspace. Pass repoPath from list_workspaces (uses gh against that repo remote). Then create_thread with sourceType=pr.",
|
|
13440
|
+
{ repoPath: import_zod.z.string() },
|
|
13441
|
+
async ({ repoPath }) => {
|
|
13442
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13443
|
+
const prs = await listPrs(root);
|
|
13444
|
+
return {
|
|
13445
|
+
content: [
|
|
13446
|
+
{
|
|
13447
|
+
type: "text",
|
|
13448
|
+
text: prs.map(
|
|
13449
|
+
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
13450
|
+
).join("\n")
|
|
13451
|
+
}
|
|
13452
|
+
]
|
|
13453
|
+
};
|
|
13454
|
+
}
|
|
13455
|
+
);
|
|
13456
|
+
server.tool(
|
|
13457
|
+
"get_pr_stack",
|
|
13458
|
+
"Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before mergePr on stacked PRs.",
|
|
13459
|
+
{ ref: import_zod.z.string() },
|
|
13460
|
+
async ({ ref }) => {
|
|
13461
|
+
const stack = await orch.getPrStack(ref);
|
|
13462
|
+
return {
|
|
13463
|
+
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13464
|
+
};
|
|
13465
|
+
}
|
|
13466
|
+
);
|
|
13467
|
+
server.tool(
|
|
13468
|
+
"open_pr_stack_layers",
|
|
13469
|
+
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13470
|
+
{
|
|
13471
|
+
ref: import_zod.z.string(),
|
|
13472
|
+
layer: import_zod.z.number().int().positive().optional()
|
|
13473
|
+
},
|
|
13474
|
+
async ({ ref, layer }) => {
|
|
13475
|
+
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13476
|
+
return {
|
|
13477
|
+
content: [
|
|
13478
|
+
{
|
|
13479
|
+
type: "text",
|
|
13480
|
+
text: JSON.stringify(
|
|
13481
|
+
{
|
|
13482
|
+
stackNumber: result.stack.stackNumber,
|
|
13483
|
+
trunk: result.stack.trunk,
|
|
13484
|
+
threads: result.threads.map((t) => ({
|
|
13485
|
+
id: t.id,
|
|
13486
|
+
title: t.title,
|
|
13487
|
+
branchName: t.branchName,
|
|
13488
|
+
stackLayer: t.stackLayer,
|
|
13489
|
+
worktreePath: t.worktreePath,
|
|
13490
|
+
prUrl: t.prUrl,
|
|
13491
|
+
link: `sideboard://thread/${t.id}`
|
|
13492
|
+
}))
|
|
13493
|
+
},
|
|
13494
|
+
null,
|
|
13495
|
+
2
|
|
13496
|
+
)
|
|
13497
|
+
}
|
|
13498
|
+
]
|
|
13499
|
+
};
|
|
13500
|
+
}
|
|
13501
|
+
);
|
|
13502
|
+
server.tool(
|
|
13503
|
+
"add_stack_layer",
|
|
13504
|
+
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13505
|
+
{
|
|
13506
|
+
ref: import_zod.z.string(),
|
|
13507
|
+
branchName: import_zod.z.string(),
|
|
13508
|
+
title: import_zod.z.string().optional()
|
|
13509
|
+
},
|
|
13510
|
+
async ({ ref, branchName, title }) => {
|
|
13511
|
+
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13512
|
+
return {
|
|
13513
|
+
content: [
|
|
13514
|
+
{
|
|
13515
|
+
type: "text",
|
|
13516
|
+
text: JSON.stringify(
|
|
13517
|
+
{
|
|
13518
|
+
id: result.thread.id,
|
|
13519
|
+
title: result.thread.title,
|
|
13520
|
+
branchName: result.thread.branchName,
|
|
13521
|
+
stackLayer: result.thread.stackLayer,
|
|
13522
|
+
worktreePath: result.thread.worktreePath,
|
|
13523
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
13524
|
+
},
|
|
13525
|
+
null,
|
|
13526
|
+
2
|
|
13527
|
+
)
|
|
13528
|
+
}
|
|
13529
|
+
]
|
|
13530
|
+
};
|
|
13531
|
+
}
|
|
13532
|
+
);
|
|
13533
|
+
server.tool(
|
|
13534
|
+
"create_pr_stack",
|
|
13535
|
+
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13536
|
+
{
|
|
13537
|
+
repoPath: import_zod.z.string(),
|
|
13538
|
+
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13539
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13540
|
+
base: import_zod.z.string().optional(),
|
|
13541
|
+
title: import_zod.z.string().optional()
|
|
13542
|
+
},
|
|
13543
|
+
async (args) => {
|
|
13544
|
+
const result = await orch.createPrStack({
|
|
13545
|
+
repoPath: args.repoPath,
|
|
13546
|
+
branches: args.branches,
|
|
13547
|
+
agent: args.agent,
|
|
13548
|
+
base: args.base,
|
|
13549
|
+
title: args.title
|
|
12961
13550
|
});
|
|
12962
13551
|
return {
|
|
12963
13552
|
content: [
|
|
12964
13553
|
{
|
|
12965
13554
|
type: "text",
|
|
12966
|
-
text: JSON.stringify(
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
13555
|
+
text: JSON.stringify(
|
|
13556
|
+
{
|
|
13557
|
+
stackNumber: result.stack.stackNumber,
|
|
13558
|
+
trunk: result.stack.trunk,
|
|
13559
|
+
threads: result.threads.map((t) => ({
|
|
13560
|
+
id: t.id,
|
|
13561
|
+
title: t.title,
|
|
13562
|
+
branchName: t.branchName,
|
|
13563
|
+
stackLayer: t.stackLayer,
|
|
13564
|
+
worktreePath: t.worktreePath,
|
|
13565
|
+
link: `sideboard://thread/${t.id}`
|
|
13566
|
+
}))
|
|
13567
|
+
},
|
|
13568
|
+
null,
|
|
13569
|
+
2
|
|
13570
|
+
)
|
|
12977
13571
|
}
|
|
12978
13572
|
]
|
|
12979
13573
|
};
|
|
12980
|
-
} catch (err) {
|
|
12981
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12982
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12983
13574
|
}
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
}
|
|
13009
|
-
);
|
|
13010
|
-
server.tool(
|
|
13011
|
-
"list_run_scripts",
|
|
13012
|
-
"List named run scripts available for a thread",
|
|
13013
|
-
{ ref: import_zod.z.string() },
|
|
13014
|
-
async ({ ref }) => {
|
|
13015
|
-
const scripts = orch.listThreadRunScripts(ref);
|
|
13016
|
-
const active = orch.getActiveRuns(ref);
|
|
13017
|
-
return {
|
|
13018
|
-
content: [
|
|
13019
|
-
{
|
|
13020
|
-
type: "text",
|
|
13021
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
13022
|
-
}
|
|
13023
|
-
]
|
|
13024
|
-
};
|
|
13025
|
-
}
|
|
13026
|
-
);
|
|
13027
|
-
server.tool(
|
|
13028
|
-
"stop_dev_script",
|
|
13029
|
-
"Stop a running script for a thread (all scripts if name omitted)",
|
|
13030
|
-
{
|
|
13031
|
-
ref: import_zod.z.string(),
|
|
13032
|
-
name: import_zod.z.string().optional()
|
|
13033
|
-
},
|
|
13034
|
-
async ({ ref, name }) => {
|
|
13035
|
-
orch.stopDev(ref, name);
|
|
13036
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
13037
|
-
}
|
|
13038
|
-
);
|
|
13039
|
-
server.tool(
|
|
13040
|
-
"run_setup",
|
|
13041
|
-
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
13042
|
-
{ ref: import_zod.z.string() },
|
|
13043
|
-
async ({ ref }) => {
|
|
13044
|
-
const result = await orch.runSetup(ref);
|
|
13045
|
-
return {
|
|
13046
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
13047
|
-
};
|
|
13048
|
-
}
|
|
13049
|
-
);
|
|
13050
|
-
server.tool(
|
|
13051
|
-
"add_workspace",
|
|
13052
|
-
"Register a git repo as a Sideboard workspace",
|
|
13053
|
-
{ repoPath: import_zod.z.string() },
|
|
13054
|
-
async ({ repoPath }) => {
|
|
13055
|
-
const ws = await orch.addWorkspace(repoPath);
|
|
13056
|
-
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
13057
|
-
}
|
|
13058
|
-
);
|
|
13059
|
-
server.tool(
|
|
13060
|
-
"remove_workspace",
|
|
13061
|
-
"Unregister a Sideboard workspace (does not archive threads)",
|
|
13062
|
-
{ repoPath: import_zod.z.string() },
|
|
13063
|
-
async ({ repoPath }) => {
|
|
13064
|
-
orch.removeWorkspace(repoPath);
|
|
13065
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
13066
|
-
}
|
|
13067
|
-
);
|
|
13068
|
-
server.tool(
|
|
13069
|
-
"fanout",
|
|
13070
|
-
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
13071
|
-
{
|
|
13072
|
-
prompt: import_zod.z.string(),
|
|
13073
|
-
agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
13074
|
-
repoPath: import_zod.z.string(),
|
|
13075
|
-
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
13076
|
-
sourceRef: import_zod.z.string().optional(),
|
|
13077
|
-
title: import_zod.z.string().optional()
|
|
13078
|
-
},
|
|
13079
|
-
async (args) => {
|
|
13080
|
-
const threads = await orch.bestOfN(args);
|
|
13081
|
-
return {
|
|
13082
|
-
content: [
|
|
13083
|
-
{
|
|
13084
|
-
type: "text",
|
|
13085
|
-
text: JSON.stringify(
|
|
13086
|
-
threads.map((t) => ({
|
|
13087
|
-
id: t.id,
|
|
13088
|
-
agent: t.agent,
|
|
13089
|
-
branchName: t.branchName,
|
|
13090
|
-
worktreePath: t.worktreePath
|
|
13091
|
-
})),
|
|
13092
|
-
null,
|
|
13093
|
-
2
|
|
13094
|
-
)
|
|
13095
|
-
}
|
|
13096
|
-
]
|
|
13097
|
-
};
|
|
13098
|
-
}
|
|
13099
|
-
);
|
|
13100
|
-
server.tool(
|
|
13101
|
-
"list_branches",
|
|
13102
|
-
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
13103
|
-
{
|
|
13104
|
-
repoPath: import_zod.z.string(),
|
|
13105
|
-
unmergedOnly: import_zod.z.boolean().optional()
|
|
13106
|
-
},
|
|
13107
|
-
async ({ repoPath, unmergedOnly }) => {
|
|
13108
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13109
|
-
const branches = await listBranches(root, {
|
|
13110
|
-
unmergedOnly: unmergedOnly !== false
|
|
13111
|
-
});
|
|
13112
|
-
return {
|
|
13113
|
-
content: [
|
|
13114
|
-
{
|
|
13115
|
-
type: "text",
|
|
13116
|
-
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
13117
|
-
}
|
|
13118
|
-
]
|
|
13119
|
-
};
|
|
13120
|
-
}
|
|
13121
|
-
);
|
|
13122
|
-
server.tool(
|
|
13123
|
-
"list_prs",
|
|
13124
|
-
"List open GitHub PRs for a registered workspace. Pass repoPath from list_workspaces (uses gh against that repo remote). Then create_thread with sourceType=pr.",
|
|
13125
|
-
{ repoPath: import_zod.z.string() },
|
|
13126
|
-
async ({ repoPath }) => {
|
|
13127
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13128
|
-
const prs = await listPrs(root);
|
|
13129
|
-
return {
|
|
13130
|
-
content: [
|
|
13131
|
-
{
|
|
13132
|
-
type: "text",
|
|
13133
|
-
text: prs.map(
|
|
13134
|
-
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
13135
|
-
).join("\n")
|
|
13136
|
-
}
|
|
13137
|
-
]
|
|
13138
|
-
};
|
|
13139
|
-
}
|
|
13140
|
-
);
|
|
13141
|
-
server.tool(
|
|
13142
|
-
"get_pr_stack",
|
|
13143
|
-
"Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before mergePr on stacked PRs.",
|
|
13144
|
-
{ ref: import_zod.z.string() },
|
|
13145
|
-
async ({ ref }) => {
|
|
13146
|
-
const stack = await orch.getPrStack(ref);
|
|
13147
|
-
return {
|
|
13148
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13149
|
-
};
|
|
13150
|
-
}
|
|
13151
|
-
);
|
|
13152
|
-
server.tool(
|
|
13153
|
-
"open_pr_stack_layers",
|
|
13154
|
-
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13155
|
-
{
|
|
13156
|
-
ref: import_zod.z.string(),
|
|
13157
|
-
layer: import_zod.z.number().int().positive().optional()
|
|
13158
|
-
},
|
|
13159
|
-
async ({ ref, layer }) => {
|
|
13160
|
-
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13161
|
-
return {
|
|
13162
|
-
content: [
|
|
13163
|
-
{
|
|
13164
|
-
type: "text",
|
|
13165
|
-
text: JSON.stringify(
|
|
13166
|
-
{
|
|
13167
|
-
stackNumber: result.stack.stackNumber,
|
|
13168
|
-
trunk: result.stack.trunk,
|
|
13169
|
-
threads: result.threads.map((t) => ({
|
|
13170
|
-
id: t.id,
|
|
13171
|
-
title: t.title,
|
|
13172
|
-
branchName: t.branchName,
|
|
13173
|
-
stackLayer: t.stackLayer,
|
|
13174
|
-
worktreePath: t.worktreePath,
|
|
13175
|
-
prUrl: t.prUrl,
|
|
13176
|
-
link: `sideboard://thread/${t.id}`
|
|
13177
|
-
}))
|
|
13178
|
-
},
|
|
13179
|
-
null,
|
|
13180
|
-
2
|
|
13181
|
-
)
|
|
13182
|
-
}
|
|
13183
|
-
]
|
|
13184
|
-
};
|
|
13185
|
-
}
|
|
13186
|
-
);
|
|
13187
|
-
server.tool(
|
|
13188
|
-
"add_stack_layer",
|
|
13189
|
-
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13190
|
-
{
|
|
13191
|
-
ref: import_zod.z.string(),
|
|
13192
|
-
branchName: import_zod.z.string(),
|
|
13193
|
-
title: import_zod.z.string().optional()
|
|
13194
|
-
},
|
|
13195
|
-
async ({ ref, branchName, title }) => {
|
|
13196
|
-
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13197
|
-
return {
|
|
13198
|
-
content: [
|
|
13199
|
-
{
|
|
13200
|
-
type: "text",
|
|
13201
|
-
text: JSON.stringify(
|
|
13202
|
-
{
|
|
13203
|
-
id: result.thread.id,
|
|
13204
|
-
title: result.thread.title,
|
|
13205
|
-
branchName: result.thread.branchName,
|
|
13206
|
-
stackLayer: result.thread.stackLayer,
|
|
13207
|
-
worktreePath: result.thread.worktreePath,
|
|
13208
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
13209
|
-
},
|
|
13210
|
-
null,
|
|
13211
|
-
2
|
|
13212
|
-
)
|
|
13213
|
-
}
|
|
13214
|
-
]
|
|
13215
|
-
};
|
|
13216
|
-
}
|
|
13217
|
-
);
|
|
13218
|
-
server.tool(
|
|
13219
|
-
"create_pr_stack",
|
|
13220
|
-
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13221
|
-
{
|
|
13222
|
-
repoPath: import_zod.z.string(),
|
|
13223
|
-
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13224
|
-
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13225
|
-
base: import_zod.z.string().optional(),
|
|
13226
|
-
title: import_zod.z.string().optional()
|
|
13227
|
-
},
|
|
13228
|
-
async (args) => {
|
|
13229
|
-
const result = await orch.createPrStack({
|
|
13230
|
-
repoPath: args.repoPath,
|
|
13231
|
-
branches: args.branches,
|
|
13232
|
-
agent: args.agent,
|
|
13233
|
-
base: args.base,
|
|
13234
|
-
title: args.title
|
|
13235
|
-
});
|
|
13236
|
-
return {
|
|
13237
|
-
content: [
|
|
13238
|
-
{
|
|
13239
|
-
type: "text",
|
|
13240
|
-
text: JSON.stringify(
|
|
13241
|
-
{
|
|
13242
|
-
stackNumber: result.stack.stackNumber,
|
|
13243
|
-
trunk: result.stack.trunk,
|
|
13244
|
-
threads: result.threads.map((t) => ({
|
|
13245
|
-
id: t.id,
|
|
13246
|
-
title: t.title,
|
|
13247
|
-
branchName: t.branchName,
|
|
13248
|
-
stackLayer: t.stackLayer,
|
|
13249
|
-
worktreePath: t.worktreePath,
|
|
13250
|
-
link: `sideboard://thread/${t.id}`
|
|
13251
|
-
}))
|
|
13252
|
-
},
|
|
13253
|
-
null,
|
|
13254
|
-
2
|
|
13255
|
-
)
|
|
13256
|
-
}
|
|
13257
|
-
]
|
|
13258
|
-
};
|
|
13259
|
-
}
|
|
13260
|
-
);
|
|
13261
|
-
server.tool(
|
|
13262
|
-
"list_issues",
|
|
13263
|
-
"List issues from Sideboard Account connections (Linear API or GitHub Issues; Linear\u2192GitHub fallback when Linear is not connected). Pass repoPath from list_workspaces \u2014 GitHub Issues are scoped to that repo. Then create_thread with sourceType=ticket.",
|
|
13264
|
-
{ repoPath: import_zod.z.string() },
|
|
13265
|
-
async ({ repoPath }) => {
|
|
13266
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13267
|
-
const result = await listIssues(root);
|
|
13268
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13269
|
-
}
|
|
13270
|
-
);
|
|
13271
|
-
server.tool(
|
|
13272
|
-
"list_linear_issues",
|
|
13273
|
-
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
13274
|
-
{
|
|
13275
|
-
agent: import_zod.z.enum(["claude", "codex", "opencode"]),
|
|
13276
|
-
repoPath: import_zod.z.string()
|
|
13277
|
-
},
|
|
13278
|
-
async ({ agent, repoPath }) => {
|
|
13279
|
-
const issues = await listLinearIssues(agent, repoPath);
|
|
13280
|
-
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
13281
|
-
}
|
|
13282
|
-
);
|
|
13575
|
+
);
|
|
13576
|
+
server.tool(
|
|
13577
|
+
"list_issues",
|
|
13578
|
+
"List issues from Sideboard Account connections (Linear API or GitHub Issues; Linear\u2192GitHub fallback when Linear is not connected). Pass repoPath from list_workspaces \u2014 GitHub Issues are scoped to that repo. Then create_thread with sourceType=ticket.",
|
|
13579
|
+
{ repoPath: import_zod.z.string() },
|
|
13580
|
+
async ({ repoPath }) => {
|
|
13581
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13582
|
+
const result = await listIssues(root);
|
|
13583
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13584
|
+
}
|
|
13585
|
+
);
|
|
13586
|
+
server.tool(
|
|
13587
|
+
"list_linear_issues",
|
|
13588
|
+
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
13589
|
+
{
|
|
13590
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode"]),
|
|
13591
|
+
repoPath: import_zod.z.string()
|
|
13592
|
+
},
|
|
13593
|
+
async ({ agent, repoPath }) => {
|
|
13594
|
+
const issues = await listLinearIssues(agent, repoPath);
|
|
13595
|
+
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
13596
|
+
}
|
|
13597
|
+
);
|
|
13598
|
+
}
|
|
13283
13599
|
const transport = new import_stdio.StdioServerTransport();
|
|
13284
13600
|
await server.connect(transport);
|
|
13285
13601
|
}
|