@sideboard-ai/core 0.1.50 → 0.1.52
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-LUB3Q773.js → agents-ON6RKKND.js} +3 -3
- package/dist/{agents-3P4N6KHC.js → agents-YKSS6VBO.js} +3 -3
- package/dist/{chunk-RQI4TOXK.js → chunk-6QTZVJ7A.js} +7 -5
- package/dist/{chunk-AJ6ROGD7.js → chunk-7LNGIP3X.js} +20 -2
- package/dist/chunk-B3SJXYIJ.js +24 -0
- package/dist/{chunk-ILQK4P5R.js → chunk-BXJ76RHF.js} +4 -4
- package/dist/{chunk-SS34TVSY.js → chunk-D3METLRW.js} +7 -5
- package/dist/{chunk-FV6FN6V5.js → chunk-DZFH2KLT.js} +340 -22
- package/dist/chunk-FKOIHGKV.js +21 -0
- package/dist/{chunk-3NAS4MWH.js → chunk-GNML24AW.js} +2 -2
- package/dist/{chunk-E35APBHV.js → chunk-H6GGDLYS.js} +1 -1
- package/dist/{chunk-EOYDCKQC.js → chunk-HLEX5AQ6.js} +4 -0
- package/dist/{chunk-ZQCQIWIP.js → chunk-LRLKJM3O.js} +2 -1
- package/dist/chunk-N5PM7HGQ.js +103 -0
- package/dist/chunk-QTUESPAW.js +101 -0
- package/dist/{chunk-O6W3P7V3.js → chunk-TSRXOSVD.js} +4 -0
- package/dist/{chunk-GZXBYHJC.js → chunk-UEAHMGHW.js} +2 -1
- package/dist/{chunk-ZVV5EEQN.js → chunk-XOU6HNQJ.js} +305 -22
- package/dist/{chunk-5WYEJ3F3.js → chunk-XX5BB7NV.js} +3 -3
- package/dist/{chunk-V4JRXYYU.js → chunk-YDXQ72MD.js} +2 -2
- package/dist/{chunk-DF5VQQKA.js → chunk-YOWIYAVA.js} +3 -3
- package/dist/{chunk-ISY4EGF6.js → chunk-ZNM4MAN4.js} +20 -2
- package/dist/{connected-teams-UBXHZGO4.js → connected-teams-2ANNZCP5.js} +2 -2
- package/dist/{connected-teams-GF52Q7LB.js → connected-teams-ONC4666A.js} +2 -2
- package/dist/{coordinator-prompt-VG4BZ5JL.js → coordinator-prompt-6FXVTSFN.js} +5 -4
- package/dist/{coordinator-prompt-SPGDT7J5.js → coordinator-prompt-S6JZD5EF.js} +5 -4
- package/dist/{global-workspace-OJF4ENH4.js → global-workspace-EV4G2WMQ.js} +6 -5
- package/dist/{global-workspace-KSR3E63K.js → global-workspace-MSX2K27Y.js} +6 -5
- package/dist/index.cjs +1369 -155
- package/dist/index.d.cts +397 -14
- package/dist/index.d.ts +397 -14
- package/dist/index.js +811 -90
- package/dist/mcp/run-stdio.cjs +1136 -147
- package/dist/mcp/run-stdio.js +635 -79
- package/dist/plan-file-6O7G4VPQ.js +23 -0
- package/dist/plan-file-PHVKUAEE.js +25 -0
- package/dist/{run-HMRSRG3U.js → run-H3IMNOYN.js} +3 -1
- package/dist/run-ZJKYHXGN.js +12 -0
- package/dist/{thread-store-XICUWFNM.js → thread-store-GHOADGL2.js} +1 -1
- package/dist/{thread-store-OV2X6PYO.js → thread-store-UJIGMI5J.js} +1 -1
- package/dist/{workspaces-OZE7LRDO.js → workspaces-3RQQZQRO.js} +7 -6
- package/dist/{workspaces-UJX7JIGH.js → workspaces-AYTBR6KQ.js} +7 -6
- package/dist/{worktree-XG5PCLZY.js → worktree-5KEQWSAF.js} +8 -3
- package/dist/{worktree-TEDAJ57S.js → worktree-RWGL7FUV.js} +8 -3
- package/package.json +1 -1
- package/dist/run-LF6E5IKL.js +0 -10
package/dist/index.cjs
CHANGED
|
@@ -916,6 +916,8 @@ function normalizeThread(raw) {
|
|
|
916
916
|
agentPid: raw.agentPid ?? null,
|
|
917
917
|
attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
|
|
918
918
|
prTitle: raw.prTitle ?? null,
|
|
919
|
+
stackId: raw.stackId ?? null,
|
|
920
|
+
stackLayer: raw.stackLayer ?? null,
|
|
919
921
|
userSetTitle: Boolean(raw.userSetTitle),
|
|
920
922
|
activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : [],
|
|
921
923
|
quotaResumeAt: raw.quotaResumeAt ?? null,
|
|
@@ -940,6 +942,8 @@ function createEmptyThread(partial) {
|
|
|
940
942
|
activeRuns: partial.activeRuns ?? [],
|
|
941
943
|
prUrl: partial.prUrl ?? null,
|
|
942
944
|
prTitle: partial.prTitle ?? null,
|
|
945
|
+
stackId: partial.stackId ?? null,
|
|
946
|
+
stackLayer: partial.stackLayer ?? null,
|
|
943
947
|
userSetTitle: partial.userSetTitle ?? false,
|
|
944
948
|
messages: partial.messages ?? [],
|
|
945
949
|
attachments: partial.attachments ?? [],
|
|
@@ -2044,6 +2048,7 @@ var run_exports = {};
|
|
|
2044
2048
|
__export(run_exports, {
|
|
2045
2049
|
gh: () => gh,
|
|
2046
2050
|
git: () => git,
|
|
2051
|
+
resolveGhAuthToken: () => resolveGhAuthToken,
|
|
2047
2052
|
run: () => run
|
|
2048
2053
|
});
|
|
2049
2054
|
async function run(file, args, opts) {
|
|
@@ -2072,11 +2077,28 @@ async function run(file, args, opts) {
|
|
|
2072
2077
|
}
|
|
2073
2078
|
}
|
|
2074
2079
|
async function git(args, cwd, opts) {
|
|
2075
|
-
|
|
2080
|
+
const prefix = [];
|
|
2081
|
+
if (opts?.config) {
|
|
2082
|
+
for (const [key, value] of Object.entries(opts.config)) {
|
|
2083
|
+
if (!key) continue;
|
|
2084
|
+
prefix.push("-c", `${key}=${value}`);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
return run("git", [...prefix, ...args], {
|
|
2088
|
+
cwd,
|
|
2089
|
+
reject: opts?.reject,
|
|
2090
|
+
env: opts?.env
|
|
2091
|
+
});
|
|
2076
2092
|
}
|
|
2077
2093
|
async function gh(args, cwd, opts) {
|
|
2078
2094
|
return run("gh", args, { cwd, reject: opts?.reject });
|
|
2079
2095
|
}
|
|
2096
|
+
async function resolveGhAuthToken(cwd) {
|
|
2097
|
+
const result = await gh(["auth", "token"], cwd, { reject: false });
|
|
2098
|
+
if (result.exitCode !== 0) return null;
|
|
2099
|
+
const token = result.stdout.trim();
|
|
2100
|
+
return token || null;
|
|
2101
|
+
}
|
|
2080
2102
|
var import_execa;
|
|
2081
2103
|
var init_run = __esm({
|
|
2082
2104
|
"src/git/run.ts"() {
|
|
@@ -2178,6 +2200,234 @@ var init_pr_gates = __esm({
|
|
|
2178
2200
|
}
|
|
2179
2201
|
});
|
|
2180
2202
|
|
|
2203
|
+
// src/git/stack.ts
|
|
2204
|
+
async function detectGhStack(cwd) {
|
|
2205
|
+
const now = Date.now();
|
|
2206
|
+
if (cachedStatus && now - cachedStatus.at < STATUS_TTL_MS) {
|
|
2207
|
+
return cachedStatus.status;
|
|
2208
|
+
}
|
|
2209
|
+
const probe = await gh(["stack", "view", "--help"], cwd, { reject: false });
|
|
2210
|
+
if (probe.exitCode === 0) {
|
|
2211
|
+
const status2 = { available: true };
|
|
2212
|
+
cachedStatus = { at: now, status: status2 };
|
|
2213
|
+
return status2;
|
|
2214
|
+
}
|
|
2215
|
+
const err = `${probe.stderr}
|
|
2216
|
+
${probe.stdout}`;
|
|
2217
|
+
const reason = /official extension|extension install|github\/gh-stack/i.test(err) ? "Install with: gh extension install github/gh-stack" : err.trim() || "gh stack is not available";
|
|
2218
|
+
const status = { available: false, reason };
|
|
2219
|
+
cachedStatus = { at: now, status };
|
|
2220
|
+
return status;
|
|
2221
|
+
}
|
|
2222
|
+
function resetGhStackDetectCache() {
|
|
2223
|
+
cachedStatus = null;
|
|
2224
|
+
}
|
|
2225
|
+
function str(v) {
|
|
2226
|
+
return typeof v === "string" ? v : v == null ? "" : String(v);
|
|
2227
|
+
}
|
|
2228
|
+
function num(v) {
|
|
2229
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2230
|
+
if (typeof v === "string" && v.trim() && Number.isFinite(Number(v))) {
|
|
2231
|
+
return Number(v);
|
|
2232
|
+
}
|
|
2233
|
+
return null;
|
|
2234
|
+
}
|
|
2235
|
+
function parseGhStackViewJson(raw) {
|
|
2236
|
+
let data;
|
|
2237
|
+
try {
|
|
2238
|
+
data = JSON.parse(raw);
|
|
2239
|
+
} catch {
|
|
2240
|
+
return null;
|
|
2241
|
+
}
|
|
2242
|
+
if (!Array.isArray(data.branches) || data.branches.length === 0) return null;
|
|
2243
|
+
const trunk = str(data.trunk) || "main";
|
|
2244
|
+
const currentBranch2 = str(data.currentBranch);
|
|
2245
|
+
const stackNumber = num(data.stackNumber) ?? num(data.number);
|
|
2246
|
+
const layers = [];
|
|
2247
|
+
for (let i = 0; i < data.branches.length; i++) {
|
|
2248
|
+
const b = data.branches[i];
|
|
2249
|
+
if (!b || typeof b !== "object") continue;
|
|
2250
|
+
const name = str(b.name);
|
|
2251
|
+
if (!name) continue;
|
|
2252
|
+
const pr = b.pr && typeof b.pr === "object" ? b.pr : null;
|
|
2253
|
+
layers.push({
|
|
2254
|
+
position: i + 1,
|
|
2255
|
+
branchName: name,
|
|
2256
|
+
headSha: str(b.head) || void 0,
|
|
2257
|
+
baseSha: str(b.base) || void 0,
|
|
2258
|
+
isCurrent: Boolean(b.isCurrent) || name === currentBranch2,
|
|
2259
|
+
isMerged: Boolean(b.isMerged),
|
|
2260
|
+
isQueued: Boolean(b.isQueued),
|
|
2261
|
+
needsRebase: Boolean(b.needsRebase),
|
|
2262
|
+
prNumber: pr ? num(pr.number) : null,
|
|
2263
|
+
prUrl: pr && str(pr.url) ? str(pr.url) : null,
|
|
2264
|
+
prState: pr && str(pr.state) ? str(pr.state).toUpperCase() : null,
|
|
2265
|
+
title: pr && str(pr.title) ? str(pr.title) : void 0
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
if (!layers.length) return null;
|
|
2269
|
+
let currentIndex = layers.findIndex((l) => l.isCurrent);
|
|
2270
|
+
if (currentIndex < 0 && currentBranch2) {
|
|
2271
|
+
currentIndex = layers.findIndex((l) => l.branchName === currentBranch2);
|
|
2272
|
+
}
|
|
2273
|
+
const { readyToMerge, blockedReason } = stackMergeReadiness(layers, currentIndex);
|
|
2274
|
+
return {
|
|
2275
|
+
stackNumber,
|
|
2276
|
+
trunk,
|
|
2277
|
+
currentBranch: currentBranch2 || layers[currentIndex]?.branchName || layers[0].branchName,
|
|
2278
|
+
layers,
|
|
2279
|
+
currentIndex,
|
|
2280
|
+
readyToMerge,
|
|
2281
|
+
blockedReason
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
function stackMergeReadiness(layers, throughIndex) {
|
|
2285
|
+
if (throughIndex < 0 || throughIndex >= layers.length) {
|
|
2286
|
+
return { readyToMerge: false, blockedReason: "Not on a stack layer" };
|
|
2287
|
+
}
|
|
2288
|
+
for (let i = 0; i <= throughIndex; i++) {
|
|
2289
|
+
const layer = layers[i];
|
|
2290
|
+
if (layer.isMerged) continue;
|
|
2291
|
+
if (!layer.prNumber) {
|
|
2292
|
+
return {
|
|
2293
|
+
readyToMerge: false,
|
|
2294
|
+
blockedReason: `Layer ${layer.branchName} has no pull request yet`
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
if (layer.needsRebase) {
|
|
2298
|
+
return {
|
|
2299
|
+
readyToMerge: false,
|
|
2300
|
+
blockedReason: `PR #${layer.prNumber} needs rebase`
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
const state = (layer.prState ?? "").toUpperCase();
|
|
2304
|
+
if (state && state !== "OPEN" && state !== "QUEUED") {
|
|
2305
|
+
return {
|
|
2306
|
+
readyToMerge: false,
|
|
2307
|
+
blockedReason: `PR #${layer.prNumber} is ${state}`
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
return { readyToMerge: true, blockedReason: null };
|
|
2312
|
+
}
|
|
2313
|
+
async function getPrStack(cwd) {
|
|
2314
|
+
const status = await detectGhStack(cwd);
|
|
2315
|
+
if (!status.available) return null;
|
|
2316
|
+
const result = await gh(["stack", "view", "--json"], cwd, { reject: false });
|
|
2317
|
+
if (result.exitCode === 2) return null;
|
|
2318
|
+
if (result.exitCode !== 0) {
|
|
2319
|
+
if (/not in a stack|no stack/i.test(`${result.stderr}
|
|
2320
|
+
${result.stdout}`)) {
|
|
2321
|
+
return null;
|
|
2322
|
+
}
|
|
2323
|
+
if (result.exitCode === 9) return null;
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
const json = result.stdout.trim();
|
|
2327
|
+
if (!json) return null;
|
|
2328
|
+
return parseGhStackViewJson(json);
|
|
2329
|
+
}
|
|
2330
|
+
async function isInPrStack(cwd) {
|
|
2331
|
+
return Boolean(await getPrStack(cwd));
|
|
2332
|
+
}
|
|
2333
|
+
async function mergePrStack(cwd, opts) {
|
|
2334
|
+
const status = await detectGhStack(cwd);
|
|
2335
|
+
if (!status.available) {
|
|
2336
|
+
throw new Error(status.reason);
|
|
2337
|
+
}
|
|
2338
|
+
const method = opts.method ?? "squash";
|
|
2339
|
+
const methodFlag = method === "rebase" ? "--rebase" : method === "merge" ? "--merge" : "--squash";
|
|
2340
|
+
const args = [
|
|
2341
|
+
"stack",
|
|
2342
|
+
"merge",
|
|
2343
|
+
String(opts.through),
|
|
2344
|
+
"--yes",
|
|
2345
|
+
methodFlag
|
|
2346
|
+
];
|
|
2347
|
+
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
2348
|
+
if (exitCode !== 0) {
|
|
2349
|
+
throw new Error(stderr.trim() || stdout.trim() || "gh stack merge failed");
|
|
2350
|
+
}
|
|
2351
|
+
return { stdout };
|
|
2352
|
+
}
|
|
2353
|
+
async function initPrStack(cwd, branches, opts) {
|
|
2354
|
+
if (!branches.length) throw new Error("initPrStack requires at least one branch name");
|
|
2355
|
+
const status = await detectGhStack(cwd);
|
|
2356
|
+
if (!status.available) throw new Error(status.reason);
|
|
2357
|
+
const args = ["stack", "init"];
|
|
2358
|
+
if (opts?.base) args.push("--base", opts.base);
|
|
2359
|
+
args.push(...branches);
|
|
2360
|
+
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
2361
|
+
if (exitCode !== 0) {
|
|
2362
|
+
throw new Error(stderr.trim() || stdout.trim() || "gh stack init failed");
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
async function addPrStackLayer(cwd, branchName) {
|
|
2366
|
+
if (!branchName.trim()) throw new Error("branch name required");
|
|
2367
|
+
const status = await detectGhStack(cwd);
|
|
2368
|
+
if (!status.available) throw new Error(status.reason);
|
|
2369
|
+
const { exitCode, stderr, stdout } = await gh(
|
|
2370
|
+
["stack", "add", branchName.trim()],
|
|
2371
|
+
cwd,
|
|
2372
|
+
{ reject: false }
|
|
2373
|
+
);
|
|
2374
|
+
if (exitCode !== 0) {
|
|
2375
|
+
throw new Error(stderr.trim() || stdout.trim() || "gh stack add failed");
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
async function submitPrStack(cwd, opts) {
|
|
2379
|
+
const status = await detectGhStack(cwd);
|
|
2380
|
+
if (!status.available) throw new Error(status.reason);
|
|
2381
|
+
const args = ["stack", "submit", "--auto"];
|
|
2382
|
+
if (opts?.open) args.push("--open");
|
|
2383
|
+
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
2384
|
+
if (exitCode !== 0) {
|
|
2385
|
+
throw new Error(stderr.trim() || stdout.trim() || "gh stack submit failed");
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
async function checkoutPrStackLayer(cwd, target) {
|
|
2389
|
+
const status = await detectGhStack(cwd);
|
|
2390
|
+
if (!status.available) throw new Error(status.reason);
|
|
2391
|
+
const { exitCode, stderr, stdout } = await gh(
|
|
2392
|
+
["stack", "checkout", String(target)],
|
|
2393
|
+
cwd,
|
|
2394
|
+
{ reject: false }
|
|
2395
|
+
);
|
|
2396
|
+
if (exitCode !== 0) {
|
|
2397
|
+
throw new Error(stderr.trim() || stdout.trim() || "gh stack checkout failed");
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
var cachedStatus, STATUS_TTL_MS;
|
|
2401
|
+
var init_stack = __esm({
|
|
2402
|
+
"src/git/stack.ts"() {
|
|
2403
|
+
"use strict";
|
|
2404
|
+
init_run();
|
|
2405
|
+
cachedStatus = null;
|
|
2406
|
+
STATUS_TTL_MS = 6e4;
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
// src/paths/workspace-scratch.ts
|
|
2411
|
+
function attachmentsGitignoreBody() {
|
|
2412
|
+
return ATTACHMENTS_GITIGNORE;
|
|
2413
|
+
}
|
|
2414
|
+
function isWorkspaceScratchPath(relativePath) {
|
|
2415
|
+
const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
2416
|
+
return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".context" || p.startsWith(".context/");
|
|
2417
|
+
}
|
|
2418
|
+
var ATTACHMENTS_DIR, LEGACY_ATTACHMENTS_DIR, ATTACHMENTS_GITIGNORE;
|
|
2419
|
+
var init_workspace_scratch = __esm({
|
|
2420
|
+
"src/paths/workspace-scratch.ts"() {
|
|
2421
|
+
"use strict";
|
|
2422
|
+
ATTACHMENTS_DIR = ".context/attachments";
|
|
2423
|
+
LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
2424
|
+
ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
|
|
2425
|
+
*
|
|
2426
|
+
!.gitignore
|
|
2427
|
+
`;
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
|
|
2181
2431
|
// src/git/worktree.ts
|
|
2182
2432
|
var worktree_exports = {};
|
|
2183
2433
|
__export(worktree_exports, {
|
|
@@ -2187,6 +2437,7 @@ __export(worktree_exports, {
|
|
|
2187
2437
|
branchDisplayLabel: () => branchDisplayLabel,
|
|
2188
2438
|
collectTakenTeamSlugs: () => collectTakenTeamSlugs,
|
|
2189
2439
|
commitAll: () => commitAll,
|
|
2440
|
+
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
2190
2441
|
createOrUpdatePr: () => createOrUpdatePr,
|
|
2191
2442
|
createThreadWorktree: () => createThreadWorktree,
|
|
2192
2443
|
currentBranch: () => currentBranch,
|
|
@@ -2201,6 +2452,7 @@ __export(worktree_exports, {
|
|
|
2201
2452
|
ghRepoSelectArgs: () => ghRepoSelectArgs,
|
|
2202
2453
|
isDirty: () => isDirty,
|
|
2203
2454
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
2455
|
+
isSideboardScratchPath: () => isSideboardScratchPath,
|
|
2204
2456
|
listBranches: () => listBranches,
|
|
2205
2457
|
listPrs: () => listPrs,
|
|
2206
2458
|
listWorktrees: () => listWorktrees,
|
|
@@ -2730,18 +2982,31 @@ async function fetchPrHead(repoPath, number, localBranch) {
|
|
|
2730
2982
|
const slug = await resolveGithubRepoSlug(repoPath);
|
|
2731
2983
|
const refspec = `+pull/${number}/head:${localBranch}`;
|
|
2732
2984
|
const errors = [];
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
}
|
|
2985
|
+
const httpsRemote = slug ? `https://github.com/${slug}.git` : null;
|
|
2986
|
+
const tryFetch = async (remote, opts) => {
|
|
2987
|
+
const label = opts?.ghAuth ? `${remote} (gh auth)` : remote;
|
|
2988
|
+
const gitOpts = { reject: false };
|
|
2989
|
+
if (opts?.ghAuth) {
|
|
2990
|
+
const token = await resolveGhAuthToken(repoPath);
|
|
2991
|
+
if (!token) {
|
|
2992
|
+
errors.push(`${label}: gh auth token unavailable`);
|
|
2993
|
+
return false;
|
|
2994
|
+
}
|
|
2995
|
+
gitOpts.env = { GIT_TERMINAL_PROMPT: "0" };
|
|
2996
|
+
gitOpts.config = {
|
|
2997
|
+
"http.extraHeader": `AUTHORIZATION: bearer ${token}`
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
const result = await git(["fetch", remote, refspec], repoPath, gitOpts);
|
|
2737
3001
|
if (result.exitCode === 0) return true;
|
|
2738
3002
|
const detail = (result.stderr || result.stdout).trim();
|
|
2739
|
-
if (detail) errors.push(`${
|
|
3003
|
+
if (detail) errors.push(`${label}: ${detail}`);
|
|
2740
3004
|
return false;
|
|
2741
3005
|
};
|
|
2742
3006
|
let fetched = await tryFetch("origin");
|
|
2743
|
-
if (!fetched &&
|
|
2744
|
-
fetched = await tryFetch(
|
|
3007
|
+
if (!fetched && httpsRemote) {
|
|
3008
|
+
fetched = await tryFetch(httpsRemote);
|
|
3009
|
+
if (!fetched) fetched = await tryFetch(httpsRemote, { ghAuth: true });
|
|
2745
3010
|
}
|
|
2746
3011
|
const localOk = async () => {
|
|
2747
3012
|
const verify = await git(["rev-parse", "--verify", localBranch], repoPath, {
|
|
@@ -2770,18 +3035,28 @@ async function fetchPrHead(repoPath, number, localBranch) {
|
|
|
2770
3035
|
}
|
|
2771
3036
|
}
|
|
2772
3037
|
if (oid) {
|
|
2773
|
-
const ensureOid = async (remote) => {
|
|
2774
|
-
const
|
|
2775
|
-
|
|
2776
|
-
|
|
3038
|
+
const ensureOid = async (remote, opts) => {
|
|
3039
|
+
const gitOpts = { reject: false };
|
|
3040
|
+
if (opts?.ghAuth) {
|
|
3041
|
+
const token = await resolveGhAuthToken(repoPath);
|
|
3042
|
+
if (!token) return false;
|
|
3043
|
+
gitOpts.env = { GIT_TERMINAL_PROMPT: "0" };
|
|
3044
|
+
gitOpts.config = {
|
|
3045
|
+
"http.extraHeader": `AUTHORIZATION: bearer ${token}`
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
const got = await git(["fetch", remote, oid], repoPath, gitOpts);
|
|
2777
3049
|
return got.exitCode === 0;
|
|
2778
3050
|
};
|
|
2779
3051
|
let haveObject = (await git(["cat-file", "-e", `${oid}^{commit}`], repoPath, {
|
|
2780
3052
|
reject: false
|
|
2781
3053
|
})).exitCode === 0;
|
|
2782
3054
|
if (!haveObject) haveObject = await ensureOid("origin");
|
|
2783
|
-
if (!haveObject &&
|
|
2784
|
-
haveObject = await ensureOid(
|
|
3055
|
+
if (!haveObject && httpsRemote) {
|
|
3056
|
+
haveObject = await ensureOid(httpsRemote);
|
|
3057
|
+
if (!haveObject) {
|
|
3058
|
+
haveObject = await ensureOid(httpsRemote, { ghAuth: true });
|
|
3059
|
+
}
|
|
2785
3060
|
}
|
|
2786
3061
|
if (haveObject) {
|
|
2787
3062
|
const branched = await git(["branch", "-f", localBranch, oid], repoPath, {
|
|
@@ -2876,6 +3151,52 @@ ${add.stdout}`;
|
|
|
2876
3151
|
await ensureGhPreferOrigin(worktreePath);
|
|
2877
3152
|
return { branchName, worktreePath };
|
|
2878
3153
|
}
|
|
3154
|
+
async function createExistingBranchWorktree(opts) {
|
|
3155
|
+
const branchName = opts.branchName.trim();
|
|
3156
|
+
if (!branchName) throw new Error("branch name required");
|
|
3157
|
+
const worktreePath = (0, import_node_path6.join)(worktreesRoot(opts.repoPath), opts.slug);
|
|
3158
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
3159
|
+
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
3160
|
+
}
|
|
3161
|
+
await ensureGhPreferOrigin(opts.repoPath);
|
|
3162
|
+
await git(["fetch", "origin", "--prune"], opts.repoPath, { reject: false });
|
|
3163
|
+
if (!branchName.startsWith("origin/") && !branchName.startsWith("refs/")) {
|
|
3164
|
+
await git(["fetch", "origin", branchName], opts.repoPath, { reject: false });
|
|
3165
|
+
}
|
|
3166
|
+
const existing = await listWorktrees(opts.repoPath);
|
|
3167
|
+
const already = existing.find((w) => w.branch === branchName);
|
|
3168
|
+
if (already?.path) {
|
|
3169
|
+
throw new Error(
|
|
3170
|
+
`Branch ${branchName} is already checked out at ${already.path}`
|
|
3171
|
+
);
|
|
3172
|
+
}
|
|
3173
|
+
const startPoint = await resolveWorktreeStartPoint(opts.repoPath, branchName);
|
|
3174
|
+
const add = await git(
|
|
3175
|
+
["worktree", "add", worktreePath, startPoint],
|
|
3176
|
+
opts.repoPath,
|
|
3177
|
+
{ reject: false }
|
|
3178
|
+
);
|
|
3179
|
+
if (add.exitCode !== 0) {
|
|
3180
|
+
const retry = await git(
|
|
3181
|
+
["worktree", "add", worktreePath, branchName],
|
|
3182
|
+
opts.repoPath,
|
|
3183
|
+
{ reject: false }
|
|
3184
|
+
);
|
|
3185
|
+
if (retry.exitCode !== 0) {
|
|
3186
|
+
throw new Error(
|
|
3187
|
+
`Failed to create worktree for ${branchName}: ${retry.stderr.trim() || add.stderr.trim() || retry.stdout.trim() || add.stdout.trim() || `exit ${add.exitCode}`}`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
|
|
3192
|
+
reject: false
|
|
3193
|
+
});
|
|
3194
|
+
if (head.stdout.trim() === "HEAD" || head.stdout.trim() !== branchName) {
|
|
3195
|
+
await git(["checkout", "-B", branchName], worktreePath, { reject: false });
|
|
3196
|
+
}
|
|
3197
|
+
await ensureGhPreferOrigin(worktreePath);
|
|
3198
|
+
return { branchName, worktreePath };
|
|
3199
|
+
}
|
|
2879
3200
|
async function removeWorktree(repoPath, worktreePath, opts) {
|
|
2880
3201
|
await git(["worktree", "remove", "--force", worktreePath], repoPath, {
|
|
2881
3202
|
reject: false
|
|
@@ -2905,8 +3226,27 @@ async function listWorktrees(repoPath) {
|
|
|
2905
3226
|
return entries;
|
|
2906
3227
|
}
|
|
2907
3228
|
async function isDirty(worktreePath) {
|
|
2908
|
-
const { stdout } = await git(["status", "--porcelain"], worktreePath);
|
|
2909
|
-
|
|
3229
|
+
const { stdout } = await git(["status", "--porcelain", "-uall"], worktreePath);
|
|
3230
|
+
for (const line of stdout.split("\n")) {
|
|
3231
|
+
if (!line.trim()) continue;
|
|
3232
|
+
const rel = porcelainStatusPath(line);
|
|
3233
|
+
if (!rel || isSideboardScratchPath(rel)) continue;
|
|
3234
|
+
return true;
|
|
3235
|
+
}
|
|
3236
|
+
return false;
|
|
3237
|
+
}
|
|
3238
|
+
function isSideboardScratchPath(relativePath) {
|
|
3239
|
+
return isWorkspaceScratchPath(relativePath);
|
|
3240
|
+
}
|
|
3241
|
+
function porcelainStatusPath(line) {
|
|
3242
|
+
const rest = line.length >= 3 ? line.slice(3) : "";
|
|
3243
|
+
if (!rest) return "";
|
|
3244
|
+
const arrow = rest.lastIndexOf(" -> ");
|
|
3245
|
+
const raw = arrow >= 0 ? rest.slice(arrow + 4) : rest;
|
|
3246
|
+
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
3247
|
+
return raw.slice(1, -1).replace(/\\([\\"])/g, "$1");
|
|
3248
|
+
}
|
|
3249
|
+
return raw;
|
|
2910
3250
|
}
|
|
2911
3251
|
async function currentBranch(worktreePath) {
|
|
2912
3252
|
const { stdout } = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath);
|
|
@@ -2924,7 +3264,7 @@ async function pushBranch(worktreePath, branchName) {
|
|
|
2924
3264
|
}
|
|
2925
3265
|
async function mergePr(cwd, selector, opts) {
|
|
2926
3266
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
2927
|
-
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
|
|
3267
|
+
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
|
|
2928
3268
|
if (slug) viewArgs.push("--repo", slug);
|
|
2929
3269
|
const before = await gh(viewArgs, cwd, { reject: false });
|
|
2930
3270
|
if (before.exitCode !== 0 || !before.stdout.trim()) {
|
|
@@ -2932,16 +3272,29 @@ async function mergePr(cwd, selector, opts) {
|
|
|
2932
3272
|
}
|
|
2933
3273
|
let url = "";
|
|
2934
3274
|
let isDraft = false;
|
|
3275
|
+
let prNumber = null;
|
|
2935
3276
|
try {
|
|
2936
3277
|
const parsed = JSON.parse(before.stdout);
|
|
2937
3278
|
url = String(parsed.url ?? "");
|
|
2938
3279
|
isDraft = Boolean(parsed.isDraft);
|
|
3280
|
+
prNumber = typeof parsed.number === "number" && Number.isFinite(parsed.number) ? parsed.number : null;
|
|
2939
3281
|
if (String(parsed.state ?? "").toUpperCase() === "MERGED") {
|
|
2940
3282
|
return { url, state: "MERGED" };
|
|
2941
3283
|
}
|
|
2942
3284
|
} catch {
|
|
2943
3285
|
throw new Error("Could not parse pull request details");
|
|
2944
3286
|
}
|
|
3287
|
+
const stack = await getPrStack(cwd);
|
|
3288
|
+
const stackLayer = stack && prNumber != null ? stack.layers.find((l) => l.prNumber === prNumber) : null;
|
|
3289
|
+
if (stack && stackLayer && prNumber != null) {
|
|
3290
|
+
const throughIndex = stack.layers.findIndex((l) => l.prNumber === prNumber);
|
|
3291
|
+
const gate = stackMergeReadiness(stack.layers, throughIndex);
|
|
3292
|
+
if (!gate.readyToMerge) {
|
|
3293
|
+
throw new Error(gate.blockedReason || "Stack is not ready to merge");
|
|
3294
|
+
}
|
|
3295
|
+
await mergePrStack(cwd, { through: prNumber, method: opts?.method ?? "squash" });
|
|
3296
|
+
return { url, state: "MERGED" };
|
|
3297
|
+
}
|
|
2945
3298
|
if (isDraft) {
|
|
2946
3299
|
const readyArgs = ["pr", "ready", selector];
|
|
2947
3300
|
if (slug) readyArgs.push("--repo", slug);
|
|
@@ -2963,10 +3316,10 @@ async function mergePr(cwd, selector, opts) {
|
|
|
2963
3316
|
const after = await gh(viewArgs, cwd, { reject: false });
|
|
2964
3317
|
if (after.exitCode === 0 && after.stdout.trim()) {
|
|
2965
3318
|
try {
|
|
2966
|
-
const parsed = JSON.parse(after.stdout);
|
|
3319
|
+
const parsed = after.stdout ? JSON.parse(after.stdout) : null;
|
|
2967
3320
|
return {
|
|
2968
|
-
url: String(parsed
|
|
2969
|
-
state: String(parsed
|
|
3321
|
+
url: String(parsed?.url ?? url),
|
|
3322
|
+
state: String(parsed?.state ?? "MERGED")
|
|
2970
3323
|
};
|
|
2971
3324
|
} catch {
|
|
2972
3325
|
}
|
|
@@ -3114,7 +3467,9 @@ var init_worktree = __esm({
|
|
|
3114
3467
|
init_gh_errors();
|
|
3115
3468
|
init_run();
|
|
3116
3469
|
init_pr_gates();
|
|
3470
|
+
init_stack();
|
|
3117
3471
|
init_teams();
|
|
3472
|
+
init_workspace_scratch();
|
|
3118
3473
|
init_worktree_labels();
|
|
3119
3474
|
}
|
|
3120
3475
|
});
|
|
@@ -3240,6 +3595,7 @@ var init_coordinator_prompt = __esm({
|
|
|
3240
3595
|
"Discover:",
|
|
3241
3596
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
3242
3597
|
"- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
|
|
3598
|
+
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
3243
3599
|
"- list_models \u2014 only when you need a specific model (rare); otherwise leave model unset = Auto",
|
|
3244
3600
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
3245
3601
|
"Workspaces:",
|
|
@@ -4651,7 +5007,9 @@ var init_injected_mcp = __esm({
|
|
|
4651
5007
|
SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
|
|
4652
5008
|
"mcp__sideboard__present_artifact",
|
|
4653
5009
|
"mcp__sideboard__present_schema",
|
|
4654
|
-
"mcp__sideboard__present_files"
|
|
5010
|
+
"mcp__sideboard__present_files",
|
|
5011
|
+
"mcp__sideboard__ask_user",
|
|
5012
|
+
"mcp__sideboard__present_plan"
|
|
4655
5013
|
];
|
|
4656
5014
|
BRIGHTSY_MCP_ALLOWED_TOOLS = [
|
|
4657
5015
|
"mcp__brightsy",
|
|
@@ -4694,7 +5052,7 @@ var PLAN_MODE_INSTRUCTION;
|
|
|
4694
5052
|
var init_types = __esm({
|
|
4695
5053
|
"src/agents/types.ts"() {
|
|
4696
5054
|
"use strict";
|
|
4697
|
-
PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or
|
|
5055
|
+
PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or Approves / Hands off the plan). Analyze the codebase, search and read files as needed, and produce or refine a clear implementation plan. Do not modify, create, or delete any project files except via Sideboard MCP present_plan (writes .context/attachments/plan.md). When you need a clarifying decision (approach forks, auth choice, scope): (1) first write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it) \u2014 do not leave the user staring at bare labels; (2) then call Sideboard MCP ask_user with the same options, including a description on every option. Sideboard shows questions in the composer and mirrors them in chat. After ask_user, wait for the user's next message with their answers before finalizing the plan. When the plan is ready for approval: (1) call present_plan with the full markdown plan (title + content) so Sideboard saves .context/attachments/plan.md and shows it in chat for Approve / Hand off / Copy; (2) Claude should also call ExitPlanMode after present_plan. Do not skip present_plan \u2014 the plan must be a markdown file, not only chat prose.";
|
|
4698
5056
|
}
|
|
4699
5057
|
});
|
|
4700
5058
|
|
|
@@ -6283,6 +6641,116 @@ var init_agents = __esm({
|
|
|
6283
6641
|
}
|
|
6284
6642
|
});
|
|
6285
6643
|
|
|
6644
|
+
// src/plan/plan-present.ts
|
|
6645
|
+
function asRecord2(v) {
|
|
6646
|
+
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
6647
|
+
}
|
|
6648
|
+
function isPresentPlanToolName(name) {
|
|
6649
|
+
if (!name) return false;
|
|
6650
|
+
return /present_plan$/i.test(name) || /^mcp__sideboard__present_plan$/i.test(name);
|
|
6651
|
+
}
|
|
6652
|
+
function extractPresentedPlan(parts) {
|
|
6653
|
+
if (!parts?.length) return null;
|
|
6654
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
6655
|
+
const p = parts[i];
|
|
6656
|
+
if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
|
|
6657
|
+
const input = asRecord2(p.input) ?? {};
|
|
6658
|
+
const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
|
|
6659
|
+
if (!content.trim()) continue;
|
|
6660
|
+
const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
|
|
6661
|
+
const path = typeof input.path === "string" && input.path.trim() ? input.path.trim() : PLAN_FILE_REL;
|
|
6662
|
+
return { title, content: content.trim(), path, source: "present_plan" };
|
|
6663
|
+
}
|
|
6664
|
+
return null;
|
|
6665
|
+
}
|
|
6666
|
+
function resolvePlanMarkdown(opts) {
|
|
6667
|
+
const fromTool = extractPresentedPlan(opts.parts);
|
|
6668
|
+
if (fromTool) return fromTool;
|
|
6669
|
+
const file = opts.fileContent?.trim();
|
|
6670
|
+
if (file) {
|
|
6671
|
+
return {
|
|
6672
|
+
title: "Plan",
|
|
6673
|
+
content: file,
|
|
6674
|
+
path: PLAN_FILE_REL,
|
|
6675
|
+
source: "exit_plan"
|
|
6676
|
+
};
|
|
6677
|
+
}
|
|
6678
|
+
const text = opts.text?.trim();
|
|
6679
|
+
if (text && text.length >= 80) {
|
|
6680
|
+
return {
|
|
6681
|
+
title: "Plan",
|
|
6682
|
+
content: text,
|
|
6683
|
+
path: PLAN_FILE_REL,
|
|
6684
|
+
source: "text"
|
|
6685
|
+
};
|
|
6686
|
+
}
|
|
6687
|
+
return null;
|
|
6688
|
+
}
|
|
6689
|
+
var PLAN_FILE_REL, PLAN_FILE_NAME, LEGACY_PLAN_FILE_REL;
|
|
6690
|
+
var init_plan_present = __esm({
|
|
6691
|
+
"src/plan/plan-present.ts"() {
|
|
6692
|
+
"use strict";
|
|
6693
|
+
init_workspace_scratch();
|
|
6694
|
+
PLAN_FILE_REL = `${ATTACHMENTS_DIR}/plan.md`;
|
|
6695
|
+
PLAN_FILE_NAME = "plan.md";
|
|
6696
|
+
LEGACY_PLAN_FILE_REL = ".sideboard/plan.md";
|
|
6697
|
+
}
|
|
6698
|
+
});
|
|
6699
|
+
|
|
6700
|
+
// src/plan/plan-file.ts
|
|
6701
|
+
var plan_file_exports = {};
|
|
6702
|
+
__export(plan_file_exports, {
|
|
6703
|
+
LEGACY_PLAN_FILE_REL: () => LEGACY_PLAN_FILE_REL,
|
|
6704
|
+
PLAN_FILE_NAME: () => PLAN_FILE_NAME,
|
|
6705
|
+
PLAN_FILE_REL: () => PLAN_FILE_REL,
|
|
6706
|
+
extractPresentedPlan: () => extractPresentedPlan,
|
|
6707
|
+
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
6708
|
+
planFileAbs: () => planFileAbs,
|
|
6709
|
+
readPlanFile: () => readPlanFile,
|
|
6710
|
+
resolvePlanMarkdown: () => resolvePlanMarkdown,
|
|
6711
|
+
writePlanFile: () => writePlanFile
|
|
6712
|
+
});
|
|
6713
|
+
function ensureAttachmentsGitignore2(worktreePath) {
|
|
6714
|
+
const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
6715
|
+
if ((0, import_node_fs26.existsSync)(gitignoreAbs)) return;
|
|
6716
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
|
|
6717
|
+
(0, import_node_fs26.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
6718
|
+
}
|
|
6719
|
+
function planFileAbs(worktreePath) {
|
|
6720
|
+
return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
|
|
6721
|
+
}
|
|
6722
|
+
function readTextIfPresent2(abs) {
|
|
6723
|
+
if (!(0, import_node_fs26.existsSync)(abs)) return null;
|
|
6724
|
+
try {
|
|
6725
|
+
const content = (0, import_node_fs26.readFileSync)(abs, "utf8");
|
|
6726
|
+
return content.trim() ? content : null;
|
|
6727
|
+
} catch {
|
|
6728
|
+
return null;
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
function readPlanFile(worktreePath) {
|
|
6732
|
+
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path24.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path24.join)(worktreePath, LEGACY_PLAN_FILE_REL));
|
|
6733
|
+
}
|
|
6734
|
+
function writePlanFile(worktreePath, content) {
|
|
6735
|
+
ensureAttachmentsGitignore2(worktreePath);
|
|
6736
|
+
const abs = planFileAbs(worktreePath);
|
|
6737
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
|
|
6738
|
+
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
6739
|
+
(0, import_node_fs26.writeFileSync)(abs, body, "utf8");
|
|
6740
|
+
return PLAN_FILE_REL;
|
|
6741
|
+
}
|
|
6742
|
+
var import_node_fs26, import_node_path24;
|
|
6743
|
+
var init_plan_file = __esm({
|
|
6744
|
+
"src/plan/plan-file.ts"() {
|
|
6745
|
+
"use strict";
|
|
6746
|
+
import_node_fs26 = require("fs");
|
|
6747
|
+
import_node_path24 = require("path");
|
|
6748
|
+
init_workspace_scratch();
|
|
6749
|
+
init_plan_present();
|
|
6750
|
+
init_plan_present();
|
|
6751
|
+
}
|
|
6752
|
+
});
|
|
6753
|
+
|
|
6286
6754
|
// src/agents/cursor-recover.ts
|
|
6287
6755
|
var cursor_recover_exports = {};
|
|
6288
6756
|
__export(cursor_recover_exports, {
|
|
@@ -6291,10 +6759,10 @@ __export(cursor_recover_exports, {
|
|
|
6291
6759
|
function recoverFinishedCursorRun(opts) {
|
|
6292
6760
|
const agentId = opts.agentId.trim();
|
|
6293
6761
|
if (!agentId) return null;
|
|
6294
|
-
const runsPath = (0,
|
|
6295
|
-
if (!(0,
|
|
6762
|
+
const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
6763
|
+
if (!(0, import_node_fs27.existsSync)(runsPath)) return null;
|
|
6296
6764
|
try {
|
|
6297
|
-
const lines = (0,
|
|
6765
|
+
const lines = (0, import_node_fs27.readFileSync)(runsPath, "utf8").split("\n");
|
|
6298
6766
|
let best = null;
|
|
6299
6767
|
for (const line of lines) {
|
|
6300
6768
|
const trimmed = line.trim();
|
|
@@ -6320,12 +6788,12 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6320
6788
|
return null;
|
|
6321
6789
|
}
|
|
6322
6790
|
}
|
|
6323
|
-
var
|
|
6791
|
+
var import_node_fs27, import_node_path25;
|
|
6324
6792
|
var init_cursor_recover = __esm({
|
|
6325
6793
|
"src/agents/cursor-recover.ts"() {
|
|
6326
6794
|
"use strict";
|
|
6327
|
-
|
|
6328
|
-
|
|
6795
|
+
import_node_fs27 = require("fs");
|
|
6796
|
+
import_node_path25 = require("path");
|
|
6329
6797
|
init_paths();
|
|
6330
6798
|
}
|
|
6331
6799
|
});
|
|
@@ -6364,6 +6832,7 @@ var init_title = __esm({
|
|
|
6364
6832
|
// src/index.ts
|
|
6365
6833
|
var index_exports = {};
|
|
6366
6834
|
__export(index_exports, {
|
|
6835
|
+
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
6367
6836
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
6368
6837
|
BrightsySideboardApi: () => BrightsySideboardApi,
|
|
6369
6838
|
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
@@ -6379,11 +6848,16 @@ __export(index_exports, {
|
|
|
6379
6848
|
FAMOUS_SOCCER_TEAMS: () => FAMOUS_SOCCER_TEAMS,
|
|
6380
6849
|
GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
|
|
6381
6850
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
6851
|
+
LEGACY_ATTACHMENTS_DIR: () => LEGACY_ATTACHMENTS_DIR,
|
|
6852
|
+
LEGACY_PLAN_FILE_REL: () => LEGACY_PLAN_FILE_REL,
|
|
6853
|
+
LEGACY_REVIEW_REQUEST_PATH: () => LEGACY_REVIEW_REQUEST_PATH,
|
|
6382
6854
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS: () => MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
6383
6855
|
ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
|
|
6384
6856
|
Orchestrator: () => Orchestrator,
|
|
6385
6857
|
PASTE_ATTACH_MIN_CHARS: () => PASTE_ATTACH_MIN_CHARS,
|
|
6386
6858
|
PASTE_ATTACH_MIN_LINES: () => PASTE_ATTACH_MIN_LINES,
|
|
6859
|
+
PLAN_FILE_NAME: () => PLAN_FILE_NAME,
|
|
6860
|
+
PLAN_FILE_REL: () => PLAN_FILE_REL,
|
|
6387
6861
|
PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
|
|
6388
6862
|
REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
|
|
6389
6863
|
REPO_REVIEW_PATH: () => REPO_REVIEW_PATH,
|
|
@@ -6393,6 +6867,8 @@ __export(index_exports, {
|
|
|
6393
6867
|
SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
|
|
6394
6868
|
SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
6395
6869
|
THINKING_EFFORTS: () => THINKING_EFFORTS,
|
|
6870
|
+
addPrStackLayer: () => addPrStackLayer,
|
|
6871
|
+
addStackLayerFromThread: () => addStackLayerFromThread,
|
|
6396
6872
|
addWorkspace: () => addWorkspace,
|
|
6397
6873
|
adoptThread: () => adoptThread,
|
|
6398
6874
|
allAdapters: () => allAdapters,
|
|
@@ -6411,6 +6887,7 @@ __export(index_exports, {
|
|
|
6411
6887
|
attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
|
|
6412
6888
|
attachmentsFromBuffers: () => attachmentsFromBuffers,
|
|
6413
6889
|
attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
|
|
6890
|
+
attachmentsGitignoreBody: () => attachmentsGitignoreBody,
|
|
6414
6891
|
autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
|
|
6415
6892
|
autoRenameBranchEnabled: () => autoRenameBranchEnabled,
|
|
6416
6893
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
@@ -6433,6 +6910,7 @@ __export(index_exports, {
|
|
|
6433
6910
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
6434
6911
|
captureLoginEnv: () => captureLoginEnv,
|
|
6435
6912
|
captureTurnBaseline: () => captureTurnBaseline,
|
|
6913
|
+
checkoutPrStackLayer: () => checkoutPrStackLayer,
|
|
6436
6914
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
6437
6915
|
claudeAdapter: () => claudeAdapter,
|
|
6438
6916
|
claudeChromeEnabled: () => claudeChromeEnabled,
|
|
@@ -6452,8 +6930,10 @@ __export(index_exports, {
|
|
|
6452
6930
|
countCacheControlBlocks: () => countCacheControlBlocks,
|
|
6453
6931
|
createChatTab: () => createChatTab,
|
|
6454
6932
|
createEmptyThread: () => createEmptyThread,
|
|
6933
|
+
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
6455
6934
|
createGlobalChat: () => createGlobalChat,
|
|
6456
6935
|
createOrUpdatePr: () => createOrUpdatePr,
|
|
6936
|
+
createPrStack: () => createPrStack,
|
|
6457
6937
|
createThread: () => createThread,
|
|
6458
6938
|
createThreadWorktree: () => createThreadWorktree,
|
|
6459
6939
|
currentBranch: () => currentBranch,
|
|
@@ -6463,6 +6943,7 @@ __export(index_exports, {
|
|
|
6463
6943
|
deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
|
|
6464
6944
|
deleteThreadRecord: () => deleteThreadRecord,
|
|
6465
6945
|
detectAgents: () => detectAgents,
|
|
6946
|
+
detectGhStack: () => detectGhStack,
|
|
6466
6947
|
detectLocalMergeConflicts: () => detectLocalMergeConflicts,
|
|
6467
6948
|
disconnectBrightsyTeam: () => disconnectBrightsyTeam,
|
|
6468
6949
|
discoverSkills: () => discoverSkills,
|
|
@@ -6478,12 +6959,15 @@ __export(index_exports, {
|
|
|
6478
6959
|
estimateThreadChars: () => estimateThreadChars,
|
|
6479
6960
|
expandComposerPrompt: () => expandComposerPrompt,
|
|
6480
6961
|
extractGhErrorDetail: () => extractGhErrorDetail,
|
|
6962
|
+
extractPendingPlanQuestions: () => extractPendingPlanQuestions,
|
|
6963
|
+
extractPresentedPlan: () => extractPresentedPlan,
|
|
6481
6964
|
extractiveSummary: () => extractiveSummary,
|
|
6482
6965
|
fetchPrHead: () => fetchPrHead,
|
|
6483
6966
|
finalizeParts: () => finalizeParts,
|
|
6484
6967
|
findInvalidCacheControlTtlOrder: () => findInvalidCacheControlTtlOrder,
|
|
6485
6968
|
findOrphanWorktrees: () => findOrphanWorktrees,
|
|
6486
6969
|
findThreadByRef: () => findThreadByRef,
|
|
6970
|
+
findThreadForStackLayer: () => findThreadForStackLayer,
|
|
6487
6971
|
flattenTurnInput: () => flattenTurnInput,
|
|
6488
6972
|
forkChatTab: () => forkChatTab,
|
|
6489
6973
|
forkMessageSlice: () => forkMessageSlice,
|
|
@@ -6494,6 +6978,8 @@ __export(index_exports, {
|
|
|
6494
6978
|
formatGhLandError: () => formatGhLandError,
|
|
6495
6979
|
formatIpcInvokeError: () => formatIpcInvokeError,
|
|
6496
6980
|
formatMessagesAsTranscript: () => formatMessagesAsTranscript,
|
|
6981
|
+
formatPlanQuestionAnswers: () => formatPlanQuestionAnswers,
|
|
6982
|
+
formatPlanQuestionsForChat: () => formatPlanQuestionsForChat,
|
|
6497
6983
|
formatRateLimitResetHint: () => formatRateLimitResetHint,
|
|
6498
6984
|
formatRenameBranchDirective: () => formatRenameBranchDirective,
|
|
6499
6985
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
@@ -6517,6 +7003,7 @@ __export(index_exports, {
|
|
|
6517
7003
|
getPrChecks: () => getPrChecks,
|
|
6518
7004
|
getPrDetails: () => getPrDetails,
|
|
6519
7005
|
getPrMeta: () => getPrMeta,
|
|
7006
|
+
getPrStack: () => getPrStack,
|
|
6520
7007
|
getRepoSetupInfo: () => getRepoSetupInfo,
|
|
6521
7008
|
getRunMode: () => getRunMode,
|
|
6522
7009
|
getRunScript: () => getRunScript,
|
|
@@ -6533,9 +7020,12 @@ __export(index_exports, {
|
|
|
6533
7020
|
healOrchestrationSoccerTitles: () => healOrchestrationSoccerTitles,
|
|
6534
7021
|
importConductorWorkspace: () => importConductorWorkspace,
|
|
6535
7022
|
importConductorWorkspaceAsync: () => importConductorWorkspaceAsync,
|
|
7023
|
+
initPrStack: () => initPrStack,
|
|
7024
|
+
initStackFromThread: () => initStackFromThread,
|
|
6536
7025
|
initializeGitRepository: () => initializeGitRepository,
|
|
6537
7026
|
inspectGitWorktree: () => inspectGitWorktree,
|
|
6538
7027
|
installAgent: () => installAgent,
|
|
7028
|
+
isAskUserToolName: () => isAskUserToolName,
|
|
6539
7029
|
isBrightsyConnected: () => isBrightsyConnected,
|
|
6540
7030
|
isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
|
|
6541
7031
|
isCloudCoordinatorThread: () => isCloudCoordinatorThread,
|
|
@@ -6545,13 +7035,17 @@ __export(index_exports, {
|
|
|
6545
7035
|
isGlobalRepoPath: () => isGlobalRepoPath,
|
|
6546
7036
|
isGlobalThread: () => isGlobalThread,
|
|
6547
7037
|
isImageFilePath: () => isImageFilePath,
|
|
7038
|
+
isInPrStack: () => isInPrStack,
|
|
6548
7039
|
isLinearConnected: () => isLinearConnected,
|
|
6549
7040
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6550
7041
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
6551
7042
|
isPidAlive: () => isPidAlive,
|
|
6552
7043
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
7044
|
+
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
6553
7045
|
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
7046
|
+
isSideboardScratchPath: () => isSideboardScratchPath,
|
|
6554
7047
|
isThinkingEffort: () => isThinkingEffort,
|
|
7048
|
+
isWorkspaceScratchPath: () => isWorkspaceScratchPath,
|
|
6555
7049
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
6556
7050
|
listBranchCommits: () => listBranchCommits,
|
|
6557
7051
|
listBranches: () => listBranches,
|
|
@@ -6588,6 +7082,7 @@ __export(index_exports, {
|
|
|
6588
7082
|
mcpAllowTools: () => mcpAllowTools,
|
|
6589
7083
|
mcpAuthWarnings: () => mcpAuthWarnings,
|
|
6590
7084
|
mergePr: () => mergePr,
|
|
7085
|
+
mergePrStack: () => mergePrStack,
|
|
6591
7086
|
mergeUsage: () => mergeUsage,
|
|
6592
7087
|
nextPastedTextName: () => nextPastedTextName,
|
|
6593
7088
|
nextThinkingEffort: () => nextThinkingEffort,
|
|
@@ -6597,6 +7092,8 @@ __export(index_exports, {
|
|
|
6597
7092
|
normalizeTurnInput: () => normalizeTurnInput,
|
|
6598
7093
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
6599
7094
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
7095
|
+
openPrStackLayers: () => openPrStackLayers,
|
|
7096
|
+
openStackLayer: () => openStackLayer,
|
|
6600
7097
|
opencodeAdapter: () => opencodeAdapter,
|
|
6601
7098
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
6602
7099
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
@@ -6605,15 +7102,19 @@ __export(index_exports, {
|
|
|
6605
7102
|
originGhRepoEnv: () => originGhRepoEnv,
|
|
6606
7103
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6607
7104
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
7105
|
+
parseGhStackViewJson: () => parseGhStackViewJson,
|
|
6608
7106
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
6609
7107
|
parseMcpList: () => parseMcpList,
|
|
7108
|
+
parsePlanQuestionsInput: () => parsePlanQuestionsInput,
|
|
6610
7109
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6611
7110
|
partsToAssistantText: () => partsToAssistantText,
|
|
6612
7111
|
pastedTextStats: () => pastedTextStats,
|
|
6613
7112
|
permissionMode: () => permissionMode,
|
|
7113
|
+
planFileAbs: () => planFileAbs,
|
|
6614
7114
|
previewLand: () => previewLand,
|
|
6615
7115
|
pushBranch: () => pushBranch,
|
|
6616
7116
|
readExistingReviewRequestFile: () => readExistingReviewRequestFile,
|
|
7117
|
+
readPlanFile: () => readPlanFile,
|
|
6617
7118
|
readSkillBody: () => readSkillBody,
|
|
6618
7119
|
readThread: () => readThread,
|
|
6619
7120
|
readWorktreeFile: () => readWorktreeFile,
|
|
@@ -6625,6 +7126,7 @@ __export(index_exports, {
|
|
|
6625
7126
|
repoSlug: () => repoSlug,
|
|
6626
7127
|
requestReview: () => requestReview,
|
|
6627
7128
|
requireAgent: () => requireAgent,
|
|
7129
|
+
resetGhStackDetectCache: () => resetGhStackDetectCache,
|
|
6628
7130
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
6629
7131
|
resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
|
|
6630
7132
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
@@ -6632,7 +7134,9 @@ __export(index_exports, {
|
|
|
6632
7134
|
resolveDiffBaseRef: () => resolveDiffBaseRef,
|
|
6633
7135
|
resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
|
|
6634
7136
|
resolveFilesToCopy: () => resolveFilesToCopy,
|
|
7137
|
+
resolveGhAuthToken: () => resolveGhAuthToken,
|
|
6635
7138
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
7139
|
+
resolvePlanMarkdown: () => resolvePlanMarkdown,
|
|
6636
7140
|
resolvePrSelector: () => resolvePrSelector,
|
|
6637
7141
|
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6638
7142
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
@@ -6660,12 +7164,16 @@ __export(index_exports, {
|
|
|
6660
7164
|
slugify: () => slugify,
|
|
6661
7165
|
spawnAgentTurn: () => spawnAgentTurn,
|
|
6662
7166
|
splitForCompaction: () => splitForCompaction,
|
|
7167
|
+
stackAgentDefaultsFrom: () => stackAgentDefaultsFrom,
|
|
7168
|
+
stackIdFrom: () => stackIdFrom,
|
|
7169
|
+
stackMergeReadiness: () => stackMergeReadiness,
|
|
6663
7170
|
stageAbsolutePathsAsAttachments: () => stageAbsolutePathsAsAttachments,
|
|
6664
7171
|
stageBuffersAsAttachments: () => stageBuffersAsAttachments,
|
|
6665
7172
|
startDevServer: () => startDevServer,
|
|
6666
7173
|
startMcpServer: () => startMcpServer,
|
|
6667
7174
|
startOrchestration: () => startOrchestration,
|
|
6668
7175
|
stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
|
|
7176
|
+
submitPrStack: () => submitPrStack,
|
|
6669
7177
|
suggestSlug: () => suggestSlug,
|
|
6670
7178
|
summarizeConversation: () => summarizeConversation,
|
|
6671
7179
|
switchBrightsyAccount: () => switchBrightsyAccount,
|
|
@@ -6701,6 +7209,7 @@ __export(index_exports, {
|
|
|
6701
7209
|
worktreeNameFromPath: () => worktreeNameFromPath,
|
|
6702
7210
|
worktreesRoot: () => worktreesRoot,
|
|
6703
7211
|
writeInjectedMcpConfig: () => writeInjectedMcpConfig,
|
|
7212
|
+
writePlanFile: () => writePlanFile,
|
|
6704
7213
|
writeThread: () => writeThread,
|
|
6705
7214
|
writeWorktreeFile: () => writeWorktreeFile
|
|
6706
7215
|
});
|
|
@@ -6714,6 +7223,7 @@ init_global_workspace();
|
|
|
6714
7223
|
init_run();
|
|
6715
7224
|
init_gh_errors();
|
|
6716
7225
|
init_worktree();
|
|
7226
|
+
init_stack();
|
|
6717
7227
|
|
|
6718
7228
|
// src/integrations/github.ts
|
|
6719
7229
|
init_run();
|
|
@@ -6910,18 +7420,18 @@ function asRecord(input) {
|
|
|
6910
7420
|
}
|
|
6911
7421
|
return void 0;
|
|
6912
7422
|
}
|
|
6913
|
-
function
|
|
7423
|
+
function str2(v) {
|
|
6914
7424
|
return typeof v === "string" && v.trim() ? v : void 0;
|
|
6915
7425
|
}
|
|
6916
7426
|
function toolDetail(name, input) {
|
|
6917
7427
|
if (!input) return void 0;
|
|
6918
|
-
const command =
|
|
7428
|
+
const command = str2(input.command) ?? str2(input.cmd);
|
|
6919
7429
|
if (command) return command;
|
|
6920
|
-
const path =
|
|
7430
|
+
const path = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
6921
7431
|
if (path) return path;
|
|
6922
|
-
const pattern =
|
|
7432
|
+
const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
|
|
6923
7433
|
if (pattern) return pattern;
|
|
6924
|
-
const query =
|
|
7434
|
+
const query = str2(input.query) ?? str2(input.prompt);
|
|
6925
7435
|
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
6926
7436
|
try {
|
|
6927
7437
|
const raw = JSON.stringify(input);
|
|
@@ -6934,23 +7444,26 @@ function toolDescription(name, input) {
|
|
|
6934
7444
|
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
6935
7445
|
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
6936
7446
|
if (/connectedAgentRequest/i.test(name)) {
|
|
6937
|
-
return
|
|
7447
|
+
return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
6938
7448
|
}
|
|
6939
7449
|
if (/present_artifact$/i.test(name)) {
|
|
6940
|
-
return
|
|
7450
|
+
return str2(input?.title) ? `Present ${str2(input?.title)}` : "Present artifact";
|
|
7451
|
+
}
|
|
7452
|
+
if (/present_plan$/i.test(name)) {
|
|
7453
|
+
return str2(input?.title) ? `Plan ${str2(input?.title)}` : "Present plan";
|
|
6941
7454
|
}
|
|
6942
7455
|
if (/present_schema$/i.test(name)) {
|
|
6943
|
-
return
|
|
7456
|
+
return str2(input?.title) ? `Schema ${str2(input?.title)}` : "Present schema";
|
|
6944
7457
|
}
|
|
6945
7458
|
if (/present_files$/i.test(name)) {
|
|
6946
|
-
return
|
|
7459
|
+
return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
|
|
6947
7460
|
}
|
|
6948
7461
|
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
6949
|
-
return
|
|
7462
|
+
return str2(input?.title) ? `Artifact ${str2(input?.title)}` : "Artifact";
|
|
6950
7463
|
}
|
|
6951
7464
|
if (!input) return n;
|
|
6952
|
-
if (/bash|shell|terminal/i.test(name) &&
|
|
6953
|
-
const cmd =
|
|
7465
|
+
if (/bash|shell|terminal/i.test(name) && str2(input.command)) {
|
|
7466
|
+
const cmd = str2(input.command);
|
|
6954
7467
|
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
6955
7468
|
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
6956
7469
|
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
@@ -6959,11 +7472,11 @@ function toolDescription(name, input) {
|
|
|
6959
7472
|
return "Run shell command";
|
|
6960
7473
|
}
|
|
6961
7474
|
if (/edit|write|apply/i.test(name)) {
|
|
6962
|
-
const path =
|
|
7475
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
6963
7476
|
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
6964
7477
|
}
|
|
6965
7478
|
if (/read/i.test(name)) {
|
|
6966
|
-
const path =
|
|
7479
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
6967
7480
|
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
6968
7481
|
}
|
|
6969
7482
|
if (/grep/i.test(name)) return "Search files";
|
|
@@ -6972,7 +7485,7 @@ function toolDescription(name, input) {
|
|
|
6972
7485
|
}
|
|
6973
7486
|
function toolFilePath(input) {
|
|
6974
7487
|
if (!input) return void 0;
|
|
6975
|
-
return
|
|
7488
|
+
return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
6976
7489
|
}
|
|
6977
7490
|
function countLines(text) {
|
|
6978
7491
|
if (!text) return 0;
|
|
@@ -6980,8 +7493,8 @@ function countLines(text) {
|
|
|
6980
7493
|
}
|
|
6981
7494
|
function diffFromInput(input) {
|
|
6982
7495
|
if (!input) return {};
|
|
6983
|
-
const oldS =
|
|
6984
|
-
const newS =
|
|
7496
|
+
const oldS = str2(input.old_string) ?? str2(input.oldString);
|
|
7497
|
+
const newS = str2(input.new_string) ?? str2(input.newString) ?? str2(input.content);
|
|
6985
7498
|
if (oldS != null || newS != null) {
|
|
6986
7499
|
return {
|
|
6987
7500
|
additions: countLines(newS),
|
|
@@ -7990,7 +8503,7 @@ async function listUntrackedPaths(worktreePath) {
|
|
|
7990
8503
|
worktreePath,
|
|
7991
8504
|
{ reject: false }
|
|
7992
8505
|
);
|
|
7993
|
-
return stdout.split("\0").filter(Boolean);
|
|
8506
|
+
return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
|
|
7994
8507
|
}
|
|
7995
8508
|
async function listUntrackedFiles(worktreePath, maxHunk) {
|
|
7996
8509
|
const paths = await listUntrackedPaths(worktreePath);
|
|
@@ -8148,33 +8661,33 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8148
8661
|
let combinedDiff = "";
|
|
8149
8662
|
let labelBase = base;
|
|
8150
8663
|
if (scope === "staged") {
|
|
8151
|
-
const [ns,
|
|
8664
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8152
8665
|
git(["diff", "--name-status", "--cached"], worktreePath, { reject: false }),
|
|
8153
8666
|
git(["diff", "--numstat", "--cached"], worktreePath, { reject: false }),
|
|
8154
8667
|
git(["diff", "--cached"], worktreePath, { reject: false })
|
|
8155
8668
|
]);
|
|
8156
8669
|
nameStatus = ns.stdout;
|
|
8157
|
-
numstat =
|
|
8670
|
+
numstat = num2.stdout;
|
|
8158
8671
|
combinedDiff = diff.stdout;
|
|
8159
8672
|
labelBase = "staged";
|
|
8160
8673
|
} else if (scope === "unstaged") {
|
|
8161
|
-
const [ns,
|
|
8674
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8162
8675
|
git(["diff", "--name-status"], worktreePath, { reject: false }),
|
|
8163
8676
|
git(["diff", "--numstat"], worktreePath, { reject: false }),
|
|
8164
8677
|
git(["diff"], worktreePath, { reject: false })
|
|
8165
8678
|
]);
|
|
8166
8679
|
nameStatus = ns.stdout;
|
|
8167
|
-
numstat =
|
|
8680
|
+
numstat = num2.stdout;
|
|
8168
8681
|
combinedDiff = diff.stdout;
|
|
8169
8682
|
labelBase = "unstaged";
|
|
8170
8683
|
} else if (scope === "uncommitted") {
|
|
8171
|
-
const [ns,
|
|
8684
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8172
8685
|
git(["diff", "--name-status", "HEAD"], worktreePath, { reject: false }),
|
|
8173
8686
|
git(["diff", "--numstat", "HEAD"], worktreePath, { reject: false }),
|
|
8174
8687
|
git(["diff", "HEAD"], worktreePath, { reject: false })
|
|
8175
8688
|
]);
|
|
8176
8689
|
nameStatus = ns.stdout;
|
|
8177
|
-
numstat =
|
|
8690
|
+
numstat = num2.stdout;
|
|
8178
8691
|
combinedDiff = diff.stdout;
|
|
8179
8692
|
labelBase = "HEAD";
|
|
8180
8693
|
} else if (scope === "last_turn") {
|
|
@@ -8193,34 +8706,34 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8193
8706
|
scopeStats
|
|
8194
8707
|
};
|
|
8195
8708
|
}
|
|
8196
|
-
const [ns,
|
|
8709
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8197
8710
|
git(["diff", "--name-status", lastTurnBase], worktreePath, { reject: false }),
|
|
8198
8711
|
git(["diff", "--numstat", lastTurnBase], worktreePath, { reject: false }),
|
|
8199
8712
|
git(["diff", lastTurnBase], worktreePath, { reject: false })
|
|
8200
8713
|
]);
|
|
8201
8714
|
nameStatus = ns.stdout;
|
|
8202
|
-
numstat =
|
|
8715
|
+
numstat = num2.stdout;
|
|
8203
8716
|
combinedDiff = diff.stdout;
|
|
8204
8717
|
labelBase = "last turn";
|
|
8205
8718
|
} else if (scope === "commits" && commitSha) {
|
|
8206
8719
|
const range = `${commitSha}^!`;
|
|
8207
|
-
const [ns,
|
|
8720
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8208
8721
|
git(["diff", "--name-status", range], worktreePath, { reject: false }),
|
|
8209
8722
|
git(["diff", "--numstat", range], worktreePath, { reject: false }),
|
|
8210
8723
|
git(["diff", range], worktreePath, { reject: false })
|
|
8211
8724
|
]);
|
|
8212
8725
|
nameStatus = ns.stdout;
|
|
8213
|
-
numstat =
|
|
8726
|
+
numstat = num2.stdout;
|
|
8214
8727
|
combinedDiff = diff.stdout;
|
|
8215
8728
|
labelBase = commitSha.slice(0, 7);
|
|
8216
8729
|
} else {
|
|
8217
|
-
const [ns,
|
|
8730
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8218
8731
|
git(["diff", "--name-status", mergeBase], worktreePath, { reject: false }),
|
|
8219
8732
|
git(["diff", "--numstat", mergeBase], worktreePath, { reject: false }),
|
|
8220
8733
|
git(["diff", mergeBase], worktreePath, { reject: false })
|
|
8221
8734
|
]);
|
|
8222
8735
|
nameStatus = ns.stdout;
|
|
8223
|
-
numstat =
|
|
8736
|
+
numstat = num2.stdout;
|
|
8224
8737
|
combinedDiff = diff.stdout;
|
|
8225
8738
|
labelBase = base;
|
|
8226
8739
|
}
|
|
@@ -8653,6 +9166,7 @@ function buildDiffCommentAttachment(input) {
|
|
|
8653
9166
|
var import_node_fs19 = require("fs");
|
|
8654
9167
|
var import_node_path19 = require("path");
|
|
8655
9168
|
var import_node_crypto2 = require("crypto");
|
|
9169
|
+
init_workspace_scratch();
|
|
8656
9170
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
8657
9171
|
"png",
|
|
8658
9172
|
"jpg",
|
|
@@ -8673,11 +9187,6 @@ var IMAGE_MIME_BY_EXT = {
|
|
|
8673
9187
|
bmp: "image/bmp",
|
|
8674
9188
|
ico: "image/x-icon"
|
|
8675
9189
|
};
|
|
8676
|
-
var ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
8677
|
-
var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
|
|
8678
|
-
*
|
|
8679
|
-
!.gitignore
|
|
8680
|
-
`;
|
|
8681
9190
|
var MAX_INLINE_BYTES = 4e5;
|
|
8682
9191
|
var MAX_PREVIEW_BYTES = 5e6;
|
|
8683
9192
|
function fileExtension(filePath) {
|
|
@@ -8695,7 +9204,7 @@ function ensureAttachmentsDir(worktreePath) {
|
|
|
8695
9204
|
(0, import_node_fs19.mkdirSync)(dir, { recursive: true });
|
|
8696
9205
|
const gi = (0, import_node_path19.join)(dir, ".gitignore");
|
|
8697
9206
|
if (!(0, import_node_fs19.existsSync)(gi)) {
|
|
8698
|
-
(0, import_node_fs19.writeFileSync)(gi,
|
|
9207
|
+
(0, import_node_fs19.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
8699
9208
|
}
|
|
8700
9209
|
return dir;
|
|
8701
9210
|
}
|
|
@@ -9355,7 +9864,7 @@ var import_node_fs20 = require("fs");
|
|
|
9355
9864
|
init_worktree();
|
|
9356
9865
|
init_thread_store();
|
|
9357
9866
|
init_workspaces();
|
|
9358
|
-
async function createThread(input,
|
|
9867
|
+
async function createThread(input, _onSetupLine) {
|
|
9359
9868
|
await requireAgent(input.agent);
|
|
9360
9869
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9361
9870
|
if (!(0, import_node_fs20.existsSync)(repoPath)) {
|
|
@@ -9365,10 +9874,10 @@ async function createThread(input, onSetupLine) {
|
|
|
9365
9874
|
let sourceIsFork = false;
|
|
9366
9875
|
let prUrl = null;
|
|
9367
9876
|
if (input.sourceType === "pr") {
|
|
9368
|
-
const
|
|
9369
|
-
if (!Number.isFinite(
|
|
9370
|
-
const pr = await getPr(repoPath,
|
|
9371
|
-
if (!pr) throw new Error(`PR #${
|
|
9877
|
+
const num2 = Number(input.sourceRef.replace(/^#/, ""));
|
|
9878
|
+
if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
|
|
9879
|
+
const pr = await getPr(repoPath, num2);
|
|
9880
|
+
if (!pr) throw new Error(`PR #${num2} not found`);
|
|
9372
9881
|
sourceIsFork = pr.isCrossRepository;
|
|
9373
9882
|
prUrl = pr.url;
|
|
9374
9883
|
const localFetchBranch = `sideboard-pr-${pr.number}`;
|
|
@@ -9413,22 +9922,6 @@ async function createThread(input, onSetupLine) {
|
|
|
9413
9922
|
});
|
|
9414
9923
|
writeThread(thread);
|
|
9415
9924
|
await ensureWorkspace(repoPath);
|
|
9416
|
-
try {
|
|
9417
|
-
let setup = await runSetupScript(repoPath, worktreePath, onSetupLine);
|
|
9418
|
-
if (!setup.ran) {
|
|
9419
|
-
setup = await runCursorWorktreeSetup(repoPath, worktreePath, onSetupLine);
|
|
9420
|
-
}
|
|
9421
|
-
if (setup.ran && setup.exitCode !== 0 && setup.exitCode !== null) {
|
|
9422
|
-
updateThread(thread.id, {
|
|
9423
|
-
lastError: `Setup exited ${setup.exitCode} (thread is still usable)`
|
|
9424
|
-
});
|
|
9425
|
-
}
|
|
9426
|
-
} catch (err) {
|
|
9427
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
9428
|
-
updateThread(thread.id, {
|
|
9429
|
-
lastError: `Setup failed: ${message}`
|
|
9430
|
-
});
|
|
9431
|
-
}
|
|
9432
9925
|
return readThread(thread.id) ?? thread;
|
|
9433
9926
|
}
|
|
9434
9927
|
async function listLinearIssues(agent, repoPath) {
|
|
@@ -9481,7 +9974,9 @@ function worktreeBindingFrom(from) {
|
|
|
9481
9974
|
sourceIsFork: from.sourceIsFork,
|
|
9482
9975
|
parentThreadId: from.parentThreadId,
|
|
9483
9976
|
prUrl: from.prUrl,
|
|
9484
|
-
prTitle: from.prTitle
|
|
9977
|
+
prTitle: from.prTitle,
|
|
9978
|
+
stackId: from.stackId,
|
|
9979
|
+
stackLayer: from.stackLayer
|
|
9485
9980
|
};
|
|
9486
9981
|
}
|
|
9487
9982
|
function threadsSharingWorktree(worktreePath) {
|
|
@@ -9596,9 +10091,308 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
9596
10091
|
return thread;
|
|
9597
10092
|
}
|
|
9598
10093
|
|
|
10094
|
+
// src/threads/stack-layers.ts
|
|
10095
|
+
var import_node_fs21 = require("fs");
|
|
10096
|
+
init_run();
|
|
10097
|
+
init_stack();
|
|
10098
|
+
init_worktree();
|
|
10099
|
+
init_thread_store();
|
|
10100
|
+
init_workspaces();
|
|
10101
|
+
function stackIdFrom(stack) {
|
|
10102
|
+
if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
|
|
10103
|
+
const key = stack.layers.map((l) => l.branchName).join("|");
|
|
10104
|
+
if (!key) return null;
|
|
10105
|
+
return `gh-stack-local-${hashShort(key)}`;
|
|
10106
|
+
}
|
|
10107
|
+
function hashShort(s) {
|
|
10108
|
+
let h = 0;
|
|
10109
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
10110
|
+
return Math.abs(h).toString(36);
|
|
10111
|
+
}
|
|
10112
|
+
function requireThread3(idOrRef) {
|
|
10113
|
+
const thread = findThreadByRef(idOrRef) ?? readThread(idOrRef);
|
|
10114
|
+
if (!thread) throw new Error(`Thread not found: ${idOrRef}`);
|
|
10115
|
+
return thread;
|
|
10116
|
+
}
|
|
10117
|
+
function sanitizeSlugPart(name) {
|
|
10118
|
+
return name.trim().replace(/^refs\/heads\//, "").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "layer";
|
|
10119
|
+
}
|
|
10120
|
+
function layerSlug(stackId, layer) {
|
|
10121
|
+
const stackPart = stackId.replace(/^gh-stack-/, "s");
|
|
10122
|
+
return sanitizeSlugPart(`${stackPart}-L${layer.position}-${layer.branchName}`);
|
|
10123
|
+
}
|
|
10124
|
+
function findThreadForStackLayer(repoPath, stackId, layer) {
|
|
10125
|
+
const threads = listThreads({ includeArchived: false }).filter(
|
|
10126
|
+
(t) => t.repoPath === repoPath && t.stackId === stackId
|
|
10127
|
+
);
|
|
10128
|
+
const byLayer = threads.find((t) => t.stackLayer === layer.position);
|
|
10129
|
+
if (byLayer) return byLayer;
|
|
10130
|
+
return threads.find((t) => t.branchName === layer.branchName) ?? listThreads({ includeArchived: false }).find(
|
|
10131
|
+
(t) => t.repoPath === repoPath && t.branchName === layer.branchName
|
|
10132
|
+
) ?? null;
|
|
10133
|
+
}
|
|
10134
|
+
async function openStackLayer(input, _onSetupLine) {
|
|
10135
|
+
await requireAgent(input.agent);
|
|
10136
|
+
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
10137
|
+
const stackId = stackIdFrom(input.stack);
|
|
10138
|
+
if (!stackId) throw new Error("Cannot open stack layer without a stack id");
|
|
10139
|
+
const existing = findThreadForStackLayer(repoPath, stackId, input.layer);
|
|
10140
|
+
if (existing) {
|
|
10141
|
+
const patch = {};
|
|
10142
|
+
if (existing.stackId !== stackId) patch.stackId = stackId;
|
|
10143
|
+
if (existing.stackLayer !== input.layer.position) {
|
|
10144
|
+
patch.stackLayer = input.layer.position;
|
|
10145
|
+
}
|
|
10146
|
+
if (input.layer.prUrl && existing.prUrl !== input.layer.prUrl) {
|
|
10147
|
+
patch.prUrl = input.layer.prUrl;
|
|
10148
|
+
}
|
|
10149
|
+
if (input.layer.title && existing.prTitle !== input.layer.title) {
|
|
10150
|
+
patch.prTitle = input.layer.title;
|
|
10151
|
+
}
|
|
10152
|
+
if (Object.keys(patch).length > 0) updateThread(existing.id, patch);
|
|
10153
|
+
return {
|
|
10154
|
+
thread: readThread(existing.id) ?? existing,
|
|
10155
|
+
createdWorktree: false
|
|
10156
|
+
};
|
|
10157
|
+
}
|
|
10158
|
+
let worktreePath = null;
|
|
10159
|
+
let branchName = input.layer.branchName;
|
|
10160
|
+
let createdWorktree = false;
|
|
10161
|
+
const trees = await listWorktrees(repoPath);
|
|
10162
|
+
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
10163
|
+
if (checkedOut?.path && (0, import_node_fs21.existsSync)(checkedOut.path)) {
|
|
10164
|
+
if (input.reuseExistingWorktree !== false) {
|
|
10165
|
+
worktreePath = checkedOut.path;
|
|
10166
|
+
} else {
|
|
10167
|
+
throw new Error(
|
|
10168
|
+
`Branch ${branchName} is already checked out at ${checkedOut.path}`
|
|
10169
|
+
);
|
|
10170
|
+
}
|
|
10171
|
+
}
|
|
10172
|
+
if (!worktreePath) {
|
|
10173
|
+
const slug = layerSlug(stackId, input.layer);
|
|
10174
|
+
const created = await createExistingBranchWorktree({
|
|
10175
|
+
repoPath,
|
|
10176
|
+
branchName,
|
|
10177
|
+
slug
|
|
10178
|
+
});
|
|
10179
|
+
worktreePath = created.worktreePath;
|
|
10180
|
+
branchName = created.branchName;
|
|
10181
|
+
copyConfiguredFiles(repoPath, worktreePath);
|
|
10182
|
+
createdWorktree = true;
|
|
10183
|
+
}
|
|
10184
|
+
const title = input.layer.title?.trim() || (input.layer.prNumber != null ? `PR #${input.layer.prNumber}` : input.layer.branchName);
|
|
10185
|
+
const thread = createEmptyThread({
|
|
10186
|
+
title,
|
|
10187
|
+
userSetTitle: Boolean(input.layer.title?.trim()),
|
|
10188
|
+
sourceType: input.layer.prNumber != null ? "pr" : "branch",
|
|
10189
|
+
sourceRef: input.layer.prNumber != null ? String(input.layer.prNumber) : input.layer.branchName,
|
|
10190
|
+
branchName,
|
|
10191
|
+
worktreePath,
|
|
10192
|
+
repoPath,
|
|
10193
|
+
agent: input.agent,
|
|
10194
|
+
autonomy: input.autonomy ?? "default",
|
|
10195
|
+
model: input.model ?? null,
|
|
10196
|
+
effort: input.effort ?? "high",
|
|
10197
|
+
fast: Boolean(input.fast),
|
|
10198
|
+
planMode: Boolean(input.planMode),
|
|
10199
|
+
parentThreadId: input.parentThreadId ?? null,
|
|
10200
|
+
status: "idle",
|
|
10201
|
+
prUrl: input.layer.prUrl,
|
|
10202
|
+
prTitle: input.layer.title ?? null,
|
|
10203
|
+
stackId,
|
|
10204
|
+
stackLayer: input.layer.position
|
|
10205
|
+
});
|
|
10206
|
+
writeThread(thread);
|
|
10207
|
+
await ensureWorkspace(repoPath);
|
|
10208
|
+
return { thread: readThread(thread.id) ?? thread, createdWorktree };
|
|
10209
|
+
}
|
|
10210
|
+
async function openPrStackLayers(input, onSetupLine) {
|
|
10211
|
+
const from = requireThread3(input.threadRef);
|
|
10212
|
+
if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
|
|
10213
|
+
throw new Error("Thread has no worktree");
|
|
10214
|
+
}
|
|
10215
|
+
const stack = await getPrStack(from.worktreePath);
|
|
10216
|
+
if (!stack) throw new Error("Current branch is not part of a GitHub PR stack");
|
|
10217
|
+
const layers = input.layer != null ? stack.layers.filter((l) => l.position === input.layer) : stack.layers;
|
|
10218
|
+
if (!layers.length) {
|
|
10219
|
+
throw new Error(
|
|
10220
|
+
input.layer != null ? `No stack layer at position ${input.layer}` : "Stack has no layers"
|
|
10221
|
+
);
|
|
10222
|
+
}
|
|
10223
|
+
const threads = [];
|
|
10224
|
+
const createdThreadIds = [];
|
|
10225
|
+
for (const layer of layers) {
|
|
10226
|
+
const { thread, createdWorktree } = await openStackLayer(
|
|
10227
|
+
{
|
|
10228
|
+
repoPath: from.repoPath,
|
|
10229
|
+
stack,
|
|
10230
|
+
layer,
|
|
10231
|
+
agent: from.agent,
|
|
10232
|
+
autonomy: from.autonomy,
|
|
10233
|
+
model: from.model,
|
|
10234
|
+
effort: from.effort,
|
|
10235
|
+
fast: from.fast,
|
|
10236
|
+
planMode: from.planMode,
|
|
10237
|
+
parentThreadId: from.id
|
|
10238
|
+
},
|
|
10239
|
+
onSetupLine
|
|
10240
|
+
);
|
|
10241
|
+
threads.push(thread);
|
|
10242
|
+
if (createdWorktree) createdThreadIds.push(thread.id);
|
|
10243
|
+
}
|
|
10244
|
+
const stackId = stackIdFrom(stack);
|
|
10245
|
+
const current = stack.layers[stack.currentIndex];
|
|
10246
|
+
if (stackId) {
|
|
10247
|
+
updateThread(from.id, {
|
|
10248
|
+
stackId,
|
|
10249
|
+
stackLayer: current?.position ?? from.stackLayer
|
|
10250
|
+
});
|
|
10251
|
+
}
|
|
10252
|
+
return { stack, threads, createdThreadIds };
|
|
10253
|
+
}
|
|
10254
|
+
async function addStackLayerFromThread(input, onSetupLine) {
|
|
10255
|
+
const from = requireThread3(input.threadRef);
|
|
10256
|
+
if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
|
|
10257
|
+
throw new Error("Thread has no worktree");
|
|
10258
|
+
}
|
|
10259
|
+
const status = await detectGhStack(from.worktreePath);
|
|
10260
|
+
if (!status.available) throw new Error(status.reason);
|
|
10261
|
+
await addPrStackLayer(from.worktreePath, input.branchName);
|
|
10262
|
+
const stack = await getPrStack(from.worktreePath);
|
|
10263
|
+
if (!stack) throw new Error("Stack not found after adding layer");
|
|
10264
|
+
const layer = stack.layers.find((l) => l.branchName === input.branchName.trim()) ?? stack.layers[stack.layers.length - 1];
|
|
10265
|
+
if (!layer) throw new Error("New stack layer not found");
|
|
10266
|
+
if (input.title?.trim()) {
|
|
10267
|
+
layer.title = input.title.trim();
|
|
10268
|
+
}
|
|
10269
|
+
const thread = await openStackLayer(
|
|
10270
|
+
{
|
|
10271
|
+
repoPath: from.repoPath,
|
|
10272
|
+
stack,
|
|
10273
|
+
layer,
|
|
10274
|
+
agent: from.agent,
|
|
10275
|
+
autonomy: from.autonomy,
|
|
10276
|
+
model: from.model,
|
|
10277
|
+
effort: from.effort,
|
|
10278
|
+
fast: from.fast,
|
|
10279
|
+
planMode: from.planMode,
|
|
10280
|
+
parentThreadId: from.id
|
|
10281
|
+
},
|
|
10282
|
+
onSetupLine
|
|
10283
|
+
);
|
|
10284
|
+
return { stack, thread: thread.thread, createdWorktree: thread.createdWorktree };
|
|
10285
|
+
}
|
|
10286
|
+
async function initStackFromThread(input, onSetupLine) {
|
|
10287
|
+
const from = requireThread3(input.threadRef);
|
|
10288
|
+
if (!from.worktreePath?.trim() || !from.branchName?.trim() || !from.repoPath?.trim()) {
|
|
10289
|
+
throw new Error("Thread has no worktree/branch");
|
|
10290
|
+
}
|
|
10291
|
+
const status = await detectGhStack(from.worktreePath);
|
|
10292
|
+
if (!status.available) throw new Error(status.reason);
|
|
10293
|
+
const branches = [
|
|
10294
|
+
from.branchName,
|
|
10295
|
+
...(input.additionalBranches ?? []).map((b) => b.trim()).filter(Boolean)
|
|
10296
|
+
];
|
|
10297
|
+
await initPrStack(from.worktreePath, branches, {
|
|
10298
|
+
base: input.base ?? await resolveDefaultBranch(from.repoPath)
|
|
10299
|
+
});
|
|
10300
|
+
return openPrStackLayers({ threadRef: from.id }, onSetupLine);
|
|
10301
|
+
}
|
|
10302
|
+
async function createPrStack(input, onSetupLine) {
|
|
10303
|
+
await requireAgent(input.agent);
|
|
10304
|
+
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
10305
|
+
if (!(0, import_node_fs21.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
10306
|
+
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
10307
|
+
const status = await detectGhStack(repoPath);
|
|
10308
|
+
if (!status.available) throw new Error(status.reason);
|
|
10309
|
+
const team = allocateTeamSlug(repoPath);
|
|
10310
|
+
const base = input.base ?? await resolveDefaultBranch(repoPath);
|
|
10311
|
+
const bootstrap = await createThreadWorktree({
|
|
10312
|
+
repoPath,
|
|
10313
|
+
sourceRef: base,
|
|
10314
|
+
slug: `${team.slug}-stack-init`
|
|
10315
|
+
});
|
|
10316
|
+
copyConfiguredFiles(repoPath, bootstrap.worktreePath);
|
|
10317
|
+
try {
|
|
10318
|
+
await initPrStack(bootstrap.worktreePath, input.branches, { base });
|
|
10319
|
+
const bottom = input.branches[0];
|
|
10320
|
+
const co = await git(["checkout", bottom], bootstrap.worktreePath, {
|
|
10321
|
+
reject: false
|
|
10322
|
+
});
|
|
10323
|
+
if (co.exitCode !== 0) {
|
|
10324
|
+
throw new Error(
|
|
10325
|
+
co.stderr.trim() || co.stdout.trim() || `Could not check out ${bottom} after stack init`
|
|
10326
|
+
);
|
|
10327
|
+
}
|
|
10328
|
+
} catch (err) {
|
|
10329
|
+
try {
|
|
10330
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10331
|
+
deleteBranch: bootstrap.branchName
|
|
10332
|
+
});
|
|
10333
|
+
} catch {
|
|
10334
|
+
}
|
|
10335
|
+
throw err;
|
|
10336
|
+
}
|
|
10337
|
+
const stack = await getPrStack(bootstrap.worktreePath);
|
|
10338
|
+
if (!stack) {
|
|
10339
|
+
try {
|
|
10340
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10341
|
+
deleteBranch: bootstrap.branchName
|
|
10342
|
+
});
|
|
10343
|
+
} catch {
|
|
10344
|
+
}
|
|
10345
|
+
throw new Error("Stack init succeeded but gh stack view returned no stack");
|
|
10346
|
+
}
|
|
10347
|
+
const threads = [];
|
|
10348
|
+
const createdThreadIds = [];
|
|
10349
|
+
for (const layer of stack.layers) {
|
|
10350
|
+
const title = layer.position === 1 && input.title?.trim() ? input.title.trim() : layer.title;
|
|
10351
|
+
const opened = await openStackLayer(
|
|
10352
|
+
{
|
|
10353
|
+
repoPath,
|
|
10354
|
+
stack,
|
|
10355
|
+
layer: title ? { ...layer, title } : layer,
|
|
10356
|
+
agent: input.agent,
|
|
10357
|
+
autonomy: input.autonomy,
|
|
10358
|
+
model: input.model,
|
|
10359
|
+
effort: input.effort,
|
|
10360
|
+
fast: input.fast,
|
|
10361
|
+
planMode: input.planMode,
|
|
10362
|
+
reuseExistingWorktree: true
|
|
10363
|
+
},
|
|
10364
|
+
onSetupLine
|
|
10365
|
+
);
|
|
10366
|
+
threads.push(opened.thread);
|
|
10367
|
+
if (opened.createdWorktree || opened.thread.worktreePath === bootstrap.worktreePath) {
|
|
10368
|
+
createdThreadIds.push(opened.thread.id);
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10371
|
+
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
10372
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs21.existsSync)(bootstrap.worktreePath)) {
|
|
10373
|
+
try {
|
|
10374
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10375
|
+
deleteBranch: bootstrap.branchName
|
|
10376
|
+
});
|
|
10377
|
+
} catch {
|
|
10378
|
+
}
|
|
10379
|
+
}
|
|
10380
|
+
return { stack, threads, createdThreadIds };
|
|
10381
|
+
}
|
|
10382
|
+
function stackAgentDefaultsFrom(input) {
|
|
10383
|
+
return {
|
|
10384
|
+
agent: input.agent,
|
|
10385
|
+
autonomy: input.autonomy,
|
|
10386
|
+
model: input.model,
|
|
10387
|
+
effort: input.effort,
|
|
10388
|
+
fast: input.fast,
|
|
10389
|
+
planMode: input.planMode
|
|
10390
|
+
};
|
|
10391
|
+
}
|
|
10392
|
+
|
|
9599
10393
|
// src/threads/adopt.ts
|
|
9600
10394
|
var import_node_child_process = require("child_process");
|
|
9601
|
-
var
|
|
10395
|
+
var import_node_fs22 = require("fs");
|
|
9602
10396
|
var import_node_os9 = require("os");
|
|
9603
10397
|
var import_node_path20 = require("path");
|
|
9604
10398
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -9623,21 +10417,21 @@ function mapAgentType(raw) {
|
|
|
9623
10417
|
return null;
|
|
9624
10418
|
}
|
|
9625
10419
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
9626
|
-
if (!workspacePath || !(0,
|
|
10420
|
+
if (!workspacePath || !(0, import_node_fs22.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
9627
10421
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
9628
10422
|
let best = null;
|
|
9629
10423
|
let hashes;
|
|
9630
10424
|
try {
|
|
9631
|
-
hashes = (0,
|
|
10425
|
+
hashes = (0, import_node_fs22.readdirSync)(CURSOR_SDK_STORE);
|
|
9632
10426
|
} catch {
|
|
9633
10427
|
return null;
|
|
9634
10428
|
}
|
|
9635
10429
|
for (const hash of hashes) {
|
|
9636
10430
|
const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
9637
|
-
if (!(0,
|
|
10431
|
+
if (!(0, import_node_fs22.existsSync)(agentsFile)) continue;
|
|
9638
10432
|
let text;
|
|
9639
10433
|
try {
|
|
9640
|
-
text = (0,
|
|
10434
|
+
text = (0, import_node_fs22.readFileSync)(agentsFile, "utf8");
|
|
9641
10435
|
} catch {
|
|
9642
10436
|
continue;
|
|
9643
10437
|
}
|
|
@@ -9661,7 +10455,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
9661
10455
|
return best?.agentId ?? null;
|
|
9662
10456
|
}
|
|
9663
10457
|
async function adoptThread(input) {
|
|
9664
|
-
if (!(0,
|
|
10458
|
+
if (!(0, import_node_fs22.existsSync)(input.worktreePath)) {
|
|
9665
10459
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
9666
10460
|
}
|
|
9667
10461
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -9688,18 +10482,18 @@ function conductorDbPath() {
|
|
|
9688
10482
|
return CONDUCTOR_DB;
|
|
9689
10483
|
}
|
|
9690
10484
|
function listConductorWorkspaces() {
|
|
9691
|
-
if (!(0,
|
|
10485
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9692
10486
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9693
10487
|
}
|
|
9694
|
-
const tmp = (0,
|
|
10488
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9695
10489
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9696
10490
|
try {
|
|
9697
|
-
(0,
|
|
10491
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9698
10492
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9699
10493
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9700
|
-
if ((0,
|
|
10494
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9701
10495
|
try {
|
|
9702
|
-
(0,
|
|
10496
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9703
10497
|
} catch {
|
|
9704
10498
|
}
|
|
9705
10499
|
}
|
|
@@ -9775,22 +10569,22 @@ function listConductorWorkspaces() {
|
|
|
9775
10569
|
db.close();
|
|
9776
10570
|
}
|
|
9777
10571
|
} finally {
|
|
9778
|
-
(0,
|
|
10572
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9779
10573
|
}
|
|
9780
10574
|
}
|
|
9781
10575
|
function importConductorWorkspace(workspaceId) {
|
|
9782
|
-
if (!(0,
|
|
10576
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9783
10577
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9784
10578
|
}
|
|
9785
|
-
const tmp = (0,
|
|
10579
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9786
10580
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9787
10581
|
try {
|
|
9788
|
-
(0,
|
|
10582
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9789
10583
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9790
10584
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9791
|
-
if ((0,
|
|
10585
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9792
10586
|
try {
|
|
9793
|
-
(0,
|
|
10587
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9794
10588
|
} catch {
|
|
9795
10589
|
}
|
|
9796
10590
|
}
|
|
@@ -9808,7 +10602,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9808
10602
|
).get(workspaceId);
|
|
9809
10603
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
9810
10604
|
const worktreePath = String(row.workspacePath);
|
|
9811
|
-
if (!(0,
|
|
10605
|
+
if (!(0, import_node_fs22.existsSync)(worktreePath)) {
|
|
9812
10606
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
9813
10607
|
}
|
|
9814
10608
|
let sessionId = null;
|
|
@@ -9871,7 +10665,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9871
10665
|
db.close();
|
|
9872
10666
|
}
|
|
9873
10667
|
} finally {
|
|
9874
|
-
(0,
|
|
10668
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9875
10669
|
}
|
|
9876
10670
|
}
|
|
9877
10671
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -9880,13 +10674,14 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
9880
10674
|
|
|
9881
10675
|
// src/orchestrator/orchestrator.ts
|
|
9882
10676
|
var import_node_events = require("events");
|
|
9883
|
-
var
|
|
10677
|
+
var import_node_fs28 = require("fs");
|
|
9884
10678
|
init_error_detail();
|
|
9885
10679
|
init_agents();
|
|
9886
10680
|
init_worktree();
|
|
10681
|
+
init_stack();
|
|
9887
10682
|
|
|
9888
10683
|
// src/git/orphan-cleanup.ts
|
|
9889
|
-
var
|
|
10684
|
+
var import_node_fs23 = require("fs");
|
|
9890
10685
|
var import_node_path21 = require("path");
|
|
9891
10686
|
init_worktree();
|
|
9892
10687
|
init_thread_store();
|
|
@@ -9902,9 +10697,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9902
10697
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
9903
10698
|
);
|
|
9904
10699
|
const homeRoot = sideboardWorkspacesDir();
|
|
9905
|
-
if ((0,
|
|
10700
|
+
if ((0, import_node_fs23.existsSync)(homeRoot)) {
|
|
9906
10701
|
try {
|
|
9907
|
-
for (const entry of (0,
|
|
10702
|
+
for (const entry of (0, import_node_fs23.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
9908
10703
|
if (!entry.isDirectory()) continue;
|
|
9909
10704
|
void entry;
|
|
9910
10705
|
}
|
|
@@ -9914,7 +10709,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9914
10709
|
const orphans = [];
|
|
9915
10710
|
const seen = /* @__PURE__ */ new Set();
|
|
9916
10711
|
for (const repoPath of repos) {
|
|
9917
|
-
if (!repoPath || !(0,
|
|
10712
|
+
if (!repoPath || !(0, import_node_fs23.existsSync)(repoPath)) continue;
|
|
9918
10713
|
try {
|
|
9919
10714
|
const wts = await listWorktrees(repoPath);
|
|
9920
10715
|
for (const wt of wts) {
|
|
@@ -9925,7 +10720,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9925
10720
|
seen.add(path);
|
|
9926
10721
|
let mtimeMs = 0;
|
|
9927
10722
|
try {
|
|
9928
|
-
mtimeMs = (0,
|
|
10723
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
9929
10724
|
} catch {
|
|
9930
10725
|
mtimeMs = 0;
|
|
9931
10726
|
}
|
|
@@ -9935,16 +10730,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9935
10730
|
}
|
|
9936
10731
|
try {
|
|
9937
10732
|
const root = worktreesRoot(repoPath);
|
|
9938
|
-
if ((0,
|
|
9939
|
-
for (const entry of (0,
|
|
10733
|
+
if ((0, import_node_fs23.existsSync)(root)) {
|
|
10734
|
+
for (const entry of (0, import_node_fs23.readdirSync)(root, { withFileTypes: true })) {
|
|
9940
10735
|
if (!entry.isDirectory()) continue;
|
|
9941
10736
|
const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
|
|
9942
10737
|
if (known.has(path) || seen.has(path)) continue;
|
|
9943
|
-
if (!(0,
|
|
10738
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
|
|
9944
10739
|
seen.add(path);
|
|
9945
10740
|
let mtimeMs = 0;
|
|
9946
10741
|
try {
|
|
9947
|
-
mtimeMs = (0,
|
|
10742
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
9948
10743
|
} catch {
|
|
9949
10744
|
mtimeMs = Date.now();
|
|
9950
10745
|
}
|
|
@@ -10090,7 +10885,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
10090
10885
|
}
|
|
10091
10886
|
|
|
10092
10887
|
// src/git/clone-repo.ts
|
|
10093
|
-
var
|
|
10888
|
+
var import_node_fs24 = require("fs");
|
|
10094
10889
|
var import_node_path22 = require("path");
|
|
10095
10890
|
var import_execa6 = require("execa");
|
|
10096
10891
|
init_paths();
|
|
@@ -10106,7 +10901,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
10106
10901
|
}
|
|
10107
10902
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
10108
10903
|
const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
|
|
10109
|
-
if ((0,
|
|
10904
|
+
if ((0, import_node_fs24.existsSync)(dest)) {
|
|
10110
10905
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
10111
10906
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
10112
10907
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -10128,7 +10923,7 @@ init_orchestrator_capable();
|
|
|
10128
10923
|
|
|
10129
10924
|
// src/review/request-review.ts
|
|
10130
10925
|
var import_node_crypto5 = require("crypto");
|
|
10131
|
-
var
|
|
10926
|
+
var import_node_fs25 = require("fs");
|
|
10132
10927
|
var import_node_path23 = require("path");
|
|
10133
10928
|
init_global_workspace();
|
|
10134
10929
|
init_thread_store();
|
|
@@ -10253,15 +11048,13 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
10253
11048
|
`;
|
|
10254
11049
|
|
|
10255
11050
|
// src/review/request-review.ts
|
|
11051
|
+
init_workspace_scratch();
|
|
10256
11052
|
var REPO_REVIEW_PATH = ".sideboard/review.md";
|
|
10257
11053
|
var REPO_REVIEW_NAME = "review.md";
|
|
10258
|
-
var REVIEW_REQUEST_PATH =
|
|
11054
|
+
var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
|
|
11055
|
+
var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
|
|
10259
11056
|
var REVIEW_REQUEST_NAME = "Review request.md";
|
|
10260
11057
|
var REVIEW_REQUEST_PREFILL = "Review.";
|
|
10261
|
-
var ATTACHMENTS_GITIGNORE2 = `# Sideboard review / composer attachments (local only)
|
|
10262
|
-
*
|
|
10263
|
-
!.gitignore
|
|
10264
|
-
`;
|
|
10265
11058
|
var LEGACY_REVIEW_TEMPLATE_MARKERS = [
|
|
10266
11059
|
"You are acting as a reviewer for a proposed code change made by another engineer.",
|
|
10267
11060
|
"HOW MANY FINDINGS TO RETURN:"
|
|
@@ -10275,19 +11068,19 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
10275
11068
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
10276
11069
|
}
|
|
10277
11070
|
function readTextIfPresent(abs) {
|
|
10278
|
-
if (!(0,
|
|
11071
|
+
if (!(0, import_node_fs25.existsSync)(abs)) return null;
|
|
10279
11072
|
try {
|
|
10280
|
-
const content = (0,
|
|
11073
|
+
const content = (0, import_node_fs25.readFileSync)(abs, "utf8");
|
|
10281
11074
|
return content.trim() ? content : null;
|
|
10282
11075
|
} catch {
|
|
10283
11076
|
return null;
|
|
10284
11077
|
}
|
|
10285
11078
|
}
|
|
10286
11079
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
10287
|
-
const gitignoreAbs = (0, import_node_path23.join)(worktreePath,
|
|
10288
|
-
if ((0,
|
|
10289
|
-
(0,
|
|
10290
|
-
(0,
|
|
11080
|
+
const gitignoreAbs = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
11081
|
+
if ((0, import_node_fs25.existsSync)(gitignoreAbs)) return;
|
|
11082
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
|
|
11083
|
+
(0, import_node_fs25.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
10291
11084
|
}
|
|
10292
11085
|
function resolveReviewGuidelines(worktreePath) {
|
|
10293
11086
|
const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -10310,9 +11103,19 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
10310
11103
|
source: "local"
|
|
10311
11104
|
};
|
|
10312
11105
|
}
|
|
11106
|
+
const legacyAbs = (0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
|
|
11107
|
+
const legacyContent = readTextIfPresent(legacyAbs);
|
|
11108
|
+
if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
|
|
11109
|
+
return {
|
|
11110
|
+
path: LEGACY_REVIEW_REQUEST_PATH,
|
|
11111
|
+
name: REVIEW_REQUEST_NAME,
|
|
11112
|
+
content: legacyContent,
|
|
11113
|
+
source: "local"
|
|
11114
|
+
};
|
|
11115
|
+
}
|
|
10313
11116
|
ensureAttachmentsGitignore(worktreePath);
|
|
10314
|
-
(0,
|
|
10315
|
-
(0,
|
|
11117
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
|
|
11118
|
+
(0, import_node_fs25.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
10316
11119
|
return {
|
|
10317
11120
|
path: REVIEW_REQUEST_PATH,
|
|
10318
11121
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -10332,17 +11135,18 @@ function ensureReviewRequestFile(worktreePath) {
|
|
|
10332
11135
|
};
|
|
10333
11136
|
}
|
|
10334
11137
|
const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
|
|
10335
|
-
const localContent = readTextIfPresent(localAbs);
|
|
11138
|
+
const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
10336
11139
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
11140
|
+
const path = (0, import_node_fs25.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
|
|
10337
11141
|
return {
|
|
10338
|
-
path
|
|
11142
|
+
path,
|
|
10339
11143
|
name: REVIEW_REQUEST_NAME,
|
|
10340
11144
|
content: localContent,
|
|
10341
11145
|
source: "local"
|
|
10342
11146
|
};
|
|
10343
11147
|
}
|
|
10344
|
-
(0,
|
|
10345
|
-
(0,
|
|
11148
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
|
|
11149
|
+
(0, import_node_fs25.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
10346
11150
|
return {
|
|
10347
11151
|
path: REPO_REVIEW_PATH,
|
|
10348
11152
|
name: REPO_REVIEW_NAME,
|
|
@@ -10362,7 +11166,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
10362
11166
|
};
|
|
10363
11167
|
}
|
|
10364
11168
|
function readExistingReviewRequestFile(worktreePath) {
|
|
10365
|
-
return readTextIfPresent((0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH));
|
|
11169
|
+
return readTextIfPresent((0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
10366
11170
|
}
|
|
10367
11171
|
async function requestReview(threadRef, send) {
|
|
10368
11172
|
const from = findThreadByRef(threadRef);
|
|
@@ -10501,6 +11305,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
10501
11305
|
|
|
10502
11306
|
// src/orchestrator/orchestrator.ts
|
|
10503
11307
|
init_types();
|
|
11308
|
+
init_plan_file();
|
|
10504
11309
|
init_settings();
|
|
10505
11310
|
|
|
10506
11311
|
// src/threads/sync-branch.ts
|
|
@@ -10620,7 +11425,7 @@ var Orchestrator = class {
|
|
|
10620
11425
|
}
|
|
10621
11426
|
continue;
|
|
10622
11427
|
}
|
|
10623
|
-
if (!(0,
|
|
11428
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
10624
11429
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10625
11430
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10626
11431
|
continue;
|
|
@@ -10765,14 +11570,9 @@ var Orchestrator = class {
|
|
|
10765
11570
|
return findThreadByRef(idOrRef) ?? readThread(idOrRef);
|
|
10766
11571
|
}
|
|
10767
11572
|
async createThread(input) {
|
|
10768
|
-
let thread = await createThread(input
|
|
10769
|
-
this.emit({
|
|
10770
|
-
type: "turn_output",
|
|
10771
|
-
threadId: "pending",
|
|
10772
|
-
event: { type: "stdout", data: line }
|
|
10773
|
-
});
|
|
10774
|
-
});
|
|
11573
|
+
let thread = await createThread(input);
|
|
10775
11574
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
11575
|
+
await this.runSetupAfterCreate(thread.id);
|
|
10776
11576
|
const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
10777
11577
|
if (autoRunAfterSetupEnabled2()) {
|
|
10778
11578
|
try {
|
|
@@ -10786,6 +11586,19 @@ var Orchestrator = class {
|
|
|
10786
11586
|
}
|
|
10787
11587
|
return thread;
|
|
10788
11588
|
}
|
|
11589
|
+
/** Run workspace setup after a new worktree is created (no-op if none configured). */
|
|
11590
|
+
async runSetupAfterCreate(threadId) {
|
|
11591
|
+
try {
|
|
11592
|
+
await this.runSetup(threadId);
|
|
11593
|
+
} catch (err) {
|
|
11594
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11595
|
+
if (/no setup script/i.test(message)) return;
|
|
11596
|
+
if (/already running/i.test(message)) return;
|
|
11597
|
+
updateThread(threadId, {
|
|
11598
|
+
lastError: `Setup failed: ${message}`
|
|
11599
|
+
});
|
|
11600
|
+
}
|
|
11601
|
+
}
|
|
10789
11602
|
listWorkspaces() {
|
|
10790
11603
|
const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
|
|
10791
11604
|
return syncWorkspacesFromThreads(fromThreads);
|
|
@@ -11134,6 +11947,19 @@ var Orchestrator = class {
|
|
|
11134
11947
|
});
|
|
11135
11948
|
}
|
|
11136
11949
|
const afterTurn = this.requireThread(threadId);
|
|
11950
|
+
if (afterTurn.planMode && afterTurn.worktreePath?.trim()) {
|
|
11951
|
+
const presented = extractPresentedPlan(parts);
|
|
11952
|
+
const exited = parts.some(
|
|
11953
|
+
(p) => p.type === "tool" && /exitplanmode/i.test(p.name)
|
|
11954
|
+
);
|
|
11955
|
+
if (presented?.content) {
|
|
11956
|
+
writePlanFile(afterTurn.worktreePath, presented.content);
|
|
11957
|
+
} else if (exited || chatText && chatText.trim().length >= 400) {
|
|
11958
|
+
if (!readPlanFile(afterTurn.worktreePath) && chatText?.trim()) {
|
|
11959
|
+
writePlanFile(afterTurn.worktreePath, chatText.trim());
|
|
11960
|
+
}
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11137
11963
|
if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
|
|
11138
11964
|
(p) => p.type === "tool" && /exitplanmode/i.test(p.name)
|
|
11139
11965
|
)) {
|
|
@@ -11615,6 +12441,82 @@ var Orchestrator = class {
|
|
|
11615
12441
|
}
|
|
11616
12442
|
return meta;
|
|
11617
12443
|
}
|
|
12444
|
+
async getPrStack(threadRef) {
|
|
12445
|
+
const thread = this.requireThread(threadRef);
|
|
12446
|
+
if (!thread.worktreePath?.trim()) return null;
|
|
12447
|
+
const stack = await getPrStack(thread.worktreePath);
|
|
12448
|
+
if (!stack) return null;
|
|
12449
|
+
const current = stack.currentIndex >= 0 ? stack.layers[stack.currentIndex] : null;
|
|
12450
|
+
const patch = {};
|
|
12451
|
+
if (stack.stackNumber != null) {
|
|
12452
|
+
const id = `gh-stack-${stack.stackNumber}`;
|
|
12453
|
+
if (thread.stackId !== id) patch.stackId = id;
|
|
12454
|
+
}
|
|
12455
|
+
if (current?.position != null && thread.stackLayer !== current.position) {
|
|
12456
|
+
patch.stackLayer = current.position;
|
|
12457
|
+
}
|
|
12458
|
+
if (current?.prUrl && current.prUrl !== thread.prUrl) patch.prUrl = current.prUrl;
|
|
12459
|
+
if (current?.title && current.title !== thread.prTitle) patch.prTitle = current.title;
|
|
12460
|
+
if (current?.branchName && current.branchName !== thread.branchName) {
|
|
12461
|
+
patch.branchName = current.branchName;
|
|
12462
|
+
}
|
|
12463
|
+
if (Object.keys(patch).length > 0) updateThread(thread.id, patch);
|
|
12464
|
+
return stack;
|
|
12465
|
+
}
|
|
12466
|
+
/** Open worktrees for all (or one) stack layers discovered from a thread. */
|
|
12467
|
+
async openPrStackLayers(threadRef, opts) {
|
|
12468
|
+
const result = await openPrStackLayers({ threadRef, layer: opts?.layer });
|
|
12469
|
+
for (const t of result.threads) {
|
|
12470
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12471
|
+
}
|
|
12472
|
+
for (const id of result.createdThreadIds) {
|
|
12473
|
+
await this.runSetupAfterCreate(id);
|
|
12474
|
+
}
|
|
12475
|
+
return { stack: result.stack, threads: result.threads };
|
|
12476
|
+
}
|
|
12477
|
+
/** Add a branch on top of the thread's stack and open its worktree. */
|
|
12478
|
+
async addStackLayer(threadRef, branchName, opts) {
|
|
12479
|
+
const result = await addStackLayerFromThread({
|
|
12480
|
+
threadRef,
|
|
12481
|
+
branchName,
|
|
12482
|
+
title: opts?.title
|
|
12483
|
+
});
|
|
12484
|
+
this.emit({
|
|
12485
|
+
type: "status_changed",
|
|
12486
|
+
threadId: result.thread.id,
|
|
12487
|
+
status: result.thread.status
|
|
12488
|
+
});
|
|
12489
|
+
if (result.createdWorktree) {
|
|
12490
|
+
await this.runSetupAfterCreate(result.thread.id);
|
|
12491
|
+
}
|
|
12492
|
+
return { stack: result.stack, thread: result.thread };
|
|
12493
|
+
}
|
|
12494
|
+
/** Initialize a stack from the current thread branch (optional extra layers). */
|
|
12495
|
+
async initStackFromThread(threadRef, opts) {
|
|
12496
|
+
const result = await initStackFromThread({
|
|
12497
|
+
threadRef,
|
|
12498
|
+
additionalBranches: opts?.additionalBranches,
|
|
12499
|
+
base: opts?.base
|
|
12500
|
+
});
|
|
12501
|
+
for (const t of result.threads) {
|
|
12502
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12503
|
+
}
|
|
12504
|
+
for (const id of result.createdThreadIds) {
|
|
12505
|
+
await this.runSetupAfterCreate(id);
|
|
12506
|
+
}
|
|
12507
|
+
return { stack: result.stack, threads: result.threads };
|
|
12508
|
+
}
|
|
12509
|
+
/** Create a new multi-layer stack with one worktree per layer. */
|
|
12510
|
+
async createPrStack(input) {
|
|
12511
|
+
const result = await createPrStack(input);
|
|
12512
|
+
for (const t of result.threads) {
|
|
12513
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12514
|
+
}
|
|
12515
|
+
for (const id of result.createdThreadIds) {
|
|
12516
|
+
await this.runSetupAfterCreate(id);
|
|
12517
|
+
}
|
|
12518
|
+
return { stack: result.stack, threads: result.threads };
|
|
12519
|
+
}
|
|
11618
12520
|
async getPrDetails(threadRef) {
|
|
11619
12521
|
const { thread, selector, cwd } = await this.withPrSelector(threadRef);
|
|
11620
12522
|
if (!selector) return null;
|
|
@@ -11677,14 +12579,9 @@ var Orchestrator = class {
|
|
|
11677
12579
|
return forkChatTab(input);
|
|
11678
12580
|
}
|
|
11679
12581
|
async forkThreadWorktree(input) {
|
|
11680
|
-
const thread = await forkThreadWorktree(input
|
|
11681
|
-
this.emit({
|
|
11682
|
-
type: "turn_output",
|
|
11683
|
-
threadId: "pending",
|
|
11684
|
-
event: { type: "stdout", data: line }
|
|
11685
|
-
});
|
|
11686
|
-
});
|
|
12582
|
+
const thread = await forkThreadWorktree(input);
|
|
11687
12583
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
12584
|
+
await this.runSetupAfterCreate(thread.id);
|
|
11688
12585
|
return thread;
|
|
11689
12586
|
}
|
|
11690
12587
|
renameThread(threadRef, title) {
|
|
@@ -11698,7 +12595,7 @@ var Orchestrator = class {
|
|
|
11698
12595
|
}
|
|
11699
12596
|
/**
|
|
11700
12597
|
* Stage OS / worktree files into composer attachments (copies external files
|
|
11701
|
-
* into `.
|
|
12598
|
+
* into `.context/attachments/` so agents can Read images and binaries).
|
|
11702
12599
|
*/
|
|
11703
12600
|
attachComposerFiles(threadRef, opts) {
|
|
11704
12601
|
const thread = this.requireThread(threadRef);
|
|
@@ -11772,7 +12669,7 @@ var Orchestrator = class {
|
|
|
11772
12669
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
11773
12670
|
return setStatus(thread.id, "idle");
|
|
11774
12671
|
}
|
|
11775
|
-
if (!(0,
|
|
12672
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
11776
12673
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
11777
12674
|
const { execa: execa7 } = await import("execa");
|
|
11778
12675
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -11880,13 +12777,116 @@ async function startOrchestration(opts) {
|
|
|
11880
12777
|
}
|
|
11881
12778
|
|
|
11882
12779
|
// src/index.ts
|
|
12780
|
+
init_workspace_scratch();
|
|
12781
|
+
|
|
12782
|
+
// src/plan/ask-user.ts
|
|
12783
|
+
function asRecord3(v) {
|
|
12784
|
+
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12785
|
+
}
|
|
12786
|
+
function parseOptions(raw) {
|
|
12787
|
+
if (!Array.isArray(raw)) return [];
|
|
12788
|
+
const out = [];
|
|
12789
|
+
for (const item of raw) {
|
|
12790
|
+
if (typeof item === "string" && item.trim()) {
|
|
12791
|
+
out.push({ label: item.trim() });
|
|
12792
|
+
continue;
|
|
12793
|
+
}
|
|
12794
|
+
const o = asRecord3(item);
|
|
12795
|
+
if (!o) continue;
|
|
12796
|
+
const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
|
|
12797
|
+
if (!label) continue;
|
|
12798
|
+
const description = typeof o.description === "string" && o.description.trim() ? o.description.trim() : void 0;
|
|
12799
|
+
out.push({ label, description });
|
|
12800
|
+
}
|
|
12801
|
+
return out;
|
|
12802
|
+
}
|
|
12803
|
+
function parseOneQuestion(raw) {
|
|
12804
|
+
const o = asRecord3(raw);
|
|
12805
|
+
if (!o) return null;
|
|
12806
|
+
const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
|
|
12807
|
+
if (!question) return null;
|
|
12808
|
+
const options = parseOptions(o.options);
|
|
12809
|
+
if (options.length < 1) return null;
|
|
12810
|
+
const header = typeof o.header === "string" && o.header.trim() ? o.header.trim() : void 0;
|
|
12811
|
+
const multiSelect = Boolean(o.multiSelect ?? o.multi_select);
|
|
12812
|
+
return { question, header, multiSelect, options };
|
|
12813
|
+
}
|
|
12814
|
+
function parsePlanQuestionsInput(input) {
|
|
12815
|
+
const root = asRecord3(input);
|
|
12816
|
+
if (!root) return [];
|
|
12817
|
+
const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
|
|
12818
|
+
const out = [];
|
|
12819
|
+
for (const item of list) {
|
|
12820
|
+
const q = parseOneQuestion(item);
|
|
12821
|
+
if (q) out.push(q);
|
|
12822
|
+
}
|
|
12823
|
+
return out;
|
|
12824
|
+
}
|
|
12825
|
+
var ASK_USER_TOOL_RE = /^(AskUserQuestion|ask_user|mcp__sideboard__ask_user)$/i;
|
|
12826
|
+
function isAskUserToolName(name) {
|
|
12827
|
+
if (!name) return false;
|
|
12828
|
+
const base = name.replace(/^mcp__sideboard__/i, "");
|
|
12829
|
+
return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
|
|
12830
|
+
}
|
|
12831
|
+
function extractPendingPlanQuestions(parts) {
|
|
12832
|
+
if (!parts?.length) return null;
|
|
12833
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
12834
|
+
const p = parts[i];
|
|
12835
|
+
if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
|
|
12836
|
+
const questions = parsePlanQuestionsInput(p.input);
|
|
12837
|
+
if (!questions.length) continue;
|
|
12838
|
+
return {
|
|
12839
|
+
id: p.id || `ask-${i}`,
|
|
12840
|
+
questions,
|
|
12841
|
+
source: p.name || "ask_user"
|
|
12842
|
+
};
|
|
12843
|
+
}
|
|
12844
|
+
return null;
|
|
12845
|
+
}
|
|
12846
|
+
function formatPlanQuestionAnswers(questions, answers) {
|
|
12847
|
+
const lines = ["Answers to your questions:"];
|
|
12848
|
+
for (let i = 0; i < questions.length; i++) {
|
|
12849
|
+
const q = questions[i];
|
|
12850
|
+
const a = answers.find((x) => x.questionIndex === i);
|
|
12851
|
+
const header = q.header ? `**${q.header}** \u2014 ` : "";
|
|
12852
|
+
const parts = [];
|
|
12853
|
+
if (a?.selected.length) parts.push(a.selected.join(", "));
|
|
12854
|
+
if (a?.other?.trim()) parts.push(a.other.trim());
|
|
12855
|
+
const body = parts.length ? parts.join(" \xB7 ") : "(no answer)";
|
|
12856
|
+
lines.push(`${i + 1}. ${header}${q.question}`);
|
|
12857
|
+
lines.push(` \u2192 ${body}`);
|
|
12858
|
+
}
|
|
12859
|
+
return lines.join("\n");
|
|
12860
|
+
}
|
|
12861
|
+
function formatPlanQuestionsForChat(questions) {
|
|
12862
|
+
const lines = ["### Questions for you", ""];
|
|
12863
|
+
for (let i = 0; i < questions.length; i++) {
|
|
12864
|
+
const q = questions[i];
|
|
12865
|
+
const header = q.header ? `**${q.header}** \u2014 ` : "";
|
|
12866
|
+
lines.push(`${i + 1}. ${header}${q.question}`);
|
|
12867
|
+
for (let oi = 0; oi < q.options.length; oi++) {
|
|
12868
|
+
const opt = q.options[oi];
|
|
12869
|
+
const desc = opt.description?.trim();
|
|
12870
|
+
lines.push(
|
|
12871
|
+
desc ? ` - **${opt.label}** \u2014 ${desc}` : ` - **${opt.label}**`
|
|
12872
|
+
);
|
|
12873
|
+
}
|
|
12874
|
+
lines.push("");
|
|
12875
|
+
}
|
|
12876
|
+
lines.push("_Answer in the composer below._");
|
|
12877
|
+
return lines.join("\n").trimEnd();
|
|
12878
|
+
}
|
|
12879
|
+
|
|
12880
|
+
// src/index.ts
|
|
12881
|
+
init_plan_present();
|
|
12882
|
+
init_plan_file();
|
|
11883
12883
|
init_coordinator_prompt();
|
|
11884
12884
|
|
|
11885
12885
|
// src/mcp/server.ts
|
|
11886
12886
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
11887
12887
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11888
12888
|
var import_zod = require("zod");
|
|
11889
|
-
var
|
|
12889
|
+
var import_node_path26 = require("path");
|
|
11890
12890
|
init_worktree();
|
|
11891
12891
|
init_global_workspace();
|
|
11892
12892
|
init_list_models();
|
|
@@ -11935,7 +12935,7 @@ async function startMcpServer() {
|
|
|
11935
12935
|
async () => {
|
|
11936
12936
|
const threads = orch.getThreads(true);
|
|
11937
12937
|
const lines = threads.map((t) => {
|
|
11938
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
12938
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path26.basename)(t.repoPath) || t.repoPath;
|
|
11939
12939
|
return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}`;
|
|
11940
12940
|
});
|
|
11941
12941
|
return {
|
|
@@ -11997,6 +12997,59 @@ async function startMcpServer() {
|
|
|
11997
12997
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
11998
12998
|
}
|
|
11999
12999
|
);
|
|
13000
|
+
server.tool(
|
|
13001
|
+
"ask_user",
|
|
13002
|
+
"Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (plan mode). Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. Use for approach forks and requirements \u2014 not for \u201Cis the plan ready?\u201D. After calling, stop and wait for their next message with answers.",
|
|
13003
|
+
{
|
|
13004
|
+
questions: import_zod.z.array(
|
|
13005
|
+
import_zod.z.object({
|
|
13006
|
+
question: import_zod.z.string().describe("Full question text ending with ?"),
|
|
13007
|
+
header: import_zod.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
13008
|
+
multiSelect: import_zod.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
13009
|
+
options: import_zod.z.array(
|
|
13010
|
+
import_zod.z.object({
|
|
13011
|
+
label: import_zod.z.string(),
|
|
13012
|
+
description: import_zod.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
13013
|
+
})
|
|
13014
|
+
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
13015
|
+
})
|
|
13016
|
+
).min(1).max(4).describe("1\u20134 questions")
|
|
13017
|
+
},
|
|
13018
|
+
async ({ questions }) => {
|
|
13019
|
+
const payload = {
|
|
13020
|
+
ok: true,
|
|
13021
|
+
questions,
|
|
13022
|
+
message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
|
|
13023
|
+
};
|
|
13024
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
13025
|
+
}
|
|
13026
|
+
);
|
|
13027
|
+
server.tool(
|
|
13028
|
+
"present_plan",
|
|
13029
|
+
"Save the implementation plan as markdown to .context/attachments/plan.md and show it in Sideboard chat for user approval (Copy / Hand off / Approve). Call this when the plan is ready \u2014 required in plan mode. Pass the full plan body in content. Then Claude should call ExitPlanMode.",
|
|
13030
|
+
{
|
|
13031
|
+
title: import_zod.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
13032
|
+
content: import_zod.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
13033
|
+
thread_id: import_zod.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
13034
|
+
},
|
|
13035
|
+
async ({ title, content, thread_id }) => {
|
|
13036
|
+
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
13037
|
+
let root = process.cwd();
|
|
13038
|
+
if (thread_id?.trim()) {
|
|
13039
|
+
const t = orch.getThread(thread_id.trim());
|
|
13040
|
+
if (t?.worktreePath?.trim()) root = t.worktreePath;
|
|
13041
|
+
}
|
|
13042
|
+
const path = writePlanFile2(root, content);
|
|
13043
|
+
const payload = {
|
|
13044
|
+
ok: true,
|
|
13045
|
+
path,
|
|
13046
|
+
title: title?.trim() || "Plan",
|
|
13047
|
+
content,
|
|
13048
|
+
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
13049
|
+
};
|
|
13050
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
13051
|
+
}
|
|
13052
|
+
);
|
|
12000
13053
|
server.tool(
|
|
12001
13054
|
"present_schema",
|
|
12002
13055
|
"Open Sideboard\u2019s schema-driven CMS side column (filterable table and/or form). Pass JSON Schema + schemaUi (Brightsy extensions supported). Use datasource=brightsy with resource_id (record type UUID) when logged into Brightsy; use datasource=inline with embedded resource/records for any other source.",
|
|
@@ -12575,6 +13628,126 @@ async function startMcpServer() {
|
|
|
12575
13628
|
};
|
|
12576
13629
|
}
|
|
12577
13630
|
);
|
|
13631
|
+
server.tool(
|
|
13632
|
+
"get_pr_stack",
|
|
13633
|
+
"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.",
|
|
13634
|
+
{ ref: import_zod.z.string() },
|
|
13635
|
+
async ({ ref }) => {
|
|
13636
|
+
const stack = await orch.getPrStack(ref);
|
|
13637
|
+
return {
|
|
13638
|
+
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13639
|
+
};
|
|
13640
|
+
}
|
|
13641
|
+
);
|
|
13642
|
+
server.tool(
|
|
13643
|
+
"open_pr_stack_layers",
|
|
13644
|
+
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13645
|
+
{
|
|
13646
|
+
ref: import_zod.z.string(),
|
|
13647
|
+
layer: import_zod.z.number().int().positive().optional()
|
|
13648
|
+
},
|
|
13649
|
+
async ({ ref, layer }) => {
|
|
13650
|
+
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13651
|
+
return {
|
|
13652
|
+
content: [
|
|
13653
|
+
{
|
|
13654
|
+
type: "text",
|
|
13655
|
+
text: JSON.stringify(
|
|
13656
|
+
{
|
|
13657
|
+
stackNumber: result.stack.stackNumber,
|
|
13658
|
+
trunk: result.stack.trunk,
|
|
13659
|
+
threads: result.threads.map((t) => ({
|
|
13660
|
+
id: t.id,
|
|
13661
|
+
title: t.title,
|
|
13662
|
+
branchName: t.branchName,
|
|
13663
|
+
stackLayer: t.stackLayer,
|
|
13664
|
+
worktreePath: t.worktreePath,
|
|
13665
|
+
prUrl: t.prUrl,
|
|
13666
|
+
link: `sideboard://thread/${t.id}`
|
|
13667
|
+
}))
|
|
13668
|
+
},
|
|
13669
|
+
null,
|
|
13670
|
+
2
|
|
13671
|
+
)
|
|
13672
|
+
}
|
|
13673
|
+
]
|
|
13674
|
+
};
|
|
13675
|
+
}
|
|
13676
|
+
);
|
|
13677
|
+
server.tool(
|
|
13678
|
+
"add_stack_layer",
|
|
13679
|
+
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13680
|
+
{
|
|
13681
|
+
ref: import_zod.z.string(),
|
|
13682
|
+
branchName: import_zod.z.string(),
|
|
13683
|
+
title: import_zod.z.string().optional()
|
|
13684
|
+
},
|
|
13685
|
+
async ({ ref, branchName, title }) => {
|
|
13686
|
+
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13687
|
+
return {
|
|
13688
|
+
content: [
|
|
13689
|
+
{
|
|
13690
|
+
type: "text",
|
|
13691
|
+
text: JSON.stringify(
|
|
13692
|
+
{
|
|
13693
|
+
id: result.thread.id,
|
|
13694
|
+
title: result.thread.title,
|
|
13695
|
+
branchName: result.thread.branchName,
|
|
13696
|
+
stackLayer: result.thread.stackLayer,
|
|
13697
|
+
worktreePath: result.thread.worktreePath,
|
|
13698
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
13699
|
+
},
|
|
13700
|
+
null,
|
|
13701
|
+
2
|
|
13702
|
+
)
|
|
13703
|
+
}
|
|
13704
|
+
]
|
|
13705
|
+
};
|
|
13706
|
+
}
|
|
13707
|
+
);
|
|
13708
|
+
server.tool(
|
|
13709
|
+
"create_pr_stack",
|
|
13710
|
+
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13711
|
+
{
|
|
13712
|
+
repoPath: import_zod.z.string(),
|
|
13713
|
+
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13714
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13715
|
+
base: import_zod.z.string().optional(),
|
|
13716
|
+
title: import_zod.z.string().optional()
|
|
13717
|
+
},
|
|
13718
|
+
async (args) => {
|
|
13719
|
+
const result = await orch.createPrStack({
|
|
13720
|
+
repoPath: args.repoPath,
|
|
13721
|
+
branches: args.branches,
|
|
13722
|
+
agent: args.agent,
|
|
13723
|
+
base: args.base,
|
|
13724
|
+
title: args.title
|
|
13725
|
+
});
|
|
13726
|
+
return {
|
|
13727
|
+
content: [
|
|
13728
|
+
{
|
|
13729
|
+
type: "text",
|
|
13730
|
+
text: JSON.stringify(
|
|
13731
|
+
{
|
|
13732
|
+
stackNumber: result.stack.stackNumber,
|
|
13733
|
+
trunk: result.stack.trunk,
|
|
13734
|
+
threads: result.threads.map((t) => ({
|
|
13735
|
+
id: t.id,
|
|
13736
|
+
title: t.title,
|
|
13737
|
+
branchName: t.branchName,
|
|
13738
|
+
stackLayer: t.stackLayer,
|
|
13739
|
+
worktreePath: t.worktreePath,
|
|
13740
|
+
link: `sideboard://thread/${t.id}`
|
|
13741
|
+
}))
|
|
13742
|
+
},
|
|
13743
|
+
null,
|
|
13744
|
+
2
|
|
13745
|
+
)
|
|
13746
|
+
}
|
|
13747
|
+
]
|
|
13748
|
+
};
|
|
13749
|
+
}
|
|
13750
|
+
);
|
|
12578
13751
|
server.tool(
|
|
12579
13752
|
"list_issues",
|
|
12580
13753
|
"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.",
|
|
@@ -12944,6 +14117,7 @@ init_connected_teams();
|
|
|
12944
14117
|
init_injected_mcp();
|
|
12945
14118
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12946
14119
|
0 && (module.exports = {
|
|
14120
|
+
ATTACHMENTS_DIR,
|
|
12947
14121
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
12948
14122
|
BrightsySideboardApi,
|
|
12949
14123
|
CLAUDE_MODEL_CATALOG,
|
|
@@ -12959,11 +14133,16 @@ init_injected_mcp();
|
|
|
12959
14133
|
FAMOUS_SOCCER_TEAMS,
|
|
12960
14134
|
GLOBAL_WORKSPACE_ID,
|
|
12961
14135
|
HARNESS_ENV_KEYS,
|
|
14136
|
+
LEGACY_ATTACHMENTS_DIR,
|
|
14137
|
+
LEGACY_PLAN_FILE_REL,
|
|
14138
|
+
LEGACY_REVIEW_REQUEST_PATH,
|
|
12962
14139
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
12963
14140
|
ORCHESTRATOR_AGENT_KINDS,
|
|
12964
14141
|
Orchestrator,
|
|
12965
14142
|
PASTE_ATTACH_MIN_CHARS,
|
|
12966
14143
|
PASTE_ATTACH_MIN_LINES,
|
|
14144
|
+
PLAN_FILE_NAME,
|
|
14145
|
+
PLAN_FILE_REL,
|
|
12967
14146
|
PLAN_MODE_INSTRUCTION,
|
|
12968
14147
|
REPO_REVIEW_NAME,
|
|
12969
14148
|
REPO_REVIEW_PATH,
|
|
@@ -12973,6 +14152,8 @@ init_injected_mcp();
|
|
|
12973
14152
|
SIDEBOARD_FORCE_STOP,
|
|
12974
14153
|
SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
12975
14154
|
THINKING_EFFORTS,
|
|
14155
|
+
addPrStackLayer,
|
|
14156
|
+
addStackLayerFromThread,
|
|
12976
14157
|
addWorkspace,
|
|
12977
14158
|
adoptThread,
|
|
12978
14159
|
allAdapters,
|
|
@@ -12991,6 +14172,7 @@ init_injected_mcp();
|
|
|
12991
14172
|
attachmentFromAbsolutePath,
|
|
12992
14173
|
attachmentsFromBuffers,
|
|
12993
14174
|
attachmentsFromWorktreePaths,
|
|
14175
|
+
attachmentsGitignoreBody,
|
|
12994
14176
|
autoCleanupOrphansEnabled,
|
|
12995
14177
|
autoRenameBranchEnabled,
|
|
12996
14178
|
autoRunAfterSetupEnabled,
|
|
@@ -13013,6 +14195,7 @@ init_injected_mcp();
|
|
|
13013
14195
|
caffeinateWhileRunningEnabled,
|
|
13014
14196
|
captureLoginEnv,
|
|
13015
14197
|
captureTurnBaseline,
|
|
14198
|
+
checkoutPrStackLayer,
|
|
13016
14199
|
childEnvWithAppSettings,
|
|
13017
14200
|
claudeAdapter,
|
|
13018
14201
|
claudeChromeEnabled,
|
|
@@ -13032,8 +14215,10 @@ init_injected_mcp();
|
|
|
13032
14215
|
countCacheControlBlocks,
|
|
13033
14216
|
createChatTab,
|
|
13034
14217
|
createEmptyThread,
|
|
14218
|
+
createExistingBranchWorktree,
|
|
13035
14219
|
createGlobalChat,
|
|
13036
14220
|
createOrUpdatePr,
|
|
14221
|
+
createPrStack,
|
|
13037
14222
|
createThread,
|
|
13038
14223
|
createThreadWorktree,
|
|
13039
14224
|
currentBranch,
|
|
@@ -13043,6 +14228,7 @@ init_injected_mcp();
|
|
|
13043
14228
|
deleteBranchOnPurgeEnabled,
|
|
13044
14229
|
deleteThreadRecord,
|
|
13045
14230
|
detectAgents,
|
|
14231
|
+
detectGhStack,
|
|
13046
14232
|
detectLocalMergeConflicts,
|
|
13047
14233
|
disconnectBrightsyTeam,
|
|
13048
14234
|
discoverSkills,
|
|
@@ -13058,12 +14244,15 @@ init_injected_mcp();
|
|
|
13058
14244
|
estimateThreadChars,
|
|
13059
14245
|
expandComposerPrompt,
|
|
13060
14246
|
extractGhErrorDetail,
|
|
14247
|
+
extractPendingPlanQuestions,
|
|
14248
|
+
extractPresentedPlan,
|
|
13061
14249
|
extractiveSummary,
|
|
13062
14250
|
fetchPrHead,
|
|
13063
14251
|
finalizeParts,
|
|
13064
14252
|
findInvalidCacheControlTtlOrder,
|
|
13065
14253
|
findOrphanWorktrees,
|
|
13066
14254
|
findThreadByRef,
|
|
14255
|
+
findThreadForStackLayer,
|
|
13067
14256
|
flattenTurnInput,
|
|
13068
14257
|
forkChatTab,
|
|
13069
14258
|
forkMessageSlice,
|
|
@@ -13074,6 +14263,8 @@ init_injected_mcp();
|
|
|
13074
14263
|
formatGhLandError,
|
|
13075
14264
|
formatIpcInvokeError,
|
|
13076
14265
|
formatMessagesAsTranscript,
|
|
14266
|
+
formatPlanQuestionAnswers,
|
|
14267
|
+
formatPlanQuestionsForChat,
|
|
13077
14268
|
formatRateLimitResetHint,
|
|
13078
14269
|
formatRenameBranchDirective,
|
|
13079
14270
|
formatTranscriptMarkdown,
|
|
@@ -13097,6 +14288,7 @@ init_injected_mcp();
|
|
|
13097
14288
|
getPrChecks,
|
|
13098
14289
|
getPrDetails,
|
|
13099
14290
|
getPrMeta,
|
|
14291
|
+
getPrStack,
|
|
13100
14292
|
getRepoSetupInfo,
|
|
13101
14293
|
getRunMode,
|
|
13102
14294
|
getRunScript,
|
|
@@ -13113,9 +14305,12 @@ init_injected_mcp();
|
|
|
13113
14305
|
healOrchestrationSoccerTitles,
|
|
13114
14306
|
importConductorWorkspace,
|
|
13115
14307
|
importConductorWorkspaceAsync,
|
|
14308
|
+
initPrStack,
|
|
14309
|
+
initStackFromThread,
|
|
13116
14310
|
initializeGitRepository,
|
|
13117
14311
|
inspectGitWorktree,
|
|
13118
14312
|
installAgent,
|
|
14313
|
+
isAskUserToolName,
|
|
13119
14314
|
isBrightsyConnected,
|
|
13120
14315
|
isBrightsyNdjsonLine,
|
|
13121
14316
|
isCloudCoordinatorThread,
|
|
@@ -13125,13 +14320,17 @@ init_injected_mcp();
|
|
|
13125
14320
|
isGlobalRepoPath,
|
|
13126
14321
|
isGlobalThread,
|
|
13127
14322
|
isImageFilePath,
|
|
14323
|
+
isInPrStack,
|
|
13128
14324
|
isLinearConnected,
|
|
13129
14325
|
isOrchestratorCapableAgent,
|
|
13130
14326
|
isOrchestratorThread,
|
|
13131
14327
|
isPidAlive,
|
|
13132
14328
|
isPlaceholderBranch,
|
|
14329
|
+
isPresentPlanToolName,
|
|
13133
14330
|
isSessionQuotaLimit,
|
|
14331
|
+
isSideboardScratchPath,
|
|
13134
14332
|
isThinkingEffort,
|
|
14333
|
+
isWorkspaceScratchPath,
|
|
13135
14334
|
listAgentSetupInfo,
|
|
13136
14335
|
listBranchCommits,
|
|
13137
14336
|
listBranches,
|
|
@@ -13168,6 +14367,7 @@ init_injected_mcp();
|
|
|
13168
14367
|
mcpAllowTools,
|
|
13169
14368
|
mcpAuthWarnings,
|
|
13170
14369
|
mergePr,
|
|
14370
|
+
mergePrStack,
|
|
13171
14371
|
mergeUsage,
|
|
13172
14372
|
nextPastedTextName,
|
|
13173
14373
|
nextThinkingEffort,
|
|
@@ -13177,6 +14377,8 @@ init_injected_mcp();
|
|
|
13177
14377
|
normalizeTurnInput,
|
|
13178
14378
|
normalizeWorktreePath,
|
|
13179
14379
|
openInSystemTerminal,
|
|
14380
|
+
openPrStackLayers,
|
|
14381
|
+
openStackLayer,
|
|
13180
14382
|
opencodeAdapter,
|
|
13181
14383
|
orchestrationQuotaFallbackAgent,
|
|
13182
14384
|
orchestrationQuotaOnLimit,
|
|
@@ -13185,15 +14387,19 @@ init_injected_mcp();
|
|
|
13185
14387
|
originGhRepoEnv,
|
|
13186
14388
|
parseCursorRunnerLine,
|
|
13187
14389
|
parseForceStopMessage,
|
|
14390
|
+
parseGhStackViewJson,
|
|
13188
14391
|
parseGithubSlugFromRemoteUrl,
|
|
13189
14392
|
parseMcpList,
|
|
14393
|
+
parsePlanQuestionsInput,
|
|
13190
14394
|
parseSessionQuotaResetAt,
|
|
13191
14395
|
partsToAssistantText,
|
|
13192
14396
|
pastedTextStats,
|
|
13193
14397
|
permissionMode,
|
|
14398
|
+
planFileAbs,
|
|
13194
14399
|
previewLand,
|
|
13195
14400
|
pushBranch,
|
|
13196
14401
|
readExistingReviewRequestFile,
|
|
14402
|
+
readPlanFile,
|
|
13197
14403
|
readSkillBody,
|
|
13198
14404
|
readThread,
|
|
13199
14405
|
readWorktreeFile,
|
|
@@ -13205,6 +14411,7 @@ init_injected_mcp();
|
|
|
13205
14411
|
repoSlug,
|
|
13206
14412
|
requestReview,
|
|
13207
14413
|
requireAgent,
|
|
14414
|
+
resetGhStackDetectCache,
|
|
13208
14415
|
resolveClaudeExecutable,
|
|
13209
14416
|
resolveConductorCursorAgentId,
|
|
13210
14417
|
resolveCursorModelId,
|
|
@@ -13212,7 +14419,9 @@ init_injected_mcp();
|
|
|
13212
14419
|
resolveDiffBaseRef,
|
|
13213
14420
|
resolveEffectiveIssueSource,
|
|
13214
14421
|
resolveFilesToCopy,
|
|
14422
|
+
resolveGhAuthToken,
|
|
13215
14423
|
resolveGithubRepoSlug,
|
|
14424
|
+
resolvePlanMarkdown,
|
|
13216
14425
|
resolvePrSelector,
|
|
13217
14426
|
resolveQuotaFallbackAgent,
|
|
13218
14427
|
resolveRepoRoot,
|
|
@@ -13240,12 +14449,16 @@ init_injected_mcp();
|
|
|
13240
14449
|
slugify,
|
|
13241
14450
|
spawnAgentTurn,
|
|
13242
14451
|
splitForCompaction,
|
|
14452
|
+
stackAgentDefaultsFrom,
|
|
14453
|
+
stackIdFrom,
|
|
14454
|
+
stackMergeReadiness,
|
|
13243
14455
|
stageAbsolutePathsAsAttachments,
|
|
13244
14456
|
stageBuffersAsAttachments,
|
|
13245
14457
|
startDevServer,
|
|
13246
14458
|
startMcpServer,
|
|
13247
14459
|
startOrchestration,
|
|
13248
14460
|
stripBrightsyNdjsonNoise,
|
|
14461
|
+
submitPrStack,
|
|
13249
14462
|
suggestSlug,
|
|
13250
14463
|
summarizeConversation,
|
|
13251
14464
|
switchBrightsyAccount,
|
|
@@ -13281,6 +14494,7 @@ init_injected_mcp();
|
|
|
13281
14494
|
worktreeNameFromPath,
|
|
13282
14495
|
worktreesRoot,
|
|
13283
14496
|
writeInjectedMcpConfig,
|
|
14497
|
+
writePlanFile,
|
|
13284
14498
|
writeThread,
|
|
13285
14499
|
writeWorktreeFile
|
|
13286
14500
|
});
|