@sideboard-ai/core 0.1.61 → 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-B2723EOF.js → agents-EWFD765A.js} +23 -7
- package/dist/{agents-EADRAAPG.js → agents-MD7SCFYX.js} +23 -7
- package/dist/{app-settings-YBNZ4XSR.js → app-settings-GJVZGU3M.js} +15 -3
- package/dist/{app-settings-TBHTJWLQ.js → app-settings-ONKMFSEJ.js} +15 -3
- package/dist/{chunk-RIWUU7FO.js → chunk-5AG5BUQN.js} +61 -3
- package/dist/{chunk-HLEX5AQ6.js → chunk-7FD7COKE.js} +4 -0
- package/dist/{chunk-XWPKLRB3.js → chunk-AKPEXIKK.js} +2 -2
- package/dist/{chunk-MX7UH5AT.js → chunk-BBEH2CJE.js} +264 -93
- package/dist/{chunk-FGNBTFRD.js → chunk-BPOTVI5J.js} +125 -3
- package/dist/{chunk-CHGOMFTA.js → chunk-DNPP7QMK.js} +32 -7
- package/dist/{chunk-MBAL5FE6.js → chunk-DZYMNW2A.js} +4 -4
- package/dist/{chunk-JB2H7E7V.js → chunk-EVD4KNKR.js} +1 -1
- package/dist/{chunk-TSRXOSVD.js → chunk-FWJYLBO6.js} +4 -0
- package/dist/{chunk-QJBC4BRK.js → chunk-HTOTTJJX.js} +2 -2
- package/dist/{chunk-MFGGIRUA.js → chunk-I2KGRAXC.js} +2 -2
- package/dist/{chunk-ZXDOI6IH.js → chunk-IFAU2DEY.js} +269 -93
- package/dist/{chunk-NTYRBRE7.js → chunk-LRBRQFBU.js} +2 -2
- package/dist/{chunk-75QR74KM.js → chunk-MHR4OGZ4.js} +125 -3
- package/dist/{chunk-2P2UK7E7.js → chunk-PESJDZTB.js} +2 -2
- package/dist/{chunk-P7EQUFB6.js → chunk-R7GUDEPY.js} +2 -2
- package/dist/{chunk-73AT5K4A.js → chunk-TXQ6K2GR.js} +61 -3
- package/dist/{chunk-MLAST5BB.js → chunk-YTNLOJPK.js} +32 -7
- 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-4P23VVFC.js → coordinator-prompt-E5UGSJES.js} +5 -5
- package/dist/{coordinator-prompt-LNWHZ64D.js → coordinator-prompt-ILSULI7C.js} +5 -5
- package/dist/{global-workspace-TMMAVRVW.js → global-workspace-44UG64NA.js} +6 -7
- package/dist/{global-workspace-BHT4XUAA.js → global-workspace-5PM5LA6E.js} +6 -7
- package/dist/index.cjs +1433 -944
- package/dist/index.d.cts +117 -2
- package/dist/index.d.ts +117 -2
- package/dist/index.js +859 -736
- package/dist/mcp/run-stdio.cjs +1388 -993
- package/dist/mcp/run-stdio.js +798 -765
- package/dist/{run-HZTBWGQR.js → run-74S2NZWB.js} +1 -1
- package/dist/{run-GPSVV5OH.js → run-JLN5JK7Q.js} +1 -1
- package/dist/{thread-store-GHOADGL2.js → thread-store-BUBR24YA.js} +1 -1
- package/dist/{thread-store-UJIGMI5J.js → thread-store-XTPFCJAI.js} +1 -1
- package/dist/{workspaces-3GO3EDQG.js → workspaces-OVBCSS5Y.js} +7 -8
- package/dist/{workspaces-GFPGDGCS.js → workspaces-RXFXQRRF.js} +7 -8
- package/dist/{worktree-GBLHEFHR.js → worktree-K2JB53R5.js} +3 -3
- package/dist/{worktree-3L2G6PCM.js → worktree-S2N63FLV.js} +3 -3
- 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
|
@@ -486,6 +486,8 @@ function normalizeThread(raw) {
|
|
|
486
486
|
agentPid: raw.agentPid ?? null,
|
|
487
487
|
attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
|
|
488
488
|
prTitle: raw.prTitle ?? null,
|
|
489
|
+
prState: raw.prState ?? null,
|
|
490
|
+
skipAutoArchiveOnMerge: Boolean(raw.skipAutoArchiveOnMerge),
|
|
489
491
|
stackId: raw.stackId ?? null,
|
|
490
492
|
stackLayer: raw.stackLayer ?? null,
|
|
491
493
|
userSetTitle: Boolean(raw.userSetTitle),
|
|
@@ -512,6 +514,8 @@ function createEmptyThread(partial) {
|
|
|
512
514
|
activeRuns: partial.activeRuns ?? [],
|
|
513
515
|
prUrl: partial.prUrl ?? null,
|
|
514
516
|
prTitle: partial.prTitle ?? null,
|
|
517
|
+
prState: partial.prState ?? null,
|
|
518
|
+
skipAutoArchiveOnMerge: partial.skipAutoArchiveOnMerge ?? false,
|
|
515
519
|
stackId: partial.stackId ?? null,
|
|
516
520
|
stackLayer: partial.stackLayer ?? null,
|
|
517
521
|
userSetTitle: partial.userSetTitle ?? false,
|
|
@@ -1503,18 +1507,28 @@ var init_gh_errors = __esm({
|
|
|
1503
1507
|
});
|
|
1504
1508
|
|
|
1505
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
|
+
}
|
|
1506
1520
|
function ensureAgentPath(env = process.env) {
|
|
1507
1521
|
const home = env.HOME || env.USERPROFILE || (0, import_node_os3.homedir)();
|
|
1508
1522
|
const current = env.PATH ?? "";
|
|
1509
1523
|
const parts = current.split(import_node_path4.delimiter).filter(Boolean);
|
|
1510
1524
|
const seen = new Set(parts);
|
|
1511
1525
|
const extras = [
|
|
1512
|
-
...EXTRA_BIN_DIRS.map((rel) =>
|
|
1526
|
+
...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path4.join)(home, rel)),
|
|
1513
1527
|
"/opt/homebrew/bin",
|
|
1514
1528
|
"/usr/local/bin"
|
|
1515
1529
|
];
|
|
1516
1530
|
for (const dir of extras.reverse()) {
|
|
1517
|
-
if (!dir || seen.has(dir)) continue;
|
|
1531
|
+
if (!dir || seen.has(dir) || !(0, import_node_fs4.existsSync)(dir)) continue;
|
|
1518
1532
|
parts.unshift(dir);
|
|
1519
1533
|
seen.add(dir);
|
|
1520
1534
|
}
|
|
@@ -1522,10 +1536,50 @@ function ensureAgentPath(env = process.env) {
|
|
|
1522
1536
|
env.PATH = next;
|
|
1523
1537
|
return next;
|
|
1524
1538
|
}
|
|
1525
|
-
|
|
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;
|
|
1526
1578
|
var init_path = __esm({
|
|
1527
1579
|
"src/agents/path.ts"() {
|
|
1528
1580
|
"use strict";
|
|
1581
|
+
import_node_fs4 = require("fs");
|
|
1582
|
+
import_node_child_process = require("child_process");
|
|
1529
1583
|
import_node_os3 = require("os");
|
|
1530
1584
|
import_node_path4 = require("path");
|
|
1531
1585
|
EXTRA_BIN_DIRS = [
|
|
@@ -1533,6 +1587,10 @@ var init_path = __esm({
|
|
|
1533
1587
|
".cargo/bin",
|
|
1534
1588
|
".nvm/current/bin",
|
|
1535
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",
|
|
1536
1594
|
// pnpm / npm global bins (where `@brightsy/cli` typically lands)
|
|
1537
1595
|
"Library/pnpm",
|
|
1538
1596
|
".pnpm"
|
|
@@ -2592,7 +2650,7 @@ function isLocalPrFetchBranch(ref) {
|
|
|
2592
2650
|
async function createThreadWorktree(opts) {
|
|
2593
2651
|
let branchName = `thread/${opts.slug}`;
|
|
2594
2652
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2595
|
-
if ((0,
|
|
2653
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2596
2654
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2597
2655
|
}
|
|
2598
2656
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2662,7 +2720,7 @@ async function createExistingBranchWorktree(opts) {
|
|
|
2662
2720
|
const branchName = opts.branchName.trim();
|
|
2663
2721
|
if (!branchName) throw new Error("branch name required");
|
|
2664
2722
|
const worktreePath = (0, import_node_path5.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
2665
|
-
if ((0,
|
|
2723
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2666
2724
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2667
2725
|
}
|
|
2668
2726
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2923,9 +2981,9 @@ function sameRepoPath(a, b) {
|
|
|
2923
2981
|
}
|
|
2924
2982
|
function listLocalThreadBranchSlugs(repoPath) {
|
|
2925
2983
|
const refsDir = (0, import_node_path5.join)(repoPath, ".git", "refs", "heads", "thread");
|
|
2926
|
-
if (!(0,
|
|
2984
|
+
if (!(0, import_node_fs5.existsSync)(refsDir)) return [];
|
|
2927
2985
|
try {
|
|
2928
|
-
return (0,
|
|
2986
|
+
return (0, import_node_fs5.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
|
|
2929
2987
|
} catch {
|
|
2930
2988
|
return [];
|
|
2931
2989
|
}
|
|
@@ -2933,8 +2991,8 @@ function listLocalThreadBranchSlugs(repoPath) {
|
|
|
2933
2991
|
function collectTakenTeamSlugs(repoPath) {
|
|
2934
2992
|
const taken = /* @__PURE__ */ new Set();
|
|
2935
2993
|
const root = worktreesRoot(repoPath);
|
|
2936
|
-
if ((0,
|
|
2937
|
-
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 })) {
|
|
2938
2996
|
if (entry.isDirectory() && entry.name !== ".DS_Store") {
|
|
2939
2997
|
taken.add(normalizeTakenSlug(entry.name));
|
|
2940
2998
|
}
|
|
@@ -2956,16 +3014,16 @@ function allocateTeamSlug(repoPath) {
|
|
|
2956
3014
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
2957
3015
|
const team = allocateTeamName(taken);
|
|
2958
3016
|
const path = (0, import_node_path5.join)(worktreesRoot(repoPath), team.slug);
|
|
2959
|
-
if (!(0,
|
|
3017
|
+
if (!(0, import_node_fs5.existsSync)(path)) return team;
|
|
2960
3018
|
taken.add(team.slug);
|
|
2961
3019
|
}
|
|
2962
3020
|
throw new Error("No available soccer team worktree directories left");
|
|
2963
3021
|
}
|
|
2964
|
-
var
|
|
3022
|
+
var import_node_fs5, import_node_path5;
|
|
2965
3023
|
var init_worktree = __esm({
|
|
2966
3024
|
"src/git/worktree.ts"() {
|
|
2967
3025
|
"use strict";
|
|
2968
|
-
|
|
3026
|
+
import_node_fs5 = require("fs");
|
|
2969
3027
|
import_node_path5 = require("path");
|
|
2970
3028
|
init_paths();
|
|
2971
3029
|
init_thread_store();
|
|
@@ -2987,11 +3045,13 @@ __export(app_settings_exports, {
|
|
|
2987
3045
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
2988
3046
|
appSettingsPath: () => appSettingsPath,
|
|
2989
3047
|
applyAppEnvironment: () => applyAppEnvironment,
|
|
3048
|
+
autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
|
|
2990
3049
|
autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
|
|
2991
3050
|
autoRenameBranchEnabled: () => autoRenameBranchEnabled,
|
|
2992
3051
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
2993
3052
|
brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
|
|
2994
3053
|
brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
|
|
3054
|
+
brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
|
|
2995
3055
|
caffeinateWhileCloudConnectEnabled: () => caffeinateWhileCloudConnectEnabled,
|
|
2996
3056
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
2997
3057
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
@@ -3010,17 +3070,21 @@ __export(app_settings_exports, {
|
|
|
3010
3070
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
3011
3071
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
3012
3072
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
3073
|
+
resolveAgentExecutable: () => resolveAgentExecutable,
|
|
3013
3074
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
3014
3075
|
resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
|
|
3015
3076
|
resolveNewThreadOptions: () => resolveNewThreadOptions,
|
|
3016
3077
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
3017
3078
|
saveAppSettings: () => saveAppSettings,
|
|
3018
3079
|
updateAdvancedSettings: () => updateAdvancedSettings,
|
|
3080
|
+
updateAgentExecutable: () => updateAgentExecutable,
|
|
3019
3081
|
updateAppEnvironment: () => updateAppEnvironment,
|
|
3020
3082
|
updateBrightsySettings: () => updateBrightsySettings,
|
|
3021
3083
|
updateClaudeSettings: () => updateClaudeSettings,
|
|
3084
|
+
updateCodexSettings: () => updateCodexSettings,
|
|
3022
3085
|
updateDefaultsSettings: () => updateDefaultsSettings,
|
|
3023
|
-
updateIntegrationsSettings: () => updateIntegrationsSettings
|
|
3086
|
+
updateIntegrationsSettings: () => updateIntegrationsSettings,
|
|
3087
|
+
updateOpencodeSettings: () => updateOpencodeSettings
|
|
3024
3088
|
});
|
|
3025
3089
|
function appSettingsPath() {
|
|
3026
3090
|
return (0, import_node_path6.join)(appDataDir(), "settings.json");
|
|
@@ -3041,16 +3105,33 @@ function normalizeClaude(raw) {
|
|
|
3041
3105
|
}
|
|
3042
3106
|
return out;
|
|
3043
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
|
+
}
|
|
3044
3118
|
function normalizeBrightsy(raw) {
|
|
3045
3119
|
if (!raw || typeof raw !== "object") return {};
|
|
3046
3120
|
const source = raw;
|
|
3047
3121
|
const out = {};
|
|
3122
|
+
if (typeof source.executablePath === "string") {
|
|
3123
|
+
const path = source.executablePath.trim();
|
|
3124
|
+
if (path) out.executablePath = path;
|
|
3125
|
+
}
|
|
3048
3126
|
if (typeof source.cloudConnectEnabled === "boolean") {
|
|
3049
3127
|
out.cloudConnectEnabled = source.cloudConnectEnabled;
|
|
3050
3128
|
}
|
|
3051
3129
|
if (typeof source.cloudConnectAgent === "string" && CLOUD_CONNECT_AGENTS.has(source.cloudConnectAgent)) {
|
|
3052
3130
|
out.cloudConnectAgent = source.cloudConnectAgent;
|
|
3053
3131
|
}
|
|
3132
|
+
if (typeof source.injectWorktreeMcp === "boolean") {
|
|
3133
|
+
out.injectWorktreeMcp = source.injectWorktreeMcp;
|
|
3134
|
+
}
|
|
3054
3135
|
return out;
|
|
3055
3136
|
}
|
|
3056
3137
|
function normalizeIntegrations(raw) {
|
|
@@ -3104,6 +3185,9 @@ function normalizeAdvanced(raw) {
|
|
|
3104
3185
|
if (typeof source.deleteBranchOnPurge === "boolean") {
|
|
3105
3186
|
out.deleteBranchOnPurge = source.deleteBranchOnPurge;
|
|
3106
3187
|
}
|
|
3188
|
+
if (typeof source.autoArchiveOnMerge === "boolean") {
|
|
3189
|
+
out.autoArchiveOnMerge = source.autoArchiveOnMerge;
|
|
3190
|
+
}
|
|
3107
3191
|
if (typeof source.maxConcurrent === "number" && Number.isFinite(source.maxConcurrent)) {
|
|
3108
3192
|
out.maxConcurrent = Math.max(1, Math.min(32, Math.floor(source.maxConcurrent)));
|
|
3109
3193
|
}
|
|
@@ -3133,6 +3217,8 @@ function normalizeAdvanced(raw) {
|
|
|
3133
3217
|
function normalizeSettings(raw) {
|
|
3134
3218
|
const env = {};
|
|
3135
3219
|
let claude = {};
|
|
3220
|
+
let codex = {};
|
|
3221
|
+
let opencode = {};
|
|
3136
3222
|
let brightsy = {};
|
|
3137
3223
|
let integrations = {};
|
|
3138
3224
|
let defaults = {};
|
|
@@ -3151,6 +3237,12 @@ function normalizeSettings(raw) {
|
|
|
3151
3237
|
if ("claude" in raw) {
|
|
3152
3238
|
claude = normalizeClaude(raw.claude);
|
|
3153
3239
|
}
|
|
3240
|
+
if ("codex" in raw) {
|
|
3241
|
+
codex = normalizeCliExecutable(raw.codex);
|
|
3242
|
+
}
|
|
3243
|
+
if ("opencode" in raw) {
|
|
3244
|
+
opencode = normalizeCliExecutable(raw.opencode);
|
|
3245
|
+
}
|
|
3154
3246
|
if ("brightsy" in raw) {
|
|
3155
3247
|
brightsy = normalizeBrightsy(raw.brightsy);
|
|
3156
3248
|
}
|
|
@@ -3166,15 +3258,24 @@ function normalizeSettings(raw) {
|
|
|
3166
3258
|
advanced = normalizeAdvanced(raw.advanced);
|
|
3167
3259
|
}
|
|
3168
3260
|
}
|
|
3169
|
-
return {
|
|
3261
|
+
return {
|
|
3262
|
+
environment: env,
|
|
3263
|
+
claude,
|
|
3264
|
+
codex,
|
|
3265
|
+
opencode,
|
|
3266
|
+
brightsy,
|
|
3267
|
+
integrations,
|
|
3268
|
+
defaults,
|
|
3269
|
+
advanced
|
|
3270
|
+
};
|
|
3170
3271
|
}
|
|
3171
3272
|
function loadAppSettings() {
|
|
3172
3273
|
const path = appSettingsPath();
|
|
3173
|
-
if (!(0,
|
|
3274
|
+
if (!(0, import_node_fs6.existsSync)(path)) {
|
|
3174
3275
|
return { ...EMPTY_SETTINGS };
|
|
3175
3276
|
}
|
|
3176
3277
|
try {
|
|
3177
|
-
const parsed = JSON.parse((0,
|
|
3278
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
|
|
3178
3279
|
return normalizeSettings(parsed);
|
|
3179
3280
|
} catch {
|
|
3180
3281
|
return { ...EMPTY_SETTINGS };
|
|
@@ -3182,8 +3283,8 @@ function loadAppSettings() {
|
|
|
3182
3283
|
}
|
|
3183
3284
|
function saveAppSettings(settings) {
|
|
3184
3285
|
const normalized = normalizeSettings(settings);
|
|
3185
|
-
(0,
|
|
3186
|
-
(0,
|
|
3286
|
+
(0, import_node_fs6.mkdirSync)(appDataDir(), { recursive: true });
|
|
3287
|
+
(0, import_node_fs6.writeFileSync)(appSettingsPath(), `${JSON.stringify(normalized, null, 2)}
|
|
3187
3288
|
`, "utf8");
|
|
3188
3289
|
return normalized;
|
|
3189
3290
|
}
|
|
@@ -3219,6 +3320,13 @@ function updateClaudeSettings(patch) {
|
|
|
3219
3320
|
function updateBrightsySettings(patch) {
|
|
3220
3321
|
const current = loadAppSettings();
|
|
3221
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
|
+
}
|
|
3222
3330
|
if (typeof patch.cloudConnectEnabled === "boolean") {
|
|
3223
3331
|
brightsy.cloudConnectEnabled = patch.cloudConnectEnabled;
|
|
3224
3332
|
}
|
|
@@ -3229,8 +3337,35 @@ function updateBrightsySettings(patch) {
|
|
|
3229
3337
|
brightsy.cloudConnectAgent = patch.cloudConnectAgent;
|
|
3230
3338
|
}
|
|
3231
3339
|
}
|
|
3340
|
+
if (typeof patch.injectWorktreeMcp === "boolean") {
|
|
3341
|
+
brightsy.injectWorktreeMcp = patch.injectWorktreeMcp;
|
|
3342
|
+
}
|
|
3232
3343
|
return saveAppSettings({ ...current, brightsy });
|
|
3233
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
|
+
}
|
|
3234
3369
|
function updateIntegrationsSettings(patch) {
|
|
3235
3370
|
const current = loadAppSettings();
|
|
3236
3371
|
const integrations = { ...current.integrations };
|
|
@@ -3338,6 +3473,9 @@ function getLinearApiKey(settings = loadAppSettings()) {
|
|
|
3338
3473
|
function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
|
|
3339
3474
|
return Boolean(settings.brightsy.cloudConnectEnabled);
|
|
3340
3475
|
}
|
|
3476
|
+
function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
|
|
3477
|
+
return Boolean(settings.brightsy.injectWorktreeMcp);
|
|
3478
|
+
}
|
|
3341
3479
|
function brightsyCloudConnectAgent(settings = loadAppSettings()) {
|
|
3342
3480
|
const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
|
|
3343
3481
|
const preferred = getDefaultAgent(settings);
|
|
@@ -3364,6 +3502,9 @@ function updateAdvancedSettings(patch) {
|
|
|
3364
3502
|
if (typeof patch.deleteBranchOnPurge === "boolean") {
|
|
3365
3503
|
advanced.deleteBranchOnPurge = patch.deleteBranchOnPurge;
|
|
3366
3504
|
}
|
|
3505
|
+
if (typeof patch.autoArchiveOnMerge === "boolean") {
|
|
3506
|
+
advanced.autoArchiveOnMerge = patch.autoArchiveOnMerge;
|
|
3507
|
+
}
|
|
3367
3508
|
if (typeof patch.maxConcurrent === "number" && Number.isFinite(patch.maxConcurrent)) {
|
|
3368
3509
|
advanced.maxConcurrent = Math.max(1, Math.min(32, Math.floor(patch.maxConcurrent)));
|
|
3369
3510
|
}
|
|
@@ -3405,6 +3546,9 @@ function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
|
|
|
3405
3546
|
function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
|
|
3406
3547
|
return Boolean(settings.advanced.deleteBranchOnPurge);
|
|
3407
3548
|
}
|
|
3549
|
+
function autoArchiveOnMergeEnabled(settings = loadAppSettings()) {
|
|
3550
|
+
return Boolean(settings.advanced.autoArchiveOnMerge);
|
|
3551
|
+
}
|
|
3408
3552
|
function autoCleanupOrphansEnabled(settings = loadAppSettings()) {
|
|
3409
3553
|
return Boolean(settings.advanced.autoCleanupOrphans);
|
|
3410
3554
|
}
|
|
@@ -3426,8 +3570,36 @@ function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
|
3426
3570
|
return 3;
|
|
3427
3571
|
}
|
|
3428
3572
|
function resolveClaudeExecutable(settings = loadAppSettings()) {
|
|
3429
|
-
|
|
3430
|
-
|
|
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 });
|
|
3431
3603
|
}
|
|
3432
3604
|
function claudeChromeEnabled(settings = loadAppSettings()) {
|
|
3433
3605
|
return Boolean(settings.claude.chromeEnabled);
|
|
@@ -3455,11 +3627,11 @@ function childEnvWithAppSettings(extra) {
|
|
|
3455
3627
|
function harnessEnvKey(harness) {
|
|
3456
3628
|
return HARNESS_ENV_KEYS[harness];
|
|
3457
3629
|
}
|
|
3458
|
-
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;
|
|
3459
3631
|
var init_app_settings = __esm({
|
|
3460
3632
|
"src/store/app-settings.ts"() {
|
|
3461
3633
|
"use strict";
|
|
3462
|
-
|
|
3634
|
+
import_node_fs6 = require("fs");
|
|
3463
3635
|
import_node_os4 = require("os");
|
|
3464
3636
|
import_node_path6 = require("path");
|
|
3465
3637
|
init_thinking_effort();
|
|
@@ -3488,11 +3660,19 @@ var init_app_settings = __esm({
|
|
|
3488
3660
|
EMPTY_SETTINGS = {
|
|
3489
3661
|
environment: {},
|
|
3490
3662
|
claude: {},
|
|
3663
|
+
codex: {},
|
|
3664
|
+
opencode: {},
|
|
3491
3665
|
brightsy: {},
|
|
3492
3666
|
integrations: {},
|
|
3493
3667
|
defaults: {},
|
|
3494
3668
|
advanced: {}
|
|
3495
3669
|
};
|
|
3670
|
+
DEFAULT_CLI_BIN = {
|
|
3671
|
+
claude: "claude",
|
|
3672
|
+
codex: "codex",
|
|
3673
|
+
opencode: "opencode",
|
|
3674
|
+
brightsy: "brightsy"
|
|
3675
|
+
};
|
|
3496
3676
|
}
|
|
3497
3677
|
});
|
|
3498
3678
|
|
|
@@ -3618,7 +3798,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
3618
3798
|
function ensureGlobalCoordinatorCwd(opts) {
|
|
3619
3799
|
const dir = globalAgentCwd();
|
|
3620
3800
|
try {
|
|
3621
|
-
(0,
|
|
3801
|
+
(0, import_node_fs7.mkdirSync)(dir, { recursive: true });
|
|
3622
3802
|
} catch {
|
|
3623
3803
|
return dir;
|
|
3624
3804
|
}
|
|
@@ -3626,7 +3806,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3626
3806
|
let orchId = opts?.orchestratorThreadId?.trim() || "";
|
|
3627
3807
|
if (!orchId) {
|
|
3628
3808
|
try {
|
|
3629
|
-
const existing = (0,
|
|
3809
|
+
const existing = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
|
|
3630
3810
|
const m = existing.match(
|
|
3631
3811
|
/YOUR orchestration thread id is `([0-9a-f-]{36})`/i
|
|
3632
3812
|
);
|
|
@@ -3668,9 +3848,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
3668
3848
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
3669
3849
|
].join("\n");
|
|
3670
3850
|
try {
|
|
3671
|
-
(0,
|
|
3851
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
|
|
3672
3852
|
`, "utf8");
|
|
3673
|
-
(0,
|
|
3853
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
|
|
3674
3854
|
`, "utf8");
|
|
3675
3855
|
} catch {
|
|
3676
3856
|
}
|
|
@@ -3703,11 +3883,11 @@ function coordinatorSystemPrompt(opts) {
|
|
|
3703
3883
|
formatWorkspaceInventory(opts.workspaces)
|
|
3704
3884
|
].join("\n");
|
|
3705
3885
|
}
|
|
3706
|
-
var
|
|
3886
|
+
var import_node_fs7, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
|
|
3707
3887
|
var init_coordinator_prompt = __esm({
|
|
3708
3888
|
"src/orchestrator/coordinator-prompt.ts"() {
|
|
3709
3889
|
"use strict";
|
|
3710
|
-
|
|
3890
|
+
import_node_fs7 = require("fs");
|
|
3711
3891
|
import_node_path7 = require("path");
|
|
3712
3892
|
init_worktree();
|
|
3713
3893
|
init_app_settings();
|
|
@@ -3933,26 +4113,26 @@ function brightsyConfigPath() {
|
|
|
3933
4113
|
}
|
|
3934
4114
|
function loadBrightsyConfig() {
|
|
3935
4115
|
const path = brightsyConfigPath();
|
|
3936
|
-
if (!(0,
|
|
4116
|
+
if (!(0, import_node_fs8.existsSync)(path)) {
|
|
3937
4117
|
throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
|
|
3938
4118
|
}
|
|
3939
|
-
const raw = JSON.parse((0,
|
|
4119
|
+
const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
|
|
3940
4120
|
if (!raw.access_token || !raw.account_id) {
|
|
3941
4121
|
throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
|
|
3942
4122
|
}
|
|
3943
4123
|
return raw;
|
|
3944
4124
|
}
|
|
3945
4125
|
function saveBrightsyConfig(cfg) {
|
|
3946
|
-
(0,
|
|
4126
|
+
(0, import_node_fs8.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
|
|
3947
4127
|
`, {
|
|
3948
4128
|
mode: 384
|
|
3949
4129
|
});
|
|
3950
4130
|
}
|
|
3951
|
-
var
|
|
4131
|
+
var import_node_fs8, import_node_os5, import_node_path8;
|
|
3952
4132
|
var init_config = __esm({
|
|
3953
4133
|
"src/brightsy/config.ts"() {
|
|
3954
4134
|
"use strict";
|
|
3955
|
-
|
|
4135
|
+
import_node_fs8 = require("fs");
|
|
3956
4136
|
import_node_os5 = require("os");
|
|
3957
4137
|
import_node_path8 = require("path");
|
|
3958
4138
|
}
|
|
@@ -3973,18 +4153,18 @@ function storePath() {
|
|
|
3973
4153
|
}
|
|
3974
4154
|
function readStore() {
|
|
3975
4155
|
const path = storePath();
|
|
3976
|
-
if (!(0,
|
|
4156
|
+
if (!(0, import_node_fs9.existsSync)(path)) return [];
|
|
3977
4157
|
try {
|
|
3978
|
-
const parsed = JSON.parse((0,
|
|
4158
|
+
const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
|
|
3979
4159
|
return Array.isArray(parsed.teams) ? parsed.teams : [];
|
|
3980
4160
|
} catch {
|
|
3981
4161
|
return [];
|
|
3982
4162
|
}
|
|
3983
4163
|
}
|
|
3984
4164
|
function writeStore(teams) {
|
|
3985
|
-
(0,
|
|
4165
|
+
(0, import_node_fs9.mkdirSync)(appDataDir(), { recursive: true });
|
|
3986
4166
|
const path = storePath();
|
|
3987
|
-
(0,
|
|
4167
|
+
(0, import_node_fs9.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
|
|
3988
4168
|
`, {
|
|
3989
4169
|
mode: 384
|
|
3990
4170
|
});
|
|
@@ -4096,11 +4276,11 @@ function brightsyMcpServerName(slug) {
|
|
|
4096
4276
|
const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
|
|
4097
4277
|
return `brightsy_${cleaned || "team"}`;
|
|
4098
4278
|
}
|
|
4099
|
-
var
|
|
4279
|
+
var import_node_fs9, import_node_path9;
|
|
4100
4280
|
var init_connected_teams = __esm({
|
|
4101
4281
|
"src/brightsy/connected-teams.ts"() {
|
|
4102
4282
|
"use strict";
|
|
4103
|
-
|
|
4283
|
+
import_node_fs9 = require("fs");
|
|
4104
4284
|
import_node_path9 = require("path");
|
|
4105
4285
|
init_paths();
|
|
4106
4286
|
init_accounts();
|
|
@@ -4148,6 +4328,11 @@ function normalizeTurnInput(input) {
|
|
|
4148
4328
|
if (typeof input === "string") return { prompt: input };
|
|
4149
4329
|
return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
|
|
4150
4330
|
}
|
|
4331
|
+
function dropCachedPrefixOnResume(input, sessionId) {
|
|
4332
|
+
const turn = normalizeTurnInput(input);
|
|
4333
|
+
if (sessionId) return { prompt: turn.prompt };
|
|
4334
|
+
return turn;
|
|
4335
|
+
}
|
|
4151
4336
|
function flattenTurnInput(input) {
|
|
4152
4337
|
const { cachedPrefix, prompt } = normalizeTurnInput(input);
|
|
4153
4338
|
if (!cachedPrefix) return prompt;
|
|
@@ -4292,9 +4477,13 @@ async function fetchTeamChatTargets(team) {
|
|
|
4292
4477
|
};
|
|
4293
4478
|
}
|
|
4294
4479
|
async function listBrightsyChatTargetsViaCli() {
|
|
4295
|
-
const listed = await run(
|
|
4296
|
-
|
|
4297
|
-
|
|
4480
|
+
const listed = await run(
|
|
4481
|
+
resolveAgentExecutable("brightsy"),
|
|
4482
|
+
["chat", "--list-targets", "--json"],
|
|
4483
|
+
{
|
|
4484
|
+
reject: false
|
|
4485
|
+
}
|
|
4486
|
+
);
|
|
4298
4487
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
4299
4488
|
throw new Error(
|
|
4300
4489
|
listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
|
|
@@ -4378,13 +4567,15 @@ async function syncCliForTarget(accountId) {
|
|
|
4378
4567
|
}
|
|
4379
4568
|
applyConnectedTeamToCli(team);
|
|
4380
4569
|
}
|
|
4381
|
-
var brightsyAdapter;
|
|
4570
|
+
var import_node_fs10, brightsyAdapter;
|
|
4382
4571
|
var init_brightsy = __esm({
|
|
4383
4572
|
"src/agents/brightsy.ts"() {
|
|
4384
4573
|
"use strict";
|
|
4574
|
+
import_node_fs10 = require("fs");
|
|
4385
4575
|
init_run();
|
|
4386
4576
|
init_connected_teams();
|
|
4387
4577
|
init_config();
|
|
4578
|
+
init_app_settings();
|
|
4388
4579
|
init_brightsy_targets();
|
|
4389
4580
|
init_error_detail();
|
|
4390
4581
|
init_turn_input();
|
|
@@ -4392,18 +4583,32 @@ var init_brightsy = __esm({
|
|
|
4392
4583
|
brightsyAdapter = {
|
|
4393
4584
|
kind: "brightsy",
|
|
4394
4585
|
async detect() {
|
|
4395
|
-
const
|
|
4396
|
-
if (
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
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
|
+
}
|
|
4405
4610
|
}
|
|
4406
|
-
const who = await run(
|
|
4611
|
+
const who = await run(brightsy, ["whoami"], { reject: false });
|
|
4407
4612
|
const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
|
|
4408
4613
|
return {
|
|
4409
4614
|
agent: "brightsy",
|
|
@@ -4428,7 +4633,7 @@ var init_brightsy = __esm({
|
|
|
4428
4633
|
target.id
|
|
4429
4634
|
];
|
|
4430
4635
|
return {
|
|
4431
|
-
file: "brightsy",
|
|
4636
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4432
4637
|
args,
|
|
4433
4638
|
cwd: thread.worktreePath,
|
|
4434
4639
|
// Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
|
|
@@ -4445,7 +4650,11 @@ var init_brightsy = __esm({
|
|
|
4445
4650
|
const target = decodeBrightsyTarget(thread.model);
|
|
4446
4651
|
await syncCliForTarget(target.accountId);
|
|
4447
4652
|
const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
|
|
4448
|
-
return {
|
|
4653
|
+
return {
|
|
4654
|
+
file: resolveAgentExecutable("brightsy"),
|
|
4655
|
+
args,
|
|
4656
|
+
cwd: thread.worktreePath
|
|
4657
|
+
};
|
|
4449
4658
|
}
|
|
4450
4659
|
};
|
|
4451
4660
|
}
|
|
@@ -4490,6 +4699,18 @@ var init_claude_mcp = __esm({
|
|
|
4490
4699
|
}
|
|
4491
4700
|
});
|
|
4492
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
|
+
|
|
4493
4714
|
// src/agents/node-launch.ts
|
|
4494
4715
|
function isAsarPath(filePath) {
|
|
4495
4716
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
@@ -4537,6 +4758,38 @@ function isBrightsyConnected() {
|
|
|
4537
4758
|
return false;
|
|
4538
4759
|
}
|
|
4539
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
|
+
}
|
|
4540
4793
|
function mcpLaunch(cmd, name, env) {
|
|
4541
4794
|
if (cmd === "brightsy-mcp") {
|
|
4542
4795
|
return { name, command: "brightsy-mcp", ...env ? { env } : {} };
|
|
@@ -4582,7 +4835,7 @@ function corePackageDir() {
|
|
|
4582
4835
|
}
|
|
4583
4836
|
function findSideboardMcpJsEntry() {
|
|
4584
4837
|
const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
|
|
4585
|
-
if (override && (0,
|
|
4838
|
+
if (override && (0, import_node_fs11.existsSync)(override)) return override;
|
|
4586
4839
|
let dir = corePackageDir();
|
|
4587
4840
|
for (let i = 0; i < 10; i++) {
|
|
4588
4841
|
const candidates = [
|
|
@@ -4595,7 +4848,7 @@ function findSideboardMcpJsEntry() {
|
|
|
4595
4848
|
(0, import_node_path10.join)(dir, "cli/dist/index.js")
|
|
4596
4849
|
];
|
|
4597
4850
|
for (const p of candidates) {
|
|
4598
|
-
if ((0,
|
|
4851
|
+
if ((0, import_node_fs11.existsSync)(p)) return p;
|
|
4599
4852
|
}
|
|
4600
4853
|
const parent = (0, import_node_path10.dirname)(dir);
|
|
4601
4854
|
if (parent === dir) break;
|
|
@@ -4626,11 +4879,13 @@ async function buildInjectedMcpServers(opts) {
|
|
|
4626
4879
|
const servers = [];
|
|
4627
4880
|
if (opts.includeSideboard) {
|
|
4628
4881
|
const sideboard = await resolveSideboardMcpServer();
|
|
4882
|
+
const orchId = opts.orchestratorThreadId?.trim();
|
|
4883
|
+
const profile = orchId ? "orchestration" : "worktree";
|
|
4629
4884
|
sideboard.env = {
|
|
4630
4885
|
...sideboard.env ?? {},
|
|
4631
|
-
SIDEBOARD_APP_DATA: appDataDir()
|
|
4886
|
+
SIDEBOARD_APP_DATA: appDataDir(),
|
|
4887
|
+
[SIDEBOARD_MCP_PROFILE_ENV]: profile
|
|
4632
4888
|
};
|
|
4633
|
-
const orchId = opts.orchestratorThreadId?.trim();
|
|
4634
4889
|
if (orchId) {
|
|
4635
4890
|
sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
|
|
4636
4891
|
}
|
|
@@ -4705,16 +4960,16 @@ function writeMcpServersConfig(servers) {
|
|
|
4705
4960
|
...s.env ? { env: s.env } : {}
|
|
4706
4961
|
};
|
|
4707
4962
|
}
|
|
4708
|
-
const dir = (0,
|
|
4963
|
+
const dir = (0, import_node_fs11.mkdtempSync)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
|
|
4709
4964
|
const cfgPath = (0, import_node_path10.join)(dir, "mcp.json");
|
|
4710
|
-
(0,
|
|
4965
|
+
(0, import_node_fs11.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
4711
4966
|
return cfgPath;
|
|
4712
4967
|
}
|
|
4713
|
-
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;
|
|
4714
4969
|
var init_injected_mcp = __esm({
|
|
4715
4970
|
"src/agents/injected-mcp.ts"() {
|
|
4716
4971
|
"use strict";
|
|
4717
|
-
|
|
4972
|
+
import_node_fs11 = require("fs");
|
|
4718
4973
|
import_node_module = require("module");
|
|
4719
4974
|
import_node_os6 = require("os");
|
|
4720
4975
|
import_node_path10 = require("path");
|
|
@@ -4722,6 +4977,8 @@ var init_injected_mcp = __esm({
|
|
|
4722
4977
|
init_run();
|
|
4723
4978
|
init_config();
|
|
4724
4979
|
init_connected_teams();
|
|
4980
|
+
init_profile();
|
|
4981
|
+
init_app_settings();
|
|
4725
4982
|
init_paths();
|
|
4726
4983
|
init_node_launch();
|
|
4727
4984
|
import_meta = {};
|
|
@@ -4737,6 +4994,7 @@ var init_injected_mcp = __esm({
|
|
|
4737
4994
|
"mcp__sideboard__present_plan"
|
|
4738
4995
|
];
|
|
4739
4996
|
brightsyMcpCommandCache = null;
|
|
4997
|
+
BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
|
|
4740
4998
|
}
|
|
4741
4999
|
});
|
|
4742
5000
|
|
|
@@ -4891,11 +5149,11 @@ function parseIssuesJson(raw) {
|
|
|
4891
5149
|
}
|
|
4892
5150
|
return [];
|
|
4893
5151
|
}
|
|
4894
|
-
var
|
|
5152
|
+
var import_node_fs12, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
|
|
4895
5153
|
var init_claude = __esm({
|
|
4896
5154
|
"src/agents/claude.ts"() {
|
|
4897
5155
|
"use strict";
|
|
4898
|
-
|
|
5156
|
+
import_node_fs12 = require("fs");
|
|
4899
5157
|
init_run();
|
|
4900
5158
|
init_app_settings();
|
|
4901
5159
|
init_claude_mcp();
|
|
@@ -4924,7 +5182,7 @@ var init_claude = __esm({
|
|
|
4924
5182
|
async detect() {
|
|
4925
5183
|
const claude = resolveClaudeExecutable();
|
|
4926
5184
|
if (claude !== "claude") {
|
|
4927
|
-
if (!(0,
|
|
5185
|
+
if (!(0, import_node_fs12.existsSync)(claude)) {
|
|
4928
5186
|
return {
|
|
4929
5187
|
agent: "claude",
|
|
4930
5188
|
installed: false,
|
|
@@ -4963,9 +5221,8 @@ var init_claude = __esm({
|
|
|
4963
5221
|
},
|
|
4964
5222
|
async buildTurn(thread, input) {
|
|
4965
5223
|
const claude = resolveClaudeExecutable();
|
|
4966
|
-
const turn = normalizeTurnInput(input);
|
|
4967
5224
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
4968
|
-
const effective = sessionId
|
|
5225
|
+
const effective = dropCachedPrefixOnResume(input, sessionId);
|
|
4969
5226
|
const promptText = flattenTurnInput(effective);
|
|
4970
5227
|
const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
|
|
4971
5228
|
if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
|
|
@@ -4978,7 +5235,9 @@ var init_claude = __esm({
|
|
|
4978
5235
|
const isOrchestrator = isOrchestratorThread2(thread);
|
|
4979
5236
|
const injectedServers = await buildInjectedMcpServers({
|
|
4980
5237
|
includeSideboard: true,
|
|
4981
|
-
includeBrightsy:
|
|
5238
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5239
|
+
orchestrator: isOrchestrator
|
|
5240
|
+
}),
|
|
4982
5241
|
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
4983
5242
|
});
|
|
4984
5243
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
@@ -5152,9 +5411,14 @@ async function listCodexModels() {
|
|
|
5152
5411
|
if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
|
|
5153
5412
|
return cachedCodexModels.models;
|
|
5154
5413
|
}
|
|
5155
|
-
const
|
|
5156
|
-
if (
|
|
5157
|
-
|
|
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 });
|
|
5158
5422
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5159
5423
|
return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
|
|
5160
5424
|
}
|
|
@@ -5193,8 +5457,8 @@ function codexConfigHasNetworkAccess() {
|
|
|
5193
5457
|
(0, import_node_path11.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
|
|
5194
5458
|
];
|
|
5195
5459
|
for (const path of candidates) {
|
|
5196
|
-
if (!(0,
|
|
5197
|
-
const text = (0,
|
|
5460
|
+
if (!(0, import_node_fs13.existsSync)(path)) continue;
|
|
5461
|
+
const text = (0, import_node_fs13.readFileSync)(path, "utf8");
|
|
5198
5462
|
if (/network_access\s*=\s*true/.test(text)) return true;
|
|
5199
5463
|
}
|
|
5200
5464
|
return false;
|
|
@@ -5227,21 +5491,23 @@ function asRecord(value) {
|
|
|
5227
5491
|
}
|
|
5228
5492
|
function codexLooksAuthenticated() {
|
|
5229
5493
|
const authPath = (0, import_node_path11.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
|
|
5230
|
-
if (!(0,
|
|
5494
|
+
if (!(0, import_node_fs13.existsSync)(authPath)) return false;
|
|
5231
5495
|
try {
|
|
5232
|
-
return (0,
|
|
5496
|
+
return (0, import_node_fs13.statSync)(authPath).size > 2;
|
|
5233
5497
|
} catch {
|
|
5234
5498
|
return false;
|
|
5235
5499
|
}
|
|
5236
5500
|
}
|
|
5237
|
-
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;
|
|
5238
5502
|
var init_codex = __esm({
|
|
5239
5503
|
"src/agents/codex.ts"() {
|
|
5240
5504
|
"use strict";
|
|
5241
|
-
|
|
5505
|
+
import_node_fs13 = require("fs");
|
|
5242
5506
|
import_node_os7 = require("os");
|
|
5243
5507
|
import_node_path11 = require("path");
|
|
5244
5508
|
init_run();
|
|
5509
|
+
init_app_settings();
|
|
5510
|
+
init_global_workspace();
|
|
5245
5511
|
init_error_detail();
|
|
5246
5512
|
init_injected_mcp();
|
|
5247
5513
|
init_turn_input();
|
|
@@ -5259,16 +5525,30 @@ var init_codex = __esm({
|
|
|
5259
5525
|
codexAdapter = {
|
|
5260
5526
|
kind: "codex",
|
|
5261
5527
|
async detect() {
|
|
5262
|
-
const
|
|
5263
|
-
if (
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
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
|
+
}
|
|
5272
5552
|
}
|
|
5273
5553
|
const authenticated = codexLooksAuthenticated();
|
|
5274
5554
|
const linearMcp = false;
|
|
@@ -5288,22 +5568,24 @@ var init_codex = __esm({
|
|
|
5288
5568
|
};
|
|
5289
5569
|
},
|
|
5290
5570
|
async buildTurn(thread, input) {
|
|
5291
|
-
const prompt = flattenTurnInput(input);
|
|
5292
5571
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5572
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5293
5573
|
const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
|
|
5294
5574
|
const promptArg = useStdin ? "-" : prompt;
|
|
5295
5575
|
if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
|
|
5296
|
-
const { cachedPrefix } = normalizeTurnInput(input);
|
|
5297
5576
|
console.error(
|
|
5298
|
-
`[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:")}`
|
|
5299
5578
|
);
|
|
5300
5579
|
}
|
|
5301
5580
|
const mode = permissionMode(thread);
|
|
5302
5581
|
const model = thread.model?.trim();
|
|
5582
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5303
5583
|
const injected = await buildInjectedMcpServers({
|
|
5304
5584
|
includeSideboard: true,
|
|
5305
|
-
includeBrightsy:
|
|
5306
|
-
|
|
5585
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
5586
|
+
orchestrator: isOrchestrator
|
|
5587
|
+
}),
|
|
5588
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5307
5589
|
});
|
|
5308
5590
|
const mcpOverrides = toCodexMcpConfigArgs(injected);
|
|
5309
5591
|
const execOpts = [
|
|
@@ -5323,7 +5605,7 @@ var init_codex = __esm({
|
|
|
5323
5605
|
];
|
|
5324
5606
|
const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
|
|
5325
5607
|
return {
|
|
5326
|
-
file: "codex",
|
|
5608
|
+
file: resolveAgentExecutable("codex"),
|
|
5327
5609
|
args,
|
|
5328
5610
|
cwd: thread.worktreePath,
|
|
5329
5611
|
stdin: useStdin ? `${prompt}
|
|
@@ -5419,16 +5701,17 @@ var init_codex = __esm({
|
|
|
5419
5701
|
return cached;
|
|
5420
5702
|
},
|
|
5421
5703
|
async buildAttach(thread) {
|
|
5704
|
+
const codex = resolveAgentExecutable("codex");
|
|
5422
5705
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5423
5706
|
if (sessionId) {
|
|
5424
5707
|
return {
|
|
5425
|
-
file:
|
|
5708
|
+
file: codex,
|
|
5426
5709
|
args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
|
|
5427
5710
|
cwd: thread.worktreePath
|
|
5428
5711
|
};
|
|
5429
5712
|
}
|
|
5430
5713
|
return {
|
|
5431
|
-
file:
|
|
5714
|
+
file: codex,
|
|
5432
5715
|
args: ["--cd", thread.worktreePath],
|
|
5433
5716
|
cwd: thread.worktreePath
|
|
5434
5717
|
};
|
|
@@ -5436,7 +5719,7 @@ var init_codex = __esm({
|
|
|
5436
5719
|
async listLinearIssues(_repoPath) {
|
|
5437
5720
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
5438
5721
|
const { stdout, exitCode } = await run(
|
|
5439
|
-
"codex",
|
|
5722
|
+
resolveAgentExecutable("codex"),
|
|
5440
5723
|
[
|
|
5441
5724
|
"exec",
|
|
5442
5725
|
"--json",
|
|
@@ -5699,21 +5982,22 @@ function cursorRunnerPath() {
|
|
|
5699
5982
|
(0, import_node_path12.join)(root, "src", "agents", "cursor-runner.ts")
|
|
5700
5983
|
];
|
|
5701
5984
|
for (const candidate of candidates) {
|
|
5702
|
-
if ((0,
|
|
5985
|
+
if ((0, import_node_fs14.existsSync)(candidate)) return candidate;
|
|
5703
5986
|
}
|
|
5704
5987
|
return candidates[0];
|
|
5705
5988
|
}
|
|
5706
|
-
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;
|
|
5707
5990
|
var init_cursor = __esm({
|
|
5708
5991
|
"src/agents/cursor.ts"() {
|
|
5709
5992
|
"use strict";
|
|
5710
|
-
|
|
5993
|
+
import_node_fs14 = require("fs");
|
|
5711
5994
|
import_node_module2 = require("module");
|
|
5712
5995
|
import_node_path12 = require("path");
|
|
5713
5996
|
import_node_url2 = require("url");
|
|
5714
5997
|
import_sdk = require("@cursor/sdk");
|
|
5715
5998
|
init_run();
|
|
5716
5999
|
init_app_settings();
|
|
6000
|
+
init_global_workspace();
|
|
5717
6001
|
init_cursor_events();
|
|
5718
6002
|
init_injected_mcp();
|
|
5719
6003
|
init_node_launch();
|
|
@@ -5750,13 +6034,16 @@ var init_cursor = __esm({
|
|
|
5750
6034
|
};
|
|
5751
6035
|
},
|
|
5752
6036
|
async buildTurn(thread, input) {
|
|
5753
|
-
const prompt = flattenTurnInput(input);
|
|
5754
6037
|
const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6038
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
|
|
5755
6039
|
const apiKey = resolveCursorApiKey() || void 0;
|
|
6040
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5756
6041
|
const injected = await buildInjectedMcpServers({
|
|
5757
6042
|
includeSideboard: true,
|
|
5758
|
-
includeBrightsy:
|
|
5759
|
-
|
|
6043
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6044
|
+
orchestrator: isOrchestrator
|
|
6045
|
+
}),
|
|
6046
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5760
6047
|
});
|
|
5761
6048
|
const mcpServers = toCursorMcpServers(injected);
|
|
5762
6049
|
const req = {
|
|
@@ -5827,9 +6114,14 @@ async function listOpencodeModels() {
|
|
|
5827
6114
|
if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
|
|
5828
6115
|
return cachedOpencodeModels.models;
|
|
5829
6116
|
}
|
|
5830
|
-
const
|
|
5831
|
-
if (
|
|
5832
|
-
|
|
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 });
|
|
5833
6125
|
if (listed.exitCode !== 0 || !listed.stdout.trim()) {
|
|
5834
6126
|
return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
|
|
5835
6127
|
}
|
|
@@ -5856,11 +6148,14 @@ function usageFromOpencode(tokens) {
|
|
|
5856
6148
|
cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
|
|
5857
6149
|
};
|
|
5858
6150
|
}
|
|
5859
|
-
var FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
6151
|
+
var import_node_fs15, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
|
|
5860
6152
|
var init_opencode = __esm({
|
|
5861
6153
|
"src/agents/opencode.ts"() {
|
|
5862
6154
|
"use strict";
|
|
6155
|
+
import_node_fs15 = require("fs");
|
|
5863
6156
|
init_run();
|
|
6157
|
+
init_app_settings();
|
|
6158
|
+
init_global_workspace();
|
|
5864
6159
|
init_error_detail();
|
|
5865
6160
|
init_injected_mcp();
|
|
5866
6161
|
init_turn_input();
|
|
@@ -5881,20 +6176,34 @@ var init_opencode = __esm({
|
|
|
5881
6176
|
opencodeAdapter = {
|
|
5882
6177
|
kind: "opencode",
|
|
5883
6178
|
async detect() {
|
|
5884
|
-
const
|
|
5885
|
-
if (
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
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
|
+
}
|
|
5894
6203
|
}
|
|
5895
|
-
const auth = await run(
|
|
6204
|
+
const auth = await run(opencode, ["auth", "list"], { reject: false });
|
|
5896
6205
|
const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
|
|
5897
|
-
const mcp = await run(
|
|
6206
|
+
const mcp = await run(opencode, ["mcp", "list"], { reject: false });
|
|
5898
6207
|
const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
|
|
5899
6208
|
return {
|
|
5900
6209
|
agent: "opencode",
|
|
@@ -5906,8 +6215,8 @@ var init_opencode = __esm({
|
|
|
5906
6215
|
};
|
|
5907
6216
|
},
|
|
5908
6217
|
async buildTurn(thread, input) {
|
|
5909
|
-
const prompt = flattenTurnInput(input);
|
|
5910
6218
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
6219
|
+
const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
|
|
5911
6220
|
const mode = permissionMode(thread);
|
|
5912
6221
|
const model = thread.model?.trim();
|
|
5913
6222
|
const args = [
|
|
@@ -5923,14 +6232,17 @@ var init_opencode = __esm({
|
|
|
5923
6232
|
if (model) {
|
|
5924
6233
|
args.push("--model", model);
|
|
5925
6234
|
}
|
|
6235
|
+
const isOrchestrator = isOrchestratorThread(thread);
|
|
5926
6236
|
const injected = await buildInjectedMcpServers({
|
|
5927
6237
|
includeSideboard: true,
|
|
5928
|
-
includeBrightsy:
|
|
5929
|
-
|
|
6238
|
+
includeBrightsy: shouldInjectBrightsyMcp(thread, {
|
|
6239
|
+
orchestrator: isOrchestrator
|
|
6240
|
+
}),
|
|
6241
|
+
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
5930
6242
|
});
|
|
5931
6243
|
const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
|
|
5932
6244
|
return {
|
|
5933
|
-
file: "opencode",
|
|
6245
|
+
file: resolveAgentExecutable("opencode"),
|
|
5934
6246
|
args,
|
|
5935
6247
|
cwd: thread.worktreePath,
|
|
5936
6248
|
// `opencode run` treats non-TTY stdin as the message body when no positional
|
|
@@ -6006,7 +6318,7 @@ var init_opencode = __esm({
|
|
|
6006
6318
|
async resolveSessionId(worktreePath, cached) {
|
|
6007
6319
|
const cachedId = cached?.trim() || null;
|
|
6008
6320
|
const listed = await run(
|
|
6009
|
-
"opencode",
|
|
6321
|
+
resolveAgentExecutable("opencode"),
|
|
6010
6322
|
["session", "list", "--format", "json"],
|
|
6011
6323
|
{ cwd: worktreePath, reject: false }
|
|
6012
6324
|
);
|
|
@@ -6037,7 +6349,7 @@ var init_opencode = __esm({
|
|
|
6037
6349
|
const args = ["--dir", thread.worktreePath];
|
|
6038
6350
|
if (sessionId) args.push("--session", sessionId);
|
|
6039
6351
|
return {
|
|
6040
|
-
file: "opencode",
|
|
6352
|
+
file: resolveAgentExecutable("opencode"),
|
|
6041
6353
|
args,
|
|
6042
6354
|
cwd: thread.worktreePath,
|
|
6043
6355
|
env: {
|
|
@@ -6048,7 +6360,7 @@ var init_opencode = __esm({
|
|
|
6048
6360
|
async listLinearIssues(_repoPath) {
|
|
6049
6361
|
const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
|
|
6050
6362
|
const { stdout, exitCode } = await run(
|
|
6051
|
-
"opencode",
|
|
6363
|
+
resolveAgentExecutable("opencode"),
|
|
6052
6364
|
["run", prompt, "--format", "json"],
|
|
6053
6365
|
{
|
|
6054
6366
|
reject: false,
|
|
@@ -6281,9 +6593,30 @@ function getAgentSetupInfo(agent) {
|
|
|
6281
6593
|
function listAgentSetupInfo() {
|
|
6282
6594
|
return Object.values(SETUP);
|
|
6283
6595
|
}
|
|
6284
|
-
|
|
6285
|
-
|
|
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();
|
|
6286
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();
|
|
6287
6620
|
if (!trimmed) throw new Error("Command is empty");
|
|
6288
6621
|
if (process.platform === "darwin") {
|
|
6289
6622
|
const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -6344,7 +6677,7 @@ async function installAgent(agent) {
|
|
|
6344
6677
|
message: `Opened Terminal to run: ${info.installCommand}`
|
|
6345
6678
|
};
|
|
6346
6679
|
}
|
|
6347
|
-
|
|
6680
|
+
enrichPathWithNpmGlobalBin();
|
|
6348
6681
|
const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
|
|
6349
6682
|
const ok = result.exitCode === 0;
|
|
6350
6683
|
if (!ok) {
|
|
@@ -6370,36 +6703,46 @@ async function installAgent(agent) {
|
|
|
6370
6703
|
};
|
|
6371
6704
|
}
|
|
6372
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
|
+
}
|
|
6373
6713
|
return {
|
|
6374
6714
|
ok: true,
|
|
6375
6715
|
command: info.installCommand,
|
|
6376
6716
|
exitCode: 0,
|
|
6377
6717
|
stdout: result.stdout,
|
|
6378
6718
|
stderr: result.stderr,
|
|
6379
|
-
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)`
|
|
6380
6720
|
};
|
|
6381
6721
|
}
|
|
6382
6722
|
async function loginAgent(agent) {
|
|
6383
6723
|
const info = getAgentSetupInfo(agent);
|
|
6384
|
-
|
|
6724
|
+
const loginCommand = resolveLoginCommand(agent);
|
|
6725
|
+
if (!loginCommand) {
|
|
6385
6726
|
return {
|
|
6386
6727
|
ok: true,
|
|
6387
6728
|
message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
|
|
6388
6729
|
};
|
|
6389
6730
|
}
|
|
6390
|
-
await
|
|
6731
|
+
const prepared = await prepareTerminalCommand(loginCommand);
|
|
6732
|
+
await openInSystemTerminal(prepared);
|
|
6391
6733
|
return {
|
|
6392
6734
|
ok: true,
|
|
6393
6735
|
openedTerminal: true,
|
|
6394
|
-
command:
|
|
6395
|
-
message: `Opened Terminal to run: ${
|
|
6736
|
+
command: prepared,
|
|
6737
|
+
message: `Opened Terminal to run: ${prepared}`
|
|
6396
6738
|
};
|
|
6397
6739
|
}
|
|
6398
|
-
var SETUP;
|
|
6740
|
+
var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
|
|
6399
6741
|
var init_install = __esm({
|
|
6400
6742
|
"src/agents/install.ts"() {
|
|
6401
6743
|
"use strict";
|
|
6402
6744
|
init_run();
|
|
6745
|
+
init_app_settings();
|
|
6403
6746
|
init_path();
|
|
6404
6747
|
SETUP = {
|
|
6405
6748
|
claude: {
|
|
@@ -6448,6 +6791,18 @@ var init_install = __esm({
|
|
|
6448
6791
|
npmPackage: "@brightsy/cli"
|
|
6449
6792
|
}
|
|
6450
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
|
+
]);
|
|
6451
6806
|
}
|
|
6452
6807
|
});
|
|
6453
6808
|
|
|
@@ -6467,6 +6822,7 @@ __export(agents_exports, {
|
|
|
6467
6822
|
cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
|
|
6468
6823
|
decodeBrightsyTarget: () => decodeBrightsyTarget,
|
|
6469
6824
|
encodeBrightsyTarget: () => encodeBrightsyTarget,
|
|
6825
|
+
enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
|
|
6470
6826
|
ensureAgentPath: () => ensureAgentPath,
|
|
6471
6827
|
getAdapter: () => getAdapter,
|
|
6472
6828
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
@@ -6486,8 +6842,13 @@ __export(agents_exports, {
|
|
|
6486
6842
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6487
6843
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6488
6844
|
permissionMode: () => permissionMode,
|
|
6845
|
+
posixShellSingleQuote: () => posixShellSingleQuote,
|
|
6846
|
+
prepareTerminalCommand: () => prepareTerminalCommand,
|
|
6847
|
+
resolveCommandBinarySync: () => resolveCommandBinarySync,
|
|
6489
6848
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
6490
|
-
|
|
6849
|
+
resolveLoginCommand: () => resolveLoginCommand,
|
|
6850
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6851
|
+
withExportedPath: () => withExportedPath
|
|
6491
6852
|
});
|
|
6492
6853
|
function getAdapter(kind) {
|
|
6493
6854
|
return adapters[kind];
|
|
@@ -6545,31 +6906,31 @@ function removedWorkspacesFile() {
|
|
|
6545
6906
|
}
|
|
6546
6907
|
function readAll() {
|
|
6547
6908
|
const path = workspacesFile();
|
|
6548
|
-
if (!(0,
|
|
6909
|
+
if (!(0, import_node_fs19.existsSync)(path)) return [];
|
|
6549
6910
|
try {
|
|
6550
|
-
const raw = JSON.parse((0,
|
|
6911
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6551
6912
|
return Array.isArray(raw) ? raw : [];
|
|
6552
6913
|
} catch {
|
|
6553
6914
|
return [];
|
|
6554
6915
|
}
|
|
6555
6916
|
}
|
|
6556
6917
|
function writeAll(list) {
|
|
6557
|
-
(0,
|
|
6558
|
-
(0,
|
|
6918
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6919
|
+
(0, import_node_fs19.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
|
|
6559
6920
|
}
|
|
6560
6921
|
function readRemoved() {
|
|
6561
6922
|
const path = removedWorkspacesFile();
|
|
6562
|
-
if (!(0,
|
|
6923
|
+
if (!(0, import_node_fs19.existsSync)(path)) return /* @__PURE__ */ new Set();
|
|
6563
6924
|
try {
|
|
6564
|
-
const raw = JSON.parse((0,
|
|
6925
|
+
const raw = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
|
|
6565
6926
|
return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
|
|
6566
6927
|
} catch {
|
|
6567
6928
|
return /* @__PURE__ */ new Set();
|
|
6568
6929
|
}
|
|
6569
6930
|
}
|
|
6570
6931
|
function writeRemoved(paths) {
|
|
6571
|
-
(0,
|
|
6572
|
-
(0,
|
|
6932
|
+
(0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
|
|
6933
|
+
(0, import_node_fs19.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
|
|
6573
6934
|
}
|
|
6574
6935
|
function rememberRemoved(repoPath) {
|
|
6575
6936
|
const next = readRemoved();
|
|
@@ -6592,7 +6953,7 @@ function listWorkspaces() {
|
|
|
6592
6953
|
async function addWorkspace(repoPath) {
|
|
6593
6954
|
const root = await resolveRepoRoot(repoPath);
|
|
6594
6955
|
if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
|
|
6595
|
-
if (!(0,
|
|
6956
|
+
if (!(0, import_node_fs19.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
|
|
6596
6957
|
forgetRemoved(root);
|
|
6597
6958
|
await ensureGhPreferOrigin(root);
|
|
6598
6959
|
const current = readAll();
|
|
@@ -6622,7 +6983,7 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6622
6983
|
if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
|
|
6623
6984
|
continue;
|
|
6624
6985
|
}
|
|
6625
|
-
if (!(0,
|
|
6986
|
+
if (!(0, import_node_fs19.existsSync)(path)) continue;
|
|
6626
6987
|
const ws = {
|
|
6627
6988
|
path,
|
|
6628
6989
|
name: (0, import_node_path16.basename)(path),
|
|
@@ -6635,11 +6996,11 @@ function syncWorkspacesFromThreads(repoPaths) {
|
|
|
6635
6996
|
if (dirty) writeAll(next);
|
|
6636
6997
|
return next.sort((a, b) => a.name.localeCompare(b.name));
|
|
6637
6998
|
}
|
|
6638
|
-
var
|
|
6999
|
+
var import_node_fs19, import_node_path16;
|
|
6639
7000
|
var init_workspaces = __esm({
|
|
6640
7001
|
"src/store/workspaces.ts"() {
|
|
6641
7002
|
"use strict";
|
|
6642
|
-
|
|
7003
|
+
import_node_fs19 = require("fs");
|
|
6643
7004
|
import_node_path16 = require("path");
|
|
6644
7005
|
init_paths();
|
|
6645
7006
|
init_global_workspace();
|
|
@@ -6718,17 +7079,17 @@ __export(plan_file_exports, {
|
|
|
6718
7079
|
});
|
|
6719
7080
|
function ensureAttachmentsGitignore2(worktreePath) {
|
|
6720
7081
|
const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
6721
|
-
if ((0,
|
|
6722
|
-
(0,
|
|
6723
|
-
(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");
|
|
6724
7085
|
}
|
|
6725
7086
|
function planFileAbs(worktreePath) {
|
|
6726
7087
|
return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
|
|
6727
7088
|
}
|
|
6728
7089
|
function readTextIfPresent2(abs) {
|
|
6729
|
-
if (!(0,
|
|
7090
|
+
if (!(0, import_node_fs29.existsSync)(abs)) return null;
|
|
6730
7091
|
try {
|
|
6731
|
-
const content = (0,
|
|
7092
|
+
const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
|
|
6732
7093
|
return content.trim() ? content : null;
|
|
6733
7094
|
} catch {
|
|
6734
7095
|
return null;
|
|
@@ -6740,16 +7101,16 @@ function readPlanFile(worktreePath) {
|
|
|
6740
7101
|
function writePlanFile(worktreePath, content) {
|
|
6741
7102
|
ensureAttachmentsGitignore2(worktreePath);
|
|
6742
7103
|
const abs = planFileAbs(worktreePath);
|
|
6743
|
-
(0,
|
|
7104
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
|
|
6744
7105
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
6745
|
-
(0,
|
|
7106
|
+
(0, import_node_fs29.writeFileSync)(abs, body, "utf8");
|
|
6746
7107
|
return PLAN_FILE_REL;
|
|
6747
7108
|
}
|
|
6748
|
-
var
|
|
7109
|
+
var import_node_fs29, import_node_path24;
|
|
6749
7110
|
var init_plan_file = __esm({
|
|
6750
7111
|
"src/plan/plan-file.ts"() {
|
|
6751
7112
|
"use strict";
|
|
6752
|
-
|
|
7113
|
+
import_node_fs29 = require("fs");
|
|
6753
7114
|
import_node_path24 = require("path");
|
|
6754
7115
|
init_workspace_scratch();
|
|
6755
7116
|
init_plan_present();
|
|
@@ -6766,9 +7127,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6766
7127
|
const agentId = opts.agentId.trim();
|
|
6767
7128
|
if (!agentId) return null;
|
|
6768
7129
|
const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
6769
|
-
if (!(0,
|
|
7130
|
+
if (!(0, import_node_fs30.existsSync)(runsPath)) return null;
|
|
6770
7131
|
try {
|
|
6771
|
-
const lines = (0,
|
|
7132
|
+
const lines = (0, import_node_fs30.readFileSync)(runsPath, "utf8").split("\n");
|
|
6772
7133
|
let best = null;
|
|
6773
7134
|
for (const line of lines) {
|
|
6774
7135
|
const trimmed = line.trim();
|
|
@@ -6794,11 +7155,11 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6794
7155
|
return null;
|
|
6795
7156
|
}
|
|
6796
7157
|
}
|
|
6797
|
-
var
|
|
7158
|
+
var import_node_fs30, import_node_path25;
|
|
6798
7159
|
var init_cursor_recover = __esm({
|
|
6799
7160
|
"src/agents/cursor-recover.ts"() {
|
|
6800
7161
|
"use strict";
|
|
6801
|
-
|
|
7162
|
+
import_node_fs30 = require("fs");
|
|
6802
7163
|
import_node_path25 = require("path");
|
|
6803
7164
|
init_paths();
|
|
6804
7165
|
}
|
|
@@ -6812,7 +7173,7 @@ var import_node_path26 = require("path");
|
|
|
6812
7173
|
|
|
6813
7174
|
// src/orchestrator/orchestrator.ts
|
|
6814
7175
|
var import_node_events = require("events");
|
|
6815
|
-
var
|
|
7176
|
+
var import_node_fs31 = require("fs");
|
|
6816
7177
|
init_error_detail();
|
|
6817
7178
|
|
|
6818
7179
|
// src/agents/spawn.ts
|
|
@@ -7187,8 +7548,22 @@ init_agents();
|
|
|
7187
7548
|
init_worktree();
|
|
7188
7549
|
init_stack();
|
|
7189
7550
|
|
|
7551
|
+
// src/git/pr-merge-archive.ts
|
|
7552
|
+
function normalizePrState(state) {
|
|
7553
|
+
return (state ?? "").trim().toUpperCase();
|
|
7554
|
+
}
|
|
7555
|
+
function shouldAutoArchiveOnPrMerge(opts) {
|
|
7556
|
+
if (!opts.autoArchiveEnabled) return false;
|
|
7557
|
+
if (opts.isGlobal) return false;
|
|
7558
|
+
if (opts.skipAutoArchiveOnMerge) return false;
|
|
7559
|
+
if (opts.threadStatus === "archived") return false;
|
|
7560
|
+
const next = normalizePrState(opts.nextPrState);
|
|
7561
|
+
if (next !== "MERGED") return false;
|
|
7562
|
+
return normalizePrState(opts.previousPrState) !== "MERGED";
|
|
7563
|
+
}
|
|
7564
|
+
|
|
7190
7565
|
// src/hook/conductor.ts
|
|
7191
|
-
var
|
|
7566
|
+
var import_node_fs16 = require("fs");
|
|
7192
7567
|
var import_node_net = require("net");
|
|
7193
7568
|
var import_node_path13 = require("path");
|
|
7194
7569
|
var import_execa3 = require("execa");
|
|
@@ -7201,8 +7576,8 @@ function matchSimpleGlob(pattern, name) {
|
|
|
7201
7576
|
}
|
|
7202
7577
|
function readWorktreeInclude(repoPath) {
|
|
7203
7578
|
const path = (0, import_node_path13.join)(repoPath, ".worktreeinclude");
|
|
7204
|
-
if (!(0,
|
|
7205
|
-
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("#"));
|
|
7206
7581
|
}
|
|
7207
7582
|
function resolveFilesToCopy(repoPath) {
|
|
7208
7583
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -7212,7 +7587,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7212
7587
|
if (settings?.fileIncludeGlobs?.length) {
|
|
7213
7588
|
const matched = [];
|
|
7214
7589
|
try {
|
|
7215
|
-
for (const entry of (0,
|
|
7590
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7216
7591
|
if (!entry.isFile()) continue;
|
|
7217
7592
|
for (const glob of settings.fileIncludeGlobs) {
|
|
7218
7593
|
if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path13.basename)(glob), entry.name)) {
|
|
@@ -7227,7 +7602,7 @@ function resolveFilesToCopy(repoPath) {
|
|
|
7227
7602
|
}
|
|
7228
7603
|
const defaults = [];
|
|
7229
7604
|
try {
|
|
7230
|
-
for (const entry of (0,
|
|
7605
|
+
for (const entry of (0, import_node_fs16.readdirSync)(repoPath, { withFileTypes: true })) {
|
|
7231
7606
|
if (entry.isFile() && entry.name.startsWith(".env")) {
|
|
7232
7607
|
defaults.push(entry.name);
|
|
7233
7608
|
}
|
|
@@ -7242,10 +7617,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
7242
7617
|
const copied = [];
|
|
7243
7618
|
for (const rel of patterns) {
|
|
7244
7619
|
const src = (0, import_node_path13.join)(repoPath, rel);
|
|
7245
|
-
if (!(0,
|
|
7620
|
+
if (!(0, import_node_fs16.existsSync)(src)) continue;
|
|
7246
7621
|
const dest = (0, import_node_path13.join)(worktreePath, rel);
|
|
7247
|
-
(0,
|
|
7248
|
-
(0,
|
|
7622
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path13.dirname)(dest), { recursive: true });
|
|
7623
|
+
(0, import_node_fs16.copyFileSync)(src, dest);
|
|
7249
7624
|
copied.push(rel);
|
|
7250
7625
|
}
|
|
7251
7626
|
return copied;
|
|
@@ -7498,15 +7873,15 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
7498
7873
|
}
|
|
7499
7874
|
|
|
7500
7875
|
// src/hook/cursor-worktrees.ts
|
|
7501
|
-
var
|
|
7876
|
+
var import_node_fs17 = require("fs");
|
|
7502
7877
|
var import_node_path14 = require("path");
|
|
7503
7878
|
var import_execa4 = require("execa");
|
|
7504
7879
|
var import_node_readline3 = require("readline");
|
|
7505
7880
|
function loadCursorWorktreesJson(rootPath) {
|
|
7506
7881
|
const path = (0, import_node_path14.join)(rootPath, ".cursor", "worktrees.json");
|
|
7507
|
-
if (!(0,
|
|
7882
|
+
if (!(0, import_node_fs17.existsSync)(path)) return null;
|
|
7508
7883
|
try {
|
|
7509
|
-
return JSON.parse((0,
|
|
7884
|
+
return JSON.parse((0, import_node_fs17.readFileSync)(path, "utf8"));
|
|
7510
7885
|
} catch {
|
|
7511
7886
|
return null;
|
|
7512
7887
|
}
|
|
@@ -7564,7 +7939,7 @@ async function runCursorWorktreeSetup(repoPath, worktreePath, onLine) {
|
|
|
7564
7939
|
}
|
|
7565
7940
|
|
|
7566
7941
|
// src/git/orphan-cleanup.ts
|
|
7567
|
-
var
|
|
7942
|
+
var import_node_fs18 = require("fs");
|
|
7568
7943
|
var import_node_path15 = require("path");
|
|
7569
7944
|
init_worktree();
|
|
7570
7945
|
init_thread_store();
|
|
@@ -7580,9 +7955,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7580
7955
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
7581
7956
|
);
|
|
7582
7957
|
const homeRoot = sideboardWorkspacesDir();
|
|
7583
|
-
if ((0,
|
|
7958
|
+
if ((0, import_node_fs18.existsSync)(homeRoot)) {
|
|
7584
7959
|
try {
|
|
7585
|
-
for (const entry of (0,
|
|
7960
|
+
for (const entry of (0, import_node_fs18.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
7586
7961
|
if (!entry.isDirectory()) continue;
|
|
7587
7962
|
void entry;
|
|
7588
7963
|
}
|
|
@@ -7592,7 +7967,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7592
7967
|
const orphans = [];
|
|
7593
7968
|
const seen = /* @__PURE__ */ new Set();
|
|
7594
7969
|
for (const repoPath of repos) {
|
|
7595
|
-
if (!repoPath || !(0,
|
|
7970
|
+
if (!repoPath || !(0, import_node_fs18.existsSync)(repoPath)) continue;
|
|
7596
7971
|
try {
|
|
7597
7972
|
const wts = await listWorktrees(repoPath);
|
|
7598
7973
|
for (const wt of wts) {
|
|
@@ -7603,7 +7978,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7603
7978
|
seen.add(path);
|
|
7604
7979
|
let mtimeMs = 0;
|
|
7605
7980
|
try {
|
|
7606
|
-
mtimeMs = (0,
|
|
7981
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7607
7982
|
} catch {
|
|
7608
7983
|
mtimeMs = 0;
|
|
7609
7984
|
}
|
|
@@ -7613,16 +7988,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
7613
7988
|
}
|
|
7614
7989
|
try {
|
|
7615
7990
|
const root = worktreesRoot(repoPath);
|
|
7616
|
-
if ((0,
|
|
7617
|
-
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 })) {
|
|
7618
7993
|
if (!entry.isDirectory()) continue;
|
|
7619
7994
|
const path = (0, import_node_path15.join)(root, entry.name).replace(/\/$/, "");
|
|
7620
7995
|
if (known.has(path) || seen.has(path)) continue;
|
|
7621
|
-
if (!(0,
|
|
7996
|
+
if (!(0, import_node_fs18.existsSync)((0, import_node_path15.join)(path, ".git"))) continue;
|
|
7622
7997
|
seen.add(path);
|
|
7623
7998
|
let mtimeMs = 0;
|
|
7624
7999
|
try {
|
|
7625
|
-
mtimeMs = (0,
|
|
8000
|
+
mtimeMs = (0, import_node_fs18.statSync)(path).mtimeMs;
|
|
7626
8001
|
} catch {
|
|
7627
8002
|
mtimeMs = Date.now();
|
|
7628
8003
|
}
|
|
@@ -7759,7 +8134,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
7759
8134
|
}
|
|
7760
8135
|
|
|
7761
8136
|
// src/git/clone-repo.ts
|
|
7762
|
-
var
|
|
8137
|
+
var import_node_fs20 = require("fs");
|
|
7763
8138
|
var import_node_path17 = require("path");
|
|
7764
8139
|
var import_execa6 = require("execa");
|
|
7765
8140
|
init_paths();
|
|
@@ -7775,7 +8150,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7775
8150
|
}
|
|
7776
8151
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
7777
8152
|
const dest = (0, import_node_path17.join)(sideboardReposDir(), name);
|
|
7778
|
-
if ((0,
|
|
8153
|
+
if ((0, import_node_fs20.existsSync)(dest)) {
|
|
7779
8154
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
7780
8155
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
7781
8156
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -7795,13 +8170,14 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
7795
8170
|
init_thread_store();
|
|
7796
8171
|
|
|
7797
8172
|
// src/threads/create.ts
|
|
7798
|
-
var
|
|
8173
|
+
var import_node_fs21 = require("fs");
|
|
7799
8174
|
|
|
7800
8175
|
// src/detect/detect.ts
|
|
7801
8176
|
init_agents();
|
|
7802
8177
|
var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
|
|
7803
8178
|
async function requireAgent(agent, opts) {
|
|
7804
8179
|
ensureAgentPath();
|
|
8180
|
+
enrichPathWithNpmGlobalBin();
|
|
7805
8181
|
const status = await Promise.race([
|
|
7806
8182
|
getAdapter(agent).detect(),
|
|
7807
8183
|
new Promise((_, reject) => {
|
|
@@ -7839,7 +8215,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
7839
8215
|
});
|
|
7840
8216
|
await requireAgent(resolved.agent);
|
|
7841
8217
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
7842
|
-
if (!(0,
|
|
8218
|
+
if (!(0, import_node_fs21.existsSync)(repoPath)) {
|
|
7843
8219
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
7844
8220
|
}
|
|
7845
8221
|
let sourceRef = input.sourceRef;
|
|
@@ -8226,6 +8602,8 @@ function worktreeBindingFrom(from) {
|
|
|
8226
8602
|
parentThreadId: from.parentThreadId,
|
|
8227
8603
|
prUrl: from.prUrl,
|
|
8228
8604
|
prTitle: from.prTitle,
|
|
8605
|
+
prState: from.prState,
|
|
8606
|
+
skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
|
|
8229
8607
|
stackId: from.stackId,
|
|
8230
8608
|
stackLayer: from.stackLayer
|
|
8231
8609
|
};
|
|
@@ -8300,7 +8678,7 @@ function forkChatTab(input) {
|
|
|
8300
8678
|
|
|
8301
8679
|
// src/review/request-review.ts
|
|
8302
8680
|
var import_node_crypto3 = require("crypto");
|
|
8303
|
-
var
|
|
8681
|
+
var import_node_fs22 = require("fs");
|
|
8304
8682
|
var import_node_path18 = require("path");
|
|
8305
8683
|
init_global_workspace();
|
|
8306
8684
|
init_thread_store();
|
|
@@ -8445,9 +8823,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
8445
8823
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
8446
8824
|
}
|
|
8447
8825
|
function readTextIfPresent(abs) {
|
|
8448
|
-
if (!(0,
|
|
8826
|
+
if (!(0, import_node_fs22.existsSync)(abs)) return null;
|
|
8449
8827
|
try {
|
|
8450
|
-
const content = (0,
|
|
8828
|
+
const content = (0, import_node_fs22.readFileSync)(abs, "utf8");
|
|
8451
8829
|
return content.trim() ? content : null;
|
|
8452
8830
|
} catch {
|
|
8453
8831
|
return null;
|
|
@@ -8455,9 +8833,9 @@ function readTextIfPresent(abs) {
|
|
|
8455
8833
|
}
|
|
8456
8834
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
8457
8835
|
const gitignoreAbs = (0, import_node_path18.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
8458
|
-
if ((0,
|
|
8459
|
-
(0,
|
|
8460
|
-
(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");
|
|
8461
8839
|
}
|
|
8462
8840
|
function resolveReviewGuidelines(worktreePath) {
|
|
8463
8841
|
const repoAbs = (0, import_node_path18.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -8491,8 +8869,8 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
8491
8869
|
};
|
|
8492
8870
|
}
|
|
8493
8871
|
ensureAttachmentsGitignore(worktreePath);
|
|
8494
|
-
(0,
|
|
8495
|
-
(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");
|
|
8496
8874
|
return {
|
|
8497
8875
|
path: REVIEW_REQUEST_PATH,
|
|
8498
8876
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -8691,8 +9069,8 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
8691
9069
|
}
|
|
8692
9070
|
|
|
8693
9071
|
// src/threads/adopt.ts
|
|
8694
|
-
var
|
|
8695
|
-
var
|
|
9072
|
+
var import_node_child_process2 = require("child_process");
|
|
9073
|
+
var import_node_fs23 = require("fs");
|
|
8696
9074
|
var import_node_os8 = require("os");
|
|
8697
9075
|
var import_node_path19 = require("path");
|
|
8698
9076
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -8717,21 +9095,21 @@ function mapAgentType(raw) {
|
|
|
8717
9095
|
return null;
|
|
8718
9096
|
}
|
|
8719
9097
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
8720
|
-
if (!workspacePath || !(0,
|
|
9098
|
+
if (!workspacePath || !(0, import_node_fs23.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
8721
9099
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
8722
9100
|
let best = null;
|
|
8723
9101
|
let hashes;
|
|
8724
9102
|
try {
|
|
8725
|
-
hashes = (0,
|
|
9103
|
+
hashes = (0, import_node_fs23.readdirSync)(CURSOR_SDK_STORE);
|
|
8726
9104
|
} catch {
|
|
8727
9105
|
return null;
|
|
8728
9106
|
}
|
|
8729
9107
|
for (const hash of hashes) {
|
|
8730
9108
|
const agentsFile = (0, import_node_path19.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
8731
|
-
if (!(0,
|
|
9109
|
+
if (!(0, import_node_fs23.existsSync)(agentsFile)) continue;
|
|
8732
9110
|
let text;
|
|
8733
9111
|
try {
|
|
8734
|
-
text = (0,
|
|
9112
|
+
text = (0, import_node_fs23.readFileSync)(agentsFile, "utf8");
|
|
8735
9113
|
} catch {
|
|
8736
9114
|
continue;
|
|
8737
9115
|
}
|
|
@@ -8755,7 +9133,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
8755
9133
|
return best?.agentId ?? null;
|
|
8756
9134
|
}
|
|
8757
9135
|
async function adoptThread(input) {
|
|
8758
|
-
if (!(0,
|
|
9136
|
+
if (!(0, import_node_fs23.existsSync)(input.worktreePath)) {
|
|
8759
9137
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
8760
9138
|
}
|
|
8761
9139
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -8779,18 +9157,18 @@ async function adoptThread(input) {
|
|
|
8779
9157
|
return thread;
|
|
8780
9158
|
}
|
|
8781
9159
|
function listConductorWorkspaces() {
|
|
8782
|
-
if (!(0,
|
|
9160
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8783
9161
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8784
9162
|
}
|
|
8785
|
-
const tmp = (0,
|
|
9163
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8786
9164
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8787
9165
|
try {
|
|
8788
|
-
(0,
|
|
9166
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8789
9167
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8790
9168
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8791
|
-
if ((0,
|
|
9169
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8792
9170
|
try {
|
|
8793
|
-
(0,
|
|
9171
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8794
9172
|
} catch {
|
|
8795
9173
|
}
|
|
8796
9174
|
}
|
|
@@ -8866,22 +9244,22 @@ function listConductorWorkspaces() {
|
|
|
8866
9244
|
db.close();
|
|
8867
9245
|
}
|
|
8868
9246
|
} finally {
|
|
8869
|
-
(0,
|
|
9247
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8870
9248
|
}
|
|
8871
9249
|
}
|
|
8872
9250
|
function importConductorWorkspace(workspaceId) {
|
|
8873
|
-
if (!(0,
|
|
9251
|
+
if (!(0, import_node_fs23.existsSync)(CONDUCTOR_DB)) {
|
|
8874
9252
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
8875
9253
|
}
|
|
8876
|
-
const tmp = (0,
|
|
9254
|
+
const tmp = (0, import_node_fs23.mkdtempSync)((0, import_node_path19.join)((0, import_node_os8.tmpdir)(), "sideboard-conductor-"));
|
|
8877
9255
|
const snapshot = (0, import_node_path19.join)(tmp, "conductor.db");
|
|
8878
9256
|
try {
|
|
8879
|
-
(0,
|
|
9257
|
+
(0, import_node_fs23.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
8880
9258
|
for (const suffix of ["-wal", "-shm"]) {
|
|
8881
9259
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
8882
|
-
if ((0,
|
|
9260
|
+
if ((0, import_node_fs23.existsSync)(src)) {
|
|
8883
9261
|
try {
|
|
8884
|
-
(0,
|
|
9262
|
+
(0, import_node_fs23.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
8885
9263
|
} catch {
|
|
8886
9264
|
}
|
|
8887
9265
|
}
|
|
@@ -8899,7 +9277,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8899
9277
|
).get(workspaceId);
|
|
8900
9278
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
8901
9279
|
const worktreePath = String(row.workspacePath);
|
|
8902
|
-
if (!(0,
|
|
9280
|
+
if (!(0, import_node_fs23.existsSync)(worktreePath)) {
|
|
8903
9281
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
8904
9282
|
}
|
|
8905
9283
|
let sessionId = null;
|
|
@@ -8939,7 +9317,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8939
9317
|
}
|
|
8940
9318
|
let repoPath = worktreePath;
|
|
8941
9319
|
try {
|
|
8942
|
-
repoPath = (0,
|
|
9320
|
+
repoPath = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
8943
9321
|
cwd: worktreePath,
|
|
8944
9322
|
encoding: "utf8"
|
|
8945
9323
|
}).trim();
|
|
@@ -8962,7 +9340,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
8962
9340
|
db.close();
|
|
8963
9341
|
}
|
|
8964
9342
|
} finally {
|
|
8965
|
-
(0,
|
|
9343
|
+
(0, import_node_fs23.rmSync)(tmp, { recursive: true, force: true });
|
|
8966
9344
|
}
|
|
8967
9345
|
}
|
|
8968
9346
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -8970,7 +9348,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
8970
9348
|
}
|
|
8971
9349
|
|
|
8972
9350
|
// src/threads/stack-layers.ts
|
|
8973
|
-
var
|
|
9351
|
+
var import_node_fs24 = require("fs");
|
|
8974
9352
|
init_run();
|
|
8975
9353
|
init_stack();
|
|
8976
9354
|
init_worktree();
|
|
@@ -9038,7 +9416,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
9038
9416
|
let createdWorktree = false;
|
|
9039
9417
|
const trees = await listWorktrees(repoPath);
|
|
9040
9418
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
9041
|
-
if (checkedOut?.path && (0,
|
|
9419
|
+
if (checkedOut?.path && (0, import_node_fs24.existsSync)(checkedOut.path)) {
|
|
9042
9420
|
if (input.reuseExistingWorktree !== false) {
|
|
9043
9421
|
worktreePath = checkedOut.path;
|
|
9044
9422
|
} else {
|
|
@@ -9180,7 +9558,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
9180
9558
|
async function createPrStack(input, onSetupLine) {
|
|
9181
9559
|
await requireAgent(input.agent);
|
|
9182
9560
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9183
|
-
if (!(0,
|
|
9561
|
+
if (!(0, import_node_fs24.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
9184
9562
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
9185
9563
|
const status = await detectGhStack(repoPath);
|
|
9186
9564
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -9247,7 +9625,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9247
9625
|
}
|
|
9248
9626
|
}
|
|
9249
9627
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
9250
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
9628
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs24.existsSync)(bootstrap.worktreePath)) {
|
|
9251
9629
|
try {
|
|
9252
9630
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
9253
9631
|
deleteBranch: bootstrap.branchName
|
|
@@ -9262,12 +9640,12 @@ async function createPrStack(input, onSetupLine) {
|
|
|
9262
9640
|
init_worktree();
|
|
9263
9641
|
|
|
9264
9642
|
// src/diff/diff.ts
|
|
9265
|
-
var
|
|
9643
|
+
var import_node_fs25 = require("fs");
|
|
9266
9644
|
var import_node_path20 = require("path");
|
|
9267
9645
|
init_run();
|
|
9268
9646
|
init_worktree();
|
|
9269
9647
|
async function inspectGitWorktree(worktreePath) {
|
|
9270
|
-
if (!worktreePath || !(0,
|
|
9648
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) return "missing_worktree";
|
|
9271
9649
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
9272
9650
|
reject: false
|
|
9273
9651
|
});
|
|
@@ -9275,7 +9653,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
9275
9653
|
return "ok";
|
|
9276
9654
|
}
|
|
9277
9655
|
async function initializeGitRepository(worktreePath) {
|
|
9278
|
-
if (!worktreePath || !(0,
|
|
9656
|
+
if (!worktreePath || !(0, import_node_fs25.existsSync)(worktreePath)) {
|
|
9279
9657
|
throw new Error("Worktree not found");
|
|
9280
9658
|
}
|
|
9281
9659
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -9713,7 +10091,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9713
10091
|
assertSafeRelativePath(relativePath);
|
|
9714
10092
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
9715
10093
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9716
|
-
const st = (0,
|
|
10094
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9717
10095
|
if (!st.isFile()) {
|
|
9718
10096
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9719
10097
|
}
|
|
@@ -9722,7 +10100,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
9722
10100
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
9723
10101
|
);
|
|
9724
10102
|
}
|
|
9725
|
-
const buf = (0,
|
|
10103
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9726
10104
|
return {
|
|
9727
10105
|
path: relativePath,
|
|
9728
10106
|
contentBase64: buf.toString("base64"),
|
|
@@ -9733,11 +10111,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
9733
10111
|
assertSafeRelativePath(relativePath);
|
|
9734
10112
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
9735
10113
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9736
|
-
const st = (0,
|
|
10114
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9737
10115
|
if (!st.isFile()) {
|
|
9738
10116
|
throw new Error(`Not a file: ${relativePath}`);
|
|
9739
10117
|
}
|
|
9740
|
-
const buf = (0,
|
|
10118
|
+
const buf = (0, import_node_fs25.readFileSync)(abs);
|
|
9741
10119
|
if (isImageRelativePath(relativePath)) {
|
|
9742
10120
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
9743
10121
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -9781,8 +10159,8 @@ function assertSafeRelativePath(relativePath) {
|
|
|
9781
10159
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
9782
10160
|
assertSafeRelativePath(relativePath);
|
|
9783
10161
|
const abs = (0, import_node_path20.join)(worktreePath, relativePath);
|
|
9784
|
-
(0,
|
|
9785
|
-
(0,
|
|
10162
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path20.dirname)(abs), { recursive: true });
|
|
10163
|
+
(0, import_node_fs25.writeFileSync)(abs, content, "utf8");
|
|
9786
10164
|
return { path: relativePath };
|
|
9787
10165
|
}
|
|
9788
10166
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -9961,7 +10339,7 @@ async function confirmLand(thread, opts) {
|
|
|
9961
10339
|
}
|
|
9962
10340
|
|
|
9963
10341
|
// src/skills/discover.ts
|
|
9964
|
-
var
|
|
10342
|
+
var import_node_fs26 = require("fs");
|
|
9965
10343
|
var import_node_os9 = require("os");
|
|
9966
10344
|
var import_node_path21 = require("path");
|
|
9967
10345
|
function toCommand(name) {
|
|
@@ -9995,7 +10373,7 @@ function parseFrontmatter(content) {
|
|
|
9995
10373
|
}
|
|
9996
10374
|
function readSkill(skillMd, source) {
|
|
9997
10375
|
try {
|
|
9998
|
-
const content = (0,
|
|
10376
|
+
const content = (0, import_node_fs26.readFileSync)(skillMd, "utf8");
|
|
9999
10377
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
10000
10378
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
10001
10379
|
const name = fmName || dirName;
|
|
@@ -10014,19 +10392,19 @@ function readSkill(skillMd, source) {
|
|
|
10014
10392
|
}
|
|
10015
10393
|
}
|
|
10016
10394
|
function scanSkillsDir(dir, source, out) {
|
|
10017
|
-
if (!(0,
|
|
10395
|
+
if (!(0, import_node_fs26.existsSync)(dir)) return;
|
|
10018
10396
|
let entries;
|
|
10019
10397
|
try {
|
|
10020
|
-
entries = (0,
|
|
10398
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10021
10399
|
} catch {
|
|
10022
10400
|
return;
|
|
10023
10401
|
}
|
|
10024
10402
|
for (const entry of entries) {
|
|
10025
10403
|
if (entry.startsWith(".")) continue;
|
|
10026
10404
|
const skillMd = (0, import_node_path21.join)(dir, entry, "SKILL.md");
|
|
10027
|
-
if (!(0,
|
|
10405
|
+
if (!(0, import_node_fs26.existsSync)(skillMd)) continue;
|
|
10028
10406
|
try {
|
|
10029
|
-
if (!(0,
|
|
10407
|
+
if (!(0, import_node_fs26.statSync)(skillMd).isFile()) continue;
|
|
10030
10408
|
} catch {
|
|
10031
10409
|
continue;
|
|
10032
10410
|
}
|
|
@@ -10035,12 +10413,12 @@ function scanSkillsDir(dir, source, out) {
|
|
|
10035
10413
|
}
|
|
10036
10414
|
}
|
|
10037
10415
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
10038
|
-
if (!(0,
|
|
10416
|
+
if (!(0, import_node_fs26.existsSync)(pluginsRoot)) return;
|
|
10039
10417
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
10040
10418
|
if (depth > 7) return;
|
|
10041
10419
|
let entries;
|
|
10042
10420
|
try {
|
|
10043
|
-
entries = (0,
|
|
10421
|
+
entries = (0, import_node_fs26.readdirSync)(dir);
|
|
10044
10422
|
} catch {
|
|
10045
10423
|
return;
|
|
10046
10424
|
}
|
|
@@ -10052,7 +10430,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
10052
10430
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
10053
10431
|
const full = (0, import_node_path21.join)(dir, entry);
|
|
10054
10432
|
try {
|
|
10055
|
-
if (!(0,
|
|
10433
|
+
if (!(0, import_node_fs26.statSync)(full).isDirectory()) continue;
|
|
10056
10434
|
} catch {
|
|
10057
10435
|
continue;
|
|
10058
10436
|
}
|
|
@@ -10092,7 +10470,7 @@ function discoverSkills(worktreePath) {
|
|
|
10092
10470
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
10093
10471
|
}
|
|
10094
10472
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
10095
|
-
const raw = (0,
|
|
10473
|
+
const raw = (0, import_node_fs26.readFileSync)(skillPath, "utf8");
|
|
10096
10474
|
if (raw.startsWith("---")) {
|
|
10097
10475
|
const end = raw.indexOf("\n---", 3);
|
|
10098
10476
|
if (end >= 0) {
|
|
@@ -10185,7 +10563,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
10185
10563
|
}
|
|
10186
10564
|
|
|
10187
10565
|
// src/composer/stage-files.ts
|
|
10188
|
-
var
|
|
10566
|
+
var import_node_fs27 = require("fs");
|
|
10189
10567
|
var import_node_path22 = require("path");
|
|
10190
10568
|
var import_node_crypto5 = require("crypto");
|
|
10191
10569
|
init_workspace_scratch();
|
|
@@ -10223,21 +10601,21 @@ function imageMimeType(filePath) {
|
|
|
10223
10601
|
}
|
|
10224
10602
|
function ensureAttachmentsDir(worktreePath) {
|
|
10225
10603
|
const dir = (0, import_node_path22.join)(worktreePath, ATTACHMENTS_DIR);
|
|
10226
|
-
(0,
|
|
10604
|
+
(0, import_node_fs27.mkdirSync)(dir, { recursive: true });
|
|
10227
10605
|
const gi = (0, import_node_path22.join)(dir, ".gitignore");
|
|
10228
|
-
if (!(0,
|
|
10229
|
-
(0,
|
|
10606
|
+
if (!(0, import_node_fs27.existsSync)(gi)) {
|
|
10607
|
+
(0, import_node_fs27.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
10230
10608
|
}
|
|
10231
10609
|
return dir;
|
|
10232
10610
|
}
|
|
10233
10611
|
function uniqueAttachmentName(dir, originalName) {
|
|
10234
10612
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
10235
|
-
if (!(0,
|
|
10613
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, safe))) return safe;
|
|
10236
10614
|
const ext = (0, import_node_path22.extname)(safe);
|
|
10237
10615
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
10238
10616
|
for (let i = 1; i < 1e4; i++) {
|
|
10239
10617
|
const candidate = `${stem}-${i}${ext}`;
|
|
10240
|
-
if (!(0,
|
|
10618
|
+
if (!(0, import_node_fs27.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
|
|
10241
10619
|
}
|
|
10242
10620
|
return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
|
|
10243
10621
|
}
|
|
@@ -10295,13 +10673,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
10295
10673
|
for (const abs of absolutePaths) {
|
|
10296
10674
|
const originalName = (0, import_node_path22.basename)(abs);
|
|
10297
10675
|
try {
|
|
10298
|
-
const st = (0,
|
|
10676
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10299
10677
|
if (!st.isFile()) continue;
|
|
10300
10678
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10301
10679
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10302
|
-
(0,
|
|
10680
|
+
(0, import_node_fs27.copyFileSync)(abs, destAbs);
|
|
10303
10681
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10304
|
-
const buf = (0,
|
|
10682
|
+
const buf = (0, import_node_fs27.readFileSync)(destAbs);
|
|
10305
10683
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10306
10684
|
} catch (err) {
|
|
10307
10685
|
out.push({
|
|
@@ -10324,7 +10702,7 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
10324
10702
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
10325
10703
|
const name = uniqueAttachmentName(dir, originalName);
|
|
10326
10704
|
const destAbs = (0, import_node_path22.join)(dir, name);
|
|
10327
|
-
(0,
|
|
10705
|
+
(0, import_node_fs27.writeFileSync)(destAbs, buf);
|
|
10328
10706
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
10329
10707
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
10330
10708
|
} catch (err) {
|
|
@@ -10353,9 +10731,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10353
10731
|
const name = (0, import_node_path22.basename)(rel);
|
|
10354
10732
|
try {
|
|
10355
10733
|
const abs = (0, import_node_path22.join)(worktreePath, rel);
|
|
10356
|
-
const st = (0,
|
|
10734
|
+
const st = (0, import_node_fs27.statSync)(abs);
|
|
10357
10735
|
if (!st.isFile()) continue;
|
|
10358
|
-
const buf = (0,
|
|
10736
|
+
const buf = (0, import_node_fs27.readFileSync)(abs);
|
|
10359
10737
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
10360
10738
|
} catch (err) {
|
|
10361
10739
|
out.push({
|
|
@@ -10370,7 +10748,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
10370
10748
|
}
|
|
10371
10749
|
|
|
10372
10750
|
// src/agents/instructions.ts
|
|
10373
|
-
var
|
|
10751
|
+
var import_node_fs28 = require("fs");
|
|
10374
10752
|
var import_node_path23 = require("path");
|
|
10375
10753
|
init_worktree_labels();
|
|
10376
10754
|
function normPath2(p) {
|
|
@@ -10470,6 +10848,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
10470
10848
|
);
|
|
10471
10849
|
return lines.join("\n");
|
|
10472
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
|
+
}
|
|
10473
10854
|
function formatArtifactDirective() {
|
|
10474
10855
|
return [
|
|
10475
10856
|
"Sideboard side column (desktop UI):",
|
|
@@ -10490,60 +10871,6 @@ function formatArtifactDirective() {
|
|
|
10490
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."
|
|
10491
10872
|
].join("\n");
|
|
10492
10873
|
}
|
|
10493
|
-
var FILES_BY_AGENT = {
|
|
10494
|
-
claude: [
|
|
10495
|
-
"CLAUDE.md",
|
|
10496
|
-
".claude/CLAUDE.md",
|
|
10497
|
-
"CLAUDE.local.md",
|
|
10498
|
-
"AGENTS.md",
|
|
10499
|
-
"AGENT.md"
|
|
10500
|
-
],
|
|
10501
|
-
codex: ["AGENTS.md", "AGENT.md", ".codex/AGENTS.md", "CLAUDE.md"],
|
|
10502
|
-
opencode: ["AGENTS.md", "AGENT.md", "OPENCODE.md", "CLAUDE.md"],
|
|
10503
|
-
brightsy: ["AGENTS.md", "AGENT.md", "CLAUDE.md"],
|
|
10504
|
-
cursor: ["AGENTS.md", "AGENT.md", ".cursor/rules", "CLAUDE.md"]
|
|
10505
|
-
};
|
|
10506
|
-
var MAX_CHARS_PER_FILE = 48e3;
|
|
10507
|
-
function loadAgentInstructions(worktreePath, agent) {
|
|
10508
|
-
const candidates = FILES_BY_AGENT[agent] ?? FILES_BY_AGENT.claude;
|
|
10509
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10510
|
-
const out = [];
|
|
10511
|
-
for (const rel of candidates) {
|
|
10512
|
-
if (seen.has(rel)) continue;
|
|
10513
|
-
const abs = (0, import_node_path23.join)(worktreePath, rel);
|
|
10514
|
-
if (!(0, import_node_fs25.existsSync)(abs)) continue;
|
|
10515
|
-
try {
|
|
10516
|
-
if (!(0, import_node_fs25.statSync)(abs).isFile()) continue;
|
|
10517
|
-
let content = (0, import_node_fs25.readFileSync)(abs, "utf8");
|
|
10518
|
-
if (!content.trim()) continue;
|
|
10519
|
-
if (content.length > MAX_CHARS_PER_FILE) {
|
|
10520
|
-
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
10521
|
-
|
|
10522
|
-
\u2026(truncated)`;
|
|
10523
|
-
}
|
|
10524
|
-
seen.add(rel);
|
|
10525
|
-
out.push({ relativePath: rel, content });
|
|
10526
|
-
} catch {
|
|
10527
|
-
}
|
|
10528
|
-
}
|
|
10529
|
-
return out;
|
|
10530
|
-
}
|
|
10531
|
-
function formatAgentInstructions(files) {
|
|
10532
|
-
if (files.length === 0) return null;
|
|
10533
|
-
const parts = [
|
|
10534
|
-
"Project agent instructions (from the worktree \u2014 follow these):",
|
|
10535
|
-
""
|
|
10536
|
-
];
|
|
10537
|
-
for (const f of files) {
|
|
10538
|
-
parts.push(`## ${f.relativePath}`);
|
|
10539
|
-
parts.push("");
|
|
10540
|
-
parts.push(f.content.trim());
|
|
10541
|
-
parts.push("");
|
|
10542
|
-
parts.push("---");
|
|
10543
|
-
parts.push("");
|
|
10544
|
-
}
|
|
10545
|
-
return parts.join("\n").trimEnd();
|
|
10546
|
-
}
|
|
10547
10874
|
|
|
10548
10875
|
// src/orchestrator/orchestrator.ts
|
|
10549
10876
|
init_types();
|
|
@@ -10668,7 +10995,7 @@ var Orchestrator = class {
|
|
|
10668
10995
|
}
|
|
10669
10996
|
continue;
|
|
10670
10997
|
}
|
|
10671
|
-
if (!(0,
|
|
10998
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
10672
10999
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10673
11000
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10674
11001
|
continue;
|
|
@@ -11085,13 +11412,14 @@ var Orchestrator = class {
|
|
|
11085
11412
|
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
11086
11413
|
"Do not say artifacts/CMS UI are unavailable."
|
|
11087
11414
|
].join(" ") : null;
|
|
11415
|
+
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
11088
11416
|
const agentPrompt = [
|
|
11089
11417
|
thread.planMode ? PLAN_MODE_INSTRUCTION : null,
|
|
11090
11418
|
orchestrationReminder,
|
|
11419
|
+
worktreeReminder,
|
|
11091
11420
|
artifactReminder,
|
|
11092
11421
|
expandedPrompt
|
|
11093
11422
|
].filter(Boolean).join("\n\n");
|
|
11094
|
-
const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
|
|
11095
11423
|
if (thread.attachments.length > 0) {
|
|
11096
11424
|
updateThread(threadId, { attachments: [] });
|
|
11097
11425
|
}
|
|
@@ -11119,7 +11447,6 @@ var Orchestrator = class {
|
|
|
11119
11447
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
11120
11448
|
customPrompt: settings?.prompts?.renameBranch
|
|
11121
11449
|
}) : null;
|
|
11122
|
-
const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
|
|
11123
11450
|
let seed = null;
|
|
11124
11451
|
if (!fresh.sessionId) {
|
|
11125
11452
|
const prior = fresh.messages.slice(0, -1);
|
|
@@ -11140,12 +11467,12 @@ var Orchestrator = class {
|
|
|
11140
11467
|
});
|
|
11141
11468
|
}
|
|
11142
11469
|
}
|
|
11143
|
-
const cachedPrefix = [
|
|
11470
|
+
const cachedPrefix = fresh.sessionId ? "" : [
|
|
11144
11471
|
coordinatorDirective,
|
|
11145
11472
|
worktreeDirective,
|
|
11146
11473
|
artifactDirective,
|
|
11147
11474
|
renameBranchDirective,
|
|
11148
|
-
|
|
11475
|
+
seed
|
|
11149
11476
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11150
11477
|
try {
|
|
11151
11478
|
const stderrTail = [];
|
|
@@ -11232,15 +11559,11 @@ var Orchestrator = class {
|
|
|
11232
11559
|
const retryThread = this.requireThread(threadId);
|
|
11233
11560
|
const prior = retryThread.messages.slice(0, -1);
|
|
11234
11561
|
const retrySeed = buildSessionSeed(prior);
|
|
11235
|
-
const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
|
|
11236
|
-
loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
|
|
11237
|
-
);
|
|
11238
11562
|
const retryPrefix = [
|
|
11239
11563
|
coordinatorDirective,
|
|
11240
11564
|
worktreeDirective,
|
|
11241
11565
|
artifactDirective,
|
|
11242
11566
|
renameBranchDirective,
|
|
11243
|
-
retryInstructions,
|
|
11244
11567
|
retrySeed
|
|
11245
11568
|
].filter(Boolean).join("\n\n---\n\n");
|
|
11246
11569
|
const retryHandle = await spawnAgentTurn(
|
|
@@ -11751,10 +12074,19 @@ var Orchestrator = class {
|
|
|
11751
12074
|
this.assertNotGlobal(thread, "Merge PR");
|
|
11752
12075
|
if (!selector) throw new Error("No pull request linked to this thread");
|
|
11753
12076
|
const result = await mergePr(cwd, selector);
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
12077
|
+
const state = normalizePrState(result.state) || "MERGED";
|
|
12078
|
+
const metaLike = {
|
|
12079
|
+
number: 0,
|
|
12080
|
+
title: thread.prTitle ?? thread.title,
|
|
12081
|
+
url: result.url || thread.prUrl || "",
|
|
12082
|
+
state,
|
|
12083
|
+
isDraft: false,
|
|
12084
|
+
reviewDecision: null,
|
|
12085
|
+
baseRefName: "",
|
|
12086
|
+
headRefName: ""
|
|
12087
|
+
};
|
|
12088
|
+
await this.persistPrMetaAndMaybeArchive(thread, metaLike);
|
|
12089
|
+
return { url: metaLike.url, state };
|
|
11758
12090
|
}
|
|
11759
12091
|
/** Resolve PR selector and optionally persist `prUrl` when found. */
|
|
11760
12092
|
async withPrSelector(threadRef) {
|
|
@@ -11776,19 +12108,55 @@ var Orchestrator = class {
|
|
|
11776
12108
|
if (!selector) return null;
|
|
11777
12109
|
const meta = await getPrMeta(cwd, selector);
|
|
11778
12110
|
if (meta) {
|
|
11779
|
-
|
|
11780
|
-
if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
|
|
11781
|
-
if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
|
|
11782
|
-
if (Object.keys(patch).length > 0) {
|
|
11783
|
-
updateThread(thread.id, patch);
|
|
11784
|
-
const latest = this.requireThread(thread.id);
|
|
11785
|
-
if (!latest.userSetTitle && meta.title && latest.title !== meta.title) {
|
|
11786
|
-
updateThread(thread.id, { title: meta.title });
|
|
11787
|
-
}
|
|
11788
|
-
}
|
|
12111
|
+
await this.persistPrMetaAndMaybeArchive(thread, meta);
|
|
11789
12112
|
}
|
|
11790
12113
|
return meta;
|
|
11791
12114
|
}
|
|
12115
|
+
/**
|
|
12116
|
+
* Persist PR URL/title/state and Conductor-style auto-archive when the PR
|
|
12117
|
+
* first becomes MERGED.
|
|
12118
|
+
*/
|
|
12119
|
+
async persistPrMetaAndMaybeArchive(thread, meta) {
|
|
12120
|
+
const prevState = normalizePrState(thread.prState);
|
|
12121
|
+
const nextState = normalizePrState(meta.state);
|
|
12122
|
+
const patch = {};
|
|
12123
|
+
if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
|
|
12124
|
+
if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
|
|
12125
|
+
if (nextState && nextState !== prevState) patch.prState = nextState;
|
|
12126
|
+
if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
|
|
12127
|
+
patch.skipAutoArchiveOnMerge = false;
|
|
12128
|
+
}
|
|
12129
|
+
if (Object.keys(patch).length > 0) {
|
|
12130
|
+
updateThread(thread.id, patch);
|
|
12131
|
+
const latest2 = this.requireThread(thread.id);
|
|
12132
|
+
if (!latest2.userSetTitle && meta.title && latest2.title !== meta.title) {
|
|
12133
|
+
updateThread(thread.id, { title: meta.title });
|
|
12134
|
+
}
|
|
12135
|
+
}
|
|
12136
|
+
const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
12137
|
+
const latest = this.requireThread(thread.id);
|
|
12138
|
+
if (!shouldAutoArchiveOnPrMerge({
|
|
12139
|
+
previousPrState: prevState || null,
|
|
12140
|
+
nextPrState: nextState,
|
|
12141
|
+
threadStatus: latest.status,
|
|
12142
|
+
skipAutoArchiveOnMerge: latest.skipAutoArchiveOnMerge,
|
|
12143
|
+
autoArchiveEnabled: autoArchiveOnMergeEnabled2(),
|
|
12144
|
+
isGlobal: isGlobalThread(latest)
|
|
12145
|
+
})) {
|
|
12146
|
+
return;
|
|
12147
|
+
}
|
|
12148
|
+
const siblings = threadsSharingWorktree(latest.worktreePath);
|
|
12149
|
+
for (const t of siblings) {
|
|
12150
|
+
const sibPatch = { prState: "MERGED" };
|
|
12151
|
+
if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
|
|
12152
|
+
if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
|
|
12153
|
+
if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
|
|
12154
|
+
}
|
|
12155
|
+
for (const t of siblings) {
|
|
12156
|
+
if (this.requireThread(t.id).status === "archived") continue;
|
|
12157
|
+
await this.archive(t.id);
|
|
12158
|
+
}
|
|
12159
|
+
}
|
|
11792
12160
|
async getPrStack(threadRef) {
|
|
11793
12161
|
const thread = this.requireThread(threadRef);
|
|
11794
12162
|
if (!thread.worktreePath?.trim()) return null;
|
|
@@ -11966,7 +12334,9 @@ var Orchestrator = class {
|
|
|
11966
12334
|
const thread = this.requireThread(threadRef);
|
|
11967
12335
|
this.stop(thread.id);
|
|
11968
12336
|
if (isGlobalThread(thread)) {
|
|
11969
|
-
|
|
12337
|
+
const archived2 = setStatus(thread.id, "archived");
|
|
12338
|
+
this.emit({ type: "status_changed", threadId: archived2.id, status: "archived" });
|
|
12339
|
+
return archived2;
|
|
11970
12340
|
}
|
|
11971
12341
|
const siblings = threadsSharingWorktree(thread.worktreePath).filter((t) => t.id !== thread.id);
|
|
11972
12342
|
if (siblings.length === 0) {
|
|
@@ -11984,6 +12354,7 @@ var Orchestrator = class {
|
|
|
11984
12354
|
await removeWorktree(thread.repoPath, thread.worktreePath);
|
|
11985
12355
|
}
|
|
11986
12356
|
const archived = setStatus(thread.id, "archived");
|
|
12357
|
+
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
11987
12358
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
11988
12359
|
try {
|
|
11989
12360
|
const { ensureWorkspace: ensureWorkspace2 } = await Promise.resolve().then(() => (init_workspaces(), workspaces_exports));
|
|
@@ -12023,9 +12394,11 @@ var Orchestrator = class {
|
|
|
12023
12394
|
if (isGlobalThread(thread)) {
|
|
12024
12395
|
const { globalAgentCwd: globalAgentCwd2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
12025
12396
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
12026
|
-
|
|
12397
|
+
const restored2 = setStatus(thread.id, "idle");
|
|
12398
|
+
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
12399
|
+
return restored2;
|
|
12027
12400
|
}
|
|
12028
|
-
if (!(0,
|
|
12401
|
+
if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
|
|
12029
12402
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
12030
12403
|
const { execa: execa7 } = await import("execa");
|
|
12031
12404
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -12036,7 +12409,31 @@ var Orchestrator = class {
|
|
|
12036
12409
|
void createThreadWorktree2;
|
|
12037
12410
|
void slug;
|
|
12038
12411
|
}
|
|
12039
|
-
|
|
12412
|
+
const restorePatch = {};
|
|
12413
|
+
let alreadyMerged = normalizePrState(thread.prState) === "MERGED";
|
|
12414
|
+
if (!alreadyMerged && thread.prUrl?.trim() && thread.worktreePath?.trim()) {
|
|
12415
|
+
try {
|
|
12416
|
+
const selector = resolvePrSelector(thread);
|
|
12417
|
+
if (selector) {
|
|
12418
|
+
const meta = await getPrMeta(thread.worktreePath, selector);
|
|
12419
|
+
if (meta) {
|
|
12420
|
+
const state = normalizePrState(meta.state);
|
|
12421
|
+
if (meta.url && meta.url !== thread.prUrl) restorePatch.prUrl = meta.url;
|
|
12422
|
+
if (meta.title && meta.title !== thread.prTitle) restorePatch.prTitle = meta.title;
|
|
12423
|
+
if (state) restorePatch.prState = state;
|
|
12424
|
+
if (state === "MERGED") alreadyMerged = true;
|
|
12425
|
+
}
|
|
12426
|
+
}
|
|
12427
|
+
} catch {
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12430
|
+
if (alreadyMerged) restorePatch.skipAutoArchiveOnMerge = true;
|
|
12431
|
+
if (Object.keys(restorePatch).length > 0) {
|
|
12432
|
+
updateThread(thread.id, restorePatch);
|
|
12433
|
+
}
|
|
12434
|
+
const restored = setStatus(thread.id, "idle");
|
|
12435
|
+
this.emit({ type: "status_changed", threadId: restored.id, status: restored.status });
|
|
12436
|
+
return restored;
|
|
12040
12437
|
}
|
|
12041
12438
|
async attachCommand(threadRef) {
|
|
12042
12439
|
const thread = this.requireThread(threadRef);
|
|
@@ -12210,6 +12607,7 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
12210
12607
|
}
|
|
12211
12608
|
|
|
12212
12609
|
// src/mcp/server.ts
|
|
12610
|
+
init_profile();
|
|
12213
12611
|
var MAX_ORCH_THREADS = 5;
|
|
12214
12612
|
var CREATE_THREAD_TIMEOUT_MS = 9e4;
|
|
12215
12613
|
function withTimeout(promise, ms, label) {
|
|
@@ -12323,14 +12721,13 @@ async function startMcpServer() {
|
|
|
12323
12721
|
),
|
|
12324
12722
|
artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
12325
12723
|
},
|
|
12326
|
-
async ({ title, type,
|
|
12724
|
+
async ({ title, type, artifact_id }) => {
|
|
12327
12725
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
12328
12726
|
const payload = {
|
|
12329
12727
|
ok: true,
|
|
12330
12728
|
artifact_id: id,
|
|
12331
12729
|
title,
|
|
12332
12730
|
type,
|
|
12333
|
-
content,
|
|
12334
12731
|
message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
12335
12732
|
};
|
|
12336
12733
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12383,7 +12780,6 @@ async function startMcpServer() {
|
|
|
12383
12780
|
ok: true,
|
|
12384
12781
|
path,
|
|
12385
12782
|
title: title?.trim() || "Plan",
|
|
12386
|
-
content,
|
|
12387
12783
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
12388
12784
|
};
|
|
12389
12785
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12413,9 +12809,6 @@ async function startMcpServer() {
|
|
|
12413
12809
|
datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
|
|
12414
12810
|
resource_id: args.resource_id,
|
|
12415
12811
|
record_id: args.record_id,
|
|
12416
|
-
resource: args.resource,
|
|
12417
|
-
record: args.record,
|
|
12418
|
-
records: args.records,
|
|
12419
12812
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
12420
12813
|
};
|
|
12421
12814
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -12443,739 +12836,741 @@ async function startMcpServer() {
|
|
|
12443
12836
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
12444
12837
|
}
|
|
12445
12838
|
);
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
if (
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
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
|
+
}
|
|
12477
12872
|
}
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
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;
|
|
12487
12921
|
return {
|
|
12488
12922
|
content: [
|
|
12489
12923
|
{
|
|
12490
12924
|
type: "text",
|
|
12491
|
-
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);
|
|
12946
|
+
return {
|
|
12947
|
+
content: [
|
|
12948
|
+
{
|
|
12949
|
+
type: "text",
|
|
12950
|
+
text: `create_thread failed: ${message}`
|
|
12492
12951
|
}
|
|
12493
12952
|
],
|
|
12494
12953
|
isError: true
|
|
12495
12954
|
};
|
|
12496
12955
|
}
|
|
12497
12956
|
}
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
agent: opts.agent,
|
|
12516
|
-
model: opts.model,
|
|
12517
|
-
effort: opts.effort,
|
|
12518
|
-
fast: opts.fast,
|
|
12519
|
-
repoPath: args.repoPath,
|
|
12520
|
-
title: args.title,
|
|
12521
|
-
parentThreadId: parentId || null
|
|
12522
|
-
}),
|
|
12523
|
-
CREATE_THREAD_TIMEOUT_MS,
|
|
12524
|
-
"create_thread"
|
|
12525
|
-
);
|
|
12526
|
-
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);
|
|
12527
12974
|
return {
|
|
12528
12975
|
content: [
|
|
12529
12976
|
{
|
|
12530
12977
|
type: "text",
|
|
12531
12978
|
text: JSON.stringify({
|
|
12532
12979
|
id: thread.id,
|
|
12533
|
-
title: thread.title,
|
|
12534
|
-
branchName: thread.branchName,
|
|
12535
|
-
worktreePath: thread.worktreePath,
|
|
12536
|
-
agent: thread.agent,
|
|
12537
|
-
model: thread.model,
|
|
12538
12980
|
status: thread.status,
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
...agentCoercedFrom ? {
|
|
12542
|
-
agentCoercedFrom,
|
|
12543
|
-
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
12544
|
-
} : {},
|
|
12545
|
-
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
12981
|
+
queueLength: thread.queue.length,
|
|
12982
|
+
forceStopped: Boolean(force_stop)
|
|
12546
12983
|
})
|
|
12547
12984
|
}
|
|
12548
12985
|
]
|
|
12549
12986
|
};
|
|
12550
|
-
}
|
|
12551
|
-
|
|
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);
|
|
12552
12999
|
return {
|
|
12553
13000
|
content: [
|
|
12554
13001
|
{
|
|
12555
13002
|
type: "text",
|
|
12556
|
-
text:
|
|
13003
|
+
text: JSON.stringify({
|
|
13004
|
+
id: thread.id,
|
|
13005
|
+
status: result.status,
|
|
13006
|
+
text: result.text
|
|
13007
|
+
})
|
|
12557
13008
|
}
|
|
12558
|
-
]
|
|
12559
|
-
isError: true
|
|
13009
|
+
]
|
|
12560
13010
|
};
|
|
12561
13011
|
}
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
force_stop: import_zod.z.boolean().optional()
|
|
12571
|
-
},
|
|
12572
|
-
async ({ ref, prompt, force_stop }) => {
|
|
12573
|
-
if (force_stop) {
|
|
12574
|
-
const existing = orch.getThread(ref);
|
|
12575
|
-
if (existing) {
|
|
12576
|
-
orch.stop(ref, { clearQueue: true });
|
|
12577
|
-
}
|
|
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) }] };
|
|
12578
13020
|
}
|
|
12579
|
-
|
|
12580
|
-
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
{
|
|
12599
|
-
ref: import_zod.z.string(),
|
|
12600
|
-
timeoutMs: import_zod.z.number().optional()
|
|
12601
|
-
},
|
|
12602
|
-
async ({ ref, timeoutMs }) => {
|
|
12603
|
-
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
12604
|
-
const result = orch.getTurnResult(thread.id);
|
|
12605
|
-
return {
|
|
12606
|
-
content: [
|
|
12607
|
-
{
|
|
12608
|
-
type: "text",
|
|
12609
|
-
text: JSON.stringify({
|
|
12610
|
-
id: thread.id,
|
|
12611
|
-
status: result.status,
|
|
12612
|
-
text: result.text
|
|
12613
|
-
})
|
|
12614
|
-
}
|
|
12615
|
-
]
|
|
12616
|
-
};
|
|
12617
|
-
}
|
|
12618
|
-
);
|
|
12619
|
-
server.tool(
|
|
12620
|
-
"get_turn_result",
|
|
12621
|
-
"Final assistant message only (not full transcript)",
|
|
12622
|
-
{ ref: import_zod.z.string() },
|
|
12623
|
-
async ({ ref }) => {
|
|
12624
|
-
const result = orch.getTurnResult(ref);
|
|
12625
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
12626
|
-
}
|
|
12627
|
-
);
|
|
12628
|
-
server.tool(
|
|
12629
|
-
"stop_thread",
|
|
12630
|
-
"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.",
|
|
12631
|
-
{
|
|
12632
|
-
ref: import_zod.z.string(),
|
|
12633
|
-
force: import_zod.z.boolean().optional()
|
|
12634
|
-
},
|
|
12635
|
-
async ({ ref, force }) => {
|
|
12636
|
-
const t = orch.getThread(ref);
|
|
12637
|
-
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 });
|
|
12638
13040
|
return {
|
|
12639
|
-
content: [
|
|
12640
|
-
|
|
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
|
+
]
|
|
12641
13051
|
};
|
|
12642
13052
|
}
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
async ({ ref }) => {
|
|
12665
|
-
const t = orch.getThread(ref);
|
|
12666
|
-
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);
|
|
12667
13074
|
return {
|
|
12668
|
-
content: [
|
|
12669
|
-
|
|
13075
|
+
content: [
|
|
13076
|
+
{
|
|
13077
|
+
type: "text",
|
|
13078
|
+
text: JSON.stringify({
|
|
13079
|
+
id: archived.id,
|
|
13080
|
+
status: archived.status
|
|
13081
|
+
})
|
|
13082
|
+
}
|
|
13083
|
+
]
|
|
12670
13084
|
};
|
|
12671
13085
|
}
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
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
|
+
}
|
|
12678
13110
|
}
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
]
|
|
12690
|
-
};
|
|
12691
|
-
}
|
|
12692
|
-
);
|
|
12693
|
-
server.tool(
|
|
12694
|
-
"restore_thread",
|
|
12695
|
-
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
12696
|
-
{ ref: import_zod.z.string() },
|
|
12697
|
-
async ({ ref }) => {
|
|
12698
|
-
try {
|
|
12699
|
-
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);
|
|
12700
13121
|
return {
|
|
12701
13122
|
content: [
|
|
12702
13123
|
{
|
|
12703
13124
|
type: "text",
|
|
12704
13125
|
text: JSON.stringify({
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
worktreePath: restored.worktreePath
|
|
13126
|
+
...summary,
|
|
13127
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
12708
13128
|
})
|
|
12709
13129
|
}
|
|
12710
13130
|
]
|
|
12711
13131
|
};
|
|
12712
|
-
} catch (err) {
|
|
12713
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12714
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12715
13132
|
}
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
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
|
+
};
|
|
12735
13244
|
}
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
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);
|
|
12748
13285
|
return {
|
|
12749
13286
|
content: [
|
|
12750
13287
|
{
|
|
12751
13288
|
type: "text",
|
|
12752
13289
|
text: JSON.stringify({
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
link: `sideboard://thread/${tab.id}`
|
|
13290
|
+
port: result.port,
|
|
13291
|
+
scriptName: result.scriptName,
|
|
13292
|
+
ports: result.ports,
|
|
13293
|
+
url: `http://localhost:${result.port}`
|
|
12758
13294
|
})
|
|
12759
13295
|
}
|
|
12760
13296
|
]
|
|
12761
13297
|
};
|
|
12762
|
-
} catch (err) {
|
|
12763
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12764
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12765
13298
|
}
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
},
|
|
12775
|
-
async ({ agent }) => {
|
|
12776
|
-
try {
|
|
12777
|
-
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);
|
|
12778
13307
|
return {
|
|
12779
|
-
content: [
|
|
13308
|
+
content: [
|
|
13309
|
+
{
|
|
13310
|
+
type: "text",
|
|
13311
|
+
text: JSON.stringify({ scripts, active }, null, 2)
|
|
13312
|
+
}
|
|
13313
|
+
]
|
|
12780
13314
|
};
|
|
12781
|
-
} catch (err) {
|
|
12782
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12783
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12784
13315
|
}
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
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
|
|
12807
13401
|
});
|
|
12808
13402
|
return {
|
|
12809
13403
|
content: [
|
|
12810
13404
|
{
|
|
12811
13405
|
type: "text",
|
|
12812
|
-
text:
|
|
12813
|
-
id: thread.id,
|
|
12814
|
-
title: thread.title,
|
|
12815
|
-
status: thread.status,
|
|
12816
|
-
agent: thread.agent,
|
|
12817
|
-
model: thread.model,
|
|
12818
|
-
branchName: thread.branchName,
|
|
12819
|
-
worktreePath: thread.worktreePath,
|
|
12820
|
-
fromThreadId: source?.id ?? ref,
|
|
12821
|
-
link: `sideboard://thread/${thread.id}`
|
|
12822
|
-
})
|
|
13406
|
+
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
12823
13407
|
}
|
|
12824
13408
|
]
|
|
12825
13409
|
};
|
|
12826
|
-
} catch (err) {
|
|
12827
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12828
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12829
13410
|
}
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
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
|
|
12857
13525
|
});
|
|
12858
13526
|
return {
|
|
12859
13527
|
content: [
|
|
12860
13528
|
{
|
|
12861
13529
|
type: "text",
|
|
12862
|
-
text: JSON.stringify(
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
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
|
+
)
|
|
12873
13546
|
}
|
|
12874
13547
|
]
|
|
12875
13548
|
};
|
|
12876
|
-
} catch (err) {
|
|
12877
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12878
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
12879
13549
|
}
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
}
|
|
12905
|
-
);
|
|
12906
|
-
server.tool(
|
|
12907
|
-
"list_run_scripts",
|
|
12908
|
-
"List named run scripts available for a thread",
|
|
12909
|
-
{ ref: import_zod.z.string() },
|
|
12910
|
-
async ({ ref }) => {
|
|
12911
|
-
const scripts = orch.listThreadRunScripts(ref);
|
|
12912
|
-
const active = orch.getActiveRuns(ref);
|
|
12913
|
-
return {
|
|
12914
|
-
content: [
|
|
12915
|
-
{
|
|
12916
|
-
type: "text",
|
|
12917
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
12918
|
-
}
|
|
12919
|
-
]
|
|
12920
|
-
};
|
|
12921
|
-
}
|
|
12922
|
-
);
|
|
12923
|
-
server.tool(
|
|
12924
|
-
"stop_dev_script",
|
|
12925
|
-
"Stop a running script for a thread (all scripts if name omitted)",
|
|
12926
|
-
{
|
|
12927
|
-
ref: import_zod.z.string(),
|
|
12928
|
-
name: import_zod.z.string().optional()
|
|
12929
|
-
},
|
|
12930
|
-
async ({ ref, name }) => {
|
|
12931
|
-
orch.stopDev(ref, name);
|
|
12932
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
12933
|
-
}
|
|
12934
|
-
);
|
|
12935
|
-
server.tool(
|
|
12936
|
-
"run_setup",
|
|
12937
|
-
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
12938
|
-
{ ref: import_zod.z.string() },
|
|
12939
|
-
async ({ ref }) => {
|
|
12940
|
-
const result = await orch.runSetup(ref);
|
|
12941
|
-
return {
|
|
12942
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
12943
|
-
};
|
|
12944
|
-
}
|
|
12945
|
-
);
|
|
12946
|
-
server.tool(
|
|
12947
|
-
"add_workspace",
|
|
12948
|
-
"Register a git repo as a Sideboard workspace",
|
|
12949
|
-
{ repoPath: import_zod.z.string() },
|
|
12950
|
-
async ({ repoPath }) => {
|
|
12951
|
-
const ws = await orch.addWorkspace(repoPath);
|
|
12952
|
-
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
12953
|
-
}
|
|
12954
|
-
);
|
|
12955
|
-
server.tool(
|
|
12956
|
-
"remove_workspace",
|
|
12957
|
-
"Unregister a Sideboard workspace (does not archive threads)",
|
|
12958
|
-
{ repoPath: import_zod.z.string() },
|
|
12959
|
-
async ({ repoPath }) => {
|
|
12960
|
-
orch.removeWorkspace(repoPath);
|
|
12961
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
12962
|
-
}
|
|
12963
|
-
);
|
|
12964
|
-
server.tool(
|
|
12965
|
-
"fanout",
|
|
12966
|
-
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
12967
|
-
{
|
|
12968
|
-
prompt: import_zod.z.string(),
|
|
12969
|
-
agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
12970
|
-
repoPath: import_zod.z.string(),
|
|
12971
|
-
sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
12972
|
-
sourceRef: import_zod.z.string().optional(),
|
|
12973
|
-
title: import_zod.z.string().optional()
|
|
12974
|
-
},
|
|
12975
|
-
async (args) => {
|
|
12976
|
-
const threads = await orch.bestOfN(args);
|
|
12977
|
-
return {
|
|
12978
|
-
content: [
|
|
12979
|
-
{
|
|
12980
|
-
type: "text",
|
|
12981
|
-
text: JSON.stringify(
|
|
12982
|
-
threads.map((t) => ({
|
|
12983
|
-
id: t.id,
|
|
12984
|
-
agent: t.agent,
|
|
12985
|
-
branchName: t.branchName,
|
|
12986
|
-
worktreePath: t.worktreePath
|
|
12987
|
-
})),
|
|
12988
|
-
null,
|
|
12989
|
-
2
|
|
12990
|
-
)
|
|
12991
|
-
}
|
|
12992
|
-
]
|
|
12993
|
-
};
|
|
12994
|
-
}
|
|
12995
|
-
);
|
|
12996
|
-
server.tool(
|
|
12997
|
-
"list_branches",
|
|
12998
|
-
"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).",
|
|
12999
|
-
{
|
|
13000
|
-
repoPath: import_zod.z.string(),
|
|
13001
|
-
unmergedOnly: import_zod.z.boolean().optional()
|
|
13002
|
-
},
|
|
13003
|
-
async ({ repoPath, unmergedOnly }) => {
|
|
13004
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13005
|
-
const branches = await listBranches(root, {
|
|
13006
|
-
unmergedOnly: unmergedOnly !== false
|
|
13007
|
-
});
|
|
13008
|
-
return {
|
|
13009
|
-
content: [
|
|
13010
|
-
{
|
|
13011
|
-
type: "text",
|
|
13012
|
-
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
13013
|
-
}
|
|
13014
|
-
]
|
|
13015
|
-
};
|
|
13016
|
-
}
|
|
13017
|
-
);
|
|
13018
|
-
server.tool(
|
|
13019
|
-
"list_prs",
|
|
13020
|
-
"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.",
|
|
13021
|
-
{ repoPath: import_zod.z.string() },
|
|
13022
|
-
async ({ repoPath }) => {
|
|
13023
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13024
|
-
const prs = await listPrs(root);
|
|
13025
|
-
return {
|
|
13026
|
-
content: [
|
|
13027
|
-
{
|
|
13028
|
-
type: "text",
|
|
13029
|
-
text: prs.map(
|
|
13030
|
-
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
13031
|
-
).join("\n")
|
|
13032
|
-
}
|
|
13033
|
-
]
|
|
13034
|
-
};
|
|
13035
|
-
}
|
|
13036
|
-
);
|
|
13037
|
-
server.tool(
|
|
13038
|
-
"get_pr_stack",
|
|
13039
|
-
"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.",
|
|
13040
|
-
{ ref: import_zod.z.string() },
|
|
13041
|
-
async ({ ref }) => {
|
|
13042
|
-
const stack = await orch.getPrStack(ref);
|
|
13043
|
-
return {
|
|
13044
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13045
|
-
};
|
|
13046
|
-
}
|
|
13047
|
-
);
|
|
13048
|
-
server.tool(
|
|
13049
|
-
"open_pr_stack_layers",
|
|
13050
|
-
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13051
|
-
{
|
|
13052
|
-
ref: import_zod.z.string(),
|
|
13053
|
-
layer: import_zod.z.number().int().positive().optional()
|
|
13054
|
-
},
|
|
13055
|
-
async ({ ref, layer }) => {
|
|
13056
|
-
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13057
|
-
return {
|
|
13058
|
-
content: [
|
|
13059
|
-
{
|
|
13060
|
-
type: "text",
|
|
13061
|
-
text: JSON.stringify(
|
|
13062
|
-
{
|
|
13063
|
-
stackNumber: result.stack.stackNumber,
|
|
13064
|
-
trunk: result.stack.trunk,
|
|
13065
|
-
threads: result.threads.map((t) => ({
|
|
13066
|
-
id: t.id,
|
|
13067
|
-
title: t.title,
|
|
13068
|
-
branchName: t.branchName,
|
|
13069
|
-
stackLayer: t.stackLayer,
|
|
13070
|
-
worktreePath: t.worktreePath,
|
|
13071
|
-
prUrl: t.prUrl,
|
|
13072
|
-
link: `sideboard://thread/${t.id}`
|
|
13073
|
-
}))
|
|
13074
|
-
},
|
|
13075
|
-
null,
|
|
13076
|
-
2
|
|
13077
|
-
)
|
|
13078
|
-
}
|
|
13079
|
-
]
|
|
13080
|
-
};
|
|
13081
|
-
}
|
|
13082
|
-
);
|
|
13083
|
-
server.tool(
|
|
13084
|
-
"add_stack_layer",
|
|
13085
|
-
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13086
|
-
{
|
|
13087
|
-
ref: import_zod.z.string(),
|
|
13088
|
-
branchName: import_zod.z.string(),
|
|
13089
|
-
title: import_zod.z.string().optional()
|
|
13090
|
-
},
|
|
13091
|
-
async ({ ref, branchName, title }) => {
|
|
13092
|
-
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13093
|
-
return {
|
|
13094
|
-
content: [
|
|
13095
|
-
{
|
|
13096
|
-
type: "text",
|
|
13097
|
-
text: JSON.stringify(
|
|
13098
|
-
{
|
|
13099
|
-
id: result.thread.id,
|
|
13100
|
-
title: result.thread.title,
|
|
13101
|
-
branchName: result.thread.branchName,
|
|
13102
|
-
stackLayer: result.thread.stackLayer,
|
|
13103
|
-
worktreePath: result.thread.worktreePath,
|
|
13104
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
13105
|
-
},
|
|
13106
|
-
null,
|
|
13107
|
-
2
|
|
13108
|
-
)
|
|
13109
|
-
}
|
|
13110
|
-
]
|
|
13111
|
-
};
|
|
13112
|
-
}
|
|
13113
|
-
);
|
|
13114
|
-
server.tool(
|
|
13115
|
-
"create_pr_stack",
|
|
13116
|
-
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13117
|
-
{
|
|
13118
|
-
repoPath: import_zod.z.string(),
|
|
13119
|
-
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13120
|
-
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13121
|
-
base: import_zod.z.string().optional(),
|
|
13122
|
-
title: import_zod.z.string().optional()
|
|
13123
|
-
},
|
|
13124
|
-
async (args) => {
|
|
13125
|
-
const result = await orch.createPrStack({
|
|
13126
|
-
repoPath: args.repoPath,
|
|
13127
|
-
branches: args.branches,
|
|
13128
|
-
agent: args.agent,
|
|
13129
|
-
base: args.base,
|
|
13130
|
-
title: args.title
|
|
13131
|
-
});
|
|
13132
|
-
return {
|
|
13133
|
-
content: [
|
|
13134
|
-
{
|
|
13135
|
-
type: "text",
|
|
13136
|
-
text: JSON.stringify(
|
|
13137
|
-
{
|
|
13138
|
-
stackNumber: result.stack.stackNumber,
|
|
13139
|
-
trunk: result.stack.trunk,
|
|
13140
|
-
threads: result.threads.map((t) => ({
|
|
13141
|
-
id: t.id,
|
|
13142
|
-
title: t.title,
|
|
13143
|
-
branchName: t.branchName,
|
|
13144
|
-
stackLayer: t.stackLayer,
|
|
13145
|
-
worktreePath: t.worktreePath,
|
|
13146
|
-
link: `sideboard://thread/${t.id}`
|
|
13147
|
-
}))
|
|
13148
|
-
},
|
|
13149
|
-
null,
|
|
13150
|
-
2
|
|
13151
|
-
)
|
|
13152
|
-
}
|
|
13153
|
-
]
|
|
13154
|
-
};
|
|
13155
|
-
}
|
|
13156
|
-
);
|
|
13157
|
-
server.tool(
|
|
13158
|
-
"list_issues",
|
|
13159
|
-
"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.",
|
|
13160
|
-
{ repoPath: import_zod.z.string() },
|
|
13161
|
-
async ({ repoPath }) => {
|
|
13162
|
-
const root = await resolveRepoRoot(repoPath);
|
|
13163
|
-
const result = await listIssues(root);
|
|
13164
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13165
|
-
}
|
|
13166
|
-
);
|
|
13167
|
-
server.tool(
|
|
13168
|
-
"list_linear_issues",
|
|
13169
|
-
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
13170
|
-
{
|
|
13171
|
-
agent: import_zod.z.enum(["claude", "codex", "opencode"]),
|
|
13172
|
-
repoPath: import_zod.z.string()
|
|
13173
|
-
},
|
|
13174
|
-
async ({ agent, repoPath }) => {
|
|
13175
|
-
const issues = await listLinearIssues(agent, repoPath);
|
|
13176
|
-
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
13177
|
-
}
|
|
13178
|
-
);
|
|
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
|
+
}
|
|
13179
13574
|
const transport = new import_stdio.StdioServerTransport();
|
|
13180
13575
|
await server.connect(transport);
|
|
13181
13576
|
}
|