@sideboard-ai/core 0.1.62 → 0.1.66
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-QLSFIQAO.js → agents-EWFD765A.js} +23 -7
- package/dist/{agents-KF3J3C53.js → agents-MD7SCFYX.js} +23 -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-RIWUU7FO.js → chunk-5AG5BUQN.js} +61 -3
- package/dist/{chunk-27MAWNKY.js → chunk-AKPEXIKK.js} +2 -2
- package/dist/{chunk-2MCNURQV.js → chunk-BBEH2CJE.js} +264 -93
- package/dist/{chunk-DDN3UF4R.js → chunk-BPOTVI5J.js} +115 -3
- package/dist/{chunk-2FEA4LDA.js → chunk-DNPP7QMK.js} +31 -6
- package/dist/{chunk-MBAL5FE6.js → chunk-DZYMNW2A.js} +4 -4
- package/dist/{chunk-JB2H7E7V.js → chunk-EVD4KNKR.js} +1 -1
- package/dist/{chunk-A6FOKNOU.js → chunk-HTOTTJJX.js} +2 -2
- package/dist/{chunk-3KKNTHUC.js → chunk-I2KGRAXC.js} +1 -1
- package/dist/{chunk-ZF5YJCXO.js → chunk-IFAU2DEY.js} +269 -93
- package/dist/{chunk-NVU7EC2E.js → chunk-LRBRQFBU.js} +2 -2
- package/dist/{chunk-6CUJQKTJ.js → chunk-MHR4OGZ4.js} +115 -3
- package/dist/{chunk-46KMKHSD.js → chunk-PESJDZTB.js} +2 -2
- package/dist/{chunk-UGXSSBFJ.js → chunk-R7GUDEPY.js} +1 -1
- package/dist/{chunk-73AT5K4A.js → chunk-TXQ6K2GR.js} +61 -3
- package/dist/{chunk-DXTGDXO2.js → chunk-YTNLOJPK.js} +31 -6
- package/dist/{connected-teams-RSL4VB3X.js → connected-teams-CHXO5FFA.js} +2 -2
- package/dist/{connected-teams-MKIYKOYA.js → connected-teams-GJTESSTX.js} +2 -2
- package/dist/{coordinator-prompt-LKG3SHMI.js → coordinator-prompt-E5UGSJES.js} +4 -4
- package/dist/{coordinator-prompt-N2R6FGZK.js → coordinator-prompt-ILSULI7C.js} +4 -4
- package/dist/{global-workspace-LALWQWRJ.js → global-workspace-44UG64NA.js} +5 -6
- package/dist/{global-workspace-YOAUQCMA.js → global-workspace-5PM5LA6E.js} +5 -6
- package/dist/index.cjs +1308 -925
- package/dist/index.d.cts +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +746 -715
- package/dist/mcp/run-stdio.cjs +1267 -976
- package/dist/mcp/run-stdio.js +691 -748
- package/dist/{run-HZTBWGQR.js → run-74S2NZWB.js} +1 -1
- package/dist/{run-GPSVV5OH.js → run-JLN5JK7Q.js} +1 -1
- package/dist/{workspaces-VCAIHVIE.js → workspaces-OVBCSS5Y.js} +6 -7
- package/dist/{workspaces-E75POJXP.js → workspaces-RXFXQRRF.js} +6 -7
- package/dist/{worktree-MV4WRW6G.js → worktree-K2JB53R5.js} +2 -2
- package/dist/{worktree-LZEGVQSN.js → worktree-S2N63FLV.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-GI7E5733.js +0 -30
- package/dist/chunk-S2LL5HA4.js +0 -33
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1507,18 +1507,28 @@ 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
|
+
}
|
|
1510
1520
|
function ensureAgentPath(env = process.env) {
|
|
1511
1521
|
const home = env.HOME || env.USERPROFILE || (0, import_node_os3.homedir)();
|
|
1512
1522
|
const current = env.PATH ?? "";
|
|
1513
1523
|
const parts = current.split(import_node_path4.delimiter).filter(Boolean);
|
|
1514
1524
|
const seen = new Set(parts);
|
|
1515
1525
|
const extras = [
|
|
1516
|
-
...EXTRA_BIN_DIRS.map((rel) =>
|
|
1526
|
+
...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path4.join)(home, rel)),
|
|
1517
1527
|
"/opt/homebrew/bin",
|
|
1518
1528
|
"/usr/local/bin"
|
|
1519
1529
|
];
|
|
1520
1530
|
for (const dir of extras.reverse()) {
|
|
1521
|
-
if (!dir || seen.has(dir)) continue;
|
|
1531
|
+
if (!dir || seen.has(dir) || !(0, import_node_fs4.existsSync)(dir)) continue;
|
|
1522
1532
|
parts.unshift(dir);
|
|
1523
1533
|
seen.add(dir);
|
|
1524
1534
|
}
|
|
@@ -1526,10 +1536,50 @@ function ensureAgentPath(env = process.env) {
|
|
|
1526
1536
|
env.PATH = next;
|
|
1527
1537
|
return next;
|
|
1528
1538
|
}
|
|
1529
|
-
|
|
1539
|
+
function enrichPathWithNpmGlobalBin(env = process.env) {
|
|
1540
|
+
ensureAgentPath(env);
|
|
1541
|
+
try {
|
|
1542
|
+
const prefix = (0, import_node_child_process.execFileSync)("npm", ["prefix", "-g"], {
|
|
1543
|
+
encoding: "utf8",
|
|
1544
|
+
env: { ...process.env, ...env },
|
|
1545
|
+
timeout: 8e3,
|
|
1546
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1547
|
+
}).trim().split(/\r?\n/).find(Boolean);
|
|
1548
|
+
if (prefix) {
|
|
1549
|
+
const binDir = process.platform === "win32" ? prefix : (0, import_node_path4.join)(prefix, "bin");
|
|
1550
|
+
prependPathDir(env, binDir);
|
|
1551
|
+
}
|
|
1552
|
+
} catch {
|
|
1553
|
+
}
|
|
1554
|
+
return env.PATH ?? "";
|
|
1555
|
+
}
|
|
1556
|
+
function posixShellSingleQuote(value) {
|
|
1557
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1558
|
+
}
|
|
1559
|
+
function resolveCommandBinarySync(command, whichPath) {
|
|
1560
|
+
const trimmed = command.trim();
|
|
1561
|
+
if (!trimmed) return trimmed;
|
|
1562
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
1563
|
+
if (!match) return trimmed;
|
|
1564
|
+
const bin = match[1];
|
|
1565
|
+
const rest = match[2] ?? "";
|
|
1566
|
+
if (bin.includes("/") || bin.includes("\\")) return trimmed;
|
|
1567
|
+
const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
|
|
1568
|
+
if (!abs) return trimmed;
|
|
1569
|
+
return `${abs}${rest}`;
|
|
1570
|
+
}
|
|
1571
|
+
function withExportedPath(command, pathValue) {
|
|
1572
|
+
const trimmed = command.trim();
|
|
1573
|
+
if (!trimmed) return trimmed;
|
|
1574
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
1575
|
+
return `export PATH=${posixShellSingleQuote(pathValue)}; ${trimmed}`;
|
|
1576
|
+
}
|
|
1577
|
+
var import_node_fs4, import_node_child_process, import_node_os3, import_node_path4, EXTRA_BIN_DIRS;
|
|
1530
1578
|
var init_path = __esm({
|
|
1531
1579
|
"src/agents/path.ts"() {
|
|
1532
1580
|
"use strict";
|
|
1581
|
+
import_node_fs4 = require("fs");
|
|
1582
|
+
import_node_child_process = require("child_process");
|
|
1533
1583
|
import_node_os3 = require("os");
|
|
1534
1584
|
import_node_path4 = require("path");
|
|
1535
1585
|
EXTRA_BIN_DIRS = [
|
|
@@ -1537,6 +1587,10 @@ var init_path = __esm({
|
|
|
1537
1587
|
".cargo/bin",
|
|
1538
1588
|
".nvm/current/bin",
|
|
1539
1589
|
".asdf/shims",
|
|
1590
|
+
".volta/bin",
|
|
1591
|
+
".npm-global/bin",
|
|
1592
|
+
// fnm default alias (common when shell init is skipped)
|
|
1593
|
+
".local/share/fnm/aliases/default/bin",
|
|
1540
1594
|
// pnpm / npm global bins (where `@brightsy/cli` typically lands)
|
|
1541
1595
|
"Library/pnpm",
|
|
1542
1596
|
".pnpm"
|
|
@@ -2596,7 +2650,7 @@ function isLocalPrFetchBranch(ref) {
|
|
|
2596
2650
|
async function createThreadWorktree(opts) {
|
|
2597
2651
|
let branchName = `thread/${opts.slug}`;
|
|
2598
2652
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2599
|
-
if ((0,
|
|
2653
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2600
2654
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2601
2655
|
}
|
|
2602
2656
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2666,7 +2720,7 @@ async function createExistingBranchWorktree(opts) {
|
|
|
2666
2720
|
const branchName = opts.branchName.trim();
|
|
2667
2721
|
if (!branchName) throw new Error("branch name required");
|
|
2668
2722
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2669
|
-
if ((0,
|
|
2723
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2670
2724
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2671
2725
|
}
|
|
2672
2726
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2927,9 +2981,9 @@ function sameRepoPath(a, b) {
|
|
|
2927
2981
|
}
|
|
2928
2982
|
function listLocalThreadBranchSlugs(repoPath) {
|
|
2929
2983
|
const refsDir = (0, import_node_path5.join)(repoPath, ".git", "refs", "heads", "thread");
|
|
2930
|
-
if (!(0,
|
|
2984
|
+
if (!(0, import_node_fs5.existsSync)(refsDir)) return [];
|
|
2931
2985
|
try {
|
|
2932
|
-
return (0,
|
|
2986
|
+
return (0, import_node_fs5.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
|
|
2933
2987
|
} catch {
|
|
2934
2988
|
return [];
|
|
2935
2989
|
}
|
|
@@ -2937,8 +2991,8 @@ function listLocalThreadBranchSlugs(repoPath) {
|
|
|
2937
2991
|
function collectTakenTeamSlugs(repoPath) {
|
|
2938
2992
|
const taken = /* @__PURE__ */ new Set();
|
|
2939
2993
|
const root = worktreesRoot(repoPath);
|
|
2940
|
-
if ((0,
|
|
2941
|
-
for (const entry of (0,
|
|
2994
|
+
if ((0, import_node_fs5.existsSync)(root)) {
|
|
2995
|
+
for (const entry of (0, import_node_fs5.readdirSync)(root, { withFileTypes: true })) {
|
|
2942
2996
|
if (entry.isDirectory() && entry.name !== ".DS_Store") {
|
|
2943
2997
|
taken.add(normalizeTakenSlug(entry.name));
|
|
2944
2998
|
}
|
|
@@ -2960,16 +3014,16 @@ function allocateTeamSlug(repoPath) {
|
|
|
2960
3014
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
2961
3015
|
const team = allocateTeamName(taken);
|
|
2962
3016
|
const path = (0, import_node_path5.join)(worktreesRoot(repoPath), team.slug);
|
|
2963
|
-
if (!(0,
|
|
3017
|
+
if (!(0, import_node_fs5.existsSync)(path)) return team;
|
|
2964
3018
|
taken.add(team.slug);
|
|
2965
3019
|
}
|
|
2966
3020
|
throw new Error("No available soccer team worktree directories left");
|
|
2967
3021
|
}
|
|
2968
|
-
var
|
|
3022
|
+
var import_node_fs5, import_node_path5;
|
|
2969
3023
|
var init_worktree = __esm({
|
|
2970
3024
|
"src/git/worktree.ts"() {
|
|
2971
3025
|
"use strict";
|
|
2972
|
-
|
|
3026
|
+
import_node_fs5 = require("fs");
|
|
2973
3027
|
import_node_path5 = require("path");
|
|
2974
3028
|
init_paths();
|
|
2975
3029
|
init_thread_store();
|
|
@@ -2997,6 +3051,7 @@ __export(app_settings_exports, {
|
|
|
2997
3051
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
2998
3052
|
brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
|
|
2999
3053
|
brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
|
|
3054
|
+
brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
|
|
3000
3055
|
caffeinateWhileCloudConnectEnabled: () => caffeinateWhileCloudConnectEnabled,
|
|
3001
3056
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
3002
3057
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
@@ -3015,17 +3070,21 @@ __export(app_settings_exports, {
|
|
|
3015
3070
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
3016
3071
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
3017
3072
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
3073
|
+
resolveAgentExecutable: () => resolveAgentExecutable,
|
|
3018
3074
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
3019
3075
|
resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
|
|
3020
3076
|
resolveNewThreadOptions: () => resolveNewThreadOptions,
|
|
3021
3077
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
3022
3078
|
saveAppSettings: () => saveAppSettings,
|
|
3023
3079
|
updateAdvancedSettings: () => updateAdvancedSettings,
|
|
3080
|
+
updateAgentExecutable: () => updateAgentExecutable,
|
|
3024
3081
|
updateAppEnvironment: () => updateAppEnvironment,
|
|
3025
3082
|
updateBrightsySettings: () => updateBrightsySettings,
|
|
3026
3083
|
updateClaudeSettings: () => updateClaudeSettings,
|
|
3084
|
+
updateCodexSettings: () => updateCodexSettings,
|
|
3027
3085
|
updateDefaultsSettings: () => updateDefaultsSettings,
|
|
3028
|
-
updateIntegrationsSettings: () => updateIntegrationsSettings
|
|
3086
|
+
updateIntegrationsSettings: () => updateIntegrationsSettings,
|
|
3087
|
+
updateOpencodeSettings: () => updateOpencodeSettings
|
|
3029
3088
|
});
|
|
3030
3089
|
function appSettingsPath() {
|
|
3031
3090
|
return (0, import_node_path6.join)(appDataDir(), "settings.json");
|
|
@@ -3046,16 +3105,33 @@ function normalizeClaude(raw) {
|
|
|
3046
3105
|
}
|
|
3047
3106
|
return out;
|
|
3048
3107
|
}
|
|
3108
|
+
function normalizeCliExecutable(raw) {
|
|
3109
|
+
if (!raw || typeof raw !== "object") return {};
|
|
3110
|
+
const source = raw;
|
|
3111
|
+
const out = {};
|
|
3112
|
+
if (typeof source.executablePath === "string") {
|
|
3113
|
+
const path = source.executablePath.trim();
|
|
3114
|
+
if (path) out.executablePath = path;
|
|
3115
|
+
}
|
|
3116
|
+
return out;
|
|
3117
|
+
}
|
|
3049
3118
|
function normalizeBrightsy(raw) {
|
|
3050
3119
|
if (!raw || typeof raw !== "object") return {};
|
|
3051
3120
|
const source = raw;
|
|
3052
3121
|
const out = {};
|
|
3122
|
+
if (typeof source.executablePath === "string") {
|
|
3123
|
+
const path = source.executablePath.trim();
|
|
3124
|
+
if (path) out.executablePath = path;
|
|
3125
|
+
}
|
|
3053
3126
|
if (typeof source.cloudConnectEnabled === "boolean") {
|
|
3054
3127
|
out.cloudConnectEnabled = source.cloudConnectEnabled;
|
|
3055
3128
|
}
|
|
3056
3129
|
if (typeof source.cloudConnectAgent === "string" && CLOUD_CONNECT_AGENTS.has(source.cloudConnectAgent)) {
|
|
3057
3130
|
out.cloudConnectAgent = source.cloudConnectAgent;
|
|
3058
3131
|
}
|
|
3132
|
+
if (typeof source.injectWorktreeMcp === "boolean") {
|
|
3133
|
+
out.injectWorktreeMcp = source.injectWorktreeMcp;
|
|
3134
|
+
}
|
|
3059
3135
|
return out;
|
|
3060
3136
|
}
|
|
3061
3137
|
function normalizeIntegrations(raw) {
|
|
@@ -3141,6 +3217,8 @@ function normalizeAdvanced(raw) {
|
|
|
3141
3217
|
function normalizeSettings(raw) {
|
|
3142
3218
|
const env = {};
|
|
3143
3219
|
let claude = {};
|
|
3220
|
+
let codex = {};
|
|
3221
|
+
let opencode = {};
|
|
3144
3222
|
let brightsy = {};
|
|
3145
3223
|
let integrations = {};
|
|
3146
3224
|
let defaults = {};
|
|
@@ -3159,6 +3237,12 @@ function normalizeSettings(raw) {
|
|
|
3159
3237
|
if ("claude" in raw) {
|
|
3160
3238
|
claude = normalizeClaude(raw.claude);
|
|
3161
3239
|
}
|
|
3240
|
+
if ("codex" in raw) {
|
|
3241
|
+
codex = normalizeCliExecutable(raw.codex);
|
|
3242
|
+
}
|
|
3243
|
+
if ("opencode" in raw) {
|
|
3244
|
+
opencode = normalizeCliExecutable(raw.opencode);
|
|
3245
|
+
}
|
|
3162
3246
|
if ("brightsy" in raw) {
|
|
3163
3247
|
brightsy = normalizeBrightsy(raw.brightsy);
|
|
3164
3248
|
}
|
|
@@ -3174,15 +3258,24 @@ function normalizeSettings(raw) {
|
|
|
3174
3258
|
advanced = normalizeAdvanced(raw.advanced);
|
|
3175
3259
|
}
|
|
3176
3260
|
}
|
|
3177
|
-
return {
|
|
3261
|
+
return {
|
|
3262
|
+
environment: env,
|
|
3263
|
+
claude,
|
|
3264
|
+
codex,
|
|
3265
|
+
opencode,
|
|
3266
|
+
brightsy,
|
|
3267
|
+
integrations,
|
|
3268
|
+
defaults,
|
|
3269
|
+
advanced
|
|
3270
|
+
};
|
|
3178
3271
|
}
|
|
3179
3272
|
function loadAppSettings() {
|
|
3180
3273
|
const path = appSettingsPath();
|
|
3181
|
-
if (!(0,
|
|
3274
|
+
if (!(0, import_node_fs6.existsSync)(path)) {
|
|
3182
3275
|
return { ...EMPTY_SETTINGS };
|
|
3183
3276
|
}
|
|
3184
3277
|
try {
|
|
3185
|
-
const parsed = JSON.parse((0,
|
|
3278
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
|
|
3186
3279
|
return normalizeSettings(parsed);
|
|
3187
3280
|
} catch {
|
|
3188
3281
|
return { ...EMPTY_SETTINGS };
|
|
@@ -3190,8 +3283,8 @@ function loadAppSettings() {
|
|
|
3190
3283
|
}
|
|
3191
3284
|
function saveAppSettings(settings) {
|
|
3192
3285
|
const normalized = normalizeSettings(settings);
|
|
3193
|
-
(0,
|
|
3194
|
-
(0,
|
|
3286
|
+
(0, import_node_fs6.mkdirSync)(appDataDir(), { recursive: true });
|
|
3287
|
+
(0, import_node_fs6.writeFileSync)(appSettingsPath(), `${JSON.stringify(normalized, null, 2)}
|
|
3195
3288
|
`, "utf8");
|
|
3196
3289
|
return normalized;
|
|
3197
3290
|
}
|
|
@@ -3227,6 +3320,13 @@ function updateClaudeSettings(patch) {
|
|
|
3227
3320
|
function updateBrightsySettings(patch) {
|
|
3228
3321
|
const current = loadAppSettings();
|
|
3229
3322
|
const brightsy = { ...current.brightsy };
|
|
3323
|
+
if ("executablePath" in patch) {
|
|
3324
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3325
|
+
delete brightsy.executablePath;
|
|
3326
|
+
} else {
|
|
3327
|
+
brightsy.executablePath = patch.executablePath.trim();
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3230
3330
|
if (typeof patch.cloudConnectEnabled === "boolean") {
|
|
3231
3331
|
brightsy.cloudConnectEnabled = patch.cloudConnectEnabled;
|
|
3232
3332
|
}
|
|
@@ -3237,8 +3337,35 @@ function updateBrightsySettings(patch) {
|
|
|
3237
3337
|
brightsy.cloudConnectAgent = patch.cloudConnectAgent;
|
|
3238
3338
|
}
|
|
3239
3339
|
}
|
|
3340
|
+
if (typeof patch.injectWorktreeMcp === "boolean") {
|
|
3341
|
+
brightsy.injectWorktreeMcp = patch.injectWorktreeMcp;
|
|
3342
|
+
}
|
|
3240
3343
|
return saveAppSettings({ ...current, brightsy });
|
|
3241
3344
|
}
|
|
3345
|
+
function updateCodexSettings(patch) {
|
|
3346
|
+
const current = loadAppSettings();
|
|
3347
|
+
const codex = { ...current.codex };
|
|
3348
|
+
if ("executablePath" in patch) {
|
|
3349
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3350
|
+
delete codex.executablePath;
|
|
3351
|
+
} else {
|
|
3352
|
+
codex.executablePath = patch.executablePath.trim();
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
return saveAppSettings({ ...current, codex });
|
|
3356
|
+
}
|
|
3357
|
+
function updateOpencodeSettings(patch) {
|
|
3358
|
+
const current = loadAppSettings();
|
|
3359
|
+
const opencode = { ...current.opencode };
|
|
3360
|
+
if ("executablePath" in patch) {
|
|
3361
|
+
if (patch.executablePath == null || patch.executablePath.trim() === "") {
|
|
3362
|
+
delete opencode.executablePath;
|
|
3363
|
+
} else {
|
|
3364
|
+
opencode.executablePath = patch.executablePath.trim();
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
return saveAppSettings({ ...current, opencode });
|
|
3368
|
+
}
|
|
3242
3369
|
function updateIntegrationsSettings(patch) {
|
|
3243
3370
|
const current = loadAppSettings();
|
|
3244
3371
|
const integrations = { ...current.integrations };
|
|
@@ -3346,6 +3473,9 @@ function getLinearApiKey(settings = loadAppSettings()) {
|
|
|
3346
3473
|
function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
|
|
3347
3474
|
return Boolean(settings.brightsy.cloudConnectEnabled);
|
|
3348
3475
|
}
|
|
3476
|
+
function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
|
|
3477
|
+
return Boolean(settings.brightsy.injectWorktreeMcp);
|
|
3478
|
+
}
|
|
3349
3479
|
function brightsyCloudConnectAgent(settings = loadAppSettings()) {
|
|
3350
3480
|
const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
|
|
3351
3481
|
const preferred = getDefaultAgent(settings);
|
|
@@ -3440,8 +3570,36 @@ function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
|
3440
3570
|
return 3;
|
|
3441
3571
|
}
|
|
3442
3572
|
function resolveClaudeExecutable(settings = loadAppSettings()) {
|
|
3443
|
-
|
|
3444
|
-
|
|
3573
|
+
return resolveAgentExecutable("claude", settings);
|
|
3574
|
+
}
|
|
3575
|
+
function resolveAgentExecutable(agent, settings = loadAppSettings()) {
|
|
3576
|
+
const fallback = DEFAULT_CLI_BIN[agent];
|
|
3577
|
+
if (agent === "claude") {
|
|
3578
|
+
const override2 = settings.claude.executablePath?.trim();
|
|
3579
|
+
return override2 || fallback;
|
|
3580
|
+
}
|
|
3581
|
+
if (agent === "codex") {
|
|
3582
|
+
const override2 = settings.codex.executablePath?.trim();
|
|
3583
|
+
return override2 || fallback;
|
|
3584
|
+
}
|
|
3585
|
+
if (agent === "opencode") {
|
|
3586
|
+
const override2 = settings.opencode.executablePath?.trim();
|
|
3587
|
+
return override2 || fallback;
|
|
3588
|
+
}
|
|
3589
|
+
const override = settings.brightsy.executablePath?.trim();
|
|
3590
|
+
return override || fallback;
|
|
3591
|
+
}
|
|
3592
|
+
function updateAgentExecutable(agent, executablePath) {
|
|
3593
|
+
if (agent === "claude") {
|
|
3594
|
+
return updateClaudeSettings({ executablePath });
|
|
3595
|
+
}
|
|
3596
|
+
if (agent === "codex") {
|
|
3597
|
+
return updateCodexSettings({ executablePath });
|
|
3598
|
+
}
|
|
3599
|
+
if (agent === "opencode") {
|
|
3600
|
+
return updateOpencodeSettings({ executablePath });
|
|
3601
|
+
}
|
|
3602
|
+
return updateBrightsySettings({ executablePath });
|
|
3445
3603
|
}
|
|
3446
3604
|
function claudeChromeEnabled(settings = loadAppSettings()) {
|
|
3447
3605
|
return Boolean(settings.claude.chromeEnabled);
|
|
@@ -3469,11 +3627,11 @@ function childEnvWithAppSettings(extra) {
|
|
|
3469
3627
|
function harnessEnvKey(harness) {
|
|
3470
3628
|
return HARNESS_ENV_KEYS[harness];
|
|
3471
3629
|
}
|
|
3472
|
-
var
|
|
3630
|
+
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
3631
|
var init_app_settings = __esm({
|
|
3474
3632
|
"src/store/app-settings.ts"() {
|
|
3475
3633
|
"use strict";
|
|
3476
|
-
|
|
3634
|
+
import_node_fs6 = require("fs");
|
|
3477
3635
|
import_node_os4 = require("os");
|
|
3478
3636
|
import_node_path6 = require("path");
|
|
3479
3637
|
init_thinking_effort();
|
|
@@ -3502,11 +3660,19 @@ var init_app_settings = __esm({
|
|
|
3502
3660
|
EMPTY_SETTINGS = {
|
|
3503
3661
|
environment: {},
|
|
3504
3662
|
claude: {},
|
|
3663
|
+
codex: {},
|
|
3664
|
+
opencode: {},
|
|
3505
3665
|
brightsy: {},
|
|
3506
3666
|
integrations: {},
|
|
3507
3667
|
defaults: {},
|
|
3508
3668
|
advanced: {}
|
|
3509
3669
|
};
|
|
3670
|
+
DEFAULT_CLI_BIN = {
|
|
3671
|
+
claude: "claude",
|
|
3672
|
+
codex: "codex",
|
|
3673
|
+
opencode: "opencode",
|
|
3674
|
+
brightsy: "brightsy"
|
|
3675
|
+
};
|
|
3510
3676
|
}
|
|
3511
3677
|
});
|
|
3512
3678
|
|
|
@@ -3632,7 +3798,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
3632
3798
|
function ensureGlobalCoordinatorCwd(opts) {
|
|
3633
3799
|
const dir = globalAgentCwd();
|
|
3634
3800
|
try {
|
|
3635
|
-
(0,
|
|
3801
|
+
(0, import_node_fs7.mkdirSync)(dir, { recursive: true });
|
|
3636
3802
|
} catch {
|
|
3637
3803
|
return dir;
|
|
3638
3804
|
}
|
|
@@ -3640,7 +3806,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3640
3806
|
let orchId = opts?.orchestratorThreadId?.trim() || "";
|
|
3641
3807
|
if (!orchId) {
|
|
3642
3808
|
try {
|
|
3643
|
-
const existing = (0,
|
|
3809
|
+
const existing = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
|
|
3644
3810
|
const m = existing.match(
|
|
3645
3811
|
/YOUR orchestration thread id is `([0-9a-f-]{36})`/i
|
|
3646
3812
|
);
|
|
@@ -3682,9 +3848,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3682
3848
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
3683
3849
|
].join("\n");
|
|
3684
3850
|
try {
|
|
3685
|
-
(0,
|
|
3851
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
|
|
3686
3852
|
`, "utf8");
|
|
3687
|
-
(0,
|
|
3853
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
|
|
3688
3854
|
`, "utf8");
|
|
3689
3855
|
} catch {
|
|
3690
3856
|
}
|
|
@@ -3717,11 +3883,11 @@ function coordinatorSystemPrompt(opts) {
|
|
|
3717
3883
|
formatWorkspaceInventory(opts.workspaces)
|
|
3718
3884
|
].join("\n");
|
|
3719
3885
|
}
|
|
3720
|
-
var
|
|
3886
|
+
var import_node_fs7, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
|
|
3721
3887
|
var init_coordinator_prompt = __esm({
|
|
3722
3888
|
"src/orchestrator/coordinator-prompt.ts"() {
|
|
3723
3889
|
"use strict";
|
|
3724
|
-
|
|
3890
|
+
import_node_fs7 = require("fs");
|
|
3725
3891
|
import_node_path7 = require("path");
|
|
3726
3892
|
init_worktree();
|
|
3727
3893
|
init_app_settings();
|
|
@@ -3947,26 +4113,26 @@ function brightsyConfigPath() {
|
|
|
3947
4113
|
}
|
|
3948
4114
|
function loadBrightsyConfig() {
|
|
3949
4115
|
const path = brightsyConfigPath();
|
|
3950
|
-
if (!(0,
|
|
4116
|
+
if (!(0, import_node_fs8.existsSync)(path)) {
|
|
3951
4117
|
throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
|
|
3952
4118
|
}
|
|
3953
|
-
const raw = JSON.parse((0,
|
|
4119
|
+
const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
|
|
3954
4120
|
if (!raw.access_token || !raw.account_id) {
|
|
3955
4121
|
throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
|
|
3956
4122
|
}
|
|
3957
4123
|
return raw;
|
|
3958
4124
|
}
|
|
3959
4125
|
function saveBrightsyConfig(cfg) {
|
|
3960
|
-
(0,
|
|
4126
|
+
(0, import_node_fs8.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
|
|
3961
4127
|
`, {
|
|
3962
4128
|
mode: 384
|
|
3963
4129
|
});
|
|
3964
4130
|
}
|
|
3965
|
-
var
|
|
4131
|
+
var import_node_fs8, import_node_os5, import_node_path8;
|
|
3966
4132
|
var init_config = __esm({
|
|
3967
4133
|
"src/brightsy/config.ts"() {
|
|
3968
4134
|
"use strict";
|
|
3969
|
-
|
|
4135
|
+
import_node_fs8 = require("fs");
|
|
3970
4136
|
import_node_os5 = require("os");
|
|
3971
4137
|
import_node_path8 = require("path");
|
|
3972
4138
|
}
|
|
@@ -3987,18 +4153,18 @@ function storePath() {
|
|
|
3987
4153
|
}
|
|
3988
4154
|
function readStore() {
|
|
3989
4155
|
const path = storePath();
|
|
3990
|
-
if (!(0,
|
|
4156
|
+
if (!(0, import_node_fs9.existsSync)(path)) return [];
|
|
3991
4157
|
try {
|
|
3992
|
-
const parsed = JSON.parse((0,
|
|
4158
|
+
const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
|
|
3993
4159
|
return Array.isArray(parsed.teams) ? parsed.teams : [];
|
|
3994
4160
|
} catch {
|
|
3995
4161
|
return [];
|
|
3996
4162
|
}
|
|
3997
4163
|
}
|
|
3998
4164
|
function writeStore(teams) {
|
|
3999
|
-
(0,
|
|
4165
|
+
(0, import_node_fs9.mkdirSync)(appDataDir(), { recursive: true });
|
|
4000
4166
|
const path = storePath();
|
|
4001
|
-
(0,
|
|
4167
|
+
(0, import_node_fs9.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
|
|
4002
4168
|
`, {
|
|
4003
4169
|
mode: 384
|
|
4004
4170
|
});
|
|
@@ -4110,11 +4276,11 @@ function brightsyMcpServerName(slug) {
|
|
|
4110
4276
|
const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
|
|
4111
4277
|
return `brightsy_${cleaned || "team"}`;
|
|
4112
4278
|
}
|
|
4113
|
-
var
|
|
4279
|
+
var import_node_fs9, import_node_path9;
|
|
4114
4280
|
var init_connected_teams = __esm({
|
|
4115
4281
|
"src/brightsy/connected-teams.ts"() {
|
|
4116
4282
|
"use strict";
|
|
4117
|
-
|
|
4283
|
+
import_node_fs9 = require("fs");
|
|
4118
4284
|
import_node_path9 = require("path");
|
|
4119
4285
|
init_paths();
|
|
4120
4286
|
init_accounts();
|
|
@@ -4162,6 +4328,11 @@ function normalizeTurnInput(input) {
|
|
|
4162
4328
|
if (typeof input === "string") return { prompt: input };
|
|
4163
4329
|
return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
|
|
4164
4330
|
}
|
|
4331
|
+
function dropCachedPrefixOnResume(input, sessionId) {
|
|
4332
|
+
const turn = normalizeTurnInput(input);
|
|
4333
|
+
if (sessionId) return { prompt: turn.prompt };
|
|
4334
|
+
return turn;
|
|
4335
|
+
}
|
|
4165
4336
|
function flattenTurnInput(input) {
|
|
4166
4337
|
const { cachedPrefix, prompt } = normalizeTurnInput(input);
|
|
4167
4338
|
if (!cachedPrefix) return prompt;
|
|
@@ -4306,9 +4477,13 @@ async function fetchTeamChatTargets(team) {
|
|
|
4306
4477
|
};
|
|
4307
4478
|
}
|
|
4308
4479
|
async function listBrightsyChatTargetsViaCli() {
|
|
4309
|
-
const listed = await run(
|
|
4310
|
-
|
|
4311
|
-
|
|
4480
|
+
const listed = await run(
|
|
4481
|
+
resolveAgentExecutable("brightsy"),
|
|
4482
|
+
["chat", "--list-targets", "--json"],
|
|
4483
|
+
{
|
|
4484
|
+
reject: false
|
|
4485
|
+
}
|
|
4486
|
+
);
|
|
4312
4487
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
4313
4488
|
throw new Error(
|
|
4314
4489
|
listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
|
|
@@ -4392,13 +4567,15 @@ async function syncCliForTarget(accountId) {
|
|
|
4392
4567
|
}
|
|
4393
4568
|
applyConnectedTeamToCli(team);
|
|
4394
4569
|
}
|
|
4395
|
-
var brightsyAdapter;
|
|
4570
|
+
var import_node_fs10, brightsyAdapter;
|
|
4396
4571
|
var init_brightsy = __esm({
|
|
4397
4572
|
"src/agents/brightsy.ts"() {
|
|
4398
4573
|
"use strict";
|
|
4574
|
+
import_node_fs10 = require("fs");
|
|
4399
4575
|
init_run();
|
|
4400
4576
|
init_connected_teams();
|
|
4401
4577
|
init_config();
|
|
4578
|
+
init_app_settings();
|
|
4402
4579
|
init_brightsy_targets();
|
|
4403
4580
|
init_error_detail();
|
|
4404
4581
|
init_turn_input();
|
|
@@ -4406,18 +4583,32 @@ var init_brightsy = __esm({
|
|
|
4406
4583
|
brightsyAdapter = {
|
|
4407
4584
|
kind: "brightsy",
|
|
4408
4585
|
async detect() {
|
|
4409
|
-
const
|
|
4410
|
-
if (
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4586
|
+
const brightsy = resolveAgentExecutable("brightsy");
|
|
4587
|
+
if (brightsy !== "brightsy") {
|
|
4588
|
+
if (!(0, import_node_fs10.existsSync)(brightsy)) {
|
|
4589
|
+
return {
|
|
4590
|
+
agent: "brightsy",
|
|
4591
|
+
installed: false,
|
|
4592
|
+
authenticated: false,
|
|
4593
|
+
linearMcp: false,
|
|
4594
|
+
warnings: [],
|
|
4595
|
+
reason: `Brightsy executable not found: ${brightsy}`
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
} else {
|
|
4599
|
+
const which = await run("which", ["brightsy"], { reject: false });
|
|
4600
|
+
if (which.exitCode !== 0) {
|
|
4601
|
+
return {
|
|
4602
|
+
agent: "brightsy",
|
|
4603
|
+
installed: false,
|
|
4604
|
+
authenticated: false,
|
|
4605
|
+
linearMcp: false,
|
|
4606
|
+
warnings: [],
|
|
4607
|
+
reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
|
|
4608
|
+
};
|
|
4609
|
+
}
|
|
4419
4610
|
}
|
|
4420
|
-
const who = await run(
|
|
4611
|
+
const who = await run(brightsy, ["whoami"], { reject: false });
|
|
4421
4612
|
const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
|
|
4422
4613
|
return {
|
|
4423
4614
|
agent: "brightsy",
|
|
@@ -4442,7 +4633,7 @@ var init_brightsy = __esm({
|
|
|
4442
4633
|
target.id
|
|
4443
4634
|
];
|
|
4444
4635
|
return {
|
|
4445
|
-
file: "brightsy",
|
|
4636
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4446
4637
|
args,
|
|
4447
4638
|
cwd: thread.worktreePath,
|
|
4448
4639
|
// Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
|
|
@@ -4459,7 +4650,11 @@ var init_brightsy = __esm({
|
|
|
4459
4650
|
const target = decodeBrightsyTarget(thread.model);
|
|
4460
4651
|
await syncCliForTarget(target.accountId);
|
|
4461
4652
|
const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
|
|
4462
|
-
return {
|
|
4653
|
+
return {
|
|
4654
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4655
|
+
args,
|
|
4656
|
+
cwd: thread.worktreePath
|
|
4657
|
+
};
|
|
4463
4658
|
}
|
|
4464
4659
|
};
|
|
4465
4660
|
}
|
|
@@ -4504,6 +4699,18 @@ var init_claude_mcp = __esm({
|
|
|
4504
4699
|
}
|
|
4505
4700
|
});
|
|
4506
4701
|
|
|
4702
|
+
// src/mcp/profile.ts
|
|
4703
|
+
function sideboardMcpProfile(env = process.env) {
|
|
4704
|
+
return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
|
|
4705
|
+
}
|
|
4706
|
+
var SIDEBOARD_MCP_PROFILE_ENV;
|
|
4707
|
+
var init_profile = __esm({
|
|
4708
|
+
"src/mcp/profile.ts"() {
|
|
4709
|
+
"use strict";
|
|
4710
|
+
SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
|
|
4711
|
+
}
|
|
4712
|
+
});
|
|
4713
|
+
|
|
4507
4714
|
// src/agents/node-launch.ts
|
|
4508
4715
|
function isAsarPath(filePath) {
|
|
4509
4716
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
@@ -4551,6 +4758,38 @@ function isBrightsyConnected() {
|
|
|
4551
4758
|
return false;
|
|
4552
4759
|
}
|
|
4553
4760
|
}
|
|
4761
|
+
function promptMentionsBrightsy(text) {
|
|
4762
|
+
return BRIGHTSY_WORD.test(text ?? "");
|
|
4763
|
+
}
|
|
4764
|
+
function isBrightsyMcpToolName(name) {
|
|
4765
|
+
const n = name.toLowerCase();
|
|
4766
|
+
return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
|
|
4767
|
+
}
|
|
4768
|
+
function toolInputUsesBrightsyDatasource(input) {
|
|
4769
|
+
const ds = input?.datasource;
|
|
4770
|
+
return typeof ds === "string" && ds.toLowerCase() === "brightsy";
|
|
4771
|
+
}
|
|
4772
|
+
function messageUsedBrightsyMcp(msg) {
|
|
4773
|
+
if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
|
|
4774
|
+
for (const part of msg.parts ?? []) {
|
|
4775
|
+
if (part.type !== "tool") continue;
|
|
4776
|
+
if (isBrightsyMcpToolName(part.name)) return true;
|
|
4777
|
+
if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
|
|
4778
|
+
return true;
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
return false;
|
|
4782
|
+
}
|
|
4783
|
+
function threadRequestsBrightsyMcp(thread) {
|
|
4784
|
+
return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
|
|
4785
|
+
}
|
|
4786
|
+
function shouldInjectBrightsyMcp(thread, opts) {
|
|
4787
|
+
if (!(opts?.connected ?? isBrightsyConnected())) return false;
|
|
4788
|
+
if (opts?.orchestrator) return true;
|
|
4789
|
+
const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
|
|
4790
|
+
if (alwaysOn) return true;
|
|
4791
|
+
return threadRequestsBrightsyMcp(thread);
|
|
4792
|
+
}
|
|
4554
4793
|
function mcpLaunch(cmd, name, env) {
|
|
4555
4794
|
if (cmd === "brightsy-mcp") {
|
|
4556
4795
|
return { name, command: "brightsy-mcp", ...env ? { env } : {} };
|
|
@@ -4596,7 +4835,7 @@ function corePackageDir() {
|
|
|
4596
4835
|
}
|
|
4597
4836
|
function findSideboardMcpJsEntry() {
|
|
4598
4837
|
const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
|
|
4599
|
-
if (override && (0,
|
|
4838
|
+
if (override && (0, import_node_fs11.existsSync)(override)) return override;
|
|
4600
4839
|
let dir = corePackageDir();
|
|
4601
4840
|
for (let i = 0; i < 10; i++) {
|
|
4602
4841
|
const candidates = [
|
|
@@ -4609,7 +4848,7 @@ function findSideboardMcpJsEntry() {
|
|
|
4609
4848
|
(0, import_node_path10.join)(dir, "cli/dist/index.js")
|
|
4610
4849
|
];
|
|
4611
4850
|
for (const p of candidates) {
|
|
4612
|
-
if ((0,
|
|
4851
|
+
if ((0, import_node_fs11.existsSync)(p)) return p;
|
|
4613
4852
|
}
|
|
4614
4853
|
const parent = (0, import_node_path10.dirname)(dir);
|
|
4615
4854
|
if (parent === dir) break;
|
|
@@ -4640,11 +4879,13 @@ async function buildInjectedMcpServers(opts) {
|
|
|
4640
4879
|
const servers = [];
|
|
4641
4880
|
if (opts.includeSideboard) {
|
|
4642
4881
|
const sideboard = await resolveSideboardMcpServer();
|
|
4882
|
+
const orchId = opts.orchestratorThreadId?.trim();
|
|
4883
|
+
const profile = orchId ? "orchestration" : "worktree";
|
|
4643
4884
|
sideboard.env = {
|
|
4644
4885
|
...sideboard.env ?? {},
|
|
4645
|
-
SIDEBOARD_APP_DATA: appDataDir()
|
|
4886
|
+
SIDEBOARD_APP_DATA: appDataDir(),
|
|
4887
|
+
[SIDEBOARD_MCP_PROFILE_ENV]: profile
|
|
4646
4888
|
};
|
|
4647
|
-
const orchId = opts.orchestratorThreadId?.trim();
|
|
4648
4889
|
if (orchId) {
|
|
4649
4890
|
sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
|
|
4650
4891
|
}
|
|
@@ -4719,16 +4960,16 @@ function writeMcpServersConfig(servers) {
|
|
|
4719
4960
|
...s.env ? { env: s.env } : {}
|
|
4720
4961
|
};
|
|
4721
4962
|
}
|
|
4722
|
-
const dir = (0,
|
|
4963
|
+
const dir = (0, import_node_fs11.mkdtempSync)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
|
|
4723
4964
|
const cfgPath = (0, import_node_path10.join)(dir, "mcp.json");
|
|
4724
|
-
(0,
|
|
4965
|
+
(0, import_node_fs11.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
4725
4966
|
return cfgPath;
|
|
4726
4967
|
}
|
|
4727
|
-
var
|
|
4968
|
+
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
4969
|
var init_injected_mcp = __esm({
|
|
4729
4970
|
"src/agents/injected-mcp.ts"() {
|
|
4730
4971
|
"use strict";
|
|
4731
|
-
|
|
4972
|
+
import_node_fs11 = require("fs");
|
|
4732
4973
|
import_node_module = require("module");
|
|
4733
4974
|
import_node_os6 = require("os");
|
|
4734
4975
|
import_node_path10 = require("path");
|
|
@@ -4736,6 +4977,8 @@ var init_injected_mcp = __esm({
|
|
|
4736
4977
|
init_run();
|
|
4737
4978
|
init_config();
|
|
4738
4979
|
init_connected_teams();
|
|
4980
|
+
init_profile();
|
|
4981
|
+
init_app_settings();
|
|
4739
4982
|
init_paths();
|
|
4740
4983
|
init_node_launch();
|
|
4741
4984
|
import_meta = {};
|
|
@@ -4751,6 +4994,7 @@ var init_injected_mcp = __esm({
|
|
|
4751
4994
|
"mcp__sideboard__present_plan"
|
|
4752
4995
|
];
|
|
4753
4996
|
brightsyMcpCommandCache = null;
|
|
4997
|
+
BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
|
|
4754
4998
|
}
|
|
4755
4999
|
});
|
|
4756
5000
|
|
|
@@ -4905,11 +5149,11 @@ function parseIssuesJson(raw) {
|
|
|
4905
5149
|
}
|
|
4906
5150
|
return [];
|
|
4907
5151
|
}
|
|
4908
|
-
var
|
|
5152
|
+
var import_node_fs12, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
|
|
4909
5153
|
var init_claude = __esm({
|
|
4910
5154
|
"src/agents/claude.ts"() {
|
|
4911
5155
|
"use strict";
|
|
4912
|
-
|
|
5156
|
+
import_node_fs12 = require("fs");
|
|
4913
5157
|
init_run();
|
|
4914
5158
|
init_app_settings();
|
|
4915
5159
|
init_claude_mcp();
|
|
@@ -4938,7 +5182,7 @@ var init_claude = __esm({
|
|
|
4938
5182
|
async detect() {
|
|
4939
5183
|
const claude = resolveClaudeExecutable();
|
|
4940
5184
|
if (claude !== "claude") {
|
|
4941
|
-
if (!(0,
|
|
5185
|
+
if (!(0, import_node_fs12.existsSync)(claude)) {
|
|
4942
5186
|
return {
|
|
4943
5187
|
agent: "claude",
|
|
4944
5188
|
installed: false,
|
|
@@ -4977,9 +5221,8 @@ var init_claude = __esm({
|
|
|
4977
5221
|
},
|
|
4978
5222
|
async buildTurn(thread, input) {
|
|
4979
5223
|
const claude = resolveClaudeExecutable();
|
|
4980
|
-
const turn = normalizeTurnInput(input);
|
|
4981
5224
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
4982
|
-
const effective = sessionId
|
|
5225
|
+
const effective = dropCachedPrefixOnResume(input, sessionId);
|
|
4983
5226
|
const promptText = flattenTurnInput(effective);
|
|
4984
5227
|
const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
|
|
4985
5228
|
if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
|
|
@@ -4992,7 +5235,9 @@ var init_claude = __esm({
|
|
|
4992
5235
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
4993
5236
|
const injectedServers = await buildInjectedMcpServers({
|
|
4994
5237
|
includeSideboard: true,
|
|
4995
|
-
includeBrightsy:
|
|
5238
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5239
|
+
orchestrator: isOrchestrator
|
|
5240
|
+
}),
|
|
4996
5241
|
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
4997
5242
|
});
|
|
4998
5243
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
@@ -5166,9 +5411,14 @@ async function listCodexModels() {
|
|
|
5166
5411
|
if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
|
|
5167
5412
|
return cachedCodexModels.models;
|
|
5168
5413
|
}
|
|
5169
|
-
const
|
|
5170
|
-
if (
|
|
5171
|
-
|
|
5414
|
+
const codex = resolveAgentExecutable("codex");
|
|
5415
|
+
if (codex === "codex") {
|
|
5416
|
+
const which = await run("which", ["codex"], { reject: false });
|
|
5417
|
+
if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
|
|
5418
|
+
} else if (!(0, import_node_fs13.existsSync)(codex)) {
|
|
5419
|
+
return FALLBACK_CODEX_MODELS;
|
|
5420
|
+
}
|
|
5421
|
+
const listed = await run(codex, ["debug", "models"], { reject: false });
|
|
5172
5422
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5173
5423
|
return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
|
|
5174
5424
|
}
|
|
@@ -5207,8 +5457,8 @@ function codexConfigHasNetworkAccess() {
|
|
|
5207
5457
|
(0, import_node_path11.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
|
|
5208
5458
|
];
|
|
5209
5459
|
for (const path of candidates) {
|
|
5210
|
-
if (!(0,
|
|
5211
|
-
const text = (0,
|
|
5460
|
+
if (!(0, import_node_fs13.existsSync)(path)) continue;
|
|
5461
|
+
const text = (0, import_node_fs13.readFileSync)(path, "utf8");
|
|
5212
5462
|
if (/network_access\s*=\s*true/.test(text)) return true;
|
|
5213
5463
|
}
|
|
5214
5464
|
return false;
|
|
@@ -5241,21 +5491,23 @@ function asRecord(value) {
|
|
|
5241
5491
|
}
|
|
5242
5492
|
function codexLooksAuthenticated() {
|
|
5243
5493
|
const authPath = (0, import_node_path11.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
|
|
5244
|
-
if (!(0,
|
|
5494
|
+
if (!(0, import_node_fs13.existsSync)(authPath)) return false;
|
|
5245
5495
|
try {
|
|
5246
|
-
return (0,
|
|
5496
|
+
return (0, import_node_fs13.statSync)(authPath).size > 2;
|
|
5247
5497
|
} catch {
|
|
5248
5498
|
return false;
|
|
5249
5499
|
}
|
|
5250
5500
|
}
|
|
5251
|
-
var
|
|
5501
|
+
var import_node_fs13, import_node_os7, import_node_path11, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
|
|
5252
5502
|
var init_codex = __esm({
|
|
5253
5503
|
"src/agents/codex.ts"() {
|
|
5254
5504
|
"use strict";
|
|
5255
|
-
|
|
5505
|
+
import_node_fs13 = require("fs");
|
|
5256
5506
|
import_node_os7 = require("os");
|
|
5257
5507
|
import_node_path11 = require("path");
|
|
5258
5508
|
init_run();
|
|
5509
|
+
init_app_settings();
|
|
5510
|
+
init_global_workspace();
|
|
5259
5511
|
init_error_detail();
|
|
5260
5512
|
init_injected_mcp();
|
|
5261
5513
|
init_turn_input();
|
|
@@ -5273,16 +5525,30 @@ var init_codex = __esm({
|
|
|
5273
5525
|
codexAdapter = {
|
|
5274
5526
|
kind: "codex",
|
|
5275
5527
|
async detect() {
|
|
5276
|
-
const
|
|
5277
|
-
if (
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5528
|
+
const codex = resolveAgentExecutable("codex");
|
|
5529
|
+
if (codex !== "codex") {
|
|
5530
|
+
if (!(0, import_node_fs13.existsSync)(codex)) {
|
|
5531
|
+
return {
|
|
5532
|
+
agent: "codex",
|
|
5533
|
+
installed: false,
|
|
5534
|
+
authenticated: false,
|
|
5535
|
+
linearMcp: false,
|
|
5536
|
+
warnings: [],
|
|
5537
|
+
reason: `Codex executable not found: ${codex}`
|
|
5538
|
+
};
|
|
5539
|
+
}
|
|
5540
|
+
} else {
|
|
5541
|
+
const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
|
|
5542
|
+
if (which.exitCode !== 0) {
|
|
5543
|
+
return {
|
|
5544
|
+
agent: "codex",
|
|
5545
|
+
installed: false,
|
|
5546
|
+
authenticated: false,
|
|
5547
|
+
linearMcp: false,
|
|
5548
|
+
warnings: [],
|
|
5549
|
+
reason: "codex CLI not found on PATH"
|
|
5550
|
+
};
|
|
5551
|
+
}
|
|
5286
5552
|
}
|
|
5287
5553
|
const authenticated = codexLooksAuthenticated();
|
|
5288
5554
|
const linearMcp = false;
|
|
@@ -5302,22 +5568,24 @@ var init_codex = __esm({
|
|
|
5302
5568
|
};
|
|
5303
5569
|
},
|
|
5304
5570
|
async buildTurn(thread, input) {
|
|
5305
|
-
const prompt = flattenTurnInput(input);
|
|
5306
5571
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5572
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5307
5573
|
const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
|
|
5308
5574
|
const promptArg = useStdin ? "-" : prompt;
|
|
5309
5575
|
if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
|
|
5310
|
-
const { cachedPrefix } = normalizeTurnInput(input);
|
|
5311
5576
|
console.error(
|
|
5312
|
-
`[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${
|
|
5577
|
+
`[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${prompt.includes("Current request:")}`
|
|
5313
5578
|
);
|
|
5314
5579
|
}
|
|
5315
5580
|
const mode = permissionMode(thread);
|
|
5316
5581
|
const model = thread.model?.trim();
|
|
5582
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5317
5583
|
const injected = await buildInjectedMcpServers({
|
|
5318
5584
|
includeSideboard: true,
|
|
5319
|
-
includeBrightsy:
|
|
5320
|
-
|
|
5585
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5586
|
+
orchestrator: isOrchestrator
|
|
5587
|
+
}),
|
|
5588
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5321
5589
|
});
|
|
5322
5590
|
const mcpOverrides = toCodexMcpConfigArgs(injected);
|
|
5323
5591
|
const execOpts = [
|
|
@@ -5337,7 +5605,7 @@ var init_codex = __esm({
|
|
|
5337
5605
|
];
|
|
5338
5606
|
const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
|
|
5339
5607
|
return {
|
|
5340
|
-
file: "codex",
|
|
5608
|
+
file: resolveAgentExecutable("codex"),
|
|
5341
5609
|
args,
|
|
5342
5610
|
cwd: thread.worktreePath,
|
|
5343
5611
|
stdin: useStdin ? `${prompt}
|
|
@@ -5433,16 +5701,17 @@ var init_codex = __esm({
|
|
|
5433
5701
|
return cached;
|
|
5434
5702
|
},
|
|
5435
5703
|
async buildAttach(thread) {
|
|
5704
|
+
const codex = resolveAgentExecutable("codex");
|
|
5436
5705
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5437
5706
|
if (sessionId) {
|
|
5438
5707
|
return {
|
|
5439
|
-
file:
|
|
5708
|
+
file: codex,
|
|
5440
5709
|
args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
|
|
5441
5710
|
cwd: thread.worktreePath
|
|
5442
5711
|
};
|
|
5443
5712
|
}
|
|
5444
5713
|
return {
|
|
5445
|
-
file:
|
|
5714
|
+
file: codex,
|
|
5446
5715
|
args: ["--cd", thread.worktreePath],
|
|
5447
5716
|
cwd: thread.worktreePath
|
|
5448
5717
|
};
|
|
@@ -5450,7 +5719,7 @@ var init_codex = __esm({
|
|
|
5450
5719
|
async listLinearIssues(_repoPath) {
|
|
5451
5720
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
5452
5721
|
const { stdout, exitCode } = await run(
|
|
5453
|
-
"codex",
|
|
5722
|
+
resolveAgentExecutable("codex"),
|
|
5454
5723
|
[
|
|
5455
5724
|
"exec",
|
|
5456
5725
|
"--json",
|
|
@@ -5713,21 +5982,22 @@ function cursorRunnerPath() {
|
|
|
5713
5982
|
(0, import_node_path12.join)(root, "src", "agents", "cursor-runner.ts")
|
|
5714
5983
|
];
|
|
5715
5984
|
for (const candidate of candidates) {
|
|
5716
|
-
if ((0,
|
|
5985
|
+
if ((0, import_node_fs14.existsSync)(candidate)) return candidate;
|
|
5717
5986
|
}
|
|
5718
5987
|
return candidates[0];
|
|
5719
5988
|
}
|
|
5720
|
-
var
|
|
5989
|
+
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
5990
|
var init_cursor = __esm({
|
|
5722
5991
|
"src/agents/cursor.ts"() {
|
|
5723
5992
|
"use strict";
|
|
5724
|
-
|
|
5993
|
+
import_node_fs14 = require("fs");
|
|
5725
5994
|
import_node_module2 = require("module");
|
|
5726
5995
|
import_node_path12 = require("path");
|
|
5727
5996
|
import_node_url2 = require("url");
|
|
5728
5997
|
import_sdk = require("@cursor/sdk");
|
|
5729
5998
|
init_run();
|
|
5730
5999
|
init_app_settings();
|
|
6000
|
+
init_global_workspace();
|
|
5731
6001
|
init_cursor_events();
|
|
5732
6002
|
init_injected_mcp();
|
|
5733
6003
|
init_node_launch();
|
|
@@ -5764,13 +6034,16 @@ var init_cursor = __esm({
|
|
|
5764
6034
|
};
|
|
5765
6035
|
},
|
|
5766
6036
|
async buildTurn(thread, input) {
|
|
5767
|
-
const prompt = flattenTurnInput(input);
|
|
5768
6037
|
const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6038
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
|
|
5769
6039
|
const apiKey = resolveCursorApiKey() || void 0;
|
|
6040
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5770
6041
|
const injected = await buildInjectedMcpServers({
|
|
5771
6042
|
includeSideboard: true,
|
|
5772
|
-
includeBrightsy:
|
|
5773
|
-
|
|
6043
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6044
|
+
orchestrator: isOrchestrator
|
|
6045
|
+
}),
|
|
6046
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5774
6047
|
});
|
|
5775
6048
|
const mcpServers = toCursorMcpServers(injected);
|
|
5776
6049
|
const req = {
|
|
@@ -5841,9 +6114,14 @@ async function listOpencodeModels() {
|
|
|
5841
6114
|
if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
|
|
5842
6115
|
return cachedOpencodeModels.models;
|
|
5843
6116
|
}
|
|
5844
|
-
const
|
|
5845
|
-
if (
|
|
5846
|
-
|
|
6117
|
+
const opencode = resolveAgentExecutable("opencode");
|
|
6118
|
+
if (opencode === "opencode") {
|
|
6119
|
+
const which = await run("which", ["opencode"], { reject: false });
|
|
6120
|
+
if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
|
|
6121
|
+
} else if (!(0, import_node_fs15.existsSync)(opencode)) {
|
|
6122
|
+
return FALLBACK_OPENCODE_MODELS;
|
|
6123
|
+
}
|
|
6124
|
+
const listed = await run(opencode, ["models"], { reject: false });
|
|
5847
6125
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5848
6126
|
return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
|
|
5849
6127
|
}
|
|
@@ -5870,11 +6148,14 @@ function usageFromOpencode(tokens) {
|
|
|
5870
6148
|
cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
|
|
5871
6149
|
};
|
|
5872
6150
|
}
|
|
5873
|
-
var FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
6151
|
+
var import_node_fs15, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
5874
6152
|
var init_opencode = __esm({
|
|
5875
6153
|
"src/agents/opencode.ts"() {
|
|
5876
6154
|
"use strict";
|
|
6155
|
+
import_node_fs15 = require("fs");
|
|
5877
6156
|
init_run();
|
|
6157
|
+
init_app_settings();
|
|
6158
|
+
init_global_workspace();
|
|
5878
6159
|
init_error_detail();
|
|
5879
6160
|
init_injected_mcp();
|
|
5880
6161
|
init_turn_input();
|
|
@@ -5895,20 +6176,34 @@ var init_opencode = __esm({
|
|
|
5895
6176
|
opencodeAdapter = {
|
|
5896
6177
|
kind: "opencode",
|
|
5897
6178
|
async detect() {
|
|
5898
|
-
const
|
|
5899
|
-
if (
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
6179
|
+
const opencode = resolveAgentExecutable("opencode");
|
|
6180
|
+
if (opencode !== "opencode") {
|
|
6181
|
+
if (!(0, import_node_fs15.existsSync)(opencode)) {
|
|
6182
|
+
return {
|
|
6183
|
+
agent: "opencode",
|
|
6184
|
+
installed: false,
|
|
6185
|
+
authenticated: false,
|
|
6186
|
+
linearMcp: false,
|
|
6187
|
+
warnings: [],
|
|
6188
|
+
reason: `OpenCode executable not found: ${opencode}`
|
|
6189
|
+
};
|
|
6190
|
+
}
|
|
6191
|
+
} else {
|
|
6192
|
+
const which = await run("which", ["opencode"], { reject: false });
|
|
6193
|
+
if (which.exitCode !== 0) {
|
|
6194
|
+
return {
|
|
6195
|
+
agent: "opencode",
|
|
6196
|
+
installed: false,
|
|
6197
|
+
authenticated: false,
|
|
6198
|
+
linearMcp: false,
|
|
6199
|
+
warnings: [],
|
|
6200
|
+
reason: "opencode CLI not found on PATH"
|
|
6201
|
+
};
|
|
6202
|
+
}
|
|
5908
6203
|
}
|
|
5909
|
-
const auth = await run(
|
|
6204
|
+
const auth = await run(opencode, ["auth", "list"], { reject: false });
|
|
5910
6205
|
const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
|
|
5911
|
-
const mcp = await run(
|
|
6206
|
+
const mcp = await run(opencode, ["mcp", "list"], { reject: false });
|
|
5912
6207
|
const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
|
|
5913
6208
|
return {
|
|
5914
6209
|
agent: "opencode",
|
|
@@ -5920,8 +6215,8 @@ var init_opencode = __esm({
|
|
|
5920
6215
|
};
|
|
5921
6216
|
},
|
|
5922
6217
|
async buildTurn(thread, input) {
|
|
5923
|
-
const prompt = flattenTurnInput(input);
|
|
5924
6218
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6219
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5925
6220
|
const mode = permissionMode(thread);
|
|
5926
6221
|
const model = thread.model?.trim();
|
|
5927
6222
|
const args = [
|
|
@@ -5937,14 +6232,17 @@ var init_opencode = __esm({
|
|
|
5937
6232
|
if (model) {
|
|
5938
6233
|
args.push("--model", model);
|
|
5939
6234
|
}
|
|
6235
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5940
6236
|
const injected = await buildInjectedMcpServers({
|
|
5941
6237
|
includeSideboard: true,
|
|
5942
|
-
includeBrightsy:
|
|
5943
|
-
|
|
6238
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6239
|
+
orchestrator: isOrchestrator
|
|
6240
|
+
}),
|
|
6241
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5944
6242
|
});
|
|
5945
6243
|
const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
|
|
5946
6244
|
return {
|
|
5947
|
-
file: "opencode",
|
|
6245
|
+
file: resolveAgentExecutable("opencode"),
|
|
5948
6246
|
args,
|
|
5949
6247
|
cwd: thread.worktreePath,
|
|
5950
6248
|
// `opencode run` treats non-TTY stdin as the message body when no positional
|
|
@@ -6020,7 +6318,7 @@ var init_opencode = __esm({
|
|
|
6020
6318
|
async resolveSessionId(worktreePath, cached) {
|
|
6021
6319
|
const cachedId = cached?.trim() || null;
|
|
6022
6320
|
const listed = await run(
|
|
6023
|
-
"opencode",
|
|
6321
|
+
resolveAgentExecutable("opencode"),
|
|
6024
6322
|
["session", "list", "--format", "json"],
|
|
6025
6323
|
{ cwd: worktreePath, reject: false }
|
|
6026
6324
|
);
|
|
@@ -6051,7 +6349,7 @@ var init_opencode = __esm({
|
|
|
6051
6349
|
const args = ["--dir", thread.worktreePath];
|
|
6052
6350
|
if (sessionId) args.push("--session", sessionId);
|
|
6053
6351
|
return {
|
|
6054
|
-
file: "opencode",
|
|
6352
|
+
file: resolveAgentExecutable("opencode"),
|
|
6055
6353
|
args,
|
|
6056
6354
|
cwd: thread.worktreePath,
|
|
6057
6355
|
env: {
|
|
@@ -6062,7 +6360,7 @@ var init_opencode = __esm({
|
|
|
6062
6360
|
async listLinearIssues(_repoPath) {
|
|
6063
6361
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
6064
6362
|
const { stdout, exitCode } = await run(
|
|
6065
|
-
"opencode",
|
|
6363
|
+
resolveAgentExecutable("opencode"),
|
|
6066
6364
|
["run", prompt, "--format", "json"],
|
|
6067
6365
|
{
|
|
6068
6366
|
reject: false,
|
|
@@ -6295,9 +6593,30 @@ function getAgentSetupInfo(agent) {
|
|
|
6295
6593
|
function listAgentSetupInfo() {
|
|
6296
6594
|
return Object.values(SETUP);
|
|
6297
6595
|
}
|
|
6298
|
-
|
|
6299
|
-
|
|
6596
|
+
function resolveLoginCommand(agent) {
|
|
6597
|
+
const info = getAgentSetupInfo(agent);
|
|
6598
|
+
if (!info.loginCommand) return null;
|
|
6599
|
+
if (!CLI_LOGIN_AGENTS.has(agent)) return info.loginCommand;
|
|
6600
|
+
const exe = resolveAgentExecutable(agent);
|
|
6601
|
+
return info.loginCommand.replace(/^\S+/, exe);
|
|
6602
|
+
}
|
|
6603
|
+
async function prepareTerminalCommand(command) {
|
|
6604
|
+
enrichPathWithNpmGlobalBin();
|
|
6300
6605
|
const trimmed = command.trim();
|
|
6606
|
+
if (!trimmed) return trimmed;
|
|
6607
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
6608
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
6609
|
+
const bin = match?.[1];
|
|
6610
|
+
let whichPath = null;
|
|
6611
|
+
if (bin && !bin.includes("/") && !bin.includes("\\")) {
|
|
6612
|
+
const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
|
|
6613
|
+
whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
|
|
6614
|
+
}
|
|
6615
|
+
const resolved = resolveCommandBinarySync(trimmed, whichPath);
|
|
6616
|
+
return withExportedPath(resolved, process.env.PATH ?? "");
|
|
6617
|
+
}
|
|
6618
|
+
async function openInSystemTerminal(command) {
|
|
6619
|
+
const trimmed = (await prepareTerminalCommand(command)).trim();
|
|
6301
6620
|
if (!trimmed) throw new Error("Command is empty");
|
|
6302
6621
|
if (process.platform === "darwin") {
|
|
6303
6622
|
const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -6358,7 +6677,7 @@ async function installAgent(agent) {
|
|
|
6358
6677
|
message: `Opened Terminal to run: ${info.installCommand}`
|
|
6359
6678
|
};
|
|
6360
6679
|
}
|
|
6361
|
-
|
|
6680
|
+
enrichPathWithNpmGlobalBin();
|
|
6362
6681
|
const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
|
|
6363
6682
|
const ok = result.exitCode === 0;
|
|
6364
6683
|
if (!ok) {
|
|
@@ -6384,36 +6703,46 @@ async function installAgent(agent) {
|
|
|
6384
6703
|
};
|
|
6385
6704
|
}
|
|
6386
6705
|
}
|
|
6706
|
+
enrichPathWithNpmGlobalBin();
|
|
6707
|
+
const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
|
|
6708
|
+
let binPath = null;
|
|
6709
|
+
if (cliBin) {
|
|
6710
|
+
const which = await run("which", [cliBin], { reject: false, timeoutMs: 3e3 });
|
|
6711
|
+
binPath = which.exitCode === 0 ? which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null : null;
|
|
6712
|
+
}
|
|
6387
6713
|
return {
|
|
6388
6714
|
ok: true,
|
|
6389
6715
|
command: info.installCommand,
|
|
6390
6716
|
exitCode: 0,
|
|
6391
6717
|
stdout: result.stdout,
|
|
6392
6718
|
stderr: result.stderr,
|
|
6393
|
-
message: `Installed ${info.npmPackage}`
|
|
6719
|
+
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
6720
|
};
|
|
6395
6721
|
}
|
|
6396
6722
|
async function loginAgent(agent) {
|
|
6397
6723
|
const info = getAgentSetupInfo(agent);
|
|
6398
|
-
|
|
6724
|
+
const loginCommand = resolveLoginCommand(agent);
|
|
6725
|
+
if (!loginCommand) {
|
|
6399
6726
|
return {
|
|
6400
6727
|
ok: true,
|
|
6401
6728
|
message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
|
|
6402
6729
|
};
|
|
6403
6730
|
}
|
|
6404
|
-
await
|
|
6731
|
+
const prepared = await prepareTerminalCommand(loginCommand);
|
|
6732
|
+
await openInSystemTerminal(prepared);
|
|
6405
6733
|
return {
|
|
6406
6734
|
ok: true,
|
|
6407
6735
|
openedTerminal: true,
|
|
6408
|
-
command:
|
|
6409
|
-
message: `Opened Terminal to run: ${
|
|
6736
|
+
command: prepared,
|
|
6737
|
+
message: `Opened Terminal to run: ${prepared}`
|
|
6410
6738
|
};
|
|
6411
6739
|
}
|
|
6412
|
-
var SETUP;
|
|
6740
|
+
var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
|
|
6413
6741
|
var init_install = __esm({
|
|
6414
6742
|
"src/agents/install.ts"() {
|
|
6415
6743
|
"use strict";
|
|
6416
6744
|
init_run();
|
|
6745
|
+
init_app_settings();
|
|
6417
6746
|
init_path();
|
|
6418
6747
|
SETUP = {
|
|
6419
6748
|
claude: {
|
|
@@ -6462,6 +6791,18 @@ var init_install = __esm({
|
|
|
6462
6791
|
npmPackage: "@brightsy/cli"
|
|
6463
6792
|
}
|
|
6464
6793
|
};
|
|
6794
|
+
CLI_BIN = {
|
|
6795
|
+
claude: "claude",
|
|
6796
|
+
codex: "codex",
|
|
6797
|
+
opencode: "opencode",
|
|
6798
|
+
brightsy: "brightsy"
|
|
6799
|
+
};
|
|
6800
|
+
CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
|
|
6801
|
+
"claude",
|
|
6802
|
+
"codex",
|
|
6803
|
+
"opencode",
|
|
6804
|
+
"brightsy"
|
|
6805
|
+
]);
|
|
6465
6806
|
}
|
|
6466
6807
|
});
|
|
6467
6808
|
|
|
@@ -6481,6 +6822,7 @@ __export(agents_exports, {
|
|
|
6481
6822
|
cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
|
|
6482
6823
|
decodeBrightsyTarget: () => decodeBrightsyTarget,
|
|
6483
6824
|
encodeBrightsyTarget: () => encodeBrightsyTarget,
|
|
6825
|
+
enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
|
|
6484
6826
|
ensureAgentPath: () => ensureAgentPath,
|
|
6485
6827
|
getAdapter: () => getAdapter,
|
|
6486
6828
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
@@ -6500,8 +6842,13 @@ __export(agents_exports, {
|
|
|
6500
6842
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6501
6843
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6502
6844
|
permissionMode: () => permissionMode,
|
|
6845
|
+
posixShellSingleQuote: () => posixShellSingleQuote,
|
|
6846
|
+
prepareTerminalCommand: () => prepareTerminalCommand,
|
|
6847
|
+
resolveCommandBinarySync: () => resolveCommandBinarySync,
|
|
6503
6848
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
6504
|
-
|
|
6849
|
+
resolveLoginCommand: () => resolveLoginCommand,
|
|
6850
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6851
|
+
withExportedPath: () => withExportedPath
|
|
6505
6852
|
});
|
|
6506
6853
|
function getAdapter(kind) {
|
|
6507
6854
|
return adapters[kind];
|
|
@@ -6559,31 +6906,31 @@ function removedWorkspacesFile() {
|
|
|
6559
6906
|
}
|
|
6560
6907
|
function readAll() {
|
|
6561
6908
|
const path = workspacesFile();
|
|
6562
|
-
if (!(0,
|
|
6909
|
+
if (!(0, import_node_fs19.existsSync)(path)) return [];
|
|
6563
6910
|
try {
|
|
6564
|
-
const raw = JSON.parse((0,
|
|
6911
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6565
6912
|
return Array.isArray(raw) ? raw : [];
|
|
6566
6913
|
} catch {
|
|
6567
6914
|
return [];
|
|
6568
6915
|
}
|
|
6569
6916
|
}
|
|
6570
6917
|
function writeAll(list) {
|
|
6571
|
-
(0,
|
|
6572
|
-
(0,
|
|
6918
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6919
|
+
(0, import_node_fs19.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
|
|
6573
6920
|
}
|
|
6574
6921
|
function readRemoved() {
|
|
6575
6922
|
const path = removedWorkspacesFile();
|
|
6576
|
-
if (!(0,
|
|
6923
|
+
if (!(0, import_node_fs19.existsSync)(path)) return /* @__PURE__ */ new Set();
|
|
6577
6924
|
try {
|
|
6578
|
-
const raw = JSON.parse((0,
|
|
6925
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6579
6926
|
return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
|
|
6580
6927
|
} catch {
|
|
6581
6928
|
return /* @__PURE__ */ new Set();
|
|
6582
6929
|
}
|
|
6583
6930
|
}
|
|
6584
6931
|
function writeRemoved(paths) {
|
|
6585
|
-
(0,
|
|
6586
|
-
(0,
|
|
6932
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6933
|
+
(0, import_node_fs19.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
|
|
6587
6934
|
}
|
|
6588
6935
|
function rememberRemoved(repoPath) {
|
|
6589
6936
|
const next = readRemoved();
|
|
@@ -6606,7 +6953,7 @@ function listWorkspaces() {
|
|
|
6606
6953
|
async function addWorkspace(repoPath) {
|
|
6607
6954
|
const root = await resolveRepoRoot(repoPath);
|
|
6608
6955
|
if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
|
|
6609
|
-
if (!(0,
|
|
6956
|
+
if (!(0, import_node_fs19.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
|
|
6610
6957
|
forgetRemoved(root);
|
|
6611
6958
|
await ensureGhPreferOrigin(root);
|
|
6612
6959
|
const current = readAll();
|
|
@@ -6636,7 +6983,7 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6636
6983
|
if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
|
|
6637
6984
|
continue;
|
|
6638
6985
|
}
|
|
6639
|
-
if (!(0,
|
|
6986
|
+
if (!(0, import_node_fs19.existsSync)(path)) continue;
|
|
6640
6987
|
const ws = {
|
|
6641
6988
|
path,
|
|
6642
6989
|
name: (0, import_node_path16.basename)(path),
|
|
@@ -6649,11 +6996,11 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6649
6996
|
if (dirty) writeAll(next);
|
|
6650
6997
|
return next.sort((a, b) => a.name.localeCompare(b.name));
|
|
6651
6998
|
}
|
|
6652
|
-
var
|
|
6999
|
+
var import_node_fs19, import_node_path16;
|
|
6653
7000
|
var init_workspaces = __esm({
|
|
6654
7001
|
"src/store/workspaces.ts"() {
|
|
6655
7002
|
"use strict";
|
|
6656
|
-
|
|
7003
|
+
import_node_fs19 = require("fs");
|
|
6657
7004
|
import_node_path16 = require("path");
|
|
6658
7005
|
init_paths();
|
|
6659
7006
|
init_global_workspace();
|
|
@@ -6732,17 +7079,17 @@ __export(plan_file_exports, {
|
|
|
6732
7079
|
});
|
|
6733
7080
|
function ensureAttachmentsGitignore2(worktreePath) {
|
|
6734
7081
|
const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
6735
|
-
if ((0,
|
|
6736
|
-
(0,
|
|
6737
|
-
(0,
|
|
7082
|
+
if ((0, import_node_fs29.existsSync)(gitignoreAbs)) return;
|
|
7083
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
|
|
7084
|
+
(0, import_node_fs29.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
6738
7085
|
}
|
|
6739
7086
|
function planFileAbs(worktreePath) {
|
|
6740
7087
|
return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
|
|
6741
7088
|
}
|
|
6742
7089
|
function readTextIfPresent2(abs) {
|
|
6743
|
-
if (!(0,
|
|
7090
|
+
if (!(0, import_node_fs29.existsSync)(abs)) return null;
|
|
6744
7091
|
try {
|
|
6745
|
-
const content = (0,
|
|
7092
|
+
const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
|
|
6746
7093
|
return content.trim() ? content : null;
|
|
6747
7094
|
} catch {
|
|
6748
7095
|
return null;
|
|
@@ -6754,16 +7101,16 @@ function readPlanFile(worktreePath) {
|
|
|
6754
7101
|
function writePlanFile(worktreePath, content) {
|
|
6755
7102
|
ensureAttachmentsGitignore2(worktreePath);
|
|
6756
7103
|
const abs = planFileAbs(worktreePath);
|
|
6757
|
-
(0,
|
|
7104
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
|
|
6758
7105
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
6759
|
-
(0,
|
|
7106
|
+
(0, import_node_fs29.writeFileSync)(abs, body, "utf8");
|
|
6760
7107
|
return PLAN_FILE_REL;
|
|
6761
7108
|
}
|
|
6762
|
-
var
|
|
7109
|
+
var import_node_fs29, import_node_path24;
|
|
6763
7110
|
var init_plan_file = __esm({
|
|
6764
7111
|
"src/plan/plan-file.ts"() {
|
|
6765
7112
|
"use strict";
|
|
6766
|
-
|
|
7113
|
+
import_node_fs29 = require("fs");
|
|
6767
7114
|
import_node_path24 = require("path");
|
|
6768
7115
|
init_workspace_scratch();
|
|
6769
7116
|
init_plan_present();
|
|
@@ -6780,9 +7127,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6780
7127
|
const agentId = opts.agentId.trim();
|
|
6781
7128
|
if (!agentId) return null;
|
|
6782
7129
|
const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
6783
|
-
if (!(0,
|
|
7130
|
+
if (!(0, import_node_fs30.existsSync)(runsPath)) return null;
|
|
6784
7131
|
try {
|
|
6785
|
-
const lines = (0,
|
|
7132
|
+
const lines = (0, import_node_fs30.readFileSync)(runsPath, "utf8").split("\n");
|
|
6786
7133
|
let best = null;
|
|
6787
7134
|
for (const line of lines) {
|
|
6788
7135
|
const trimmed = line.trim();
|
|
@@ -6808,11 +7155,11 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6808
7155
|
return null;
|
|
6809
7156
|
}
|
|
6810
7157
|
}
|
|
6811
|
-
var
|
|
7158
|
+
var import_node_fs30, import_node_path25;
|
|
6812
7159
|
var init_cursor_recover = __esm({
|
|
6813
7160
|
"src/agents/cursor-recover.ts"() {
|
|
6814
7161
|
"use strict";
|
|
6815
|
-
|
|
7162
|
+
import_node_fs30 = require("fs");
|
|
6816
7163
|
import_node_path25 = require("path");
|
|
6817
7164
|
init_paths();
|
|
6818
7165
|
}
|
|
@@ -6826,7 +7173,7 @@ var import_node_path26 = require("path");
|
|
|
6826
7173
|
|
|
6827
7174
|
// src/orchestrator/orchestrator.ts
|
|
6828
7175
|
var import_node_events = require("events");
|
|
6829
|
-
var
|
|
7176
|
+
var import_node_fs31 = require("fs");
|
|
6830
7177
|
init_error_detail();
|
|
6831
7178
|
|
|
6832
7179
|
// src/agents/spawn.ts
|
|
@@ -7216,7 +7563,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
7216
7563
|
}
|
|
7217
7564
|
|
|
7218
7565
|
// src/hook/conductor.ts
|
|
7219
|
-
var
|
|
7566
|
+
var import_node_fs16 = require("fs");
|
|
7220
7567
|
var import_node_net = require("net");
|
|
7221
7568
|
var import_node_path13 = require("path");
|
|
7222
7569
|
var import_execa3 = require("execa");
|
|
@@ -7229,8 +7576,8 @@ function matchSimpleGlob(pattern, name) {
|
|
|
7229
7576
|
}
|
|
7230
7577
|
function readWorktreeInclude(repoPath) {
|
|
7231
7578
|
const path = (0, import_node_path13.join)(repoPath, ".worktreeinclude");
|
|
7232
|
-
if (!(0,
|
|
7233
|
-
return (0,
|
|
7579
|
+
if (!(0, import_node_fs16.existsSync)(path)) return [];
|
|
7580
|
+
return (0, import_node_fs16.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
7234
7581
|
}
|
|
7235
7582
|
function resolveFilesToCopy(repoPath) {
|
|
7236
7583
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -7240,7 +7587,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7240
7587
|
if (settings?.fileIncludeGlobs?.length) {
|
|
7241
7588
|
const matched = [];
|
|
7242
7589
|
try {
|
|
7243
|
-
for (const entry of (0,
|
|
7590
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7244
7591
|
if (!entry.isFile()) continue;
|
|
7245
7592
|
for (const glob of settings.fileIncludeGlobs) {
|
|
7246
7593
|
if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path13.basename)(glob), entry.name)) {
|
|
@@ -7255,7 +7602,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7255
7602
|
}
|
|
7256
7603
|
const defaults = [];
|
|
7257
7604
|
try {
|
|
7258
|
-
for (const entry of (0,
|
|
7605
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7259
7606
|
if (entry.isFile() && entry.name.startsWith(".env")) {
|
|
7260
7607
|
defaults.push(entry.name);
|
|
7261
7608
|
}
|
|
@@ -7270,10 +7617,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
7270
7617
|
const copied = [];
|
|
7271
7618
|
for (const rel of patterns) {
|
|
7272
7619
|
const src = (0, import_node_path13.join)(repoPath, rel);
|
|
7273
|
-
if (!(0,
|
|
7620
|
+
if (!(0, import_node_fs16.existsSync)(src)) continue;
|
|
7274
7621
|
const dest = (0, import_node_path13.join)(worktreePath, rel);
|
|
7275
|
-
(0,
|
|
7276
|
-
(0,
|
|
7622
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path13.dirname)(dest), { recursive: true });
|
|
7623
|
+
(0, import_node_fs16.copyFileSync)(src, dest);
|
|
7277
7624
|
copied.push(rel);
|
|
7278
7625
|
}
|
|
7279
7626
|
return copied;
|
|
@@ -7526,15 +7873,15 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
7526
7873
|
}
|
|
7527
7874
|
|
|
7528
7875
|
// src/hook/cursor-worktrees.ts
|
|
7529
|
-
var
|
|
7876
|
+
var import_node_fs17 = require("fs");
|
|
7530
7877
|
var import_node_path14 = require("path");
|
|
7531
7878
|
var import_execa4 = require("execa");
|
|
7532
7879
|
var import_node_readline3 = require("readline");
|
|
7533
7880
|
function loadCursorWorktreesJson(rootPath) {
|
|
7534
7881
|
const path = (0, import_node_path14.join)(rootPath, ".cursor", "worktrees.json");
|
|
7535
|
-
if (!(0,
|
|
7882
|
+
if (!(0, import_node_fs17.existsSync)(path)) return null;
|
|
7536
7883
|
try {
|
|
7537
|
-
return JSON.parse((0,
|
|
7884
|
+
return JSON.parse((0, import_node_fs17.readFileSync)(path, "utf8"));
|
|
7538
7885
|
} catch {
|
|
7539
7886
|
return null;
|
|
7540
7887
|
}
|
|
@@ -7592,7 +7939,7 @@ async function runCursorWorktreeSetup(repoPath, worktreePath, onLine) {
|
|
|
7592
7939
|
}
|
|
7593
7940
|
|
|
7594
7941
|
// src/git/orphan-cleanup.ts
|
|
7595
|
-
var
|
|
7942
|
+
var import_node_fs18 = require("fs");
|
|
7596
7943
|
var import_node_path15 = require("path");
|
|
7597
7944
|
init_worktree();
|
|
7598
7945
|
init_thread_store();
|
|
@@ -7608,9 +7955,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7608
7955
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
7609
7956
|
);
|
|
7610
7957
|
const homeRoot = sideboardWorkspacesDir();
|
|
7611
|
-
if ((0,
|
|
7958
|
+
if ((0, import_node_fs18.existsSync)(homeRoot)) {
|
|
7612
7959
|
try {
|
|
7613
|
-
for (const entry of (0,
|
|
7960
|
+
for (const entry of (0, import_node_fs18.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
7614
7961
|
if (!entry.isDirectory()) continue;
|
|
7615
7962
|
void entry;
|
|
7616
7963
|
}
|
|
@@ -7620,7 +7967,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7620
7967
|
const orphans = [];
|
|
7621
7968
|
const seen = /* @__PURE__ */ new Set();
|
|
7622
7969
|
for (const repoPath of repos) {
|
|
7623
|
-
if (!repoPath || !(0,
|
|
7970
|
+
if (!repoPath || !(0, import_node_fs18.existsSync)(repoPath)) continue;
|
|
7624
7971
|
try {
|
|
7625
7972
|
const wts = await listWorktrees(repoPath);
|
|
7626
7973
|
for (const wt of wts) {
|
|
@@ -7631,7 +7978,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7631
7978
|
seen.add(path);
|
|
7632
7979
|
let mtimeMs = 0;
|
|
7633
7980
|
try {
|
|
7634
|
-
mtimeMs = (0,
|
|
7981
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7635
7982
|
} catch {
|
|
7636
7983
|
mtimeMs = 0;
|
|
7637
7984
|
}
|
|
@@ -7641,16 +7988,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7641
7988
|
}
|
|
7642
7989
|
try {
|
|
7643
7990
|
const root = worktreesRoot(repoPath);
|
|
7644
|
-
if ((0,
|
|
7645
|
-
for (const entry of (0,
|
|
7991
|
+
if ((0, import_node_fs18.existsSync)(root)) {
|
|
7992
|
+
for (const entry of (0, import_node_fs18.readdirSync)(root, { withFileTypes: true })) {
|
|
7646
7993
|
if (!entry.isDirectory()) continue;
|
|
7647
7994
|
const path = (0, import_node_path15.join)(root, entry.name).replace(/\/$/, "");
|
|
7648
7995
|
if (known.has(path) || seen.has(path)) continue;
|
|
7649
|
-
if (!(0,
|
|
7996
|
+
if (!(0, import_node_fs18.existsSync)((0, import_node_path15.join)(path, ".git"))) continue;
|
|
7650
7997
|
seen.add(path);
|
|
7651
7998
|
let mtimeMs = 0;
|
|
7652
7999
|
try {
|
|
7653
|
-
mtimeMs = (0,
|
|
8000
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7654
8001
|
} catch {
|
|
7655
8002
|
mtimeMs = Date.now();
|
|
7656
8003
|
}
|
|
@@ -7787,7 +8134,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
7787
8134
|
}
|
|
7788
8135
|
|
|
7789
8136
|
// src/git/clone-repo.ts
|
|
7790
|
-
var
|
|
8137
|
+
var import_node_fs20 = require("fs");
|
|
7791
8138
|
var import_node_path17 = require("path");
|
|
7792
8139
|
var import_execa6 = require("execa");
|
|
7793
8140
|
init_paths();
|
|
@@ -7803,7 +8150,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7803
8150
|
}
|
|
7804
8151
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
7805
8152
|
const dest = (0, import_node_path17.join)(sideboardReposDir(), name);
|
|
7806
|
-
if ((0,
|
|
8153
|
+
if ((0, import_node_fs20.existsSync)(dest)) {
|
|
7807
8154
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
7808
8155
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
7809
8156
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -7823,13 +8170,14 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7823
8170
|
init_thread_store();
|
|
7824
8171
|
|
|
7825
8172
|
// src/threads/create.ts
|
|
7826
|
-
var
|
|
8173
|
+
var import_node_fs21 = require("fs");
|
|
7827
8174
|
|
|
7828
8175
|
// src/detect/detect.ts
|
|
7829
8176
|
init_agents();
|
|
7830
8177
|
var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
|
|
7831
8178
|
async function requireAgent(agent, opts) {
|
|
7832
8179
|
ensureAgentPath();
|
|
8180
|
+
enrichPathWithNpmGlobalBin();
|
|
7833
8181
|
const status = await Promise.race([
|
|
7834
8182
|
getAdapter(agent).detect(),
|
|
7835
8183
|
new Promise((_, reject) => {
|
|
@@ -7867,7 +8215,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
7867
8215
|
});
|
|
7868
8216
|
await requireAgent(resolved.agent);
|
|
7869
8217
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
7870
|
-
if (!(0,
|
|
8218
|
+
if (!(0, import_node_fs21.existsSync)(repoPath)) {
|
|
7871
8219
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
7872
8220
|
}
|
|
7873
8221
|
let sourceRef = input.sourceRef;
|
|
@@ -8330,7 +8678,7 @@ function forkChatTab(input) {
|
|
|
8330
8678
|
|
|
8331
8679
|
// src/review/request-review.ts
|
|
8332
8680
|
var import_node_crypto3 = require("crypto");
|
|
8333
|
-
var
|
|
8681
|
+
var import_node_fs22 = require("fs");
|
|
8334
8682
|
var import_node_path18 = require("path");
|
|
8335
8683
|
init_global_workspace();
|
|
8336
8684
|
init_thread_store();
|
|
@@ -8475,9 +8823,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
8475
8823
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
8476
8824
|
}
|
|
8477
8825
|
function readTextIfPresent(abs) {
|
|
8478
|
-
if (!(0,
|
|
8826
|
+
if (!(0, import_node_fs22.existsSync)(abs)) return null;
|
|
8479
8827
|
try {
|
|
8480
|
-
const content = (0,
|
|
8828
|
+
const content = (0, import_node_fs22.readFileSync)(abs, "utf8");
|
|
8481
8829
|
return content.trim() ? content : null;
|
|
8482
8830
|
} catch {
|
|
8483
8831
|
return null;
|
|
@@ -8485,9 +8833,9 @@ function readTextIfPresent(abs) {
|
|
|
8485
8833
|
}
|
|
8486
8834
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
8487
8835
|
const gitignoreAbs = (0, import_node_path18.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
8488
|
-
if ((0,
|
|
8489
|
-
(0,
|
|
8490
|
-
(0,
|
|
8836
|
+
if ((0, import_node_fs22.existsSync)(gitignoreAbs)) return;
|
|
8837
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path18.dirname)(gitignoreAbs), { recursive: true });
|
|
8838
|
+
(0, import_node_fs22.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
8491
8839
|
}
|
|
8492
8840
|
function resolveReviewGuidelines(worktreePath) {
|
|
8493
8841
|
const repoAbs = (0, import_node_path18.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -8521,8 +8869,8 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
8521
8869
|
};
|
|
8522
8870
|
}
|
|
8523
8871
|
ensureAttachmentsGitignore(worktreePath);
|
|
8524
|
-
(0,
|
|
8525
|
-
(0,
|
|
8872
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path18.dirname)(localAbs), { recursive: true });
|
|
8873
|
+
(0, import_node_fs22.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
8526
8874
|
return {
|
|
8527
8875
|
path: REVIEW_REQUEST_PATH,
|
|
8528
8876
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -8721,8 +9069,8 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
8721
9069
|
}
|
|
8722
9070
|
|
|
8723
9071
|
// src/threads/adopt.ts
|
|
8724
|
-
var
|
|
8725
|
-
var
|
|
9072
|
+
var import_node_child_process2 = require("child_process");
|
|
9073
|
+
var import_node_fs23 = require("fs");
|
|
8726
9074
|
var import_node_os8 = require("os");
|
|
8727
9075
|
var import_node_path19 = require("path");
|
|
8728
9076
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -8747,21 +9095,21 @@ function mapAgentType(raw) {
|
|
|
8747
9095
|
return null;
|
|
8748
9096
|
}
|
|
8749
9097
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
8750
|
-
if (!workspacePath || !(0,
|
|
9098
|
+
if (!workspacePath || !(0, import_node_fs23.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
8751
9099
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
8752
9100
|
let best = null;
|
|
8753
9101
|
let hashes;
|
|
8754
9102
|
try {
|
|
8755
|
-
hashes = (0,
|
|
9103
|
+
hashes = (0, import_node_fs23.readdirSync)(CURSOR_SDK_STORE);
|
|
8756
9104
|
} catch {
|
|
8757
9105
|
return null;
|
|
8758
9106
|
}
|
|
8759
9107
|
for (const hash of hashes) {
|
|
8760
9108
|
const agentsFile = (0, import_node_path19.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
8761
|
-
if (!(0,
|
|
9109
|
+
if (!(0, import_node_fs23.existsSync)(agentsFile)) continue;
|
|
8762
9110
|
let text;
|
|
8763
9111
|
try {
|
|
8764
|
-
text = (0,
|
|
9112
|
+
text = (0, import_node_fs23.readFileSync)(agentsFile, "utf8");
|
|
8765
9113
|
} catch {
|
|
8766
9114
|
continue;
|
|
8767
9115
|
}
|
|
@@ -8785,7 +9133,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
8785
9133
|
return best?.agentId ?? null;
|
|
8786
9134
|
}
|
|
8787
9135
|
async function adoptThread(input) {
|
|
8788
|
-
if (!(0,
|
|
9136
|
+
if (!(0, import_node_fs23.existsSync)(input.worktreePath)) {
|
|
8789
9137
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
8790
9138
|
}
|
|
8791
9139
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -8809,18 +9157,18 @@ async function adoptThread(input) {
|
|
|
8809
9157
|
return thread;
|
|
8810
9158
|
}
|
|
8811
9159
|
function listConductorWorkspaces() {
|
|
8812
|
-
if (!(0,
|
|
9160
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8813
9161
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8814
9162
|
}
|
|
8815
|
-
const tmp = (0,
|
|
9163
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8816
9164
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8817
9165
|
try {
|
|
8818
|
-
(0,
|
|
9166
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8819
9167
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8820
9168
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8821
|
-
if ((0,
|
|
9169
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8822
9170
|
try {
|
|
8823
|
-
(0,
|
|
9171
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8824
9172
|
} catch {
|
|
8825
9173
|
}
|
|
8826
9174
|
}
|
|
@@ -8896,22 +9244,22 @@ function listConductorWorkspaces() {
|
|
|
8896
9244
|
db.close();
|
|
8897
9245
|
}
|
|
8898
9246
|
} finally {
|
|
8899
|
-
(0,
|
|
9247
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8900
9248
|
}
|
|
8901
9249
|
}
|
|
8902
9250
|
function importConductorWorkspace(workspaceId) {
|
|
8903
|
-
if (!(0,
|
|
9251
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8904
9252
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8905
9253
|
}
|
|
8906
|
-
const tmp = (0,
|
|
9254
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8907
9255
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8908
9256
|
try {
|
|
8909
|
-
(0,
|
|
9257
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8910
9258
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8911
9259
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8912
|
-
if ((0,
|
|
9260
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8913
9261
|
try {
|
|
8914
|
-
(0,
|
|
9262
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8915
9263
|
} catch {
|
|
8916
9264
|
}
|
|
8917
9265
|
}
|
|
@@ -8929,7 +9277,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8929
9277
|
).get(workspaceId);
|
|
8930
9278
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
8931
9279
|
const worktreePath = String(row.workspacePath);
|
|
8932
|
-
if (!(0,
|
|
9280
|
+
if (!(0, import_node_fs23.existsSync)(worktreePath)) {
|
|
8933
9281
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
8934
9282
|
}
|
|
8935
9283
|
let sessionId = null;
|
|
@@ -8969,7 +9317,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8969
9317
|
}
|
|
8970
9318
|
let repoPath = worktreePath;
|
|
8971
9319
|
try {
|
|
8972
|
-
repoPath = (0,
|
|
9320
|
+
repoPath = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
8973
9321
|
cwd: worktreePath,
|
|
8974
9322
|
encoding: "utf8"
|
|
8975
9323
|
}).trim();
|
|
@@ -8992,7 +9340,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8992
9340
|
db.close();
|
|
8993
9341
|
}
|
|
8994
9342
|
} finally {
|
|
8995
|
-
(0,
|
|
9343
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8996
9344
|
}
|
|
8997
9345
|
}
|
|
8998
9346
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -9000,7 +9348,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
9000
9348
|
}
|
|
9001
9349
|
|
|
9002
9350
|
// src/threads/stack-layers.ts
|
|
9003
|
-
var
|
|
9351
|
+
var import_node_fs24 = require("fs");
|
|
9004
9352
|
init_run();
|
|
9005
9353
|
init_stack();
|
|
9006
9354
|
init_worktree();
|
|
@@ -9068,7 +9416,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
9068
9416
|
let createdWorktree = false;
|
|
9069
9417
|
const trees = await listWorktrees(repoPath);
|
|
9070
9418
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
9071
|
-
if (checkedOut?.path && (0,
|
|
9419
|
+
if (checkedOut?.path && (0, import_node_fs24.existsSync)(checkedOut.path)) {
|
|
9072
9420
|
if (input.reuseExistingWorktree !== false) {
|
|
9073
9421
|
worktreePath = checkedOut.path;
|
|
9074
9422
|
} else {
|
|
@@ -9210,7 +9558,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
9210
9558
|
async function createPrStack(input, onSetupLine) {
|
|
9211
9559
|
await requireAgent(input.agent);
|
|
9212
9560
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9213
|
-
if (!(0,
|
|
9561
|
+
if (!(0, import_node_fs24.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
9214
9562
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
9215
9563
|
const status = await detectGhStack(repoPath);
|
|
9216
9564
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -9277,7 +9625,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9277
9625
|
}
|
|
9278
9626
|
}
|
|
9279
9627
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
9280
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
9628
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs24.existsSync)(bootstrap.worktreePath)) {
|
|
9281
9629
|
try {
|
|
9282
9630
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
9283
9631
|
deleteBranch: bootstrap.branchName
|
|
@@ -9292,12 +9640,12 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9292
9640
|
init_worktree();
|
|
9293
9641
|
|
|
9294
9642
|
// src/diff/diff.ts
|
|
9295
|
-
var
|
|
9643
|
+
var import_node_fs25 = require("fs");
|
|
9296
9644
|
var import_node_path20 = require("path");
|
|
9297
9645
|
init_run();
|
|
9298
9646
|
init_worktree();
|
|
9299
9647
|
async function inspectGitWorktree(worktreePath) {
|
|
9300
|
-
if (!worktreePath || !(0,
|
|
9648
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) return "missing_worktree";
|
|
9301
9649
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
9302
9650
|
reject: false
|
|
9303
9651
|
});
|
|
@@ -9305,7 +9653,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
9305
9653
|
return "ok";
|
|
9306
9654
|
}
|
|
9307
9655
|
async function initializeGitRepository(worktreePath) {
|
|
9308
|
-
if (!worktreePath || !(0,
|
|
9656
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) {
|
|
9309
9657
|
throw new Error("Worktree not found");
|
|
9310
9658
|
}
|
|
9311
9659
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -9743,7 +10091,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9743
10091
|
assertSafeRelativePath(relativePath);
|
|
9744
10092
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
9745
10093
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9746
|
-
const st = (0,
|
|
10094
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9747
10095
|
if (!st.isFile()) {
|
|
9748
10096
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9749
10097
|
}
|
|
@@ -9752,7 +10100,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9752
10100
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
9753
10101
|
);
|
|
9754
10102
|
}
|
|
9755
|
-
const buf = (0,
|
|
10103
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9756
10104
|
return {
|
|
9757
10105
|
path: relativePath,
|
|
9758
10106
|
contentBase64: buf.toString("base64"),
|
|
@@ -9763,11 +10111,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
9763
10111
|
assertSafeRelativePath(relativePath);
|
|
9764
10112
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
9765
10113
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9766
|
-
const st = (0,
|
|
10114
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9767
10115
|
if (!st.isFile()) {
|
|
9768
10116
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9769
10117
|
}
|
|
9770
|
-
const buf = (0,
|
|
10118
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9771
10119
|
if (isImageRelativePath(relativePath)) {
|
|
9772
10120
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
9773
10121
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -9811,8 +10159,8 @@ function assertSafeRelativePath(relativePath) {
|
|
|
9811
10159
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
9812
10160
|
assertSafeRelativePath(relativePath);
|
|
9813
10161
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9814
|
-
(0,
|
|
9815
|
-
(0,
|
|
10162
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path20.dirname)(abs), { recursive: true });
|
|
10163
|
+
(0, import_node_fs25.writeFileSync)(abs, content, "utf8");
|
|
9816
10164
|
return { path: relativePath };
|
|
9817
10165
|
}
|
|
9818
10166
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -9991,7 +10339,7 @@ async function confirmLand(thread, opts) {
|
|
|
9991
10339
|
}
|
|
9992
10340
|
|
|
9993
10341
|
// src/skills/discover.ts
|
|
9994
|
-
var
|
|
10342
|
+
var import_node_fs26 = require("fs");
|
|
9995
10343
|
var import_node_os9 = require("os");
|
|
9996
10344
|
var import_node_path21 = require("path");
|
|
9997
10345
|
function toCommand(name) {
|
|
@@ -10025,7 +10373,7 @@ function parseFrontmatter(content) {
|
|
|
10025
10373
|
}
|
|
10026
10374
|
function readSkill(skillMd, source) {
|
|
10027
10375
|
try {
|
|
10028
|
-
const content = (0,
|
|
10376
|
+
const content = (0, import_node_fs26.readFileSync)(skillMd, "utf8");
|
|
10029
10377
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
10030
10378
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
10031
10379
|
const name = fmName || dirName;
|
|
@@ -10044,19 +10392,19 @@ function readSkill(skillMd, source) {
|
|
|
10044
10392
|
}
|
|
10045
10393
|
}
|
|
10046
10394
|
function scanSkillsDir(dir, source, out) {
|
|
10047
|
-
if (!(0,
|
|
10395
|
+
if (!(0, import_node_fs26.existsSync)(dir)) return;
|
|
10048
10396
|
let entries;
|
|
10049
10397
|
try {
|
|
10050
|
-
entries = (0,
|
|
10398
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10051
10399
|
} catch {
|
|
10052
10400
|
return;
|
|
10053
10401
|
}
|
|
10054
10402
|
for (const entry of entries) {
|
|
10055
10403
|
if (entry.startsWith(".")) continue;
|
|
10056
10404
|
const skillMd = (0, import_node_path21.join)(dir, entry, "SKILL.md");
|
|
10057
|
-
if (!(0,
|
|
10405
|
+
if (!(0, import_node_fs26.existsSync)(skillMd)) continue;
|
|
10058
10406
|
try {
|
|
10059
|
-
if (!(0,
|
|
10407
|
+
if (!(0, import_node_fs26.statSync)(skillMd).isFile()) continue;
|
|
10060
10408
|
} catch {
|
|
10061
10409
|
continue;
|
|
10062
10410
|
}
|
|
@@ -10065,12 +10413,12 @@ function scanSkillsDir(dir, source, out) {
|
|
|
10065
10413
|
}
|
|
10066
10414
|
}
|
|
10067
10415
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
10068
|
-
if (!(0,
|
|
10416
|
+
if (!(0, import_node_fs26.existsSync)(pluginsRoot)) return;
|
|
10069
10417
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
10070
10418
|
if (depth > 7) return;
|
|
10071
10419
|
let entries;
|
|
10072
10420
|
try {
|
|
10073
|
-
entries = (0,
|
|
10421
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10074
10422
|
} catch {
|
|
10075
10423
|
return;
|
|
10076
10424
|
}
|
|
@@ -10082,7 +10430,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
10082
10430
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
10083
10431
|
const full = (0, import_node_path21.join)(dir, entry);
|
|
10084
10432
|
try {
|
|
10085
|
-
if (!(0,
|
|
10433
|
+
if (!(0, import_node_fs26.statSync)(full).isDirectory()) continue;
|
|
10086
10434
|
} catch {
|
|
10087
10435
|
continue;
|
|
10088
10436
|
}
|
|
@@ -10122,7 +10470,7 @@ function discoverSkills(worktreePath) {
|
|
|
10122
10470
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
10123
10471
|
}
|
|
10124
10472
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
10125
|
-
const raw = (0,
|
|
10473
|
+
const raw = (0, import_node_fs26.readFileSync)(skillPath, "utf8");
|
|
10126
10474
|
if (raw.startsWith("---")) {
|
|
10127
10475
|
const end = raw.indexOf("\n---", 3);
|
|
10128
10476
|
if (end >= 0) {
|
|
@@ -10215,7 +10563,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
10215
10563
|
}
|
|
10216
10564
|
|
|
10217
10565
|
// src/composer/stage-files.ts
|
|
10218
|
-
var
|
|
10566
|
+
var import_node_fs27 = require("fs");
|
|
10219
10567
|
var import_node_path22 = require("path");
|
|
10220
10568
|
var import_node_crypto5 = require("crypto");
|
|
10221
10569
|
init_workspace_scratch();
|
|
@@ -10253,21 +10601,21 @@ function imageMimeType(filePath) {
|
|
|
10253
10601
|
}
|
|
10254
10602
|
function ensureAttachmentsDir(worktreePath) {
|
|
10255
10603
|
const dir = (0, import_node_path22.join)(worktreePath, ATTACHMENTS_DIR);
|
|
10256
|
-
(0,
|
|
10604
|
+
(0, import_node_fs27.mkdirSync)(dir, { recursive: true });
|
|
10257
10605
|
const gi = (0, import_node_path22.join)(dir, ".gitignore");
|
|
10258
|
-
if (!(0,
|
|
10259
|
-
(0,
|
|
10606
|
+
if (!(0, import_node_fs27.existsSync)(gi)) {
|
|
10607
|
+
(0, import_node_fs27.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
10260
10608
|
}
|
|
10261
10609
|
return dir;
|
|
10262
10610
|
}
|
|
10263
10611
|
function uniqueAttachmentName(dir, originalName) {
|
|
10264
10612
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
10265
|
-
if (!(0,
|
|
10613
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, safe))) return safe;
|
|
10266
10614
|
const ext = (0, import_node_path22.extname)(safe);
|
|
10267
10615
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
10268
10616
|
for (let i = 1; i < 1e4; i++) {
|
|
10269
10617
|
const candidate = `${stem}-${i}${ext}`;
|
|
10270
|
-
if (!(0,
|
|
10618
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
|
|
10271
10619
|
}
|
|
10272
10620
|
return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
|
|
10273
10621
|
}
|
|
@@ -10325,13 +10673,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
10325
10673
|
for (const abs of absolutePaths) {
|
|
10326
10674
|
const originalName = (0, import_node_path22.basename)(abs);
|
|
10327
10675
|
try {
|
|
10328
|
-
const st = (0,
|
|
10676
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10329
10677
|
if (!st.isFile()) continue;
|
|
10330
10678
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10331
10679
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10332
|
-
(0,
|
|
10680
|
+
(0, import_node_fs27.copyFileSync)(abs, destAbs);
|
|
10333
10681
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10334
|
-
const buf = (0,
|
|
10682
|
+
const buf = (0, import_node_fs27.readFileSync)(destAbs);
|
|
10335
10683
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10336
10684
|
} catch (err) {
|
|
10337
10685
|
out.push({
|
|
@@ -10354,7 +10702,7 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
10354
10702
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
10355
10703
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10356
10704
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10357
|
-
(0,
|
|
10705
|
+
(0, import_node_fs27.writeFileSync)(destAbs, buf);
|
|
10358
10706
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10359
10707
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
10360
10708
|
} catch (err) {
|
|
@@ -10383,9 +10731,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10383
10731
|
const name = (0, import_node_path22.basename)(rel);
|
|
10384
10732
|
try {
|
|
10385
10733
|
const abs = (0, import_node_path22.join)(worktreePath, rel);
|
|
10386
|
-
const st = (0,
|
|
10734
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10387
10735
|
if (!st.isFile()) continue;
|
|
10388
|
-
const buf = (0,
|
|
10736
|
+
const buf = (0, import_node_fs27.readFileSync)(abs);
|
|
10389
10737
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10390
10738
|
} catch (err) {
|
|
10391
10739
|
out.push({
|
|
@@ -10400,7 +10748,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10400
10748
|
}
|
|
10401
10749
|
|
|
10402
10750
|
// src/agents/instructions.ts
|
|
10403
|
-
var
|
|
10751
|
+
var import_node_fs28 = require("fs");
|
|
10404
10752
|
var import_node_path23 = require("path");
|
|
10405
10753
|
init_worktree_labels();
|
|
10406
10754
|
function normPath2(p) {
|
|
@@ -10500,6 +10848,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
10500
10848
|
);
|
|
10501
10849
|
return lines.join("\n");
|
|
10502
10850
|
}
|
|
10851
|
+
function formatWorktreeReminder() {
|
|
10852
|
+
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.";
|
|
10853
|
+
}
|
|
10503
10854
|
function formatArtifactDirective() {
|
|
10504
10855
|
return [
|
|
10505
10856
|
"Sideboard side column (desktop UI):",
|
|
@@ -10520,60 +10871,6 @@ function formatArtifactDirective() {
|
|
|
10520
10871
|
"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
10872
|
].join("\n");
|
|
10522
10873
|
}
|
|
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
10874
|
|
|
10578
10875
|
// src/orchestrator/orchestrator.ts
|
|
10579
10876
|
init_types();
|
|
@@ -10698,7 +10995,7 @@ var Orchestrator = class {
|
|
|
10698
10995
|
}
|
|
10699
10996
|
continue;
|
|
10700
10997
|
}
|
|
10701
|
-
if (!(0,
|
|
10998
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
10702
10999
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10703
11000
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10704
11001
|
continue;
|
|
@@ -11115,13 +11412,14 @@ var Orchestrator = class {
|
|
|
11115
11412
|
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
11116
11413
|
"Do not say artifacts/CMS UI are unavailable."
|
|
11117
11414
|
].join(" ") : null;
|
|
11415
|
+
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
11118
11416
|
const agentPrompt = [
|
|
11119
11417
|
thread.planMode ? PLAN_MODE_INSTRUCTION : null,
|
|
11120
11418
|
orchestrationReminder,
|
|
11419
|
+
worktreeReminder,
|
|
11121
11420
|
artifactReminder,
|
|
11122
11421
|
expandedPrompt
|
|
11123
11422
|
].filter(Boolean).join("\n\n");
|
|
11124
|
-
const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
|
|
11125
11423
|
if (thread.attachments.length > 0) {
|
|
11126
11424
|
updateThread(threadId, { attachments: [] });
|
|
11127
11425
|
}
|
|
@@ -11149,7 +11447,6 @@ var Orchestrator = class {
|
|
|
11149
11447
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
11150
11448
|
customPrompt: settings?.prompts?.renameBranch
|
|
11151
11449
|
}) : null;
|
|
11152
|
-
const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
|
|
11153
11450
|
let seed = null;
|
|
11154
11451
|
if (!fresh.sessionId) {
|
|
11155
11452
|
const prior = fresh.messages.slice(0, -1);
|
|
@@ -11170,12 +11467,12 @@ var Orchestrator = class {
|
|
|
11170
11467
|
});
|
|
11171
11468
|
}
|
|
11172
11469
|
}
|
|
11173
|
-
const cachedPrefix = [
|
|
11470
|
+
const cachedPrefix = fresh.sessionId ? "" : [
|
|
11174
11471
|
coordinatorDirective,
|
|
11175
11472
|
worktreeDirective,
|
|
11176
11473
|
artifactDirective,
|
|
11177
11474
|
renameBranchDirective,
|
|
11178
|
-
|
|
11475
|
+
seed
|
|
11179
11476
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11180
11477
|
try {
|
|
11181
11478
|
const stderrTail = [];
|
|
@@ -11262,15 +11559,11 @@ var Orchestrator = class {
|
|
|
11262
11559
|
const retryThread = this.requireThread(threadId);
|
|
11263
11560
|
const prior = retryThread.messages.slice(0, -1);
|
|
11264
11561
|
const retrySeed = buildSessionSeed(prior);
|
|
11265
|
-
const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
|
|
11266
|
-
loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
|
|
11267
|
-
);
|
|
11268
11562
|
const retryPrefix = [
|
|
11269
11563
|
coordinatorDirective,
|
|
11270
11564
|
worktreeDirective,
|
|
11271
11565
|
artifactDirective,
|
|
11272
11566
|
renameBranchDirective,
|
|
11273
|
-
retryInstructions,
|
|
11274
11567
|
retrySeed
|
|
11275
11568
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11276
11569
|
const retryHandle = await spawnAgentTurn(
|
|
@@ -12105,7 +12398,7 @@ var Orchestrator = class {
|
|
|
12105
12398
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
12106
12399
|
return restored2;
|
|
12107
12400
|
}
|
|
12108
|
-
if (!(0,
|
|
12401
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
12109
12402
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
12110
12403
|
const { execa: execa7 } = await import("execa");
|
|
12111
12404
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -12314,6 +12607,7 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
12314
12607
|
}
|
|
12315
12608
|
|
|
12316
12609
|
// src/mcp/server.ts
|
|
12610
|
+
init_profile();
|
|
12317
12611
|
var MAX_ORCH_THREADS = 5;
|
|
12318
12612
|
var CREATE_THREAD_TIMEOUT_MS = 9e4;
|
|
12319
12613
|
function withTimeout(promise, ms, label) {
|
|
@@ -12427,14 +12721,13 @@ async function startMcpServer() {
|
|
|
12427
12721
|
),
|
|
12428
12722
|
artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
12429
12723
|
},
|
|
12430
|
-
async ({ title, type,
|
|
12724
|
+
async ({ title, type, artifact_id }) => {
|
|
12431
12725
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
12432
12726
|
const payload = {
|
|
12433
12727
|
ok: true,
|
|
12434
12728
|
artifact_id: id,
|
|
12435
12729
|
title,
|
|
12436
12730
|
type,
|
|
12437
|
-
content,
|
|
12438
12731
|
message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
12439
12732
|
};
|
|
12440
12733
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12487,7 +12780,6 @@ async function startMcpServer() {
|
|
|
12487
12780
|
ok: true,
|
|
12488
12781
|
path,
|
|
12489
12782
|
title: title?.trim() || "Plan",
|
|
12490
|
-
content,
|
|
12491
12783
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
12492
12784
|
};
|
|
12493
12785
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12517,9 +12809,6 @@ async function startMcpServer() {
|
|
|
12517
12809
|
datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
|
|
12518
12810
|
resource_id: args.resource_id,
|
|
12519
12811
|
record_id: args.record_id,
|
|
12520
|
-
resource: args.resource,
|
|
12521
|
-
record: args.record,
|
|
12522
|
-
records: args.records,
|
|
12523
12812
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
12524
12813
|
};
|
|
12525
12814
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12547,739 +12836,741 @@ async function startMcpServer() {
|
|
|
12547
12836
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
12548
12837
|
}
|
|
12549
12838
|
);
|
|
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
|
-
|
|
12839
|
+
if (sideboardMcpProfile() !== "worktree") {
|
|
12840
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
12841
|
+
const accountDefaults = resolveThreadDefaults2();
|
|
12842
|
+
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
12843
|
+
server.tool(
|
|
12844
|
+
"create_thread",
|
|
12845
|
+
`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.`,
|
|
12846
|
+
{
|
|
12847
|
+
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
|
|
12848
|
+
sourceRef: import_zod.z.string(),
|
|
12849
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
12850
|
+
model: import_zod.z.string().nullable().optional().describe(
|
|
12851
|
+
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
12852
|
+
),
|
|
12853
|
+
repoPath: import_zod.z.string(),
|
|
12854
|
+
title: import_zod.z.string().optional(),
|
|
12855
|
+
parentThreadId: import_zod.z.string().optional().describe(
|
|
12856
|
+
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
12857
|
+
)
|
|
12858
|
+
},
|
|
12859
|
+
async (args) => {
|
|
12860
|
+
const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
|
|
12861
|
+
let parentId = args.parentThreadId?.trim() || "";
|
|
12862
|
+
let parent = parentId ? orch.getThread(parentId) : null;
|
|
12863
|
+
let parentCorrectedFrom;
|
|
12864
|
+
if (envParentId) {
|
|
12865
|
+
const envParent = orch.getThread(envParentId);
|
|
12866
|
+
if (envParent) {
|
|
12867
|
+
if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
|
|
12868
|
+
else if (!parentId) parentCorrectedFrom = void 0;
|
|
12869
|
+
parentId = envParentId;
|
|
12870
|
+
parent = envParent;
|
|
12871
|
+
}
|
|
12581
12872
|
}
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12873
|
+
if (parentId && !parent) {
|
|
12874
|
+
parentCorrectedFrom = parentId;
|
|
12875
|
+
parentId = "";
|
|
12876
|
+
parent = null;
|
|
12877
|
+
}
|
|
12878
|
+
if (parentId) {
|
|
12879
|
+
const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
|
|
12880
|
+
if (children.length >= MAX_ORCH_THREADS) {
|
|
12881
|
+
return {
|
|
12882
|
+
content: [
|
|
12883
|
+
{
|
|
12884
|
+
type: "text",
|
|
12885
|
+
text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
|
|
12886
|
+
}
|
|
12887
|
+
],
|
|
12888
|
+
isError: true
|
|
12889
|
+
};
|
|
12890
|
+
}
|
|
12891
|
+
}
|
|
12892
|
+
let agentArg = args.agent;
|
|
12893
|
+
let agentCoercedFrom;
|
|
12894
|
+
const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
|
|
12895
|
+
if (resolvedProbe === "codex") {
|
|
12896
|
+
agentCoercedFrom = agentArg ?? "codex";
|
|
12897
|
+
const accountAgent = resolveNewThreadOptions2({}).agent;
|
|
12898
|
+
agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
|
|
12899
|
+
}
|
|
12900
|
+
const opts = resolveNewThreadOptions2({
|
|
12901
|
+
agent: agentArg,
|
|
12902
|
+
model: args.model
|
|
12903
|
+
});
|
|
12904
|
+
try {
|
|
12905
|
+
const thread = await withTimeout(
|
|
12906
|
+
orch.createThread({
|
|
12907
|
+
sourceType: args.sourceType,
|
|
12908
|
+
sourceRef: args.sourceRef,
|
|
12909
|
+
agent: opts.agent,
|
|
12910
|
+
model: opts.model,
|
|
12911
|
+
effort: opts.effort,
|
|
12912
|
+
fast: opts.fast,
|
|
12913
|
+
repoPath: args.repoPath,
|
|
12914
|
+
title: args.title,
|
|
12915
|
+
parentThreadId: parentId || null
|
|
12916
|
+
}),
|
|
12917
|
+
CREATE_THREAD_TIMEOUT_MS,
|
|
12918
|
+
"create_thread"
|
|
12919
|
+
);
|
|
12920
|
+
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
12921
|
+
return {
|
|
12922
|
+
content: [
|
|
12923
|
+
{
|
|
12924
|
+
type: "text",
|
|
12925
|
+
text: JSON.stringify({
|
|
12926
|
+
id: thread.id,
|
|
12927
|
+
title: thread.title,
|
|
12928
|
+
branchName: thread.branchName,
|
|
12929
|
+
worktreePath: thread.worktreePath,
|
|
12930
|
+
agent: thread.agent,
|
|
12931
|
+
model: thread.model,
|
|
12932
|
+
status: thread.status,
|
|
12933
|
+
link: `sideboard://thread/${thread.id}`,
|
|
12934
|
+
parentThreadId: thread.parentThreadId,
|
|
12935
|
+
...agentCoercedFrom ? {
|
|
12936
|
+
agentCoercedFrom,
|
|
12937
|
+
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
12938
|
+
} : {},
|
|
12939
|
+
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
12940
|
+
})
|
|
12941
|
+
}
|
|
12942
|
+
]
|
|
12943
|
+
};
|
|
12944
|
+
} catch (err) {
|
|
12945
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12591
12946
|
return {
|
|
12592
12947
|
content: [
|
|
12593
12948
|
{
|
|
12594
12949
|
type: "text",
|
|
12595
|
-
text: `
|
|
12950
|
+
text: `create_thread failed: ${message}`
|
|
12596
12951
|
}
|
|
12597
12952
|
],
|
|
12598
12953
|
isError: true
|
|
12599
12954
|
};
|
|
12600
12955
|
}
|
|
12601
12956
|
}
|
|
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;
|
|
12957
|
+
);
|
|
12958
|
+
server.tool(
|
|
12959
|
+
"send_to_thread",
|
|
12960
|
+
"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.",
|
|
12961
|
+
{
|
|
12962
|
+
ref: import_zod.z.string(),
|
|
12963
|
+
prompt: import_zod.z.string(),
|
|
12964
|
+
force_stop: import_zod.z.boolean().optional()
|
|
12965
|
+
},
|
|
12966
|
+
async ({ ref, prompt, force_stop }) => {
|
|
12967
|
+
if (force_stop) {
|
|
12968
|
+
const existing = orch.getThread(ref);
|
|
12969
|
+
if (existing) {
|
|
12970
|
+
orch.stop(ref, { clearQueue: true });
|
|
12971
|
+
}
|
|
12972
|
+
}
|
|
12973
|
+
const thread = await orch.send(ref, prompt);
|
|
12631
12974
|
return {
|
|
12632
12975
|
content: [
|
|
12633
12976
|
{
|
|
12634
12977
|
type: "text",
|
|
12635
12978
|
text: JSON.stringify({
|
|
12636
12979
|
id: thread.id,
|
|
12637
|
-
title: thread.title,
|
|
12638
|
-
branchName: thread.branchName,
|
|
12639
|
-
worktreePath: thread.worktreePath,
|
|
12640
|
-
agent: thread.agent,
|
|
12641
|
-
model: thread.model,
|
|
12642
12980
|
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 } : {}
|
|
12981
|
+
queueLength: thread.queue.length,
|
|
12982
|
+
forceStopped: Boolean(force_stop)
|
|
12650
12983
|
})
|
|
12651
12984
|
}
|
|
12652
12985
|
]
|
|
12653
12986
|
};
|
|
12654
|
-
}
|
|
12655
|
-
|
|
12987
|
+
}
|
|
12988
|
+
);
|
|
12989
|
+
server.tool(
|
|
12990
|
+
"wait_for_turn",
|
|
12991
|
+
"Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
|
|
12992
|
+
{
|
|
12993
|
+
ref: import_zod.z.string(),
|
|
12994
|
+
timeoutMs: import_zod.z.number().optional()
|
|
12995
|
+
},
|
|
12996
|
+
async ({ ref, timeoutMs }) => {
|
|
12997
|
+
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
12998
|
+
const result = orch.getTurnResult(thread.id);
|
|
12656
12999
|
return {
|
|
12657
13000
|
content: [
|
|
12658
13001
|
{
|
|
12659
13002
|
type: "text",
|
|
12660
|
-
text:
|
|
13003
|
+
text: JSON.stringify({
|
|
13004
|
+
id: thread.id,
|
|
13005
|
+
status: result.status,
|
|
13006
|
+
text: result.text
|
|
13007
|
+
})
|
|
12661
13008
|
}
|
|
12662
|
-
]
|
|
12663
|
-
isError: true
|
|
13009
|
+
]
|
|
12664
13010
|
};
|
|
12665
13011
|
}
|
|
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
|
-
}
|
|
13012
|
+
);
|
|
13013
|
+
server.tool(
|
|
13014
|
+
"get_turn_result",
|
|
13015
|
+
"Final assistant message only (not full transcript)",
|
|
13016
|
+
{ ref: import_zod.z.string() },
|
|
13017
|
+
async ({ ref }) => {
|
|
13018
|
+
const result = orch.getTurnResult(ref);
|
|
13019
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
12682
13020
|
}
|
|
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) {
|
|
13021
|
+
);
|
|
13022
|
+
server.tool(
|
|
13023
|
+
"stop_thread",
|
|
13024
|
+
"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.",
|
|
13025
|
+
{
|
|
13026
|
+
ref: import_zod.z.string(),
|
|
13027
|
+
force: import_zod.z.boolean().optional()
|
|
13028
|
+
},
|
|
13029
|
+
async ({ ref, force }) => {
|
|
13030
|
+
const t = orch.getThread(ref);
|
|
13031
|
+
if (!t) {
|
|
13032
|
+
return {
|
|
13033
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13034
|
+
isError: true
|
|
13035
|
+
};
|
|
13036
|
+
}
|
|
13037
|
+
const clearQueue = force !== false;
|
|
13038
|
+
const hadQueued = t.queue.length > 0;
|
|
13039
|
+
const stopped = orch.stop(ref, { clearQueue });
|
|
12742
13040
|
return {
|
|
12743
|
-
content: [
|
|
12744
|
-
|
|
13041
|
+
content: [
|
|
13042
|
+
{
|
|
13043
|
+
type: "text",
|
|
13044
|
+
text: JSON.stringify({
|
|
13045
|
+
id: stopped.id,
|
|
13046
|
+
status: stopped.status,
|
|
13047
|
+
clearedQueue: clearQueue && hadQueued
|
|
13048
|
+
})
|
|
13049
|
+
}
|
|
13050
|
+
]
|
|
12745
13051
|
};
|
|
12746
13052
|
}
|
|
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) {
|
|
13053
|
+
);
|
|
13054
|
+
server.tool(
|
|
13055
|
+
"archive_thread",
|
|
13056
|
+
"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.",
|
|
13057
|
+
{ ref: import_zod.z.string() },
|
|
13058
|
+
async ({ ref }) => {
|
|
13059
|
+
const t = orch.getThread(ref);
|
|
13060
|
+
if (!t) {
|
|
13061
|
+
return {
|
|
13062
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13063
|
+
isError: true
|
|
13064
|
+
};
|
|
13065
|
+
}
|
|
13066
|
+
const blocked = mcpArchiveBlockedReason(t);
|
|
13067
|
+
if (blocked) {
|
|
13068
|
+
return {
|
|
13069
|
+
content: [{ type: "text", text: blocked }],
|
|
13070
|
+
isError: true
|
|
13071
|
+
};
|
|
13072
|
+
}
|
|
13073
|
+
const archived = await orch.archive(ref);
|
|
12771
13074
|
return {
|
|
12772
|
-
content: [
|
|
12773
|
-
|
|
13075
|
+
content: [
|
|
13076
|
+
{
|
|
13077
|
+
type: "text",
|
|
13078
|
+
text: JSON.stringify({
|
|
13079
|
+
id: archived.id,
|
|
13080
|
+
status: archived.status
|
|
13081
|
+
})
|
|
13082
|
+
}
|
|
13083
|
+
]
|
|
12774
13084
|
};
|
|
12775
13085
|
}
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
13086
|
+
);
|
|
13087
|
+
server.tool(
|
|
13088
|
+
"restore_thread",
|
|
13089
|
+
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
13090
|
+
{ ref: import_zod.z.string() },
|
|
13091
|
+
async ({ ref }) => {
|
|
13092
|
+
try {
|
|
13093
|
+
const restored = await orch.restore(ref);
|
|
13094
|
+
return {
|
|
13095
|
+
content: [
|
|
13096
|
+
{
|
|
13097
|
+
type: "text",
|
|
13098
|
+
text: JSON.stringify({
|
|
13099
|
+
id: restored.id,
|
|
13100
|
+
status: restored.status,
|
|
13101
|
+
worktreePath: restored.worktreePath
|
|
13102
|
+
})
|
|
13103
|
+
}
|
|
13104
|
+
]
|
|
13105
|
+
};
|
|
13106
|
+
} catch (err) {
|
|
13107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13108
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13109
|
+
}
|
|
12782
13110
|
}
|
|
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);
|
|
13111
|
+
);
|
|
13112
|
+
server.tool(
|
|
13113
|
+
"get_diff",
|
|
13114
|
+
"Compact diff summary (capped hunks, paginated)",
|
|
13115
|
+
{
|
|
13116
|
+
ref: import_zod.z.string(),
|
|
13117
|
+
maxFiles: import_zod.z.number().optional()
|
|
13118
|
+
},
|
|
13119
|
+
async ({ ref, maxFiles }) => {
|
|
13120
|
+
const summary = await orch.diffSummary(ref);
|
|
12804
13121
|
return {
|
|
12805
13122
|
content: [
|
|
12806
13123
|
{
|
|
12807
13124
|
type: "text",
|
|
12808
13125
|
text: JSON.stringify({
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
worktreePath: restored.worktreePath
|
|
13126
|
+
...summary,
|
|
13127
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
12812
13128
|
})
|
|
12813
13129
|
}
|
|
12814
13130
|
]
|
|
12815
13131
|
};
|
|
12816
|
-
} catch (err) {
|
|
12817
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12818
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12819
13132
|
}
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
13133
|
+
);
|
|
13134
|
+
server.tool(
|
|
13135
|
+
"request_review",
|
|
13136
|
+
'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.',
|
|
13137
|
+
{ ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
|
|
13138
|
+
async ({ ref }) => {
|
|
13139
|
+
try {
|
|
13140
|
+
const tab = await orch.requestReview(ref);
|
|
13141
|
+
const from = orch.getThread(ref);
|
|
13142
|
+
return {
|
|
13143
|
+
content: [
|
|
13144
|
+
{
|
|
13145
|
+
type: "text",
|
|
13146
|
+
text: JSON.stringify({
|
|
13147
|
+
id: tab.id,
|
|
13148
|
+
title: tab.title,
|
|
13149
|
+
status: tab.status,
|
|
13150
|
+
fromThreadId: from?.id ?? ref,
|
|
13151
|
+
link: `sideboard://thread/${tab.id}`
|
|
13152
|
+
})
|
|
13153
|
+
}
|
|
13154
|
+
]
|
|
13155
|
+
};
|
|
13156
|
+
} catch (err) {
|
|
13157
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13158
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13159
|
+
}
|
|
13160
|
+
}
|
|
13161
|
+
);
|
|
13162
|
+
const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
13163
|
+
server.tool(
|
|
13164
|
+
"list_models",
|
|
13165
|
+
"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.",
|
|
13166
|
+
{
|
|
13167
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
13168
|
+
},
|
|
13169
|
+
async ({ agent }) => {
|
|
13170
|
+
try {
|
|
13171
|
+
const catalogs = await listModelsForAgent(agent);
|
|
13172
|
+
return {
|
|
13173
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
13174
|
+
};
|
|
13175
|
+
} catch (err) {
|
|
13176
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13177
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13178
|
+
}
|
|
13179
|
+
}
|
|
13180
|
+
);
|
|
13181
|
+
server.tool(
|
|
13182
|
+
"fork_worktree",
|
|
13183
|
+
"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.",
|
|
13184
|
+
{
|
|
13185
|
+
ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
|
|
13186
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
13187
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
13188
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
13189
|
+
title: import_zod.z.string().optional()
|
|
13190
|
+
},
|
|
13191
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
13192
|
+
try {
|
|
13193
|
+
const source = orch.getThread(ref);
|
|
13194
|
+
if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
|
|
13195
|
+
const thread = await orch.forkThreadWorktree({
|
|
13196
|
+
threadId: ref,
|
|
13197
|
+
throughIndex: through_index,
|
|
13198
|
+
agent,
|
|
13199
|
+
model,
|
|
13200
|
+
title
|
|
13201
|
+
});
|
|
13202
|
+
return {
|
|
13203
|
+
content: [
|
|
13204
|
+
{
|
|
13205
|
+
type: "text",
|
|
13206
|
+
text: JSON.stringify({
|
|
13207
|
+
id: thread.id,
|
|
13208
|
+
title: thread.title,
|
|
13209
|
+
status: thread.status,
|
|
13210
|
+
agent: thread.agent,
|
|
13211
|
+
model: thread.model,
|
|
13212
|
+
branchName: thread.branchName,
|
|
13213
|
+
worktreePath: thread.worktreePath,
|
|
13214
|
+
fromThreadId: source?.id ?? ref,
|
|
13215
|
+
link: `sideboard://thread/${thread.id}`
|
|
13216
|
+
})
|
|
13217
|
+
}
|
|
13218
|
+
]
|
|
13219
|
+
};
|
|
13220
|
+
} catch (err) {
|
|
13221
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13222
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13223
|
+
}
|
|
13224
|
+
}
|
|
13225
|
+
);
|
|
13226
|
+
server.tool(
|
|
13227
|
+
"fork_chat",
|
|
13228
|
+
"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.",
|
|
13229
|
+
{
|
|
13230
|
+
ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
13231
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
13232
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
13233
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
13234
|
+
title: import_zod.z.string().optional()
|
|
13235
|
+
},
|
|
13236
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
13237
|
+
try {
|
|
13238
|
+
const source = orch.getThread(ref);
|
|
13239
|
+
if (!source) {
|
|
13240
|
+
return {
|
|
13241
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
13242
|
+
isError: true
|
|
13243
|
+
};
|
|
12839
13244
|
}
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
13245
|
+
const tab = orch.forkChatTab({
|
|
13246
|
+
threadId: source.id,
|
|
13247
|
+
throughIndex: through_index,
|
|
13248
|
+
agent,
|
|
13249
|
+
model,
|
|
13250
|
+
title
|
|
13251
|
+
});
|
|
13252
|
+
return {
|
|
13253
|
+
content: [
|
|
13254
|
+
{
|
|
13255
|
+
type: "text",
|
|
13256
|
+
text: JSON.stringify({
|
|
13257
|
+
id: tab.id,
|
|
13258
|
+
title: tab.title,
|
|
13259
|
+
status: tab.status,
|
|
13260
|
+
agent: tab.agent,
|
|
13261
|
+
model: tab.model,
|
|
13262
|
+
sourceType: tab.sourceType,
|
|
13263
|
+
worktreePath: tab.worktreePath,
|
|
13264
|
+
fromThreadId: source.id,
|
|
13265
|
+
link: `sideboard://thread/${tab.id}`
|
|
13266
|
+
})
|
|
13267
|
+
}
|
|
13268
|
+
]
|
|
13269
|
+
};
|
|
13270
|
+
} catch (err) {
|
|
13271
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13272
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13273
|
+
}
|
|
13274
|
+
}
|
|
13275
|
+
);
|
|
13276
|
+
server.tool(
|
|
13277
|
+
"run_dev_script",
|
|
13278
|
+
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
13279
|
+
{
|
|
13280
|
+
ref: import_zod.z.string(),
|
|
13281
|
+
name: import_zod.z.string().optional()
|
|
13282
|
+
},
|
|
13283
|
+
async ({ ref, name }) => {
|
|
13284
|
+
const result = await orch.startDev(ref, name);
|
|
12852
13285
|
return {
|
|
12853
13286
|
content: [
|
|
12854
13287
|
{
|
|
12855
13288
|
type: "text",
|
|
12856
13289
|
text: JSON.stringify({
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
link: `sideboard://thread/${tab.id}`
|
|
13290
|
+
port: result.port,
|
|
13291
|
+
scriptName: result.scriptName,
|
|
13292
|
+
ports: result.ports,
|
|
13293
|
+
url: `http://localhost:${result.port}`
|
|
12862
13294
|
})
|
|
12863
13295
|
}
|
|
12864
13296
|
]
|
|
12865
13297
|
};
|
|
12866
|
-
} catch (err) {
|
|
12867
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12868
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12869
13298
|
}
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
},
|
|
12879
|
-
async ({ agent }) => {
|
|
12880
|
-
try {
|
|
12881
|
-
const catalogs = await listModelsForAgent(agent);
|
|
13299
|
+
);
|
|
13300
|
+
server.tool(
|
|
13301
|
+
"list_run_scripts",
|
|
13302
|
+
"List named run scripts available for a thread",
|
|
13303
|
+
{ ref: import_zod.z.string() },
|
|
13304
|
+
async ({ ref }) => {
|
|
13305
|
+
const scripts = orch.listThreadRunScripts(ref);
|
|
13306
|
+
const active = orch.getActiveRuns(ref);
|
|
12882
13307
|
return {
|
|
12883
|
-
content: [
|
|
13308
|
+
content: [
|
|
13309
|
+
{
|
|
13310
|
+
type: "text",
|
|
13311
|
+
text: JSON.stringify({ scripts, active }, null, 2)
|
|
13312
|
+
}
|
|
13313
|
+
]
|
|
12884
13314
|
};
|
|
12885
|
-
} catch (err) {
|
|
12886
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12887
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12888
13315
|
}
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
13316
|
+
);
|
|
13317
|
+
server.tool(
|
|
13318
|
+
"stop_dev_script",
|
|
13319
|
+
"Stop a running script for a thread (all scripts if name omitted)",
|
|
13320
|
+
{
|
|
13321
|
+
ref: import_zod.z.string(),
|
|
13322
|
+
name: import_zod.z.string().optional()
|
|
13323
|
+
},
|
|
13324
|
+
async ({ ref, name }) => {
|
|
13325
|
+
orch.stopDev(ref, name);
|
|
13326
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
13327
|
+
}
|
|
13328
|
+
);
|
|
13329
|
+
server.tool(
|
|
13330
|
+
"run_setup",
|
|
13331
|
+
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
13332
|
+
{ ref: import_zod.z.string() },
|
|
13333
|
+
async ({ ref }) => {
|
|
13334
|
+
const result = await orch.runSetup(ref);
|
|
13335
|
+
return {
|
|
13336
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
13337
|
+
};
|
|
13338
|
+
}
|
|
13339
|
+
);
|
|
13340
|
+
server.tool(
|
|
13341
|
+
"add_workspace",
|
|
13342
|
+
"Register a git repo as a Sideboard workspace",
|
|
13343
|
+
{ repoPath: import_zod.z.string() },
|
|
13344
|
+
async ({ repoPath }) => {
|
|
13345
|
+
const ws = await orch.addWorkspace(repoPath);
|
|
13346
|
+
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
13347
|
+
}
|
|
13348
|
+
);
|
|
13349
|
+
server.tool(
|
|
13350
|
+
"remove_workspace",
|
|
13351
|
+
"Unregister a Sideboard workspace (does not archive threads)",
|
|
13352
|
+
{ repoPath: import_zod.z.string() },
|
|
13353
|
+
async ({ repoPath }) => {
|
|
13354
|
+
orch.removeWorkspace(repoPath);
|
|
13355
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
13356
|
+
}
|
|
13357
|
+
);
|
|
13358
|
+
server.tool(
|
|
13359
|
+
"fanout",
|
|
13360
|
+
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
13361
|
+
{
|
|
13362
|
+
prompt: import_zod.z.string(),
|
|
13363
|
+
agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
13364
|
+
repoPath: import_zod.z.string(),
|
|
13365
|
+
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
13366
|
+
sourceRef: import_zod.z.string().optional(),
|
|
13367
|
+
title: import_zod.z.string().optional()
|
|
13368
|
+
},
|
|
13369
|
+
async (args) => {
|
|
13370
|
+
const threads = await orch.bestOfN(args);
|
|
13371
|
+
return {
|
|
13372
|
+
content: [
|
|
13373
|
+
{
|
|
13374
|
+
type: "text",
|
|
13375
|
+
text: JSON.stringify(
|
|
13376
|
+
threads.map((t) => ({
|
|
13377
|
+
id: t.id,
|
|
13378
|
+
agent: t.agent,
|
|
13379
|
+
branchName: t.branchName,
|
|
13380
|
+
worktreePath: t.worktreePath
|
|
13381
|
+
})),
|
|
13382
|
+
null,
|
|
13383
|
+
2
|
|
13384
|
+
)
|
|
13385
|
+
}
|
|
13386
|
+
]
|
|
13387
|
+
};
|
|
13388
|
+
}
|
|
13389
|
+
);
|
|
13390
|
+
server.tool(
|
|
13391
|
+
"list_branches",
|
|
13392
|
+
"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).",
|
|
13393
|
+
{
|
|
13394
|
+
repoPath: import_zod.z.string(),
|
|
13395
|
+
unmergedOnly: import_zod.z.boolean().optional()
|
|
13396
|
+
},
|
|
13397
|
+
async ({ repoPath, unmergedOnly }) => {
|
|
13398
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13399
|
+
const branches = await listBranches(root, {
|
|
13400
|
+
unmergedOnly: unmergedOnly !== false
|
|
12911
13401
|
});
|
|
12912
13402
|
return {
|
|
12913
13403
|
content: [
|
|
12914
13404
|
{
|
|
12915
13405
|
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
|
-
})
|
|
13406
|
+
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
12927
13407
|
}
|
|
12928
13408
|
]
|
|
12929
13409
|
};
|
|
12930
|
-
} catch (err) {
|
|
12931
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12932
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12933
13410
|
}
|
|
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
|
-
|
|
13411
|
+
);
|
|
13412
|
+
server.tool(
|
|
13413
|
+
"list_prs",
|
|
13414
|
+
"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.",
|
|
13415
|
+
{ repoPath: import_zod.z.string() },
|
|
13416
|
+
async ({ repoPath }) => {
|
|
13417
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13418
|
+
const prs = await listPrs(root);
|
|
13419
|
+
return {
|
|
13420
|
+
content: [
|
|
13421
|
+
{
|
|
13422
|
+
type: "text",
|
|
13423
|
+
text: prs.map(
|
|
13424
|
+
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
13425
|
+
).join("\n")
|
|
13426
|
+
}
|
|
13427
|
+
]
|
|
13428
|
+
};
|
|
13429
|
+
}
|
|
13430
|
+
);
|
|
13431
|
+
server.tool(
|
|
13432
|
+
"get_pr_stack",
|
|
13433
|
+
"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.",
|
|
13434
|
+
{ ref: import_zod.z.string() },
|
|
13435
|
+
async ({ ref }) => {
|
|
13436
|
+
const stack = await orch.getPrStack(ref);
|
|
13437
|
+
return {
|
|
13438
|
+
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13439
|
+
};
|
|
13440
|
+
}
|
|
13441
|
+
);
|
|
13442
|
+
server.tool(
|
|
13443
|
+
"open_pr_stack_layers",
|
|
13444
|
+
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13445
|
+
{
|
|
13446
|
+
ref: import_zod.z.string(),
|
|
13447
|
+
layer: import_zod.z.number().int().positive().optional()
|
|
13448
|
+
},
|
|
13449
|
+
async ({ ref, layer }) => {
|
|
13450
|
+
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13451
|
+
return {
|
|
13452
|
+
content: [
|
|
13453
|
+
{
|
|
13454
|
+
type: "text",
|
|
13455
|
+
text: JSON.stringify(
|
|
13456
|
+
{
|
|
13457
|
+
stackNumber: result.stack.stackNumber,
|
|
13458
|
+
trunk: result.stack.trunk,
|
|
13459
|
+
threads: result.threads.map((t) => ({
|
|
13460
|
+
id: t.id,
|
|
13461
|
+
title: t.title,
|
|
13462
|
+
branchName: t.branchName,
|
|
13463
|
+
stackLayer: t.stackLayer,
|
|
13464
|
+
worktreePath: t.worktreePath,
|
|
13465
|
+
prUrl: t.prUrl,
|
|
13466
|
+
link: `sideboard://thread/${t.id}`
|
|
13467
|
+
}))
|
|
13468
|
+
},
|
|
13469
|
+
null,
|
|
13470
|
+
2
|
|
13471
|
+
)
|
|
13472
|
+
}
|
|
13473
|
+
]
|
|
13474
|
+
};
|
|
13475
|
+
}
|
|
13476
|
+
);
|
|
13477
|
+
server.tool(
|
|
13478
|
+
"add_stack_layer",
|
|
13479
|
+
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13480
|
+
{
|
|
13481
|
+
ref: import_zod.z.string(),
|
|
13482
|
+
branchName: import_zod.z.string(),
|
|
13483
|
+
title: import_zod.z.string().optional()
|
|
13484
|
+
},
|
|
13485
|
+
async ({ ref, branchName, title }) => {
|
|
13486
|
+
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13487
|
+
return {
|
|
13488
|
+
content: [
|
|
13489
|
+
{
|
|
13490
|
+
type: "text",
|
|
13491
|
+
text: JSON.stringify(
|
|
13492
|
+
{
|
|
13493
|
+
id: result.thread.id,
|
|
13494
|
+
title: result.thread.title,
|
|
13495
|
+
branchName: result.thread.branchName,
|
|
13496
|
+
stackLayer: result.thread.stackLayer,
|
|
13497
|
+
worktreePath: result.thread.worktreePath,
|
|
13498
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
13499
|
+
},
|
|
13500
|
+
null,
|
|
13501
|
+
2
|
|
13502
|
+
)
|
|
13503
|
+
}
|
|
13504
|
+
]
|
|
13505
|
+
};
|
|
13506
|
+
}
|
|
13507
|
+
);
|
|
13508
|
+
server.tool(
|
|
13509
|
+
"create_pr_stack",
|
|
13510
|
+
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13511
|
+
{
|
|
13512
|
+
repoPath: import_zod.z.string(),
|
|
13513
|
+
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13514
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13515
|
+
base: import_zod.z.string().optional(),
|
|
13516
|
+
title: import_zod.z.string().optional()
|
|
13517
|
+
},
|
|
13518
|
+
async (args) => {
|
|
13519
|
+
const result = await orch.createPrStack({
|
|
13520
|
+
repoPath: args.repoPath,
|
|
13521
|
+
branches: args.branches,
|
|
13522
|
+
agent: args.agent,
|
|
13523
|
+
base: args.base,
|
|
13524
|
+
title: args.title
|
|
12961
13525
|
});
|
|
12962
13526
|
return {
|
|
12963
13527
|
content: [
|
|
12964
13528
|
{
|
|
12965
13529
|
type: "text",
|
|
12966
|
-
text: JSON.stringify(
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
13530
|
+
text: JSON.stringify(
|
|
13531
|
+
{
|
|
13532
|
+
stackNumber: result.stack.stackNumber,
|
|
13533
|
+
trunk: result.stack.trunk,
|
|
13534
|
+
threads: result.threads.map((t) => ({
|
|
13535
|
+
id: t.id,
|
|
13536
|
+
title: t.title,
|
|
13537
|
+
branchName: t.branchName,
|
|
13538
|
+
stackLayer: t.stackLayer,
|
|
13539
|
+
worktreePath: t.worktreePath,
|
|
13540
|
+
link: `sideboard://thread/${t.id}`
|
|
13541
|
+
}))
|
|
13542
|
+
},
|
|
13543
|
+
null,
|
|
13544
|
+
2
|
|
13545
|
+
)
|
|
12977
13546
|
}
|
|
12978
13547
|
]
|
|
12979
13548
|
};
|
|
12980
|
-
} catch (err) {
|
|
12981
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12982
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12983
13549
|
}
|
|
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
|
-
);
|
|
13550
|
+
);
|
|
13551
|
+
server.tool(
|
|
13552
|
+
"list_issues",
|
|
13553
|
+
"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.",
|
|
13554
|
+
{ repoPath: import_zod.z.string() },
|
|
13555
|
+
async ({ repoPath }) => {
|
|
13556
|
+
const root = await resolveRepoRoot(repoPath);
|
|
13557
|
+
const result = await listIssues(root);
|
|
13558
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13559
|
+
}
|
|
13560
|
+
);
|
|
13561
|
+
server.tool(
|
|
13562
|
+
"list_linear_issues",
|
|
13563
|
+
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
13564
|
+
{
|
|
13565
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode"]),
|
|
13566
|
+
repoPath: import_zod.z.string()
|
|
13567
|
+
},
|
|
13568
|
+
async ({ agent, repoPath }) => {
|
|
13569
|
+
const issues = await listLinearIssues(agent, repoPath);
|
|
13570
|
+
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
13571
|
+
}
|
|
13572
|
+
);
|
|
13573
|
+
}
|
|
13283
13574
|
const transport = new import_stdio.StdioServerTransport();
|
|
13284
13575
|
await server.connect(transport);
|
|
13285
13576
|
}
|