@sideboard-ai/core 0.1.51 → 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-HIEJL3UV.js → agents-ON6RKKND.js} +1 -1
- package/dist/{agents-5ROTZNCX.js → agents-YKSS6VBO.js} +1 -1
- package/dist/{chunk-7EUWSBWR.js → chunk-6QTZVJ7A.js} +5 -3
- package/dist/chunk-B3SJXYIJ.js +24 -0
- package/dist/{chunk-5ZPSH7VI.js → chunk-D3METLRW.js} +5 -3
- package/dist/{chunk-ZH5QZ4CR.js → chunk-DZFH2KLT.js} +280 -7
- package/dist/chunk-FKOIHGKV.js +21 -0
- package/dist/{chunk-E4VVEKAM.js → chunk-GNML24AW.js} +2 -2
- package/dist/{chunk-EOYDCKQC.js → chunk-HLEX5AQ6.js} +4 -0
- package/dist/{chunk-K3WMKGFY.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-F4Q3IM6V.js → chunk-UEAHMGHW.js} +2 -1
- package/dist/{chunk-XRSAGVRW.js → chunk-XOU6HNQJ.js} +245 -7
- package/dist/{chunk-O5DOO7DP.js → chunk-XX5BB7NV.js} +3 -3
- package/dist/{chunk-QN7XNQAT.js → chunk-YDXQ72MD.js} +2 -2
- package/dist/{chunk-YFJ4FG2P.js → chunk-YOWIYAVA.js} +3 -3
- package/dist/{coordinator-prompt-7HHJRO7B.js → coordinator-prompt-6FXVTSFN.js} +4 -3
- package/dist/{coordinator-prompt-WD7FAMA2.js → coordinator-prompt-S6JZD5EF.js} +4 -3
- package/dist/{global-workspace-OJEPGDXA.js → global-workspace-EV4G2WMQ.js} +5 -4
- package/dist/{global-workspace-ECYN2MKL.js → global-workspace-MSX2K27Y.js} +5 -4
- package/dist/index.cjs +1288 -140
- package/dist/index.d.cts +388 -15
- package/dist/index.d.ts +388 -15
- package/dist/index.js +801 -84
- package/dist/mcp/run-stdio.cjs +1059 -132
- package/dist/mcp/run-stdio.js +628 -73
- package/dist/plan-file-6O7G4VPQ.js +23 -0
- package/dist/plan-file-PHVKUAEE.js +25 -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-MUU7RGVV.js → workspaces-3RQQZQRO.js} +6 -5
- package/dist/{workspaces-ZWOOFZUV.js → workspaces-AYTBR6KQ.js} +6 -5
- package/dist/{worktree-DVNDMWZ7.js → worktree-5KEQWSAF.js} +5 -2
- package/dist/{worktree-GDV56MX4.js → worktree-RWGL7FUV.js} +5 -2
- package/package.json +1 -1
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 ?? [],
|
|
@@ -2196,6 +2200,234 @@ var init_pr_gates = __esm({
|
|
|
2196
2200
|
}
|
|
2197
2201
|
});
|
|
2198
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
|
+
|
|
2199
2431
|
// src/git/worktree.ts
|
|
2200
2432
|
var worktree_exports = {};
|
|
2201
2433
|
__export(worktree_exports, {
|
|
@@ -2205,6 +2437,7 @@ __export(worktree_exports, {
|
|
|
2205
2437
|
branchDisplayLabel: () => branchDisplayLabel,
|
|
2206
2438
|
collectTakenTeamSlugs: () => collectTakenTeamSlugs,
|
|
2207
2439
|
commitAll: () => commitAll,
|
|
2440
|
+
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
2208
2441
|
createOrUpdatePr: () => createOrUpdatePr,
|
|
2209
2442
|
createThreadWorktree: () => createThreadWorktree,
|
|
2210
2443
|
currentBranch: () => currentBranch,
|
|
@@ -2918,6 +3151,52 @@ ${add.stdout}`;
|
|
|
2918
3151
|
await ensureGhPreferOrigin(worktreePath);
|
|
2919
3152
|
return { branchName, worktreePath };
|
|
2920
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
|
+
}
|
|
2921
3200
|
async function removeWorktree(repoPath, worktreePath, opts) {
|
|
2922
3201
|
await git(["worktree", "remove", "--force", worktreePath], repoPath, {
|
|
2923
3202
|
reject: false
|
|
@@ -2957,8 +3236,7 @@ async function isDirty(worktreePath) {
|
|
|
2957
3236
|
return false;
|
|
2958
3237
|
}
|
|
2959
3238
|
function isSideboardScratchPath(relativePath) {
|
|
2960
|
-
|
|
2961
|
-
return p === ".sideboard/attachments" || p.startsWith(".sideboard/attachments/");
|
|
3239
|
+
return isWorkspaceScratchPath(relativePath);
|
|
2962
3240
|
}
|
|
2963
3241
|
function porcelainStatusPath(line) {
|
|
2964
3242
|
const rest = line.length >= 3 ? line.slice(3) : "";
|
|
@@ -2986,7 +3264,7 @@ async function pushBranch(worktreePath, branchName) {
|
|
|
2986
3264
|
}
|
|
2987
3265
|
async function mergePr(cwd, selector, opts) {
|
|
2988
3266
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
2989
|
-
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
|
|
3267
|
+
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
|
|
2990
3268
|
if (slug) viewArgs.push("--repo", slug);
|
|
2991
3269
|
const before = await gh(viewArgs, cwd, { reject: false });
|
|
2992
3270
|
if (before.exitCode !== 0 || !before.stdout.trim()) {
|
|
@@ -2994,16 +3272,29 @@ async function mergePr(cwd, selector, opts) {
|
|
|
2994
3272
|
}
|
|
2995
3273
|
let url = "";
|
|
2996
3274
|
let isDraft = false;
|
|
3275
|
+
let prNumber = null;
|
|
2997
3276
|
try {
|
|
2998
3277
|
const parsed = JSON.parse(before.stdout);
|
|
2999
3278
|
url = String(parsed.url ?? "");
|
|
3000
3279
|
isDraft = Boolean(parsed.isDraft);
|
|
3280
|
+
prNumber = typeof parsed.number === "number" && Number.isFinite(parsed.number) ? parsed.number : null;
|
|
3001
3281
|
if (String(parsed.state ?? "").toUpperCase() === "MERGED") {
|
|
3002
3282
|
return { url, state: "MERGED" };
|
|
3003
3283
|
}
|
|
3004
3284
|
} catch {
|
|
3005
3285
|
throw new Error("Could not parse pull request details");
|
|
3006
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
|
+
}
|
|
3007
3298
|
if (isDraft) {
|
|
3008
3299
|
const readyArgs = ["pr", "ready", selector];
|
|
3009
3300
|
if (slug) readyArgs.push("--repo", slug);
|
|
@@ -3025,10 +3316,10 @@ async function mergePr(cwd, selector, opts) {
|
|
|
3025
3316
|
const after = await gh(viewArgs, cwd, { reject: false });
|
|
3026
3317
|
if (after.exitCode === 0 && after.stdout.trim()) {
|
|
3027
3318
|
try {
|
|
3028
|
-
const parsed = JSON.parse(after.stdout);
|
|
3319
|
+
const parsed = after.stdout ? JSON.parse(after.stdout) : null;
|
|
3029
3320
|
return {
|
|
3030
|
-
url: String(parsed
|
|
3031
|
-
state: String(parsed
|
|
3321
|
+
url: String(parsed?.url ?? url),
|
|
3322
|
+
state: String(parsed?.state ?? "MERGED")
|
|
3032
3323
|
};
|
|
3033
3324
|
} catch {
|
|
3034
3325
|
}
|
|
@@ -3176,7 +3467,9 @@ var init_worktree = __esm({
|
|
|
3176
3467
|
init_gh_errors();
|
|
3177
3468
|
init_run();
|
|
3178
3469
|
init_pr_gates();
|
|
3470
|
+
init_stack();
|
|
3179
3471
|
init_teams();
|
|
3472
|
+
init_workspace_scratch();
|
|
3180
3473
|
init_worktree_labels();
|
|
3181
3474
|
}
|
|
3182
3475
|
});
|
|
@@ -3302,6 +3595,7 @@ var init_coordinator_prompt = __esm({
|
|
|
3302
3595
|
"Discover:",
|
|
3303
3596
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
3304
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",
|
|
3305
3599
|
"- list_models \u2014 only when you need a specific model (rare); otherwise leave model unset = Auto",
|
|
3306
3600
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
3307
3601
|
"Workspaces:",
|
|
@@ -4713,7 +5007,9 @@ var init_injected_mcp = __esm({
|
|
|
4713
5007
|
SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
|
|
4714
5008
|
"mcp__sideboard__present_artifact",
|
|
4715
5009
|
"mcp__sideboard__present_schema",
|
|
4716
|
-
"mcp__sideboard__present_files"
|
|
5010
|
+
"mcp__sideboard__present_files",
|
|
5011
|
+
"mcp__sideboard__ask_user",
|
|
5012
|
+
"mcp__sideboard__present_plan"
|
|
4717
5013
|
];
|
|
4718
5014
|
BRIGHTSY_MCP_ALLOWED_TOOLS = [
|
|
4719
5015
|
"mcp__brightsy",
|
|
@@ -4756,7 +5052,7 @@ var PLAN_MODE_INSTRUCTION;
|
|
|
4756
5052
|
var init_types = __esm({
|
|
4757
5053
|
"src/agents/types.ts"() {
|
|
4758
5054
|
"use strict";
|
|
4759
|
-
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.";
|
|
4760
5056
|
}
|
|
4761
5057
|
});
|
|
4762
5058
|
|
|
@@ -6345,6 +6641,116 @@ var init_agents = __esm({
|
|
|
6345
6641
|
}
|
|
6346
6642
|
});
|
|
6347
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
|
+
|
|
6348
6754
|
// src/agents/cursor-recover.ts
|
|
6349
6755
|
var cursor_recover_exports = {};
|
|
6350
6756
|
__export(cursor_recover_exports, {
|
|
@@ -6353,10 +6759,10 @@ __export(cursor_recover_exports, {
|
|
|
6353
6759
|
function recoverFinishedCursorRun(opts) {
|
|
6354
6760
|
const agentId = opts.agentId.trim();
|
|
6355
6761
|
if (!agentId) return null;
|
|
6356
|
-
const runsPath = (0,
|
|
6357
|
-
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;
|
|
6358
6764
|
try {
|
|
6359
|
-
const lines = (0,
|
|
6765
|
+
const lines = (0, import_node_fs27.readFileSync)(runsPath, "utf8").split("\n");
|
|
6360
6766
|
let best = null;
|
|
6361
6767
|
for (const line of lines) {
|
|
6362
6768
|
const trimmed = line.trim();
|
|
@@ -6382,12 +6788,12 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6382
6788
|
return null;
|
|
6383
6789
|
}
|
|
6384
6790
|
}
|
|
6385
|
-
var
|
|
6791
|
+
var import_node_fs27, import_node_path25;
|
|
6386
6792
|
var init_cursor_recover = __esm({
|
|
6387
6793
|
"src/agents/cursor-recover.ts"() {
|
|
6388
6794
|
"use strict";
|
|
6389
|
-
|
|
6390
|
-
|
|
6795
|
+
import_node_fs27 = require("fs");
|
|
6796
|
+
import_node_path25 = require("path");
|
|
6391
6797
|
init_paths();
|
|
6392
6798
|
}
|
|
6393
6799
|
});
|
|
@@ -6426,6 +6832,7 @@ var init_title = __esm({
|
|
|
6426
6832
|
// src/index.ts
|
|
6427
6833
|
var index_exports = {};
|
|
6428
6834
|
__export(index_exports, {
|
|
6835
|
+
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
6429
6836
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
6430
6837
|
BrightsySideboardApi: () => BrightsySideboardApi,
|
|
6431
6838
|
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
@@ -6441,11 +6848,16 @@ __export(index_exports, {
|
|
|
6441
6848
|
FAMOUS_SOCCER_TEAMS: () => FAMOUS_SOCCER_TEAMS,
|
|
6442
6849
|
GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
|
|
6443
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,
|
|
6444
6854
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS: () => MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
6445
6855
|
ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
|
|
6446
6856
|
Orchestrator: () => Orchestrator,
|
|
6447
6857
|
PASTE_ATTACH_MIN_CHARS: () => PASTE_ATTACH_MIN_CHARS,
|
|
6448
6858
|
PASTE_ATTACH_MIN_LINES: () => PASTE_ATTACH_MIN_LINES,
|
|
6859
|
+
PLAN_FILE_NAME: () => PLAN_FILE_NAME,
|
|
6860
|
+
PLAN_FILE_REL: () => PLAN_FILE_REL,
|
|
6449
6861
|
PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
|
|
6450
6862
|
REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
|
|
6451
6863
|
REPO_REVIEW_PATH: () => REPO_REVIEW_PATH,
|
|
@@ -6455,6 +6867,8 @@ __export(index_exports, {
|
|
|
6455
6867
|
SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
|
|
6456
6868
|
SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
6457
6869
|
THINKING_EFFORTS: () => THINKING_EFFORTS,
|
|
6870
|
+
addPrStackLayer: () => addPrStackLayer,
|
|
6871
|
+
addStackLayerFromThread: () => addStackLayerFromThread,
|
|
6458
6872
|
addWorkspace: () => addWorkspace,
|
|
6459
6873
|
adoptThread: () => adoptThread,
|
|
6460
6874
|
allAdapters: () => allAdapters,
|
|
@@ -6473,6 +6887,7 @@ __export(index_exports, {
|
|
|
6473
6887
|
attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
|
|
6474
6888
|
attachmentsFromBuffers: () => attachmentsFromBuffers,
|
|
6475
6889
|
attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
|
|
6890
|
+
attachmentsGitignoreBody: () => attachmentsGitignoreBody,
|
|
6476
6891
|
autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
|
|
6477
6892
|
autoRenameBranchEnabled: () => autoRenameBranchEnabled,
|
|
6478
6893
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
@@ -6495,6 +6910,7 @@ __export(index_exports, {
|
|
|
6495
6910
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
6496
6911
|
captureLoginEnv: () => captureLoginEnv,
|
|
6497
6912
|
captureTurnBaseline: () => captureTurnBaseline,
|
|
6913
|
+
checkoutPrStackLayer: () => checkoutPrStackLayer,
|
|
6498
6914
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
6499
6915
|
claudeAdapter: () => claudeAdapter,
|
|
6500
6916
|
claudeChromeEnabled: () => claudeChromeEnabled,
|
|
@@ -6514,8 +6930,10 @@ __export(index_exports, {
|
|
|
6514
6930
|
countCacheControlBlocks: () => countCacheControlBlocks,
|
|
6515
6931
|
createChatTab: () => createChatTab,
|
|
6516
6932
|
createEmptyThread: () => createEmptyThread,
|
|
6933
|
+
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
6517
6934
|
createGlobalChat: () => createGlobalChat,
|
|
6518
6935
|
createOrUpdatePr: () => createOrUpdatePr,
|
|
6936
|
+
createPrStack: () => createPrStack,
|
|
6519
6937
|
createThread: () => createThread,
|
|
6520
6938
|
createThreadWorktree: () => createThreadWorktree,
|
|
6521
6939
|
currentBranch: () => currentBranch,
|
|
@@ -6525,6 +6943,7 @@ __export(index_exports, {
|
|
|
6525
6943
|
deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
|
|
6526
6944
|
deleteThreadRecord: () => deleteThreadRecord,
|
|
6527
6945
|
detectAgents: () => detectAgents,
|
|
6946
|
+
detectGhStack: () => detectGhStack,
|
|
6528
6947
|
detectLocalMergeConflicts: () => detectLocalMergeConflicts,
|
|
6529
6948
|
disconnectBrightsyTeam: () => disconnectBrightsyTeam,
|
|
6530
6949
|
discoverSkills: () => discoverSkills,
|
|
@@ -6540,12 +6959,15 @@ __export(index_exports, {
|
|
|
6540
6959
|
estimateThreadChars: () => estimateThreadChars,
|
|
6541
6960
|
expandComposerPrompt: () => expandComposerPrompt,
|
|
6542
6961
|
extractGhErrorDetail: () => extractGhErrorDetail,
|
|
6962
|
+
extractPendingPlanQuestions: () => extractPendingPlanQuestions,
|
|
6963
|
+
extractPresentedPlan: () => extractPresentedPlan,
|
|
6543
6964
|
extractiveSummary: () => extractiveSummary,
|
|
6544
6965
|
fetchPrHead: () => fetchPrHead,
|
|
6545
6966
|
finalizeParts: () => finalizeParts,
|
|
6546
6967
|
findInvalidCacheControlTtlOrder: () => findInvalidCacheControlTtlOrder,
|
|
6547
6968
|
findOrphanWorktrees: () => findOrphanWorktrees,
|
|
6548
6969
|
findThreadByRef: () => findThreadByRef,
|
|
6970
|
+
findThreadForStackLayer: () => findThreadForStackLayer,
|
|
6549
6971
|
flattenTurnInput: () => flattenTurnInput,
|
|
6550
6972
|
forkChatTab: () => forkChatTab,
|
|
6551
6973
|
forkMessageSlice: () => forkMessageSlice,
|
|
@@ -6556,6 +6978,8 @@ __export(index_exports, {
|
|
|
6556
6978
|
formatGhLandError: () => formatGhLandError,
|
|
6557
6979
|
formatIpcInvokeError: () => formatIpcInvokeError,
|
|
6558
6980
|
formatMessagesAsTranscript: () => formatMessagesAsTranscript,
|
|
6981
|
+
formatPlanQuestionAnswers: () => formatPlanQuestionAnswers,
|
|
6982
|
+
formatPlanQuestionsForChat: () => formatPlanQuestionsForChat,
|
|
6559
6983
|
formatRateLimitResetHint: () => formatRateLimitResetHint,
|
|
6560
6984
|
formatRenameBranchDirective: () => formatRenameBranchDirective,
|
|
6561
6985
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
@@ -6579,6 +7003,7 @@ __export(index_exports, {
|
|
|
6579
7003
|
getPrChecks: () => getPrChecks,
|
|
6580
7004
|
getPrDetails: () => getPrDetails,
|
|
6581
7005
|
getPrMeta: () => getPrMeta,
|
|
7006
|
+
getPrStack: () => getPrStack,
|
|
6582
7007
|
getRepoSetupInfo: () => getRepoSetupInfo,
|
|
6583
7008
|
getRunMode: () => getRunMode,
|
|
6584
7009
|
getRunScript: () => getRunScript,
|
|
@@ -6595,9 +7020,12 @@ __export(index_exports, {
|
|
|
6595
7020
|
healOrchestrationSoccerTitles: () => healOrchestrationSoccerTitles,
|
|
6596
7021
|
importConductorWorkspace: () => importConductorWorkspace,
|
|
6597
7022
|
importConductorWorkspaceAsync: () => importConductorWorkspaceAsync,
|
|
7023
|
+
initPrStack: () => initPrStack,
|
|
7024
|
+
initStackFromThread: () => initStackFromThread,
|
|
6598
7025
|
initializeGitRepository: () => initializeGitRepository,
|
|
6599
7026
|
inspectGitWorktree: () => inspectGitWorktree,
|
|
6600
7027
|
installAgent: () => installAgent,
|
|
7028
|
+
isAskUserToolName: () => isAskUserToolName,
|
|
6601
7029
|
isBrightsyConnected: () => isBrightsyConnected,
|
|
6602
7030
|
isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
|
|
6603
7031
|
isCloudCoordinatorThread: () => isCloudCoordinatorThread,
|
|
@@ -6607,14 +7035,17 @@ __export(index_exports, {
|
|
|
6607
7035
|
isGlobalRepoPath: () => isGlobalRepoPath,
|
|
6608
7036
|
isGlobalThread: () => isGlobalThread,
|
|
6609
7037
|
isImageFilePath: () => isImageFilePath,
|
|
7038
|
+
isInPrStack: () => isInPrStack,
|
|
6610
7039
|
isLinearConnected: () => isLinearConnected,
|
|
6611
7040
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6612
7041
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
6613
7042
|
isPidAlive: () => isPidAlive,
|
|
6614
7043
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
7044
|
+
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
6615
7045
|
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
6616
7046
|
isSideboardScratchPath: () => isSideboardScratchPath,
|
|
6617
7047
|
isThinkingEffort: () => isThinkingEffort,
|
|
7048
|
+
isWorkspaceScratchPath: () => isWorkspaceScratchPath,
|
|
6618
7049
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
6619
7050
|
listBranchCommits: () => listBranchCommits,
|
|
6620
7051
|
listBranches: () => listBranches,
|
|
@@ -6651,6 +7082,7 @@ __export(index_exports, {
|
|
|
6651
7082
|
mcpAllowTools: () => mcpAllowTools,
|
|
6652
7083
|
mcpAuthWarnings: () => mcpAuthWarnings,
|
|
6653
7084
|
mergePr: () => mergePr,
|
|
7085
|
+
mergePrStack: () => mergePrStack,
|
|
6654
7086
|
mergeUsage: () => mergeUsage,
|
|
6655
7087
|
nextPastedTextName: () => nextPastedTextName,
|
|
6656
7088
|
nextThinkingEffort: () => nextThinkingEffort,
|
|
@@ -6660,6 +7092,8 @@ __export(index_exports, {
|
|
|
6660
7092
|
normalizeTurnInput: () => normalizeTurnInput,
|
|
6661
7093
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
6662
7094
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
7095
|
+
openPrStackLayers: () => openPrStackLayers,
|
|
7096
|
+
openStackLayer: () => openStackLayer,
|
|
6663
7097
|
opencodeAdapter: () => opencodeAdapter,
|
|
6664
7098
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
6665
7099
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
@@ -6668,15 +7102,19 @@ __export(index_exports, {
|
|
|
6668
7102
|
originGhRepoEnv: () => originGhRepoEnv,
|
|
6669
7103
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6670
7104
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
7105
|
+
parseGhStackViewJson: () => parseGhStackViewJson,
|
|
6671
7106
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
6672
7107
|
parseMcpList: () => parseMcpList,
|
|
7108
|
+
parsePlanQuestionsInput: () => parsePlanQuestionsInput,
|
|
6673
7109
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6674
7110
|
partsToAssistantText: () => partsToAssistantText,
|
|
6675
7111
|
pastedTextStats: () => pastedTextStats,
|
|
6676
7112
|
permissionMode: () => permissionMode,
|
|
7113
|
+
planFileAbs: () => planFileAbs,
|
|
6677
7114
|
previewLand: () => previewLand,
|
|
6678
7115
|
pushBranch: () => pushBranch,
|
|
6679
7116
|
readExistingReviewRequestFile: () => readExistingReviewRequestFile,
|
|
7117
|
+
readPlanFile: () => readPlanFile,
|
|
6680
7118
|
readSkillBody: () => readSkillBody,
|
|
6681
7119
|
readThread: () => readThread,
|
|
6682
7120
|
readWorktreeFile: () => readWorktreeFile,
|
|
@@ -6688,6 +7126,7 @@ __export(index_exports, {
|
|
|
6688
7126
|
repoSlug: () => repoSlug,
|
|
6689
7127
|
requestReview: () => requestReview,
|
|
6690
7128
|
requireAgent: () => requireAgent,
|
|
7129
|
+
resetGhStackDetectCache: () => resetGhStackDetectCache,
|
|
6691
7130
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
6692
7131
|
resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
|
|
6693
7132
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
@@ -6697,6 +7136,7 @@ __export(index_exports, {
|
|
|
6697
7136
|
resolveFilesToCopy: () => resolveFilesToCopy,
|
|
6698
7137
|
resolveGhAuthToken: () => resolveGhAuthToken,
|
|
6699
7138
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
7139
|
+
resolvePlanMarkdown: () => resolvePlanMarkdown,
|
|
6700
7140
|
resolvePrSelector: () => resolvePrSelector,
|
|
6701
7141
|
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6702
7142
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
@@ -6724,12 +7164,16 @@ __export(index_exports, {
|
|
|
6724
7164
|
slugify: () => slugify,
|
|
6725
7165
|
spawnAgentTurn: () => spawnAgentTurn,
|
|
6726
7166
|
splitForCompaction: () => splitForCompaction,
|
|
7167
|
+
stackAgentDefaultsFrom: () => stackAgentDefaultsFrom,
|
|
7168
|
+
stackIdFrom: () => stackIdFrom,
|
|
7169
|
+
stackMergeReadiness: () => stackMergeReadiness,
|
|
6727
7170
|
stageAbsolutePathsAsAttachments: () => stageAbsolutePathsAsAttachments,
|
|
6728
7171
|
stageBuffersAsAttachments: () => stageBuffersAsAttachments,
|
|
6729
7172
|
startDevServer: () => startDevServer,
|
|
6730
7173
|
startMcpServer: () => startMcpServer,
|
|
6731
7174
|
startOrchestration: () => startOrchestration,
|
|
6732
7175
|
stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
|
|
7176
|
+
submitPrStack: () => submitPrStack,
|
|
6733
7177
|
suggestSlug: () => suggestSlug,
|
|
6734
7178
|
summarizeConversation: () => summarizeConversation,
|
|
6735
7179
|
switchBrightsyAccount: () => switchBrightsyAccount,
|
|
@@ -6765,6 +7209,7 @@ __export(index_exports, {
|
|
|
6765
7209
|
worktreeNameFromPath: () => worktreeNameFromPath,
|
|
6766
7210
|
worktreesRoot: () => worktreesRoot,
|
|
6767
7211
|
writeInjectedMcpConfig: () => writeInjectedMcpConfig,
|
|
7212
|
+
writePlanFile: () => writePlanFile,
|
|
6768
7213
|
writeThread: () => writeThread,
|
|
6769
7214
|
writeWorktreeFile: () => writeWorktreeFile
|
|
6770
7215
|
});
|
|
@@ -6778,6 +7223,7 @@ init_global_workspace();
|
|
|
6778
7223
|
init_run();
|
|
6779
7224
|
init_gh_errors();
|
|
6780
7225
|
init_worktree();
|
|
7226
|
+
init_stack();
|
|
6781
7227
|
|
|
6782
7228
|
// src/integrations/github.ts
|
|
6783
7229
|
init_run();
|
|
@@ -6974,18 +7420,18 @@ function asRecord(input) {
|
|
|
6974
7420
|
}
|
|
6975
7421
|
return void 0;
|
|
6976
7422
|
}
|
|
6977
|
-
function
|
|
7423
|
+
function str2(v) {
|
|
6978
7424
|
return typeof v === "string" && v.trim() ? v : void 0;
|
|
6979
7425
|
}
|
|
6980
7426
|
function toolDetail(name, input) {
|
|
6981
7427
|
if (!input) return void 0;
|
|
6982
|
-
const command =
|
|
7428
|
+
const command = str2(input.command) ?? str2(input.cmd);
|
|
6983
7429
|
if (command) return command;
|
|
6984
|
-
const path =
|
|
7430
|
+
const path = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
6985
7431
|
if (path) return path;
|
|
6986
|
-
const pattern =
|
|
7432
|
+
const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
|
|
6987
7433
|
if (pattern) return pattern;
|
|
6988
|
-
const query =
|
|
7434
|
+
const query = str2(input.query) ?? str2(input.prompt);
|
|
6989
7435
|
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
6990
7436
|
try {
|
|
6991
7437
|
const raw = JSON.stringify(input);
|
|
@@ -6998,23 +7444,26 @@ function toolDescription(name, input) {
|
|
|
6998
7444
|
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
6999
7445
|
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
7000
7446
|
if (/connectedAgentRequest/i.test(name)) {
|
|
7001
|
-
return
|
|
7447
|
+
return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
7002
7448
|
}
|
|
7003
7449
|
if (/present_artifact$/i.test(name)) {
|
|
7004
|
-
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";
|
|
7005
7454
|
}
|
|
7006
7455
|
if (/present_schema$/i.test(name)) {
|
|
7007
|
-
return
|
|
7456
|
+
return str2(input?.title) ? `Schema ${str2(input?.title)}` : "Present schema";
|
|
7008
7457
|
}
|
|
7009
7458
|
if (/present_files$/i.test(name)) {
|
|
7010
|
-
return
|
|
7459
|
+
return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
|
|
7011
7460
|
}
|
|
7012
7461
|
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
7013
|
-
return
|
|
7462
|
+
return str2(input?.title) ? `Artifact ${str2(input?.title)}` : "Artifact";
|
|
7014
7463
|
}
|
|
7015
7464
|
if (!input) return n;
|
|
7016
|
-
if (/bash|shell|terminal/i.test(name) &&
|
|
7017
|
-
const cmd =
|
|
7465
|
+
if (/bash|shell|terminal/i.test(name) && str2(input.command)) {
|
|
7466
|
+
const cmd = str2(input.command);
|
|
7018
7467
|
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
7019
7468
|
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
7020
7469
|
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
@@ -7023,11 +7472,11 @@ function toolDescription(name, input) {
|
|
|
7023
7472
|
return "Run shell command";
|
|
7024
7473
|
}
|
|
7025
7474
|
if (/edit|write|apply/i.test(name)) {
|
|
7026
|
-
const path =
|
|
7475
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
7027
7476
|
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
7028
7477
|
}
|
|
7029
7478
|
if (/read/i.test(name)) {
|
|
7030
|
-
const path =
|
|
7479
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
7031
7480
|
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
7032
7481
|
}
|
|
7033
7482
|
if (/grep/i.test(name)) return "Search files";
|
|
@@ -7036,7 +7485,7 @@ function toolDescription(name, input) {
|
|
|
7036
7485
|
}
|
|
7037
7486
|
function toolFilePath(input) {
|
|
7038
7487
|
if (!input) return void 0;
|
|
7039
|
-
return
|
|
7488
|
+
return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
7040
7489
|
}
|
|
7041
7490
|
function countLines(text) {
|
|
7042
7491
|
if (!text) return 0;
|
|
@@ -7044,8 +7493,8 @@ function countLines(text) {
|
|
|
7044
7493
|
}
|
|
7045
7494
|
function diffFromInput(input) {
|
|
7046
7495
|
if (!input) return {};
|
|
7047
|
-
const oldS =
|
|
7048
|
-
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);
|
|
7049
7498
|
if (oldS != null || newS != null) {
|
|
7050
7499
|
return {
|
|
7051
7500
|
additions: countLines(newS),
|
|
@@ -8212,33 +8661,33 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8212
8661
|
let combinedDiff = "";
|
|
8213
8662
|
let labelBase = base;
|
|
8214
8663
|
if (scope === "staged") {
|
|
8215
|
-
const [ns,
|
|
8664
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8216
8665
|
git(["diff", "--name-status", "--cached"], worktreePath, { reject: false }),
|
|
8217
8666
|
git(["diff", "--numstat", "--cached"], worktreePath, { reject: false }),
|
|
8218
8667
|
git(["diff", "--cached"], worktreePath, { reject: false })
|
|
8219
8668
|
]);
|
|
8220
8669
|
nameStatus = ns.stdout;
|
|
8221
|
-
numstat =
|
|
8670
|
+
numstat = num2.stdout;
|
|
8222
8671
|
combinedDiff = diff.stdout;
|
|
8223
8672
|
labelBase = "staged";
|
|
8224
8673
|
} else if (scope === "unstaged") {
|
|
8225
|
-
const [ns,
|
|
8674
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8226
8675
|
git(["diff", "--name-status"], worktreePath, { reject: false }),
|
|
8227
8676
|
git(["diff", "--numstat"], worktreePath, { reject: false }),
|
|
8228
8677
|
git(["diff"], worktreePath, { reject: false })
|
|
8229
8678
|
]);
|
|
8230
8679
|
nameStatus = ns.stdout;
|
|
8231
|
-
numstat =
|
|
8680
|
+
numstat = num2.stdout;
|
|
8232
8681
|
combinedDiff = diff.stdout;
|
|
8233
8682
|
labelBase = "unstaged";
|
|
8234
8683
|
} else if (scope === "uncommitted") {
|
|
8235
|
-
const [ns,
|
|
8684
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8236
8685
|
git(["diff", "--name-status", "HEAD"], worktreePath, { reject: false }),
|
|
8237
8686
|
git(["diff", "--numstat", "HEAD"], worktreePath, { reject: false }),
|
|
8238
8687
|
git(["diff", "HEAD"], worktreePath, { reject: false })
|
|
8239
8688
|
]);
|
|
8240
8689
|
nameStatus = ns.stdout;
|
|
8241
|
-
numstat =
|
|
8690
|
+
numstat = num2.stdout;
|
|
8242
8691
|
combinedDiff = diff.stdout;
|
|
8243
8692
|
labelBase = "HEAD";
|
|
8244
8693
|
} else if (scope === "last_turn") {
|
|
@@ -8257,34 +8706,34 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8257
8706
|
scopeStats
|
|
8258
8707
|
};
|
|
8259
8708
|
}
|
|
8260
|
-
const [ns,
|
|
8709
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8261
8710
|
git(["diff", "--name-status", lastTurnBase], worktreePath, { reject: false }),
|
|
8262
8711
|
git(["diff", "--numstat", lastTurnBase], worktreePath, { reject: false }),
|
|
8263
8712
|
git(["diff", lastTurnBase], worktreePath, { reject: false })
|
|
8264
8713
|
]);
|
|
8265
8714
|
nameStatus = ns.stdout;
|
|
8266
|
-
numstat =
|
|
8715
|
+
numstat = num2.stdout;
|
|
8267
8716
|
combinedDiff = diff.stdout;
|
|
8268
8717
|
labelBase = "last turn";
|
|
8269
8718
|
} else if (scope === "commits" && commitSha) {
|
|
8270
8719
|
const range = `${commitSha}^!`;
|
|
8271
|
-
const [ns,
|
|
8720
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8272
8721
|
git(["diff", "--name-status", range], worktreePath, { reject: false }),
|
|
8273
8722
|
git(["diff", "--numstat", range], worktreePath, { reject: false }),
|
|
8274
8723
|
git(["diff", range], worktreePath, { reject: false })
|
|
8275
8724
|
]);
|
|
8276
8725
|
nameStatus = ns.stdout;
|
|
8277
|
-
numstat =
|
|
8726
|
+
numstat = num2.stdout;
|
|
8278
8727
|
combinedDiff = diff.stdout;
|
|
8279
8728
|
labelBase = commitSha.slice(0, 7);
|
|
8280
8729
|
} else {
|
|
8281
|
-
const [ns,
|
|
8730
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8282
8731
|
git(["diff", "--name-status", mergeBase], worktreePath, { reject: false }),
|
|
8283
8732
|
git(["diff", "--numstat", mergeBase], worktreePath, { reject: false }),
|
|
8284
8733
|
git(["diff", mergeBase], worktreePath, { reject: false })
|
|
8285
8734
|
]);
|
|
8286
8735
|
nameStatus = ns.stdout;
|
|
8287
|
-
numstat =
|
|
8736
|
+
numstat = num2.stdout;
|
|
8288
8737
|
combinedDiff = diff.stdout;
|
|
8289
8738
|
labelBase = base;
|
|
8290
8739
|
}
|
|
@@ -8717,6 +9166,7 @@ function buildDiffCommentAttachment(input) {
|
|
|
8717
9166
|
var import_node_fs19 = require("fs");
|
|
8718
9167
|
var import_node_path19 = require("path");
|
|
8719
9168
|
var import_node_crypto2 = require("crypto");
|
|
9169
|
+
init_workspace_scratch();
|
|
8720
9170
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
8721
9171
|
"png",
|
|
8722
9172
|
"jpg",
|
|
@@ -8737,11 +9187,6 @@ var IMAGE_MIME_BY_EXT = {
|
|
|
8737
9187
|
bmp: "image/bmp",
|
|
8738
9188
|
ico: "image/x-icon"
|
|
8739
9189
|
};
|
|
8740
|
-
var ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
8741
|
-
var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
|
|
8742
|
-
*
|
|
8743
|
-
!.gitignore
|
|
8744
|
-
`;
|
|
8745
9190
|
var MAX_INLINE_BYTES = 4e5;
|
|
8746
9191
|
var MAX_PREVIEW_BYTES = 5e6;
|
|
8747
9192
|
function fileExtension(filePath) {
|
|
@@ -8759,7 +9204,7 @@ function ensureAttachmentsDir(worktreePath) {
|
|
|
8759
9204
|
(0, import_node_fs19.mkdirSync)(dir, { recursive: true });
|
|
8760
9205
|
const gi = (0, import_node_path19.join)(dir, ".gitignore");
|
|
8761
9206
|
if (!(0, import_node_fs19.existsSync)(gi)) {
|
|
8762
|
-
(0, import_node_fs19.writeFileSync)(gi,
|
|
9207
|
+
(0, import_node_fs19.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
8763
9208
|
}
|
|
8764
9209
|
return dir;
|
|
8765
9210
|
}
|
|
@@ -9419,7 +9864,7 @@ var import_node_fs20 = require("fs");
|
|
|
9419
9864
|
init_worktree();
|
|
9420
9865
|
init_thread_store();
|
|
9421
9866
|
init_workspaces();
|
|
9422
|
-
async function createThread(input,
|
|
9867
|
+
async function createThread(input, _onSetupLine) {
|
|
9423
9868
|
await requireAgent(input.agent);
|
|
9424
9869
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9425
9870
|
if (!(0, import_node_fs20.existsSync)(repoPath)) {
|
|
@@ -9429,10 +9874,10 @@ async function createThread(input, onSetupLine) {
|
|
|
9429
9874
|
let sourceIsFork = false;
|
|
9430
9875
|
let prUrl = null;
|
|
9431
9876
|
if (input.sourceType === "pr") {
|
|
9432
|
-
const
|
|
9433
|
-
if (!Number.isFinite(
|
|
9434
|
-
const pr = await getPr(repoPath,
|
|
9435
|
-
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`);
|
|
9436
9881
|
sourceIsFork = pr.isCrossRepository;
|
|
9437
9882
|
prUrl = pr.url;
|
|
9438
9883
|
const localFetchBranch = `sideboard-pr-${pr.number}`;
|
|
@@ -9477,22 +9922,6 @@ async function createThread(input, onSetupLine) {
|
|
|
9477
9922
|
});
|
|
9478
9923
|
writeThread(thread);
|
|
9479
9924
|
await ensureWorkspace(repoPath);
|
|
9480
|
-
try {
|
|
9481
|
-
let setup = await runSetupScript(repoPath, worktreePath, onSetupLine);
|
|
9482
|
-
if (!setup.ran) {
|
|
9483
|
-
setup = await runCursorWorktreeSetup(repoPath, worktreePath, onSetupLine);
|
|
9484
|
-
}
|
|
9485
|
-
if (setup.ran && setup.exitCode !== 0 && setup.exitCode !== null) {
|
|
9486
|
-
updateThread(thread.id, {
|
|
9487
|
-
lastError: `Setup exited ${setup.exitCode} (thread is still usable)`
|
|
9488
|
-
});
|
|
9489
|
-
}
|
|
9490
|
-
} catch (err) {
|
|
9491
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
9492
|
-
updateThread(thread.id, {
|
|
9493
|
-
lastError: `Setup failed: ${message}`
|
|
9494
|
-
});
|
|
9495
|
-
}
|
|
9496
9925
|
return readThread(thread.id) ?? thread;
|
|
9497
9926
|
}
|
|
9498
9927
|
async function listLinearIssues(agent, repoPath) {
|
|
@@ -9545,7 +9974,9 @@ function worktreeBindingFrom(from) {
|
|
|
9545
9974
|
sourceIsFork: from.sourceIsFork,
|
|
9546
9975
|
parentThreadId: from.parentThreadId,
|
|
9547
9976
|
prUrl: from.prUrl,
|
|
9548
|
-
prTitle: from.prTitle
|
|
9977
|
+
prTitle: from.prTitle,
|
|
9978
|
+
stackId: from.stackId,
|
|
9979
|
+
stackLayer: from.stackLayer
|
|
9549
9980
|
};
|
|
9550
9981
|
}
|
|
9551
9982
|
function threadsSharingWorktree(worktreePath) {
|
|
@@ -9660,9 +10091,308 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
9660
10091
|
return thread;
|
|
9661
10092
|
}
|
|
9662
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
|
+
|
|
9663
10393
|
// src/threads/adopt.ts
|
|
9664
10394
|
var import_node_child_process = require("child_process");
|
|
9665
|
-
var
|
|
10395
|
+
var import_node_fs22 = require("fs");
|
|
9666
10396
|
var import_node_os9 = require("os");
|
|
9667
10397
|
var import_node_path20 = require("path");
|
|
9668
10398
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -9687,21 +10417,21 @@ function mapAgentType(raw) {
|
|
|
9687
10417
|
return null;
|
|
9688
10418
|
}
|
|
9689
10419
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
9690
|
-
if (!workspacePath || !(0,
|
|
10420
|
+
if (!workspacePath || !(0, import_node_fs22.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
9691
10421
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
9692
10422
|
let best = null;
|
|
9693
10423
|
let hashes;
|
|
9694
10424
|
try {
|
|
9695
|
-
hashes = (0,
|
|
10425
|
+
hashes = (0, import_node_fs22.readdirSync)(CURSOR_SDK_STORE);
|
|
9696
10426
|
} catch {
|
|
9697
10427
|
return null;
|
|
9698
10428
|
}
|
|
9699
10429
|
for (const hash of hashes) {
|
|
9700
10430
|
const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
9701
|
-
if (!(0,
|
|
10431
|
+
if (!(0, import_node_fs22.existsSync)(agentsFile)) continue;
|
|
9702
10432
|
let text;
|
|
9703
10433
|
try {
|
|
9704
|
-
text = (0,
|
|
10434
|
+
text = (0, import_node_fs22.readFileSync)(agentsFile, "utf8");
|
|
9705
10435
|
} catch {
|
|
9706
10436
|
continue;
|
|
9707
10437
|
}
|
|
@@ -9725,7 +10455,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
9725
10455
|
return best?.agentId ?? null;
|
|
9726
10456
|
}
|
|
9727
10457
|
async function adoptThread(input) {
|
|
9728
|
-
if (!(0,
|
|
10458
|
+
if (!(0, import_node_fs22.existsSync)(input.worktreePath)) {
|
|
9729
10459
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
9730
10460
|
}
|
|
9731
10461
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -9752,18 +10482,18 @@ function conductorDbPath() {
|
|
|
9752
10482
|
return CONDUCTOR_DB;
|
|
9753
10483
|
}
|
|
9754
10484
|
function listConductorWorkspaces() {
|
|
9755
|
-
if (!(0,
|
|
10485
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9756
10486
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9757
10487
|
}
|
|
9758
|
-
const tmp = (0,
|
|
10488
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9759
10489
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9760
10490
|
try {
|
|
9761
|
-
(0,
|
|
10491
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9762
10492
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9763
10493
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9764
|
-
if ((0,
|
|
10494
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9765
10495
|
try {
|
|
9766
|
-
(0,
|
|
10496
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9767
10497
|
} catch {
|
|
9768
10498
|
}
|
|
9769
10499
|
}
|
|
@@ -9839,22 +10569,22 @@ function listConductorWorkspaces() {
|
|
|
9839
10569
|
db.close();
|
|
9840
10570
|
}
|
|
9841
10571
|
} finally {
|
|
9842
|
-
(0,
|
|
10572
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9843
10573
|
}
|
|
9844
10574
|
}
|
|
9845
10575
|
function importConductorWorkspace(workspaceId) {
|
|
9846
|
-
if (!(0,
|
|
10576
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9847
10577
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9848
10578
|
}
|
|
9849
|
-
const tmp = (0,
|
|
10579
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9850
10580
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9851
10581
|
try {
|
|
9852
|
-
(0,
|
|
10582
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9853
10583
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9854
10584
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9855
|
-
if ((0,
|
|
10585
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9856
10586
|
try {
|
|
9857
|
-
(0,
|
|
10587
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9858
10588
|
} catch {
|
|
9859
10589
|
}
|
|
9860
10590
|
}
|
|
@@ -9872,7 +10602,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9872
10602
|
).get(workspaceId);
|
|
9873
10603
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
9874
10604
|
const worktreePath = String(row.workspacePath);
|
|
9875
|
-
if (!(0,
|
|
10605
|
+
if (!(0, import_node_fs22.existsSync)(worktreePath)) {
|
|
9876
10606
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
9877
10607
|
}
|
|
9878
10608
|
let sessionId = null;
|
|
@@ -9935,7 +10665,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9935
10665
|
db.close();
|
|
9936
10666
|
}
|
|
9937
10667
|
} finally {
|
|
9938
|
-
(0,
|
|
10668
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9939
10669
|
}
|
|
9940
10670
|
}
|
|
9941
10671
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -9944,13 +10674,14 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
9944
10674
|
|
|
9945
10675
|
// src/orchestrator/orchestrator.ts
|
|
9946
10676
|
var import_node_events = require("events");
|
|
9947
|
-
var
|
|
10677
|
+
var import_node_fs28 = require("fs");
|
|
9948
10678
|
init_error_detail();
|
|
9949
10679
|
init_agents();
|
|
9950
10680
|
init_worktree();
|
|
10681
|
+
init_stack();
|
|
9951
10682
|
|
|
9952
10683
|
// src/git/orphan-cleanup.ts
|
|
9953
|
-
var
|
|
10684
|
+
var import_node_fs23 = require("fs");
|
|
9954
10685
|
var import_node_path21 = require("path");
|
|
9955
10686
|
init_worktree();
|
|
9956
10687
|
init_thread_store();
|
|
@@ -9966,9 +10697,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9966
10697
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
9967
10698
|
);
|
|
9968
10699
|
const homeRoot = sideboardWorkspacesDir();
|
|
9969
|
-
if ((0,
|
|
10700
|
+
if ((0, import_node_fs23.existsSync)(homeRoot)) {
|
|
9970
10701
|
try {
|
|
9971
|
-
for (const entry of (0,
|
|
10702
|
+
for (const entry of (0, import_node_fs23.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
9972
10703
|
if (!entry.isDirectory()) continue;
|
|
9973
10704
|
void entry;
|
|
9974
10705
|
}
|
|
@@ -9978,7 +10709,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9978
10709
|
const orphans = [];
|
|
9979
10710
|
const seen = /* @__PURE__ */ new Set();
|
|
9980
10711
|
for (const repoPath of repos) {
|
|
9981
|
-
if (!repoPath || !(0,
|
|
10712
|
+
if (!repoPath || !(0, import_node_fs23.existsSync)(repoPath)) continue;
|
|
9982
10713
|
try {
|
|
9983
10714
|
const wts = await listWorktrees(repoPath);
|
|
9984
10715
|
for (const wt of wts) {
|
|
@@ -9989,7 +10720,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9989
10720
|
seen.add(path);
|
|
9990
10721
|
let mtimeMs = 0;
|
|
9991
10722
|
try {
|
|
9992
|
-
mtimeMs = (0,
|
|
10723
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
9993
10724
|
} catch {
|
|
9994
10725
|
mtimeMs = 0;
|
|
9995
10726
|
}
|
|
@@ -9999,16 +10730,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9999
10730
|
}
|
|
10000
10731
|
try {
|
|
10001
10732
|
const root = worktreesRoot(repoPath);
|
|
10002
|
-
if ((0,
|
|
10003
|
-
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 })) {
|
|
10004
10735
|
if (!entry.isDirectory()) continue;
|
|
10005
10736
|
const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
|
|
10006
10737
|
if (known.has(path) || seen.has(path)) continue;
|
|
10007
|
-
if (!(0,
|
|
10738
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
|
|
10008
10739
|
seen.add(path);
|
|
10009
10740
|
let mtimeMs = 0;
|
|
10010
10741
|
try {
|
|
10011
|
-
mtimeMs = (0,
|
|
10742
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
10012
10743
|
} catch {
|
|
10013
10744
|
mtimeMs = Date.now();
|
|
10014
10745
|
}
|
|
@@ -10154,7 +10885,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
10154
10885
|
}
|
|
10155
10886
|
|
|
10156
10887
|
// src/git/clone-repo.ts
|
|
10157
|
-
var
|
|
10888
|
+
var import_node_fs24 = require("fs");
|
|
10158
10889
|
var import_node_path22 = require("path");
|
|
10159
10890
|
var import_execa6 = require("execa");
|
|
10160
10891
|
init_paths();
|
|
@@ -10170,7 +10901,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
10170
10901
|
}
|
|
10171
10902
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
10172
10903
|
const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
|
|
10173
|
-
if ((0,
|
|
10904
|
+
if ((0, import_node_fs24.existsSync)(dest)) {
|
|
10174
10905
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
10175
10906
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
10176
10907
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -10192,7 +10923,7 @@ init_orchestrator_capable();
|
|
|
10192
10923
|
|
|
10193
10924
|
// src/review/request-review.ts
|
|
10194
10925
|
var import_node_crypto5 = require("crypto");
|
|
10195
|
-
var
|
|
10926
|
+
var import_node_fs25 = require("fs");
|
|
10196
10927
|
var import_node_path23 = require("path");
|
|
10197
10928
|
init_global_workspace();
|
|
10198
10929
|
init_thread_store();
|
|
@@ -10317,15 +11048,13 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
10317
11048
|
`;
|
|
10318
11049
|
|
|
10319
11050
|
// src/review/request-review.ts
|
|
11051
|
+
init_workspace_scratch();
|
|
10320
11052
|
var REPO_REVIEW_PATH = ".sideboard/review.md";
|
|
10321
11053
|
var REPO_REVIEW_NAME = "review.md";
|
|
10322
|
-
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`;
|
|
10323
11056
|
var REVIEW_REQUEST_NAME = "Review request.md";
|
|
10324
11057
|
var REVIEW_REQUEST_PREFILL = "Review.";
|
|
10325
|
-
var ATTACHMENTS_GITIGNORE2 = `# Sideboard review / composer attachments (local only)
|
|
10326
|
-
*
|
|
10327
|
-
!.gitignore
|
|
10328
|
-
`;
|
|
10329
11058
|
var LEGACY_REVIEW_TEMPLATE_MARKERS = [
|
|
10330
11059
|
"You are acting as a reviewer for a proposed code change made by another engineer.",
|
|
10331
11060
|
"HOW MANY FINDINGS TO RETURN:"
|
|
@@ -10339,19 +11068,19 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
10339
11068
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
10340
11069
|
}
|
|
10341
11070
|
function readTextIfPresent(abs) {
|
|
10342
|
-
if (!(0,
|
|
11071
|
+
if (!(0, import_node_fs25.existsSync)(abs)) return null;
|
|
10343
11072
|
try {
|
|
10344
|
-
const content = (0,
|
|
11073
|
+
const content = (0, import_node_fs25.readFileSync)(abs, "utf8");
|
|
10345
11074
|
return content.trim() ? content : null;
|
|
10346
11075
|
} catch {
|
|
10347
11076
|
return null;
|
|
10348
11077
|
}
|
|
10349
11078
|
}
|
|
10350
11079
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
10351
|
-
const gitignoreAbs = (0, import_node_path23.join)(worktreePath,
|
|
10352
|
-
if ((0,
|
|
10353
|
-
(0,
|
|
10354
|
-
(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");
|
|
10355
11084
|
}
|
|
10356
11085
|
function resolveReviewGuidelines(worktreePath) {
|
|
10357
11086
|
const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -10374,9 +11103,19 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
10374
11103
|
source: "local"
|
|
10375
11104
|
};
|
|
10376
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
|
+
}
|
|
10377
11116
|
ensureAttachmentsGitignore(worktreePath);
|
|
10378
|
-
(0,
|
|
10379
|
-
(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");
|
|
10380
11119
|
return {
|
|
10381
11120
|
path: REVIEW_REQUEST_PATH,
|
|
10382
11121
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -10396,17 +11135,18 @@ function ensureReviewRequestFile(worktreePath) {
|
|
|
10396
11135
|
};
|
|
10397
11136
|
}
|
|
10398
11137
|
const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
|
|
10399
|
-
const localContent = readTextIfPresent(localAbs);
|
|
11138
|
+
const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
10400
11139
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
11140
|
+
const path = (0, import_node_fs25.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
|
|
10401
11141
|
return {
|
|
10402
|
-
path
|
|
11142
|
+
path,
|
|
10403
11143
|
name: REVIEW_REQUEST_NAME,
|
|
10404
11144
|
content: localContent,
|
|
10405
11145
|
source: "local"
|
|
10406
11146
|
};
|
|
10407
11147
|
}
|
|
10408
|
-
(0,
|
|
10409
|
-
(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");
|
|
10410
11150
|
return {
|
|
10411
11151
|
path: REPO_REVIEW_PATH,
|
|
10412
11152
|
name: REPO_REVIEW_NAME,
|
|
@@ -10426,7 +11166,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
10426
11166
|
};
|
|
10427
11167
|
}
|
|
10428
11168
|
function readExistingReviewRequestFile(worktreePath) {
|
|
10429
|
-
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));
|
|
10430
11170
|
}
|
|
10431
11171
|
async function requestReview(threadRef, send) {
|
|
10432
11172
|
const from = findThreadByRef(threadRef);
|
|
@@ -10565,6 +11305,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
10565
11305
|
|
|
10566
11306
|
// src/orchestrator/orchestrator.ts
|
|
10567
11307
|
init_types();
|
|
11308
|
+
init_plan_file();
|
|
10568
11309
|
init_settings();
|
|
10569
11310
|
|
|
10570
11311
|
// src/threads/sync-branch.ts
|
|
@@ -10684,7 +11425,7 @@ var Orchestrator = class {
|
|
|
10684
11425
|
}
|
|
10685
11426
|
continue;
|
|
10686
11427
|
}
|
|
10687
|
-
if (!(0,
|
|
11428
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
10688
11429
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10689
11430
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10690
11431
|
continue;
|
|
@@ -10829,14 +11570,9 @@ var Orchestrator = class {
|
|
|
10829
11570
|
return findThreadByRef(idOrRef) ?? readThread(idOrRef);
|
|
10830
11571
|
}
|
|
10831
11572
|
async createThread(input) {
|
|
10832
|
-
let thread = await createThread(input
|
|
10833
|
-
this.emit({
|
|
10834
|
-
type: "turn_output",
|
|
10835
|
-
threadId: "pending",
|
|
10836
|
-
event: { type: "stdout", data: line }
|
|
10837
|
-
});
|
|
10838
|
-
});
|
|
11573
|
+
let thread = await createThread(input);
|
|
10839
11574
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
11575
|
+
await this.runSetupAfterCreate(thread.id);
|
|
10840
11576
|
const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
10841
11577
|
if (autoRunAfterSetupEnabled2()) {
|
|
10842
11578
|
try {
|
|
@@ -10850,6 +11586,19 @@ var Orchestrator = class {
|
|
|
10850
11586
|
}
|
|
10851
11587
|
return thread;
|
|
10852
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
|
+
}
|
|
10853
11602
|
listWorkspaces() {
|
|
10854
11603
|
const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
|
|
10855
11604
|
return syncWorkspacesFromThreads(fromThreads);
|
|
@@ -11198,6 +11947,19 @@ var Orchestrator = class {
|
|
|
11198
11947
|
});
|
|
11199
11948
|
}
|
|
11200
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
|
+
}
|
|
11201
11963
|
if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
|
|
11202
11964
|
(p) => p.type === "tool" && /exitplanmode/i.test(p.name)
|
|
11203
11965
|
)) {
|
|
@@ -11679,6 +12441,82 @@ var Orchestrator = class {
|
|
|
11679
12441
|
}
|
|
11680
12442
|
return meta;
|
|
11681
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
|
+
}
|
|
11682
12520
|
async getPrDetails(threadRef) {
|
|
11683
12521
|
const { thread, selector, cwd } = await this.withPrSelector(threadRef);
|
|
11684
12522
|
if (!selector) return null;
|
|
@@ -11741,14 +12579,9 @@ var Orchestrator = class {
|
|
|
11741
12579
|
return forkChatTab(input);
|
|
11742
12580
|
}
|
|
11743
12581
|
async forkThreadWorktree(input) {
|
|
11744
|
-
const thread = await forkThreadWorktree(input
|
|
11745
|
-
this.emit({
|
|
11746
|
-
type: "turn_output",
|
|
11747
|
-
threadId: "pending",
|
|
11748
|
-
event: { type: "stdout", data: line }
|
|
11749
|
-
});
|
|
11750
|
-
});
|
|
12582
|
+
const thread = await forkThreadWorktree(input);
|
|
11751
12583
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
12584
|
+
await this.runSetupAfterCreate(thread.id);
|
|
11752
12585
|
return thread;
|
|
11753
12586
|
}
|
|
11754
12587
|
renameThread(threadRef, title) {
|
|
@@ -11762,7 +12595,7 @@ var Orchestrator = class {
|
|
|
11762
12595
|
}
|
|
11763
12596
|
/**
|
|
11764
12597
|
* Stage OS / worktree files into composer attachments (copies external files
|
|
11765
|
-
* into `.
|
|
12598
|
+
* into `.context/attachments/` so agents can Read images and binaries).
|
|
11766
12599
|
*/
|
|
11767
12600
|
attachComposerFiles(threadRef, opts) {
|
|
11768
12601
|
const thread = this.requireThread(threadRef);
|
|
@@ -11836,7 +12669,7 @@ var Orchestrator = class {
|
|
|
11836
12669
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
11837
12670
|
return setStatus(thread.id, "idle");
|
|
11838
12671
|
}
|
|
11839
|
-
if (!(0,
|
|
12672
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
11840
12673
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
11841
12674
|
const { execa: execa7 } = await import("execa");
|
|
11842
12675
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -11944,13 +12777,116 @@ async function startOrchestration(opts) {
|
|
|
11944
12777
|
}
|
|
11945
12778
|
|
|
11946
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();
|
|
11947
12883
|
init_coordinator_prompt();
|
|
11948
12884
|
|
|
11949
12885
|
// src/mcp/server.ts
|
|
11950
12886
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
11951
12887
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11952
12888
|
var import_zod = require("zod");
|
|
11953
|
-
var
|
|
12889
|
+
var import_node_path26 = require("path");
|
|
11954
12890
|
init_worktree();
|
|
11955
12891
|
init_global_workspace();
|
|
11956
12892
|
init_list_models();
|
|
@@ -11999,7 +12935,7 @@ async function startMcpServer() {
|
|
|
11999
12935
|
async () => {
|
|
12000
12936
|
const threads = orch.getThreads(true);
|
|
12001
12937
|
const lines = threads.map((t) => {
|
|
12002
|
-
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;
|
|
12003
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}` : ""}`;
|
|
12004
12940
|
});
|
|
12005
12941
|
return {
|
|
@@ -12061,6 +12997,59 @@ async function startMcpServer() {
|
|
|
12061
12997
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
12062
12998
|
}
|
|
12063
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
|
+
);
|
|
12064
13053
|
server.tool(
|
|
12065
13054
|
"present_schema",
|
|
12066
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.",
|
|
@@ -12639,6 +13628,126 @@ async function startMcpServer() {
|
|
|
12639
13628
|
};
|
|
12640
13629
|
}
|
|
12641
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
|
+
);
|
|
12642
13751
|
server.tool(
|
|
12643
13752
|
"list_issues",
|
|
12644
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.",
|
|
@@ -13008,6 +14117,7 @@ init_connected_teams();
|
|
|
13008
14117
|
init_injected_mcp();
|
|
13009
14118
|
// Annotate the CommonJS export names for ESM import in node:
|
|
13010
14119
|
0 && (module.exports = {
|
|
14120
|
+
ATTACHMENTS_DIR,
|
|
13011
14121
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
13012
14122
|
BrightsySideboardApi,
|
|
13013
14123
|
CLAUDE_MODEL_CATALOG,
|
|
@@ -13023,11 +14133,16 @@ init_injected_mcp();
|
|
|
13023
14133
|
FAMOUS_SOCCER_TEAMS,
|
|
13024
14134
|
GLOBAL_WORKSPACE_ID,
|
|
13025
14135
|
HARNESS_ENV_KEYS,
|
|
14136
|
+
LEGACY_ATTACHMENTS_DIR,
|
|
14137
|
+
LEGACY_PLAN_FILE_REL,
|
|
14138
|
+
LEGACY_REVIEW_REQUEST_PATH,
|
|
13026
14139
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
13027
14140
|
ORCHESTRATOR_AGENT_KINDS,
|
|
13028
14141
|
Orchestrator,
|
|
13029
14142
|
PASTE_ATTACH_MIN_CHARS,
|
|
13030
14143
|
PASTE_ATTACH_MIN_LINES,
|
|
14144
|
+
PLAN_FILE_NAME,
|
|
14145
|
+
PLAN_FILE_REL,
|
|
13031
14146
|
PLAN_MODE_INSTRUCTION,
|
|
13032
14147
|
REPO_REVIEW_NAME,
|
|
13033
14148
|
REPO_REVIEW_PATH,
|
|
@@ -13037,6 +14152,8 @@ init_injected_mcp();
|
|
|
13037
14152
|
SIDEBOARD_FORCE_STOP,
|
|
13038
14153
|
SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
13039
14154
|
THINKING_EFFORTS,
|
|
14155
|
+
addPrStackLayer,
|
|
14156
|
+
addStackLayerFromThread,
|
|
13040
14157
|
addWorkspace,
|
|
13041
14158
|
adoptThread,
|
|
13042
14159
|
allAdapters,
|
|
@@ -13055,6 +14172,7 @@ init_injected_mcp();
|
|
|
13055
14172
|
attachmentFromAbsolutePath,
|
|
13056
14173
|
attachmentsFromBuffers,
|
|
13057
14174
|
attachmentsFromWorktreePaths,
|
|
14175
|
+
attachmentsGitignoreBody,
|
|
13058
14176
|
autoCleanupOrphansEnabled,
|
|
13059
14177
|
autoRenameBranchEnabled,
|
|
13060
14178
|
autoRunAfterSetupEnabled,
|
|
@@ -13077,6 +14195,7 @@ init_injected_mcp();
|
|
|
13077
14195
|
caffeinateWhileRunningEnabled,
|
|
13078
14196
|
captureLoginEnv,
|
|
13079
14197
|
captureTurnBaseline,
|
|
14198
|
+
checkoutPrStackLayer,
|
|
13080
14199
|
childEnvWithAppSettings,
|
|
13081
14200
|
claudeAdapter,
|
|
13082
14201
|
claudeChromeEnabled,
|
|
@@ -13096,8 +14215,10 @@ init_injected_mcp();
|
|
|
13096
14215
|
countCacheControlBlocks,
|
|
13097
14216
|
createChatTab,
|
|
13098
14217
|
createEmptyThread,
|
|
14218
|
+
createExistingBranchWorktree,
|
|
13099
14219
|
createGlobalChat,
|
|
13100
14220
|
createOrUpdatePr,
|
|
14221
|
+
createPrStack,
|
|
13101
14222
|
createThread,
|
|
13102
14223
|
createThreadWorktree,
|
|
13103
14224
|
currentBranch,
|
|
@@ -13107,6 +14228,7 @@ init_injected_mcp();
|
|
|
13107
14228
|
deleteBranchOnPurgeEnabled,
|
|
13108
14229
|
deleteThreadRecord,
|
|
13109
14230
|
detectAgents,
|
|
14231
|
+
detectGhStack,
|
|
13110
14232
|
detectLocalMergeConflicts,
|
|
13111
14233
|
disconnectBrightsyTeam,
|
|
13112
14234
|
discoverSkills,
|
|
@@ -13122,12 +14244,15 @@ init_injected_mcp();
|
|
|
13122
14244
|
estimateThreadChars,
|
|
13123
14245
|
expandComposerPrompt,
|
|
13124
14246
|
extractGhErrorDetail,
|
|
14247
|
+
extractPendingPlanQuestions,
|
|
14248
|
+
extractPresentedPlan,
|
|
13125
14249
|
extractiveSummary,
|
|
13126
14250
|
fetchPrHead,
|
|
13127
14251
|
finalizeParts,
|
|
13128
14252
|
findInvalidCacheControlTtlOrder,
|
|
13129
14253
|
findOrphanWorktrees,
|
|
13130
14254
|
findThreadByRef,
|
|
14255
|
+
findThreadForStackLayer,
|
|
13131
14256
|
flattenTurnInput,
|
|
13132
14257
|
forkChatTab,
|
|
13133
14258
|
forkMessageSlice,
|
|
@@ -13138,6 +14263,8 @@ init_injected_mcp();
|
|
|
13138
14263
|
formatGhLandError,
|
|
13139
14264
|
formatIpcInvokeError,
|
|
13140
14265
|
formatMessagesAsTranscript,
|
|
14266
|
+
formatPlanQuestionAnswers,
|
|
14267
|
+
formatPlanQuestionsForChat,
|
|
13141
14268
|
formatRateLimitResetHint,
|
|
13142
14269
|
formatRenameBranchDirective,
|
|
13143
14270
|
formatTranscriptMarkdown,
|
|
@@ -13161,6 +14288,7 @@ init_injected_mcp();
|
|
|
13161
14288
|
getPrChecks,
|
|
13162
14289
|
getPrDetails,
|
|
13163
14290
|
getPrMeta,
|
|
14291
|
+
getPrStack,
|
|
13164
14292
|
getRepoSetupInfo,
|
|
13165
14293
|
getRunMode,
|
|
13166
14294
|
getRunScript,
|
|
@@ -13177,9 +14305,12 @@ init_injected_mcp();
|
|
|
13177
14305
|
healOrchestrationSoccerTitles,
|
|
13178
14306
|
importConductorWorkspace,
|
|
13179
14307
|
importConductorWorkspaceAsync,
|
|
14308
|
+
initPrStack,
|
|
14309
|
+
initStackFromThread,
|
|
13180
14310
|
initializeGitRepository,
|
|
13181
14311
|
inspectGitWorktree,
|
|
13182
14312
|
installAgent,
|
|
14313
|
+
isAskUserToolName,
|
|
13183
14314
|
isBrightsyConnected,
|
|
13184
14315
|
isBrightsyNdjsonLine,
|
|
13185
14316
|
isCloudCoordinatorThread,
|
|
@@ -13189,14 +14320,17 @@ init_injected_mcp();
|
|
|
13189
14320
|
isGlobalRepoPath,
|
|
13190
14321
|
isGlobalThread,
|
|
13191
14322
|
isImageFilePath,
|
|
14323
|
+
isInPrStack,
|
|
13192
14324
|
isLinearConnected,
|
|
13193
14325
|
isOrchestratorCapableAgent,
|
|
13194
14326
|
isOrchestratorThread,
|
|
13195
14327
|
isPidAlive,
|
|
13196
14328
|
isPlaceholderBranch,
|
|
14329
|
+
isPresentPlanToolName,
|
|
13197
14330
|
isSessionQuotaLimit,
|
|
13198
14331
|
isSideboardScratchPath,
|
|
13199
14332
|
isThinkingEffort,
|
|
14333
|
+
isWorkspaceScratchPath,
|
|
13200
14334
|
listAgentSetupInfo,
|
|
13201
14335
|
listBranchCommits,
|
|
13202
14336
|
listBranches,
|
|
@@ -13233,6 +14367,7 @@ init_injected_mcp();
|
|
|
13233
14367
|
mcpAllowTools,
|
|
13234
14368
|
mcpAuthWarnings,
|
|
13235
14369
|
mergePr,
|
|
14370
|
+
mergePrStack,
|
|
13236
14371
|
mergeUsage,
|
|
13237
14372
|
nextPastedTextName,
|
|
13238
14373
|
nextThinkingEffort,
|
|
@@ -13242,6 +14377,8 @@ init_injected_mcp();
|
|
|
13242
14377
|
normalizeTurnInput,
|
|
13243
14378
|
normalizeWorktreePath,
|
|
13244
14379
|
openInSystemTerminal,
|
|
14380
|
+
openPrStackLayers,
|
|
14381
|
+
openStackLayer,
|
|
13245
14382
|
opencodeAdapter,
|
|
13246
14383
|
orchestrationQuotaFallbackAgent,
|
|
13247
14384
|
orchestrationQuotaOnLimit,
|
|
@@ -13250,15 +14387,19 @@ init_injected_mcp();
|
|
|
13250
14387
|
originGhRepoEnv,
|
|
13251
14388
|
parseCursorRunnerLine,
|
|
13252
14389
|
parseForceStopMessage,
|
|
14390
|
+
parseGhStackViewJson,
|
|
13253
14391
|
parseGithubSlugFromRemoteUrl,
|
|
13254
14392
|
parseMcpList,
|
|
14393
|
+
parsePlanQuestionsInput,
|
|
13255
14394
|
parseSessionQuotaResetAt,
|
|
13256
14395
|
partsToAssistantText,
|
|
13257
14396
|
pastedTextStats,
|
|
13258
14397
|
permissionMode,
|
|
14398
|
+
planFileAbs,
|
|
13259
14399
|
previewLand,
|
|
13260
14400
|
pushBranch,
|
|
13261
14401
|
readExistingReviewRequestFile,
|
|
14402
|
+
readPlanFile,
|
|
13262
14403
|
readSkillBody,
|
|
13263
14404
|
readThread,
|
|
13264
14405
|
readWorktreeFile,
|
|
@@ -13270,6 +14411,7 @@ init_injected_mcp();
|
|
|
13270
14411
|
repoSlug,
|
|
13271
14412
|
requestReview,
|
|
13272
14413
|
requireAgent,
|
|
14414
|
+
resetGhStackDetectCache,
|
|
13273
14415
|
resolveClaudeExecutable,
|
|
13274
14416
|
resolveConductorCursorAgentId,
|
|
13275
14417
|
resolveCursorModelId,
|
|
@@ -13279,6 +14421,7 @@ init_injected_mcp();
|
|
|
13279
14421
|
resolveFilesToCopy,
|
|
13280
14422
|
resolveGhAuthToken,
|
|
13281
14423
|
resolveGithubRepoSlug,
|
|
14424
|
+
resolvePlanMarkdown,
|
|
13282
14425
|
resolvePrSelector,
|
|
13283
14426
|
resolveQuotaFallbackAgent,
|
|
13284
14427
|
resolveRepoRoot,
|
|
@@ -13306,12 +14449,16 @@ init_injected_mcp();
|
|
|
13306
14449
|
slugify,
|
|
13307
14450
|
spawnAgentTurn,
|
|
13308
14451
|
splitForCompaction,
|
|
14452
|
+
stackAgentDefaultsFrom,
|
|
14453
|
+
stackIdFrom,
|
|
14454
|
+
stackMergeReadiness,
|
|
13309
14455
|
stageAbsolutePathsAsAttachments,
|
|
13310
14456
|
stageBuffersAsAttachments,
|
|
13311
14457
|
startDevServer,
|
|
13312
14458
|
startMcpServer,
|
|
13313
14459
|
startOrchestration,
|
|
13314
14460
|
stripBrightsyNdjsonNoise,
|
|
14461
|
+
submitPrStack,
|
|
13315
14462
|
suggestSlug,
|
|
13316
14463
|
summarizeConversation,
|
|
13317
14464
|
switchBrightsyAccount,
|
|
@@ -13347,6 +14494,7 @@ init_injected_mcp();
|
|
|
13347
14494
|
worktreeNameFromPath,
|
|
13348
14495
|
worktreesRoot,
|
|
13349
14496
|
writeInjectedMcpConfig,
|
|
14497
|
+
writePlanFile,
|
|
13350
14498
|
writeThread,
|
|
13351
14499
|
writeWorktreeFile
|
|
13352
14500
|
});
|