@sideboard-ai/core 0.1.51 → 0.1.53
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/cursor-runner.cjs +43 -4
- package/dist/agents/cursor-runner.js +45 -6
- package/dist/{agents-HIEJL3UV.js → agents-KP7UJEHJ.js} +1 -1
- package/dist/{agents-5ROTZNCX.js → agents-KYACODJ3.js} +2 -2
- package/dist/{chunk-5ZPSH7VI.js → chunk-A6HVEMIB.js} +16 -7
- package/dist/chunk-B3SJXYIJ.js +24 -0
- package/dist/{chunk-7EUWSBWR.js → chunk-BZST4HMJ.js} +21 -6
- 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-J5JTEJ5O.js → chunk-VG22SETP.js} +6 -0
- 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 +1383 -149
- package/dist/index.d.cts +389 -15
- package/dist/index.d.ts +389 -15
- package/dist/index.js +883 -91
- package/dist/mcp/run-stdio.cjs +1154 -141
- package/dist/mcp/run-stdio.js +709 -79
- 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:",
|
|
@@ -4022,6 +4316,11 @@ function summarizeTurnStderr(tail, maxChars = 500) {
|
|
|
4022
4316
|
if (joined.length <= maxChars) return joined;
|
|
4023
4317
|
return joined.slice(joined.length - maxChars);
|
|
4024
4318
|
}
|
|
4319
|
+
function looksLikeInvalidAgentSession(text) {
|
|
4320
|
+
const lower = text.trim().toLowerCase();
|
|
4321
|
+
if (!lower) return false;
|
|
4322
|
+
return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower);
|
|
4323
|
+
}
|
|
4025
4324
|
function looksLikeAgentFailureMessage(text) {
|
|
4026
4325
|
const lower = text.trim().toLowerCase();
|
|
4027
4326
|
if (!lower) return false;
|
|
@@ -4713,7 +5012,9 @@ var init_injected_mcp = __esm({
|
|
|
4713
5012
|
SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
|
|
4714
5013
|
"mcp__sideboard__present_artifact",
|
|
4715
5014
|
"mcp__sideboard__present_schema",
|
|
4716
|
-
"mcp__sideboard__present_files"
|
|
5015
|
+
"mcp__sideboard__present_files",
|
|
5016
|
+
"mcp__sideboard__ask_user",
|
|
5017
|
+
"mcp__sideboard__present_plan"
|
|
4717
5018
|
];
|
|
4718
5019
|
BRIGHTSY_MCP_ALLOWED_TOOLS = [
|
|
4719
5020
|
"mcp__brightsy",
|
|
@@ -4756,7 +5057,7 @@ var PLAN_MODE_INSTRUCTION;
|
|
|
4756
5057
|
var init_types = __esm({
|
|
4757
5058
|
"src/agents/types.ts"() {
|
|
4758
5059
|
"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
|
|
5060
|
+
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
5061
|
}
|
|
4761
5062
|
});
|
|
4762
5063
|
|
|
@@ -5828,6 +6129,7 @@ var init_opencode = __esm({
|
|
|
5828
6129
|
}
|
|
5829
6130
|
},
|
|
5830
6131
|
async resolveSessionId(worktreePath, cached) {
|
|
6132
|
+
const cachedId = cached?.trim() || null;
|
|
5831
6133
|
const listed = await run(
|
|
5832
6134
|
"opencode",
|
|
5833
6135
|
["session", "list", "--format", "json"],
|
|
@@ -5839,15 +6141,21 @@ var init_opencode = __esm({
|
|
|
5839
6141
|
if (Array.isArray(sessions) && sessions.length > 0) {
|
|
5840
6142
|
const norm = (p) => p.replace(/\/+$/, "");
|
|
5841
6143
|
const wt = norm(worktreePath);
|
|
5842
|
-
const
|
|
6144
|
+
const forWorktree = sessions.filter(
|
|
5843
6145
|
(s) => s.directory && norm(s.directory) === wt || s.path && norm(s.path) === wt
|
|
5844
6146
|
);
|
|
5845
|
-
if (
|
|
6147
|
+
if (cachedId && forWorktree.some((s) => s.id === cachedId)) {
|
|
6148
|
+
return cachedId;
|
|
6149
|
+
}
|
|
6150
|
+
if (cachedId && sessions.some((s) => s.id === cachedId)) {
|
|
6151
|
+
return cachedId;
|
|
6152
|
+
}
|
|
6153
|
+
return null;
|
|
5846
6154
|
}
|
|
5847
6155
|
} catch {
|
|
5848
6156
|
}
|
|
5849
6157
|
}
|
|
5850
|
-
return
|
|
6158
|
+
return cachedId;
|
|
5851
6159
|
},
|
|
5852
6160
|
async buildAttach(thread) {
|
|
5853
6161
|
const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
@@ -6345,6 +6653,116 @@ var init_agents = __esm({
|
|
|
6345
6653
|
}
|
|
6346
6654
|
});
|
|
6347
6655
|
|
|
6656
|
+
// src/plan/plan-present.ts
|
|
6657
|
+
function asRecord2(v) {
|
|
6658
|
+
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
6659
|
+
}
|
|
6660
|
+
function isPresentPlanToolName(name) {
|
|
6661
|
+
if (!name) return false;
|
|
6662
|
+
return /present_plan$/i.test(name) || /^mcp__sideboard__present_plan$/i.test(name);
|
|
6663
|
+
}
|
|
6664
|
+
function extractPresentedPlan(parts) {
|
|
6665
|
+
if (!parts?.length) return null;
|
|
6666
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
6667
|
+
const p = parts[i];
|
|
6668
|
+
if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
|
|
6669
|
+
const input = asRecord2(p.input) ?? {};
|
|
6670
|
+
const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
|
|
6671
|
+
if (!content.trim()) continue;
|
|
6672
|
+
const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
|
|
6673
|
+
const path = typeof input.path === "string" && input.path.trim() ? input.path.trim() : PLAN_FILE_REL;
|
|
6674
|
+
return { title, content: content.trim(), path, source: "present_plan" };
|
|
6675
|
+
}
|
|
6676
|
+
return null;
|
|
6677
|
+
}
|
|
6678
|
+
function resolvePlanMarkdown(opts) {
|
|
6679
|
+
const fromTool = extractPresentedPlan(opts.parts);
|
|
6680
|
+
if (fromTool) return fromTool;
|
|
6681
|
+
const file = opts.fileContent?.trim();
|
|
6682
|
+
if (file) {
|
|
6683
|
+
return {
|
|
6684
|
+
title: "Plan",
|
|
6685
|
+
content: file,
|
|
6686
|
+
path: PLAN_FILE_REL,
|
|
6687
|
+
source: "exit_plan"
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
const text = opts.text?.trim();
|
|
6691
|
+
if (text && text.length >= 80) {
|
|
6692
|
+
return {
|
|
6693
|
+
title: "Plan",
|
|
6694
|
+
content: text,
|
|
6695
|
+
path: PLAN_FILE_REL,
|
|
6696
|
+
source: "text"
|
|
6697
|
+
};
|
|
6698
|
+
}
|
|
6699
|
+
return null;
|
|
6700
|
+
}
|
|
6701
|
+
var PLAN_FILE_REL, PLAN_FILE_NAME, LEGACY_PLAN_FILE_REL;
|
|
6702
|
+
var init_plan_present = __esm({
|
|
6703
|
+
"src/plan/plan-present.ts"() {
|
|
6704
|
+
"use strict";
|
|
6705
|
+
init_workspace_scratch();
|
|
6706
|
+
PLAN_FILE_REL = `${ATTACHMENTS_DIR}/plan.md`;
|
|
6707
|
+
PLAN_FILE_NAME = "plan.md";
|
|
6708
|
+
LEGACY_PLAN_FILE_REL = ".sideboard/plan.md";
|
|
6709
|
+
}
|
|
6710
|
+
});
|
|
6711
|
+
|
|
6712
|
+
// src/plan/plan-file.ts
|
|
6713
|
+
var plan_file_exports = {};
|
|
6714
|
+
__export(plan_file_exports, {
|
|
6715
|
+
LEGACY_PLAN_FILE_REL: () => LEGACY_PLAN_FILE_REL,
|
|
6716
|
+
PLAN_FILE_NAME: () => PLAN_FILE_NAME,
|
|
6717
|
+
PLAN_FILE_REL: () => PLAN_FILE_REL,
|
|
6718
|
+
extractPresentedPlan: () => extractPresentedPlan,
|
|
6719
|
+
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
6720
|
+
planFileAbs: () => planFileAbs,
|
|
6721
|
+
readPlanFile: () => readPlanFile,
|
|
6722
|
+
resolvePlanMarkdown: () => resolvePlanMarkdown,
|
|
6723
|
+
writePlanFile: () => writePlanFile
|
|
6724
|
+
});
|
|
6725
|
+
function ensureAttachmentsGitignore2(worktreePath) {
|
|
6726
|
+
const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
6727
|
+
if ((0, import_node_fs26.existsSync)(gitignoreAbs)) return;
|
|
6728
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
|
|
6729
|
+
(0, import_node_fs26.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
6730
|
+
}
|
|
6731
|
+
function planFileAbs(worktreePath) {
|
|
6732
|
+
return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
|
|
6733
|
+
}
|
|
6734
|
+
function readTextIfPresent2(abs) {
|
|
6735
|
+
if (!(0, import_node_fs26.existsSync)(abs)) return null;
|
|
6736
|
+
try {
|
|
6737
|
+
const content = (0, import_node_fs26.readFileSync)(abs, "utf8");
|
|
6738
|
+
return content.trim() ? content : null;
|
|
6739
|
+
} catch {
|
|
6740
|
+
return null;
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
function readPlanFile(worktreePath) {
|
|
6744
|
+
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));
|
|
6745
|
+
}
|
|
6746
|
+
function writePlanFile(worktreePath, content) {
|
|
6747
|
+
ensureAttachmentsGitignore2(worktreePath);
|
|
6748
|
+
const abs = planFileAbs(worktreePath);
|
|
6749
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
|
|
6750
|
+
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
6751
|
+
(0, import_node_fs26.writeFileSync)(abs, body, "utf8");
|
|
6752
|
+
return PLAN_FILE_REL;
|
|
6753
|
+
}
|
|
6754
|
+
var import_node_fs26, import_node_path24;
|
|
6755
|
+
var init_plan_file = __esm({
|
|
6756
|
+
"src/plan/plan-file.ts"() {
|
|
6757
|
+
"use strict";
|
|
6758
|
+
import_node_fs26 = require("fs");
|
|
6759
|
+
import_node_path24 = require("path");
|
|
6760
|
+
init_workspace_scratch();
|
|
6761
|
+
init_plan_present();
|
|
6762
|
+
init_plan_present();
|
|
6763
|
+
}
|
|
6764
|
+
});
|
|
6765
|
+
|
|
6348
6766
|
// src/agents/cursor-recover.ts
|
|
6349
6767
|
var cursor_recover_exports = {};
|
|
6350
6768
|
__export(cursor_recover_exports, {
|
|
@@ -6353,10 +6771,10 @@ __export(cursor_recover_exports, {
|
|
|
6353
6771
|
function recoverFinishedCursorRun(opts) {
|
|
6354
6772
|
const agentId = opts.agentId.trim();
|
|
6355
6773
|
if (!agentId) return null;
|
|
6356
|
-
const runsPath = (0,
|
|
6357
|
-
if (!(0,
|
|
6774
|
+
const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
6775
|
+
if (!(0, import_node_fs27.existsSync)(runsPath)) return null;
|
|
6358
6776
|
try {
|
|
6359
|
-
const lines = (0,
|
|
6777
|
+
const lines = (0, import_node_fs27.readFileSync)(runsPath, "utf8").split("\n");
|
|
6360
6778
|
let best = null;
|
|
6361
6779
|
for (const line of lines) {
|
|
6362
6780
|
const trimmed = line.trim();
|
|
@@ -6382,12 +6800,12 @@ function recoverFinishedCursorRun(opts) {
|
|
|
6382
6800
|
return null;
|
|
6383
6801
|
}
|
|
6384
6802
|
}
|
|
6385
|
-
var
|
|
6803
|
+
var import_node_fs27, import_node_path25;
|
|
6386
6804
|
var init_cursor_recover = __esm({
|
|
6387
6805
|
"src/agents/cursor-recover.ts"() {
|
|
6388
6806
|
"use strict";
|
|
6389
|
-
|
|
6390
|
-
|
|
6807
|
+
import_node_fs27 = require("fs");
|
|
6808
|
+
import_node_path25 = require("path");
|
|
6391
6809
|
init_paths();
|
|
6392
6810
|
}
|
|
6393
6811
|
});
|
|
@@ -6426,6 +6844,7 @@ var init_title = __esm({
|
|
|
6426
6844
|
// src/index.ts
|
|
6427
6845
|
var index_exports = {};
|
|
6428
6846
|
__export(index_exports, {
|
|
6847
|
+
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
6429
6848
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
6430
6849
|
BrightsySideboardApi: () => BrightsySideboardApi,
|
|
6431
6850
|
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
@@ -6441,11 +6860,16 @@ __export(index_exports, {
|
|
|
6441
6860
|
FAMOUS_SOCCER_TEAMS: () => FAMOUS_SOCCER_TEAMS,
|
|
6442
6861
|
GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
|
|
6443
6862
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
6863
|
+
LEGACY_ATTACHMENTS_DIR: () => LEGACY_ATTACHMENTS_DIR,
|
|
6864
|
+
LEGACY_PLAN_FILE_REL: () => LEGACY_PLAN_FILE_REL,
|
|
6865
|
+
LEGACY_REVIEW_REQUEST_PATH: () => LEGACY_REVIEW_REQUEST_PATH,
|
|
6444
6866
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS: () => MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
6445
6867
|
ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
|
|
6446
6868
|
Orchestrator: () => Orchestrator,
|
|
6447
6869
|
PASTE_ATTACH_MIN_CHARS: () => PASTE_ATTACH_MIN_CHARS,
|
|
6448
6870
|
PASTE_ATTACH_MIN_LINES: () => PASTE_ATTACH_MIN_LINES,
|
|
6871
|
+
PLAN_FILE_NAME: () => PLAN_FILE_NAME,
|
|
6872
|
+
PLAN_FILE_REL: () => PLAN_FILE_REL,
|
|
6449
6873
|
PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
|
|
6450
6874
|
REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
|
|
6451
6875
|
REPO_REVIEW_PATH: () => REPO_REVIEW_PATH,
|
|
@@ -6455,6 +6879,8 @@ __export(index_exports, {
|
|
|
6455
6879
|
SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
|
|
6456
6880
|
SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
6457
6881
|
THINKING_EFFORTS: () => THINKING_EFFORTS,
|
|
6882
|
+
addPrStackLayer: () => addPrStackLayer,
|
|
6883
|
+
addStackLayerFromThread: () => addStackLayerFromThread,
|
|
6458
6884
|
addWorkspace: () => addWorkspace,
|
|
6459
6885
|
adoptThread: () => adoptThread,
|
|
6460
6886
|
allAdapters: () => allAdapters,
|
|
@@ -6473,6 +6899,7 @@ __export(index_exports, {
|
|
|
6473
6899
|
attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
|
|
6474
6900
|
attachmentsFromBuffers: () => attachmentsFromBuffers,
|
|
6475
6901
|
attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
|
|
6902
|
+
attachmentsGitignoreBody: () => attachmentsGitignoreBody,
|
|
6476
6903
|
autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
|
|
6477
6904
|
autoRenameBranchEnabled: () => autoRenameBranchEnabled,
|
|
6478
6905
|
autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
|
|
@@ -6495,6 +6922,7 @@ __export(index_exports, {
|
|
|
6495
6922
|
caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
|
|
6496
6923
|
captureLoginEnv: () => captureLoginEnv,
|
|
6497
6924
|
captureTurnBaseline: () => captureTurnBaseline,
|
|
6925
|
+
checkoutPrStackLayer: () => checkoutPrStackLayer,
|
|
6498
6926
|
childEnvWithAppSettings: () => childEnvWithAppSettings,
|
|
6499
6927
|
claudeAdapter: () => claudeAdapter,
|
|
6500
6928
|
claudeChromeEnabled: () => claudeChromeEnabled,
|
|
@@ -6514,8 +6942,10 @@ __export(index_exports, {
|
|
|
6514
6942
|
countCacheControlBlocks: () => countCacheControlBlocks,
|
|
6515
6943
|
createChatTab: () => createChatTab,
|
|
6516
6944
|
createEmptyThread: () => createEmptyThread,
|
|
6945
|
+
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
6517
6946
|
createGlobalChat: () => createGlobalChat,
|
|
6518
6947
|
createOrUpdatePr: () => createOrUpdatePr,
|
|
6948
|
+
createPrStack: () => createPrStack,
|
|
6519
6949
|
createThread: () => createThread,
|
|
6520
6950
|
createThreadWorktree: () => createThreadWorktree,
|
|
6521
6951
|
currentBranch: () => currentBranch,
|
|
@@ -6525,6 +6955,7 @@ __export(index_exports, {
|
|
|
6525
6955
|
deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
|
|
6526
6956
|
deleteThreadRecord: () => deleteThreadRecord,
|
|
6527
6957
|
detectAgents: () => detectAgents,
|
|
6958
|
+
detectGhStack: () => detectGhStack,
|
|
6528
6959
|
detectLocalMergeConflicts: () => detectLocalMergeConflicts,
|
|
6529
6960
|
disconnectBrightsyTeam: () => disconnectBrightsyTeam,
|
|
6530
6961
|
discoverSkills: () => discoverSkills,
|
|
@@ -6540,12 +6971,15 @@ __export(index_exports, {
|
|
|
6540
6971
|
estimateThreadChars: () => estimateThreadChars,
|
|
6541
6972
|
expandComposerPrompt: () => expandComposerPrompt,
|
|
6542
6973
|
extractGhErrorDetail: () => extractGhErrorDetail,
|
|
6974
|
+
extractPendingPlanQuestions: () => extractPendingPlanQuestions,
|
|
6975
|
+
extractPresentedPlan: () => extractPresentedPlan,
|
|
6543
6976
|
extractiveSummary: () => extractiveSummary,
|
|
6544
6977
|
fetchPrHead: () => fetchPrHead,
|
|
6545
6978
|
finalizeParts: () => finalizeParts,
|
|
6546
6979
|
findInvalidCacheControlTtlOrder: () => findInvalidCacheControlTtlOrder,
|
|
6547
6980
|
findOrphanWorktrees: () => findOrphanWorktrees,
|
|
6548
6981
|
findThreadByRef: () => findThreadByRef,
|
|
6982
|
+
findThreadForStackLayer: () => findThreadForStackLayer,
|
|
6549
6983
|
flattenTurnInput: () => flattenTurnInput,
|
|
6550
6984
|
forkChatTab: () => forkChatTab,
|
|
6551
6985
|
forkMessageSlice: () => forkMessageSlice,
|
|
@@ -6556,6 +6990,8 @@ __export(index_exports, {
|
|
|
6556
6990
|
formatGhLandError: () => formatGhLandError,
|
|
6557
6991
|
formatIpcInvokeError: () => formatIpcInvokeError,
|
|
6558
6992
|
formatMessagesAsTranscript: () => formatMessagesAsTranscript,
|
|
6993
|
+
formatPlanQuestionAnswers: () => formatPlanQuestionAnswers,
|
|
6994
|
+
formatPlanQuestionsForChat: () => formatPlanQuestionsForChat,
|
|
6559
6995
|
formatRateLimitResetHint: () => formatRateLimitResetHint,
|
|
6560
6996
|
formatRenameBranchDirective: () => formatRenameBranchDirective,
|
|
6561
6997
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
@@ -6579,6 +7015,7 @@ __export(index_exports, {
|
|
|
6579
7015
|
getPrChecks: () => getPrChecks,
|
|
6580
7016
|
getPrDetails: () => getPrDetails,
|
|
6581
7017
|
getPrMeta: () => getPrMeta,
|
|
7018
|
+
getPrStack: () => getPrStack,
|
|
6582
7019
|
getRepoSetupInfo: () => getRepoSetupInfo,
|
|
6583
7020
|
getRunMode: () => getRunMode,
|
|
6584
7021
|
getRunScript: () => getRunScript,
|
|
@@ -6595,9 +7032,12 @@ __export(index_exports, {
|
|
|
6595
7032
|
healOrchestrationSoccerTitles: () => healOrchestrationSoccerTitles,
|
|
6596
7033
|
importConductorWorkspace: () => importConductorWorkspace,
|
|
6597
7034
|
importConductorWorkspaceAsync: () => importConductorWorkspaceAsync,
|
|
7035
|
+
initPrStack: () => initPrStack,
|
|
7036
|
+
initStackFromThread: () => initStackFromThread,
|
|
6598
7037
|
initializeGitRepository: () => initializeGitRepository,
|
|
6599
7038
|
inspectGitWorktree: () => inspectGitWorktree,
|
|
6600
7039
|
installAgent: () => installAgent,
|
|
7040
|
+
isAskUserToolName: () => isAskUserToolName,
|
|
6601
7041
|
isBrightsyConnected: () => isBrightsyConnected,
|
|
6602
7042
|
isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
|
|
6603
7043
|
isCloudCoordinatorThread: () => isCloudCoordinatorThread,
|
|
@@ -6607,14 +7047,17 @@ __export(index_exports, {
|
|
|
6607
7047
|
isGlobalRepoPath: () => isGlobalRepoPath,
|
|
6608
7048
|
isGlobalThread: () => isGlobalThread,
|
|
6609
7049
|
isImageFilePath: () => isImageFilePath,
|
|
7050
|
+
isInPrStack: () => isInPrStack,
|
|
6610
7051
|
isLinearConnected: () => isLinearConnected,
|
|
6611
7052
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6612
7053
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
6613
7054
|
isPidAlive: () => isPidAlive,
|
|
6614
7055
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
7056
|
+
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
6615
7057
|
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
6616
7058
|
isSideboardScratchPath: () => isSideboardScratchPath,
|
|
6617
7059
|
isThinkingEffort: () => isThinkingEffort,
|
|
7060
|
+
isWorkspaceScratchPath: () => isWorkspaceScratchPath,
|
|
6618
7061
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
6619
7062
|
listBranchCommits: () => listBranchCommits,
|
|
6620
7063
|
listBranches: () => listBranches,
|
|
@@ -6651,6 +7094,7 @@ __export(index_exports, {
|
|
|
6651
7094
|
mcpAllowTools: () => mcpAllowTools,
|
|
6652
7095
|
mcpAuthWarnings: () => mcpAuthWarnings,
|
|
6653
7096
|
mergePr: () => mergePr,
|
|
7097
|
+
mergePrStack: () => mergePrStack,
|
|
6654
7098
|
mergeUsage: () => mergeUsage,
|
|
6655
7099
|
nextPastedTextName: () => nextPastedTextName,
|
|
6656
7100
|
nextThinkingEffort: () => nextThinkingEffort,
|
|
@@ -6660,6 +7104,8 @@ __export(index_exports, {
|
|
|
6660
7104
|
normalizeTurnInput: () => normalizeTurnInput,
|
|
6661
7105
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
6662
7106
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
7107
|
+
openPrStackLayers: () => openPrStackLayers,
|
|
7108
|
+
openStackLayer: () => openStackLayer,
|
|
6663
7109
|
opencodeAdapter: () => opencodeAdapter,
|
|
6664
7110
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
6665
7111
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
@@ -6668,15 +7114,19 @@ __export(index_exports, {
|
|
|
6668
7114
|
originGhRepoEnv: () => originGhRepoEnv,
|
|
6669
7115
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6670
7116
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
7117
|
+
parseGhStackViewJson: () => parseGhStackViewJson,
|
|
6671
7118
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
6672
7119
|
parseMcpList: () => parseMcpList,
|
|
7120
|
+
parsePlanQuestionsInput: () => parsePlanQuestionsInput,
|
|
6673
7121
|
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6674
7122
|
partsToAssistantText: () => partsToAssistantText,
|
|
6675
7123
|
pastedTextStats: () => pastedTextStats,
|
|
6676
7124
|
permissionMode: () => permissionMode,
|
|
7125
|
+
planFileAbs: () => planFileAbs,
|
|
6677
7126
|
previewLand: () => previewLand,
|
|
6678
7127
|
pushBranch: () => pushBranch,
|
|
6679
7128
|
readExistingReviewRequestFile: () => readExistingReviewRequestFile,
|
|
7129
|
+
readPlanFile: () => readPlanFile,
|
|
6680
7130
|
readSkillBody: () => readSkillBody,
|
|
6681
7131
|
readThread: () => readThread,
|
|
6682
7132
|
readWorktreeFile: () => readWorktreeFile,
|
|
@@ -6688,6 +7138,7 @@ __export(index_exports, {
|
|
|
6688
7138
|
repoSlug: () => repoSlug,
|
|
6689
7139
|
requestReview: () => requestReview,
|
|
6690
7140
|
requireAgent: () => requireAgent,
|
|
7141
|
+
resetGhStackDetectCache: () => resetGhStackDetectCache,
|
|
6691
7142
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
6692
7143
|
resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
|
|
6693
7144
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
@@ -6697,6 +7148,7 @@ __export(index_exports, {
|
|
|
6697
7148
|
resolveFilesToCopy: () => resolveFilesToCopy,
|
|
6698
7149
|
resolveGhAuthToken: () => resolveGhAuthToken,
|
|
6699
7150
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
7151
|
+
resolvePlanMarkdown: () => resolvePlanMarkdown,
|
|
6700
7152
|
resolvePrSelector: () => resolvePrSelector,
|
|
6701
7153
|
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6702
7154
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
@@ -6724,12 +7176,16 @@ __export(index_exports, {
|
|
|
6724
7176
|
slugify: () => slugify,
|
|
6725
7177
|
spawnAgentTurn: () => spawnAgentTurn,
|
|
6726
7178
|
splitForCompaction: () => splitForCompaction,
|
|
7179
|
+
stackAgentDefaultsFrom: () => stackAgentDefaultsFrom,
|
|
7180
|
+
stackIdFrom: () => stackIdFrom,
|
|
7181
|
+
stackMergeReadiness: () => stackMergeReadiness,
|
|
6727
7182
|
stageAbsolutePathsAsAttachments: () => stageAbsolutePathsAsAttachments,
|
|
6728
7183
|
stageBuffersAsAttachments: () => stageBuffersAsAttachments,
|
|
6729
7184
|
startDevServer: () => startDevServer,
|
|
6730
7185
|
startMcpServer: () => startMcpServer,
|
|
6731
7186
|
startOrchestration: () => startOrchestration,
|
|
6732
7187
|
stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
|
|
7188
|
+
submitPrStack: () => submitPrStack,
|
|
6733
7189
|
suggestSlug: () => suggestSlug,
|
|
6734
7190
|
summarizeConversation: () => summarizeConversation,
|
|
6735
7191
|
switchBrightsyAccount: () => switchBrightsyAccount,
|
|
@@ -6765,6 +7221,7 @@ __export(index_exports, {
|
|
|
6765
7221
|
worktreeNameFromPath: () => worktreeNameFromPath,
|
|
6766
7222
|
worktreesRoot: () => worktreesRoot,
|
|
6767
7223
|
writeInjectedMcpConfig: () => writeInjectedMcpConfig,
|
|
7224
|
+
writePlanFile: () => writePlanFile,
|
|
6768
7225
|
writeThread: () => writeThread,
|
|
6769
7226
|
writeWorktreeFile: () => writeWorktreeFile
|
|
6770
7227
|
});
|
|
@@ -6778,6 +7235,7 @@ init_global_workspace();
|
|
|
6778
7235
|
init_run();
|
|
6779
7236
|
init_gh_errors();
|
|
6780
7237
|
init_worktree();
|
|
7238
|
+
init_stack();
|
|
6781
7239
|
|
|
6782
7240
|
// src/integrations/github.ts
|
|
6783
7241
|
init_run();
|
|
@@ -6974,18 +7432,18 @@ function asRecord(input) {
|
|
|
6974
7432
|
}
|
|
6975
7433
|
return void 0;
|
|
6976
7434
|
}
|
|
6977
|
-
function
|
|
7435
|
+
function str2(v) {
|
|
6978
7436
|
return typeof v === "string" && v.trim() ? v : void 0;
|
|
6979
7437
|
}
|
|
6980
7438
|
function toolDetail(name, input) {
|
|
6981
7439
|
if (!input) return void 0;
|
|
6982
|
-
const command =
|
|
7440
|
+
const command = str2(input.command) ?? str2(input.cmd);
|
|
6983
7441
|
if (command) return command;
|
|
6984
|
-
const path =
|
|
7442
|
+
const path = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
6985
7443
|
if (path) return path;
|
|
6986
|
-
const pattern =
|
|
7444
|
+
const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
|
|
6987
7445
|
if (pattern) return pattern;
|
|
6988
|
-
const query =
|
|
7446
|
+
const query = str2(input.query) ?? str2(input.prompt);
|
|
6989
7447
|
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
6990
7448
|
try {
|
|
6991
7449
|
const raw = JSON.stringify(input);
|
|
@@ -6998,23 +7456,26 @@ function toolDescription(name, input) {
|
|
|
6998
7456
|
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
6999
7457
|
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
7000
7458
|
if (/connectedAgentRequest/i.test(name)) {
|
|
7001
|
-
return
|
|
7459
|
+
return str2(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
7002
7460
|
}
|
|
7003
7461
|
if (/present_artifact$/i.test(name)) {
|
|
7004
|
-
return
|
|
7462
|
+
return str2(input?.title) ? `Present ${str2(input?.title)}` : "Present artifact";
|
|
7463
|
+
}
|
|
7464
|
+
if (/present_plan$/i.test(name)) {
|
|
7465
|
+
return str2(input?.title) ? `Plan ${str2(input?.title)}` : "Present plan";
|
|
7005
7466
|
}
|
|
7006
7467
|
if (/present_schema$/i.test(name)) {
|
|
7007
|
-
return
|
|
7468
|
+
return str2(input?.title) ? `Schema ${str2(input?.title)}` : "Present schema";
|
|
7008
7469
|
}
|
|
7009
7470
|
if (/present_files$/i.test(name)) {
|
|
7010
|
-
return
|
|
7471
|
+
return str2(input?.title) ? `Files ${str2(input?.title)}` : "Present files";
|
|
7011
7472
|
}
|
|
7012
7473
|
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
7013
|
-
return
|
|
7474
|
+
return str2(input?.title) ? `Artifact ${str2(input?.title)}` : "Artifact";
|
|
7014
7475
|
}
|
|
7015
7476
|
if (!input) return n;
|
|
7016
|
-
if (/bash|shell|terminal/i.test(name) &&
|
|
7017
|
-
const cmd =
|
|
7477
|
+
if (/bash|shell|terminal/i.test(name) && str2(input.command)) {
|
|
7478
|
+
const cmd = str2(input.command);
|
|
7018
7479
|
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
7019
7480
|
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
7020
7481
|
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
@@ -7023,11 +7484,11 @@ function toolDescription(name, input) {
|
|
|
7023
7484
|
return "Run shell command";
|
|
7024
7485
|
}
|
|
7025
7486
|
if (/edit|write|apply/i.test(name)) {
|
|
7026
|
-
const path =
|
|
7487
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
7027
7488
|
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
7028
7489
|
}
|
|
7029
7490
|
if (/read/i.test(name)) {
|
|
7030
|
-
const path =
|
|
7491
|
+
const path = str2(input.file_path) ?? str2(input.path);
|
|
7031
7492
|
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
7032
7493
|
}
|
|
7033
7494
|
if (/grep/i.test(name)) return "Search files";
|
|
@@ -7036,7 +7497,7 @@ function toolDescription(name, input) {
|
|
|
7036
7497
|
}
|
|
7037
7498
|
function toolFilePath(input) {
|
|
7038
7499
|
if (!input) return void 0;
|
|
7039
|
-
return
|
|
7500
|
+
return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
7040
7501
|
}
|
|
7041
7502
|
function countLines(text) {
|
|
7042
7503
|
if (!text) return 0;
|
|
@@ -7044,8 +7505,8 @@ function countLines(text) {
|
|
|
7044
7505
|
}
|
|
7045
7506
|
function diffFromInput(input) {
|
|
7046
7507
|
if (!input) return {};
|
|
7047
|
-
const oldS =
|
|
7048
|
-
const newS =
|
|
7508
|
+
const oldS = str2(input.old_string) ?? str2(input.oldString);
|
|
7509
|
+
const newS = str2(input.new_string) ?? str2(input.newString) ?? str2(input.content);
|
|
7049
7510
|
if (oldS != null || newS != null) {
|
|
7050
7511
|
return {
|
|
7051
7512
|
additions: countLines(newS),
|
|
@@ -8212,33 +8673,33 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8212
8673
|
let combinedDiff = "";
|
|
8213
8674
|
let labelBase = base;
|
|
8214
8675
|
if (scope === "staged") {
|
|
8215
|
-
const [ns,
|
|
8676
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8216
8677
|
git(["diff", "--name-status", "--cached"], worktreePath, { reject: false }),
|
|
8217
8678
|
git(["diff", "--numstat", "--cached"], worktreePath, { reject: false }),
|
|
8218
8679
|
git(["diff", "--cached"], worktreePath, { reject: false })
|
|
8219
8680
|
]);
|
|
8220
8681
|
nameStatus = ns.stdout;
|
|
8221
|
-
numstat =
|
|
8682
|
+
numstat = num2.stdout;
|
|
8222
8683
|
combinedDiff = diff.stdout;
|
|
8223
8684
|
labelBase = "staged";
|
|
8224
8685
|
} else if (scope === "unstaged") {
|
|
8225
|
-
const [ns,
|
|
8686
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8226
8687
|
git(["diff", "--name-status"], worktreePath, { reject: false }),
|
|
8227
8688
|
git(["diff", "--numstat"], worktreePath, { reject: false }),
|
|
8228
8689
|
git(["diff"], worktreePath, { reject: false })
|
|
8229
8690
|
]);
|
|
8230
8691
|
nameStatus = ns.stdout;
|
|
8231
|
-
numstat =
|
|
8692
|
+
numstat = num2.stdout;
|
|
8232
8693
|
combinedDiff = diff.stdout;
|
|
8233
8694
|
labelBase = "unstaged";
|
|
8234
8695
|
} else if (scope === "uncommitted") {
|
|
8235
|
-
const [ns,
|
|
8696
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8236
8697
|
git(["diff", "--name-status", "HEAD"], worktreePath, { reject: false }),
|
|
8237
8698
|
git(["diff", "--numstat", "HEAD"], worktreePath, { reject: false }),
|
|
8238
8699
|
git(["diff", "HEAD"], worktreePath, { reject: false })
|
|
8239
8700
|
]);
|
|
8240
8701
|
nameStatus = ns.stdout;
|
|
8241
|
-
numstat =
|
|
8702
|
+
numstat = num2.stdout;
|
|
8242
8703
|
combinedDiff = diff.stdout;
|
|
8243
8704
|
labelBase = "HEAD";
|
|
8244
8705
|
} else if (scope === "last_turn") {
|
|
@@ -8257,34 +8718,34 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
8257
8718
|
scopeStats
|
|
8258
8719
|
};
|
|
8259
8720
|
}
|
|
8260
|
-
const [ns,
|
|
8721
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8261
8722
|
git(["diff", "--name-status", lastTurnBase], worktreePath, { reject: false }),
|
|
8262
8723
|
git(["diff", "--numstat", lastTurnBase], worktreePath, { reject: false }),
|
|
8263
8724
|
git(["diff", lastTurnBase], worktreePath, { reject: false })
|
|
8264
8725
|
]);
|
|
8265
8726
|
nameStatus = ns.stdout;
|
|
8266
|
-
numstat =
|
|
8727
|
+
numstat = num2.stdout;
|
|
8267
8728
|
combinedDiff = diff.stdout;
|
|
8268
8729
|
labelBase = "last turn";
|
|
8269
8730
|
} else if (scope === "commits" && commitSha) {
|
|
8270
8731
|
const range = `${commitSha}^!`;
|
|
8271
|
-
const [ns,
|
|
8732
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8272
8733
|
git(["diff", "--name-status", range], worktreePath, { reject: false }),
|
|
8273
8734
|
git(["diff", "--numstat", range], worktreePath, { reject: false }),
|
|
8274
8735
|
git(["diff", range], worktreePath, { reject: false })
|
|
8275
8736
|
]);
|
|
8276
8737
|
nameStatus = ns.stdout;
|
|
8277
|
-
numstat =
|
|
8738
|
+
numstat = num2.stdout;
|
|
8278
8739
|
combinedDiff = diff.stdout;
|
|
8279
8740
|
labelBase = commitSha.slice(0, 7);
|
|
8280
8741
|
} else {
|
|
8281
|
-
const [ns,
|
|
8742
|
+
const [ns, num2, diff] = await Promise.all([
|
|
8282
8743
|
git(["diff", "--name-status", mergeBase], worktreePath, { reject: false }),
|
|
8283
8744
|
git(["diff", "--numstat", mergeBase], worktreePath, { reject: false }),
|
|
8284
8745
|
git(["diff", mergeBase], worktreePath, { reject: false })
|
|
8285
8746
|
]);
|
|
8286
8747
|
nameStatus = ns.stdout;
|
|
8287
|
-
numstat =
|
|
8748
|
+
numstat = num2.stdout;
|
|
8288
8749
|
combinedDiff = diff.stdout;
|
|
8289
8750
|
labelBase = base;
|
|
8290
8751
|
}
|
|
@@ -8717,6 +9178,7 @@ function buildDiffCommentAttachment(input) {
|
|
|
8717
9178
|
var import_node_fs19 = require("fs");
|
|
8718
9179
|
var import_node_path19 = require("path");
|
|
8719
9180
|
var import_node_crypto2 = require("crypto");
|
|
9181
|
+
init_workspace_scratch();
|
|
8720
9182
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
8721
9183
|
"png",
|
|
8722
9184
|
"jpg",
|
|
@@ -8737,11 +9199,6 @@ var IMAGE_MIME_BY_EXT = {
|
|
|
8737
9199
|
bmp: "image/bmp",
|
|
8738
9200
|
ico: "image/x-icon"
|
|
8739
9201
|
};
|
|
8740
|
-
var ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
8741
|
-
var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
|
|
8742
|
-
*
|
|
8743
|
-
!.gitignore
|
|
8744
|
-
`;
|
|
8745
9202
|
var MAX_INLINE_BYTES = 4e5;
|
|
8746
9203
|
var MAX_PREVIEW_BYTES = 5e6;
|
|
8747
9204
|
function fileExtension(filePath) {
|
|
@@ -8759,7 +9216,7 @@ function ensureAttachmentsDir(worktreePath) {
|
|
|
8759
9216
|
(0, import_node_fs19.mkdirSync)(dir, { recursive: true });
|
|
8760
9217
|
const gi = (0, import_node_path19.join)(dir, ".gitignore");
|
|
8761
9218
|
if (!(0, import_node_fs19.existsSync)(gi)) {
|
|
8762
|
-
(0, import_node_fs19.writeFileSync)(gi,
|
|
9219
|
+
(0, import_node_fs19.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
8763
9220
|
}
|
|
8764
9221
|
return dir;
|
|
8765
9222
|
}
|
|
@@ -9419,7 +9876,7 @@ var import_node_fs20 = require("fs");
|
|
|
9419
9876
|
init_worktree();
|
|
9420
9877
|
init_thread_store();
|
|
9421
9878
|
init_workspaces();
|
|
9422
|
-
async function createThread(input,
|
|
9879
|
+
async function createThread(input, _onSetupLine) {
|
|
9423
9880
|
await requireAgent(input.agent);
|
|
9424
9881
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
9425
9882
|
if (!(0, import_node_fs20.existsSync)(repoPath)) {
|
|
@@ -9429,10 +9886,10 @@ async function createThread(input, onSetupLine) {
|
|
|
9429
9886
|
let sourceIsFork = false;
|
|
9430
9887
|
let prUrl = null;
|
|
9431
9888
|
if (input.sourceType === "pr") {
|
|
9432
|
-
const
|
|
9433
|
-
if (!Number.isFinite(
|
|
9434
|
-
const pr = await getPr(repoPath,
|
|
9435
|
-
if (!pr) throw new Error(`PR #${
|
|
9889
|
+
const num2 = Number(input.sourceRef.replace(/^#/, ""));
|
|
9890
|
+
if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
|
|
9891
|
+
const pr = await getPr(repoPath, num2);
|
|
9892
|
+
if (!pr) throw new Error(`PR #${num2} not found`);
|
|
9436
9893
|
sourceIsFork = pr.isCrossRepository;
|
|
9437
9894
|
prUrl = pr.url;
|
|
9438
9895
|
const localFetchBranch = `sideboard-pr-${pr.number}`;
|
|
@@ -9477,22 +9934,6 @@ async function createThread(input, onSetupLine) {
|
|
|
9477
9934
|
});
|
|
9478
9935
|
writeThread(thread);
|
|
9479
9936
|
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
9937
|
return readThread(thread.id) ?? thread;
|
|
9497
9938
|
}
|
|
9498
9939
|
async function listLinearIssues(agent, repoPath) {
|
|
@@ -9545,7 +9986,9 @@ function worktreeBindingFrom(from) {
|
|
|
9545
9986
|
sourceIsFork: from.sourceIsFork,
|
|
9546
9987
|
parentThreadId: from.parentThreadId,
|
|
9547
9988
|
prUrl: from.prUrl,
|
|
9548
|
-
prTitle: from.prTitle
|
|
9989
|
+
prTitle: from.prTitle,
|
|
9990
|
+
stackId: from.stackId,
|
|
9991
|
+
stackLayer: from.stackLayer
|
|
9549
9992
|
};
|
|
9550
9993
|
}
|
|
9551
9994
|
function threadsSharingWorktree(worktreePath) {
|
|
@@ -9660,9 +10103,308 @@ async function forkThreadWorktree(input, onSetupLine) {
|
|
|
9660
10103
|
return thread;
|
|
9661
10104
|
}
|
|
9662
10105
|
|
|
10106
|
+
// src/threads/stack-layers.ts
|
|
10107
|
+
var import_node_fs21 = require("fs");
|
|
10108
|
+
init_run();
|
|
10109
|
+
init_stack();
|
|
10110
|
+
init_worktree();
|
|
10111
|
+
init_thread_store();
|
|
10112
|
+
init_workspaces();
|
|
10113
|
+
function stackIdFrom(stack) {
|
|
10114
|
+
if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
|
|
10115
|
+
const key = stack.layers.map((l) => l.branchName).join("|");
|
|
10116
|
+
if (!key) return null;
|
|
10117
|
+
return `gh-stack-local-${hashShort(key)}`;
|
|
10118
|
+
}
|
|
10119
|
+
function hashShort(s) {
|
|
10120
|
+
let h = 0;
|
|
10121
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
10122
|
+
return Math.abs(h).toString(36);
|
|
10123
|
+
}
|
|
10124
|
+
function requireThread3(idOrRef) {
|
|
10125
|
+
const thread = findThreadByRef(idOrRef) ?? readThread(idOrRef);
|
|
10126
|
+
if (!thread) throw new Error(`Thread not found: ${idOrRef}`);
|
|
10127
|
+
return thread;
|
|
10128
|
+
}
|
|
10129
|
+
function sanitizeSlugPart(name) {
|
|
10130
|
+
return name.trim().replace(/^refs\/heads\//, "").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "layer";
|
|
10131
|
+
}
|
|
10132
|
+
function layerSlug(stackId, layer) {
|
|
10133
|
+
const stackPart = stackId.replace(/^gh-stack-/, "s");
|
|
10134
|
+
return sanitizeSlugPart(`${stackPart}-L${layer.position}-${layer.branchName}`);
|
|
10135
|
+
}
|
|
10136
|
+
function findThreadForStackLayer(repoPath, stackId, layer) {
|
|
10137
|
+
const threads = listThreads({ includeArchived: false }).filter(
|
|
10138
|
+
(t) => t.repoPath === repoPath && t.stackId === stackId
|
|
10139
|
+
);
|
|
10140
|
+
const byLayer = threads.find((t) => t.stackLayer === layer.position);
|
|
10141
|
+
if (byLayer) return byLayer;
|
|
10142
|
+
return threads.find((t) => t.branchName === layer.branchName) ?? listThreads({ includeArchived: false }).find(
|
|
10143
|
+
(t) => t.repoPath === repoPath && t.branchName === layer.branchName
|
|
10144
|
+
) ?? null;
|
|
10145
|
+
}
|
|
10146
|
+
async function openStackLayer(input, _onSetupLine) {
|
|
10147
|
+
await requireAgent(input.agent);
|
|
10148
|
+
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
10149
|
+
const stackId = stackIdFrom(input.stack);
|
|
10150
|
+
if (!stackId) throw new Error("Cannot open stack layer without a stack id");
|
|
10151
|
+
const existing = findThreadForStackLayer(repoPath, stackId, input.layer);
|
|
10152
|
+
if (existing) {
|
|
10153
|
+
const patch = {};
|
|
10154
|
+
if (existing.stackId !== stackId) patch.stackId = stackId;
|
|
10155
|
+
if (existing.stackLayer !== input.layer.position) {
|
|
10156
|
+
patch.stackLayer = input.layer.position;
|
|
10157
|
+
}
|
|
10158
|
+
if (input.layer.prUrl && existing.prUrl !== input.layer.prUrl) {
|
|
10159
|
+
patch.prUrl = input.layer.prUrl;
|
|
10160
|
+
}
|
|
10161
|
+
if (input.layer.title && existing.prTitle !== input.layer.title) {
|
|
10162
|
+
patch.prTitle = input.layer.title;
|
|
10163
|
+
}
|
|
10164
|
+
if (Object.keys(patch).length > 0) updateThread(existing.id, patch);
|
|
10165
|
+
return {
|
|
10166
|
+
thread: readThread(existing.id) ?? existing,
|
|
10167
|
+
createdWorktree: false
|
|
10168
|
+
};
|
|
10169
|
+
}
|
|
10170
|
+
let worktreePath = null;
|
|
10171
|
+
let branchName = input.layer.branchName;
|
|
10172
|
+
let createdWorktree = false;
|
|
10173
|
+
const trees = await listWorktrees(repoPath);
|
|
10174
|
+
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
10175
|
+
if (checkedOut?.path && (0, import_node_fs21.existsSync)(checkedOut.path)) {
|
|
10176
|
+
if (input.reuseExistingWorktree !== false) {
|
|
10177
|
+
worktreePath = checkedOut.path;
|
|
10178
|
+
} else {
|
|
10179
|
+
throw new Error(
|
|
10180
|
+
`Branch ${branchName} is already checked out at ${checkedOut.path}`
|
|
10181
|
+
);
|
|
10182
|
+
}
|
|
10183
|
+
}
|
|
10184
|
+
if (!worktreePath) {
|
|
10185
|
+
const slug = layerSlug(stackId, input.layer);
|
|
10186
|
+
const created = await createExistingBranchWorktree({
|
|
10187
|
+
repoPath,
|
|
10188
|
+
branchName,
|
|
10189
|
+
slug
|
|
10190
|
+
});
|
|
10191
|
+
worktreePath = created.worktreePath;
|
|
10192
|
+
branchName = created.branchName;
|
|
10193
|
+
copyConfiguredFiles(repoPath, worktreePath);
|
|
10194
|
+
createdWorktree = true;
|
|
10195
|
+
}
|
|
10196
|
+
const title = input.layer.title?.trim() || (input.layer.prNumber != null ? `PR #${input.layer.prNumber}` : input.layer.branchName);
|
|
10197
|
+
const thread = createEmptyThread({
|
|
10198
|
+
title,
|
|
10199
|
+
userSetTitle: Boolean(input.layer.title?.trim()),
|
|
10200
|
+
sourceType: input.layer.prNumber != null ? "pr" : "branch",
|
|
10201
|
+
sourceRef: input.layer.prNumber != null ? String(input.layer.prNumber) : input.layer.branchName,
|
|
10202
|
+
branchName,
|
|
10203
|
+
worktreePath,
|
|
10204
|
+
repoPath,
|
|
10205
|
+
agent: input.agent,
|
|
10206
|
+
autonomy: input.autonomy ?? "default",
|
|
10207
|
+
model: input.model ?? null,
|
|
10208
|
+
effort: input.effort ?? "high",
|
|
10209
|
+
fast: Boolean(input.fast),
|
|
10210
|
+
planMode: Boolean(input.planMode),
|
|
10211
|
+
parentThreadId: input.parentThreadId ?? null,
|
|
10212
|
+
status: "idle",
|
|
10213
|
+
prUrl: input.layer.prUrl,
|
|
10214
|
+
prTitle: input.layer.title ?? null,
|
|
10215
|
+
stackId,
|
|
10216
|
+
stackLayer: input.layer.position
|
|
10217
|
+
});
|
|
10218
|
+
writeThread(thread);
|
|
10219
|
+
await ensureWorkspace(repoPath);
|
|
10220
|
+
return { thread: readThread(thread.id) ?? thread, createdWorktree };
|
|
10221
|
+
}
|
|
10222
|
+
async function openPrStackLayers(input, onSetupLine) {
|
|
10223
|
+
const from = requireThread3(input.threadRef);
|
|
10224
|
+
if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
|
|
10225
|
+
throw new Error("Thread has no worktree");
|
|
10226
|
+
}
|
|
10227
|
+
const stack = await getPrStack(from.worktreePath);
|
|
10228
|
+
if (!stack) throw new Error("Current branch is not part of a GitHub PR stack");
|
|
10229
|
+
const layers = input.layer != null ? stack.layers.filter((l) => l.position === input.layer) : stack.layers;
|
|
10230
|
+
if (!layers.length) {
|
|
10231
|
+
throw new Error(
|
|
10232
|
+
input.layer != null ? `No stack layer at position ${input.layer}` : "Stack has no layers"
|
|
10233
|
+
);
|
|
10234
|
+
}
|
|
10235
|
+
const threads = [];
|
|
10236
|
+
const createdThreadIds = [];
|
|
10237
|
+
for (const layer of layers) {
|
|
10238
|
+
const { thread, createdWorktree } = await openStackLayer(
|
|
10239
|
+
{
|
|
10240
|
+
repoPath: from.repoPath,
|
|
10241
|
+
stack,
|
|
10242
|
+
layer,
|
|
10243
|
+
agent: from.agent,
|
|
10244
|
+
autonomy: from.autonomy,
|
|
10245
|
+
model: from.model,
|
|
10246
|
+
effort: from.effort,
|
|
10247
|
+
fast: from.fast,
|
|
10248
|
+
planMode: from.planMode,
|
|
10249
|
+
parentThreadId: from.id
|
|
10250
|
+
},
|
|
10251
|
+
onSetupLine
|
|
10252
|
+
);
|
|
10253
|
+
threads.push(thread);
|
|
10254
|
+
if (createdWorktree) createdThreadIds.push(thread.id);
|
|
10255
|
+
}
|
|
10256
|
+
const stackId = stackIdFrom(stack);
|
|
10257
|
+
const current = stack.layers[stack.currentIndex];
|
|
10258
|
+
if (stackId) {
|
|
10259
|
+
updateThread(from.id, {
|
|
10260
|
+
stackId,
|
|
10261
|
+
stackLayer: current?.position ?? from.stackLayer
|
|
10262
|
+
});
|
|
10263
|
+
}
|
|
10264
|
+
return { stack, threads, createdThreadIds };
|
|
10265
|
+
}
|
|
10266
|
+
async function addStackLayerFromThread(input, onSetupLine) {
|
|
10267
|
+
const from = requireThread3(input.threadRef);
|
|
10268
|
+
if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
|
|
10269
|
+
throw new Error("Thread has no worktree");
|
|
10270
|
+
}
|
|
10271
|
+
const status = await detectGhStack(from.worktreePath);
|
|
10272
|
+
if (!status.available) throw new Error(status.reason);
|
|
10273
|
+
await addPrStackLayer(from.worktreePath, input.branchName);
|
|
10274
|
+
const stack = await getPrStack(from.worktreePath);
|
|
10275
|
+
if (!stack) throw new Error("Stack not found after adding layer");
|
|
10276
|
+
const layer = stack.layers.find((l) => l.branchName === input.branchName.trim()) ?? stack.layers[stack.layers.length - 1];
|
|
10277
|
+
if (!layer) throw new Error("New stack layer not found");
|
|
10278
|
+
if (input.title?.trim()) {
|
|
10279
|
+
layer.title = input.title.trim();
|
|
10280
|
+
}
|
|
10281
|
+
const thread = await openStackLayer(
|
|
10282
|
+
{
|
|
10283
|
+
repoPath: from.repoPath,
|
|
10284
|
+
stack,
|
|
10285
|
+
layer,
|
|
10286
|
+
agent: from.agent,
|
|
10287
|
+
autonomy: from.autonomy,
|
|
10288
|
+
model: from.model,
|
|
10289
|
+
effort: from.effort,
|
|
10290
|
+
fast: from.fast,
|
|
10291
|
+
planMode: from.planMode,
|
|
10292
|
+
parentThreadId: from.id
|
|
10293
|
+
},
|
|
10294
|
+
onSetupLine
|
|
10295
|
+
);
|
|
10296
|
+
return { stack, thread: thread.thread, createdWorktree: thread.createdWorktree };
|
|
10297
|
+
}
|
|
10298
|
+
async function initStackFromThread(input, onSetupLine) {
|
|
10299
|
+
const from = requireThread3(input.threadRef);
|
|
10300
|
+
if (!from.worktreePath?.trim() || !from.branchName?.trim() || !from.repoPath?.trim()) {
|
|
10301
|
+
throw new Error("Thread has no worktree/branch");
|
|
10302
|
+
}
|
|
10303
|
+
const status = await detectGhStack(from.worktreePath);
|
|
10304
|
+
if (!status.available) throw new Error(status.reason);
|
|
10305
|
+
const branches = [
|
|
10306
|
+
from.branchName,
|
|
10307
|
+
...(input.additionalBranches ?? []).map((b) => b.trim()).filter(Boolean)
|
|
10308
|
+
];
|
|
10309
|
+
await initPrStack(from.worktreePath, branches, {
|
|
10310
|
+
base: input.base ?? await resolveDefaultBranch(from.repoPath)
|
|
10311
|
+
});
|
|
10312
|
+
return openPrStackLayers({ threadRef: from.id }, onSetupLine);
|
|
10313
|
+
}
|
|
10314
|
+
async function createPrStack(input, onSetupLine) {
|
|
10315
|
+
await requireAgent(input.agent);
|
|
10316
|
+
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
10317
|
+
if (!(0, import_node_fs21.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
10318
|
+
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
10319
|
+
const status = await detectGhStack(repoPath);
|
|
10320
|
+
if (!status.available) throw new Error(status.reason);
|
|
10321
|
+
const team = allocateTeamSlug(repoPath);
|
|
10322
|
+
const base = input.base ?? await resolveDefaultBranch(repoPath);
|
|
10323
|
+
const bootstrap = await createThreadWorktree({
|
|
10324
|
+
repoPath,
|
|
10325
|
+
sourceRef: base,
|
|
10326
|
+
slug: `${team.slug}-stack-init`
|
|
10327
|
+
});
|
|
10328
|
+
copyConfiguredFiles(repoPath, bootstrap.worktreePath);
|
|
10329
|
+
try {
|
|
10330
|
+
await initPrStack(bootstrap.worktreePath, input.branches, { base });
|
|
10331
|
+
const bottom = input.branches[0];
|
|
10332
|
+
const co = await git(["checkout", bottom], bootstrap.worktreePath, {
|
|
10333
|
+
reject: false
|
|
10334
|
+
});
|
|
10335
|
+
if (co.exitCode !== 0) {
|
|
10336
|
+
throw new Error(
|
|
10337
|
+
co.stderr.trim() || co.stdout.trim() || `Could not check out ${bottom} after stack init`
|
|
10338
|
+
);
|
|
10339
|
+
}
|
|
10340
|
+
} catch (err) {
|
|
10341
|
+
try {
|
|
10342
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10343
|
+
deleteBranch: bootstrap.branchName
|
|
10344
|
+
});
|
|
10345
|
+
} catch {
|
|
10346
|
+
}
|
|
10347
|
+
throw err;
|
|
10348
|
+
}
|
|
10349
|
+
const stack = await getPrStack(bootstrap.worktreePath);
|
|
10350
|
+
if (!stack) {
|
|
10351
|
+
try {
|
|
10352
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10353
|
+
deleteBranch: bootstrap.branchName
|
|
10354
|
+
});
|
|
10355
|
+
} catch {
|
|
10356
|
+
}
|
|
10357
|
+
throw new Error("Stack init succeeded but gh stack view returned no stack");
|
|
10358
|
+
}
|
|
10359
|
+
const threads = [];
|
|
10360
|
+
const createdThreadIds = [];
|
|
10361
|
+
for (const layer of stack.layers) {
|
|
10362
|
+
const title = layer.position === 1 && input.title?.trim() ? input.title.trim() : layer.title;
|
|
10363
|
+
const opened = await openStackLayer(
|
|
10364
|
+
{
|
|
10365
|
+
repoPath,
|
|
10366
|
+
stack,
|
|
10367
|
+
layer: title ? { ...layer, title } : layer,
|
|
10368
|
+
agent: input.agent,
|
|
10369
|
+
autonomy: input.autonomy,
|
|
10370
|
+
model: input.model,
|
|
10371
|
+
effort: input.effort,
|
|
10372
|
+
fast: input.fast,
|
|
10373
|
+
planMode: input.planMode,
|
|
10374
|
+
reuseExistingWorktree: true
|
|
10375
|
+
},
|
|
10376
|
+
onSetupLine
|
|
10377
|
+
);
|
|
10378
|
+
threads.push(opened.thread);
|
|
10379
|
+
if (opened.createdWorktree || opened.thread.worktreePath === bootstrap.worktreePath) {
|
|
10380
|
+
createdThreadIds.push(opened.thread.id);
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
10384
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs21.existsSync)(bootstrap.worktreePath)) {
|
|
10385
|
+
try {
|
|
10386
|
+
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
10387
|
+
deleteBranch: bootstrap.branchName
|
|
10388
|
+
});
|
|
10389
|
+
} catch {
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
return { stack, threads, createdThreadIds };
|
|
10393
|
+
}
|
|
10394
|
+
function stackAgentDefaultsFrom(input) {
|
|
10395
|
+
return {
|
|
10396
|
+
agent: input.agent,
|
|
10397
|
+
autonomy: input.autonomy,
|
|
10398
|
+
model: input.model,
|
|
10399
|
+
effort: input.effort,
|
|
10400
|
+
fast: input.fast,
|
|
10401
|
+
planMode: input.planMode
|
|
10402
|
+
};
|
|
10403
|
+
}
|
|
10404
|
+
|
|
9663
10405
|
// src/threads/adopt.ts
|
|
9664
10406
|
var import_node_child_process = require("child_process");
|
|
9665
|
-
var
|
|
10407
|
+
var import_node_fs22 = require("fs");
|
|
9666
10408
|
var import_node_os9 = require("os");
|
|
9667
10409
|
var import_node_path20 = require("path");
|
|
9668
10410
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
@@ -9687,21 +10429,21 @@ function mapAgentType(raw) {
|
|
|
9687
10429
|
return null;
|
|
9688
10430
|
}
|
|
9689
10431
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
9690
|
-
if (!workspacePath || !(0,
|
|
10432
|
+
if (!workspacePath || !(0, import_node_fs22.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
9691
10433
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
9692
10434
|
let best = null;
|
|
9693
10435
|
let hashes;
|
|
9694
10436
|
try {
|
|
9695
|
-
hashes = (0,
|
|
10437
|
+
hashes = (0, import_node_fs22.readdirSync)(CURSOR_SDK_STORE);
|
|
9696
10438
|
} catch {
|
|
9697
10439
|
return null;
|
|
9698
10440
|
}
|
|
9699
10441
|
for (const hash of hashes) {
|
|
9700
10442
|
const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
9701
|
-
if (!(0,
|
|
10443
|
+
if (!(0, import_node_fs22.existsSync)(agentsFile)) continue;
|
|
9702
10444
|
let text;
|
|
9703
10445
|
try {
|
|
9704
|
-
text = (0,
|
|
10446
|
+
text = (0, import_node_fs22.readFileSync)(agentsFile, "utf8");
|
|
9705
10447
|
} catch {
|
|
9706
10448
|
continue;
|
|
9707
10449
|
}
|
|
@@ -9725,7 +10467,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
9725
10467
|
return best?.agentId ?? null;
|
|
9726
10468
|
}
|
|
9727
10469
|
async function adoptThread(input) {
|
|
9728
|
-
if (!(0,
|
|
10470
|
+
if (!(0, import_node_fs22.existsSync)(input.worktreePath)) {
|
|
9729
10471
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
9730
10472
|
}
|
|
9731
10473
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -9752,18 +10494,18 @@ function conductorDbPath() {
|
|
|
9752
10494
|
return CONDUCTOR_DB;
|
|
9753
10495
|
}
|
|
9754
10496
|
function listConductorWorkspaces() {
|
|
9755
|
-
if (!(0,
|
|
10497
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9756
10498
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9757
10499
|
}
|
|
9758
|
-
const tmp = (0,
|
|
10500
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9759
10501
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9760
10502
|
try {
|
|
9761
|
-
(0,
|
|
10503
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9762
10504
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9763
10505
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9764
|
-
if ((0,
|
|
10506
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9765
10507
|
try {
|
|
9766
|
-
(0,
|
|
10508
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9767
10509
|
} catch {
|
|
9768
10510
|
}
|
|
9769
10511
|
}
|
|
@@ -9839,22 +10581,22 @@ function listConductorWorkspaces() {
|
|
|
9839
10581
|
db.close();
|
|
9840
10582
|
}
|
|
9841
10583
|
} finally {
|
|
9842
|
-
(0,
|
|
10584
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9843
10585
|
}
|
|
9844
10586
|
}
|
|
9845
10587
|
function importConductorWorkspace(workspaceId) {
|
|
9846
|
-
if (!(0,
|
|
10588
|
+
if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
|
|
9847
10589
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
9848
10590
|
}
|
|
9849
|
-
const tmp = (0,
|
|
10591
|
+
const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
|
|
9850
10592
|
const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
|
|
9851
10593
|
try {
|
|
9852
|
-
(0,
|
|
10594
|
+
(0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
9853
10595
|
for (const suffix of ["-wal", "-shm"]) {
|
|
9854
10596
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
9855
|
-
if ((0,
|
|
10597
|
+
if ((0, import_node_fs22.existsSync)(src)) {
|
|
9856
10598
|
try {
|
|
9857
|
-
(0,
|
|
10599
|
+
(0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
9858
10600
|
} catch {
|
|
9859
10601
|
}
|
|
9860
10602
|
}
|
|
@@ -9872,7 +10614,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9872
10614
|
).get(workspaceId);
|
|
9873
10615
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
9874
10616
|
const worktreePath = String(row.workspacePath);
|
|
9875
|
-
if (!(0,
|
|
10617
|
+
if (!(0, import_node_fs22.existsSync)(worktreePath)) {
|
|
9876
10618
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
9877
10619
|
}
|
|
9878
10620
|
let sessionId = null;
|
|
@@ -9935,7 +10677,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
9935
10677
|
db.close();
|
|
9936
10678
|
}
|
|
9937
10679
|
} finally {
|
|
9938
|
-
(0,
|
|
10680
|
+
(0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
|
|
9939
10681
|
}
|
|
9940
10682
|
}
|
|
9941
10683
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -9944,13 +10686,14 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
9944
10686
|
|
|
9945
10687
|
// src/orchestrator/orchestrator.ts
|
|
9946
10688
|
var import_node_events = require("events");
|
|
9947
|
-
var
|
|
10689
|
+
var import_node_fs28 = require("fs");
|
|
9948
10690
|
init_error_detail();
|
|
9949
10691
|
init_agents();
|
|
9950
10692
|
init_worktree();
|
|
10693
|
+
init_stack();
|
|
9951
10694
|
|
|
9952
10695
|
// src/git/orphan-cleanup.ts
|
|
9953
|
-
var
|
|
10696
|
+
var import_node_fs23 = require("fs");
|
|
9954
10697
|
var import_node_path21 = require("path");
|
|
9955
10698
|
init_worktree();
|
|
9956
10699
|
init_thread_store();
|
|
@@ -9966,9 +10709,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9966
10709
|
repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
|
|
9967
10710
|
);
|
|
9968
10711
|
const homeRoot = sideboardWorkspacesDir();
|
|
9969
|
-
if ((0,
|
|
10712
|
+
if ((0, import_node_fs23.existsSync)(homeRoot)) {
|
|
9970
10713
|
try {
|
|
9971
|
-
for (const entry of (0,
|
|
10714
|
+
for (const entry of (0, import_node_fs23.readdirSync)(homeRoot, { withFileTypes: true })) {
|
|
9972
10715
|
if (!entry.isDirectory()) continue;
|
|
9973
10716
|
void entry;
|
|
9974
10717
|
}
|
|
@@ -9978,7 +10721,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9978
10721
|
const orphans = [];
|
|
9979
10722
|
const seen = /* @__PURE__ */ new Set();
|
|
9980
10723
|
for (const repoPath of repos) {
|
|
9981
|
-
if (!repoPath || !(0,
|
|
10724
|
+
if (!repoPath || !(0, import_node_fs23.existsSync)(repoPath)) continue;
|
|
9982
10725
|
try {
|
|
9983
10726
|
const wts = await listWorktrees(repoPath);
|
|
9984
10727
|
for (const wt of wts) {
|
|
@@ -9989,7 +10732,7 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9989
10732
|
seen.add(path);
|
|
9990
10733
|
let mtimeMs = 0;
|
|
9991
10734
|
try {
|
|
9992
|
-
mtimeMs = (0,
|
|
10735
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
9993
10736
|
} catch {
|
|
9994
10737
|
mtimeMs = 0;
|
|
9995
10738
|
}
|
|
@@ -9999,16 +10742,16 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
9999
10742
|
}
|
|
10000
10743
|
try {
|
|
10001
10744
|
const root = worktreesRoot(repoPath);
|
|
10002
|
-
if ((0,
|
|
10003
|
-
for (const entry of (0,
|
|
10745
|
+
if ((0, import_node_fs23.existsSync)(root)) {
|
|
10746
|
+
for (const entry of (0, import_node_fs23.readdirSync)(root, { withFileTypes: true })) {
|
|
10004
10747
|
if (!entry.isDirectory()) continue;
|
|
10005
10748
|
const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
|
|
10006
10749
|
if (known.has(path) || seen.has(path)) continue;
|
|
10007
|
-
if (!(0,
|
|
10750
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
|
|
10008
10751
|
seen.add(path);
|
|
10009
10752
|
let mtimeMs = 0;
|
|
10010
10753
|
try {
|
|
10011
|
-
mtimeMs = (0,
|
|
10754
|
+
mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
|
|
10012
10755
|
} catch {
|
|
10013
10756
|
mtimeMs = Date.now();
|
|
10014
10757
|
}
|
|
@@ -10154,7 +10897,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
10154
10897
|
}
|
|
10155
10898
|
|
|
10156
10899
|
// src/git/clone-repo.ts
|
|
10157
|
-
var
|
|
10900
|
+
var import_node_fs24 = require("fs");
|
|
10158
10901
|
var import_node_path22 = require("path");
|
|
10159
10902
|
var import_execa6 = require("execa");
|
|
10160
10903
|
init_paths();
|
|
@@ -10170,7 +10913,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
10170
10913
|
}
|
|
10171
10914
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
10172
10915
|
const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
|
|
10173
|
-
if ((0,
|
|
10916
|
+
if ((0, import_node_fs24.existsSync)(dest)) {
|
|
10174
10917
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
10175
10918
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
10176
10919
|
return { repoPath: repoPath2, workspace: workspace2 };
|
|
@@ -10192,7 +10935,7 @@ init_orchestrator_capable();
|
|
|
10192
10935
|
|
|
10193
10936
|
// src/review/request-review.ts
|
|
10194
10937
|
var import_node_crypto5 = require("crypto");
|
|
10195
|
-
var
|
|
10938
|
+
var import_node_fs25 = require("fs");
|
|
10196
10939
|
var import_node_path23 = require("path");
|
|
10197
10940
|
init_global_workspace();
|
|
10198
10941
|
init_thread_store();
|
|
@@ -10317,15 +11060,13 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
10317
11060
|
`;
|
|
10318
11061
|
|
|
10319
11062
|
// src/review/request-review.ts
|
|
11063
|
+
init_workspace_scratch();
|
|
10320
11064
|
var REPO_REVIEW_PATH = ".sideboard/review.md";
|
|
10321
11065
|
var REPO_REVIEW_NAME = "review.md";
|
|
10322
|
-
var REVIEW_REQUEST_PATH =
|
|
11066
|
+
var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
|
|
11067
|
+
var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
|
|
10323
11068
|
var REVIEW_REQUEST_NAME = "Review request.md";
|
|
10324
11069
|
var REVIEW_REQUEST_PREFILL = "Review.";
|
|
10325
|
-
var ATTACHMENTS_GITIGNORE2 = `# Sideboard review / composer attachments (local only)
|
|
10326
|
-
*
|
|
10327
|
-
!.gitignore
|
|
10328
|
-
`;
|
|
10329
11070
|
var LEGACY_REVIEW_TEMPLATE_MARKERS = [
|
|
10330
11071
|
"You are acting as a reviewer for a proposed code change made by another engineer.",
|
|
10331
11072
|
"HOW MANY FINDINGS TO RETURN:"
|
|
@@ -10339,19 +11080,19 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
10339
11080
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
10340
11081
|
}
|
|
10341
11082
|
function readTextIfPresent(abs) {
|
|
10342
|
-
if (!(0,
|
|
11083
|
+
if (!(0, import_node_fs25.existsSync)(abs)) return null;
|
|
10343
11084
|
try {
|
|
10344
|
-
const content = (0,
|
|
11085
|
+
const content = (0, import_node_fs25.readFileSync)(abs, "utf8");
|
|
10345
11086
|
return content.trim() ? content : null;
|
|
10346
11087
|
} catch {
|
|
10347
11088
|
return null;
|
|
10348
11089
|
}
|
|
10349
11090
|
}
|
|
10350
11091
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
10351
|
-
const gitignoreAbs = (0, import_node_path23.join)(worktreePath,
|
|
10352
|
-
if ((0,
|
|
10353
|
-
(0,
|
|
10354
|
-
(0,
|
|
11092
|
+
const gitignoreAbs = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
11093
|
+
if ((0, import_node_fs25.existsSync)(gitignoreAbs)) return;
|
|
11094
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
|
|
11095
|
+
(0, import_node_fs25.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
10355
11096
|
}
|
|
10356
11097
|
function resolveReviewGuidelines(worktreePath) {
|
|
10357
11098
|
const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -10374,9 +11115,19 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
10374
11115
|
source: "local"
|
|
10375
11116
|
};
|
|
10376
11117
|
}
|
|
11118
|
+
const legacyAbs = (0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
|
|
11119
|
+
const legacyContent = readTextIfPresent(legacyAbs);
|
|
11120
|
+
if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
|
|
11121
|
+
return {
|
|
11122
|
+
path: LEGACY_REVIEW_REQUEST_PATH,
|
|
11123
|
+
name: REVIEW_REQUEST_NAME,
|
|
11124
|
+
content: legacyContent,
|
|
11125
|
+
source: "local"
|
|
11126
|
+
};
|
|
11127
|
+
}
|
|
10377
11128
|
ensureAttachmentsGitignore(worktreePath);
|
|
10378
|
-
(0,
|
|
10379
|
-
(0,
|
|
11129
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
|
|
11130
|
+
(0, import_node_fs25.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
10380
11131
|
return {
|
|
10381
11132
|
path: REVIEW_REQUEST_PATH,
|
|
10382
11133
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -10396,17 +11147,18 @@ function ensureReviewRequestFile(worktreePath) {
|
|
|
10396
11147
|
};
|
|
10397
11148
|
}
|
|
10398
11149
|
const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
|
|
10399
|
-
const localContent = readTextIfPresent(localAbs);
|
|
11150
|
+
const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
10400
11151
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
11152
|
+
const path = (0, import_node_fs25.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
|
|
10401
11153
|
return {
|
|
10402
|
-
path
|
|
11154
|
+
path,
|
|
10403
11155
|
name: REVIEW_REQUEST_NAME,
|
|
10404
11156
|
content: localContent,
|
|
10405
11157
|
source: "local"
|
|
10406
11158
|
};
|
|
10407
11159
|
}
|
|
10408
|
-
(0,
|
|
10409
|
-
(0,
|
|
11160
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
|
|
11161
|
+
(0, import_node_fs25.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
10410
11162
|
return {
|
|
10411
11163
|
path: REPO_REVIEW_PATH,
|
|
10412
11164
|
name: REPO_REVIEW_NAME,
|
|
@@ -10426,7 +11178,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
10426
11178
|
};
|
|
10427
11179
|
}
|
|
10428
11180
|
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));
|
|
11181
|
+
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
11182
|
}
|
|
10431
11183
|
async function requestReview(threadRef, send) {
|
|
10432
11184
|
const from = findThreadByRef(threadRef);
|
|
@@ -10565,6 +11317,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
10565
11317
|
|
|
10566
11318
|
// src/orchestrator/orchestrator.ts
|
|
10567
11319
|
init_types();
|
|
11320
|
+
init_plan_file();
|
|
10568
11321
|
init_settings();
|
|
10569
11322
|
|
|
10570
11323
|
// src/threads/sync-branch.ts
|
|
@@ -10684,7 +11437,7 @@ var Orchestrator = class {
|
|
|
10684
11437
|
}
|
|
10685
11438
|
continue;
|
|
10686
11439
|
}
|
|
10687
|
-
if (!(0,
|
|
11440
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
10688
11441
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
10689
11442
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
10690
11443
|
continue;
|
|
@@ -10829,26 +11582,43 @@ var Orchestrator = class {
|
|
|
10829
11582
|
return findThreadByRef(idOrRef) ?? readThread(idOrRef);
|
|
10830
11583
|
}
|
|
10831
11584
|
async createThread(input) {
|
|
10832
|
-
|
|
10833
|
-
this.emit({
|
|
10834
|
-
type: "turn_output",
|
|
10835
|
-
threadId: "pending",
|
|
10836
|
-
event: { type: "stdout", data: line }
|
|
10837
|
-
});
|
|
10838
|
-
});
|
|
11585
|
+
const thread = await createThread(input);
|
|
10839
11586
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
11587
|
+
void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
|
|
11588
|
+
return thread;
|
|
11589
|
+
}
|
|
11590
|
+
async finishCreateThread(threadId, prompt) {
|
|
11591
|
+
await this.runSetupAfterCreate(threadId);
|
|
10840
11592
|
const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
10841
11593
|
if (autoRunAfterSetupEnabled2()) {
|
|
10842
11594
|
try {
|
|
10843
|
-
await this.startDev(
|
|
11595
|
+
await this.startDev(threadId);
|
|
10844
11596
|
} catch {
|
|
10845
11597
|
}
|
|
10846
11598
|
}
|
|
10847
|
-
const prompt = input.prompt?.trim();
|
|
10848
11599
|
if (prompt) {
|
|
10849
|
-
|
|
11600
|
+
try {
|
|
11601
|
+
await this.send(threadId, prompt);
|
|
11602
|
+
} catch (err) {
|
|
11603
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11604
|
+
updateThread(threadId, {
|
|
11605
|
+
lastError: `First prompt failed: ${message}`
|
|
11606
|
+
});
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
}
|
|
11610
|
+
/** Run workspace setup after a new worktree is created (no-op if none configured). */
|
|
11611
|
+
async runSetupAfterCreate(threadId) {
|
|
11612
|
+
try {
|
|
11613
|
+
await this.runSetup(threadId);
|
|
11614
|
+
} catch (err) {
|
|
11615
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11616
|
+
if (/no setup script/i.test(message)) return;
|
|
11617
|
+
if (/already running/i.test(message)) return;
|
|
11618
|
+
updateThread(threadId, {
|
|
11619
|
+
lastError: `Setup failed: ${message}`
|
|
11620
|
+
});
|
|
10850
11621
|
}
|
|
10851
|
-
return thread;
|
|
10852
11622
|
}
|
|
10853
11623
|
listWorkspaces() {
|
|
10854
11624
|
const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
|
|
@@ -11181,8 +11951,73 @@ var Orchestrator = class {
|
|
|
11181
11951
|
}
|
|
11182
11952
|
}
|
|
11183
11953
|
}
|
|
11184
|
-
|
|
11185
|
-
|
|
11954
|
+
let lastStderr = summarizeTurnStderr(stderrTail);
|
|
11955
|
+
let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
11956
|
+
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "cursor" && this.requireThread(threadId).agent !== "brightsy") {
|
|
11957
|
+
updateThread(threadId, { sessionId: null });
|
|
11958
|
+
pushTurnStderr(
|
|
11959
|
+
stderrTail,
|
|
11960
|
+
"Agent session missing \u2014 starting a fresh session"
|
|
11961
|
+
);
|
|
11962
|
+
this.emit({
|
|
11963
|
+
type: "turn_output",
|
|
11964
|
+
threadId,
|
|
11965
|
+
event: {
|
|
11966
|
+
type: "stderr",
|
|
11967
|
+
data: "Agent session missing \u2014 starting a fresh session"
|
|
11968
|
+
}
|
|
11969
|
+
});
|
|
11970
|
+
const retryThread = this.requireThread(threadId);
|
|
11971
|
+
const prior = retryThread.messages.slice(0, -1);
|
|
11972
|
+
const retrySeed = buildSessionSeed(prior);
|
|
11973
|
+
const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
|
|
11974
|
+
loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
|
|
11975
|
+
);
|
|
11976
|
+
const retryPrefix = [
|
|
11977
|
+
coordinatorDirective,
|
|
11978
|
+
worktreeDirective,
|
|
11979
|
+
artifactDirective,
|
|
11980
|
+
renameBranchDirective,
|
|
11981
|
+
retryInstructions,
|
|
11982
|
+
retrySeed
|
|
11983
|
+
].filter(Boolean).join("\n\n---\n\n");
|
|
11984
|
+
const retryHandle = await spawnAgentTurn(
|
|
11985
|
+
retryThread,
|
|
11986
|
+
{ cachedPrefix: retryPrefix, prompt: agentPrompt },
|
|
11987
|
+
(event) => {
|
|
11988
|
+
this.emit({ type: "turn_output", threadId, event });
|
|
11989
|
+
if (event.type === "session_id") {
|
|
11990
|
+
updateThread(threadId, { sessionId: event.data });
|
|
11991
|
+
}
|
|
11992
|
+
if (event.type === "stderr" && typeof event.data === "string") {
|
|
11993
|
+
pushTurnStderr(stderrTail, event.data);
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
);
|
|
11997
|
+
this.activeTurns.set(threadId, retryHandle);
|
|
11998
|
+
if (typeof retryHandle.pid === "number" && retryHandle.pid > 0) {
|
|
11999
|
+
updateThread(threadId, { agentPid: retryHandle.pid });
|
|
12000
|
+
}
|
|
12001
|
+
this.processes.set(`${threadId}:agent`, {
|
|
12002
|
+
kind: "agent",
|
|
12003
|
+
pid: retryHandle.pid,
|
|
12004
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12005
|
+
kill: retryHandle.kill
|
|
12006
|
+
});
|
|
12007
|
+
if (this.stoppedTurns.has(threadId)) {
|
|
12008
|
+
retryHandle.kill();
|
|
12009
|
+
}
|
|
12010
|
+
const retryResult = await retryHandle.done;
|
|
12011
|
+
if (retryResult.sessionId) {
|
|
12012
|
+
updateThread(threadId, { sessionId: retryResult.sessionId });
|
|
12013
|
+
}
|
|
12014
|
+
assistantText = retryResult.assistantText.trim();
|
|
12015
|
+
parts = retryResult.parts;
|
|
12016
|
+
usage = retryResult.usage ?? void 0;
|
|
12017
|
+
exitCode = retryResult.exitCode;
|
|
12018
|
+
lastStderr = summarizeTurnStderr(stderrTail);
|
|
12019
|
+
detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
12020
|
+
}
|
|
11186
12021
|
let chatText = assistantText;
|
|
11187
12022
|
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
11188
12023
|
chatText = humanizeAgentFailDetail(detail);
|
|
@@ -11198,6 +12033,19 @@ var Orchestrator = class {
|
|
|
11198
12033
|
});
|
|
11199
12034
|
}
|
|
11200
12035
|
const afterTurn = this.requireThread(threadId);
|
|
12036
|
+
if (afterTurn.planMode && afterTurn.worktreePath?.trim()) {
|
|
12037
|
+
const presented = extractPresentedPlan(parts);
|
|
12038
|
+
const exited = parts.some(
|
|
12039
|
+
(p) => p.type === "tool" && /exitplanmode/i.test(p.name)
|
|
12040
|
+
);
|
|
12041
|
+
if (presented?.content) {
|
|
12042
|
+
writePlanFile(afterTurn.worktreePath, presented.content);
|
|
12043
|
+
} else if (exited || chatText && chatText.trim().length >= 400) {
|
|
12044
|
+
if (!readPlanFile(afterTurn.worktreePath) && chatText?.trim()) {
|
|
12045
|
+
writePlanFile(afterTurn.worktreePath, chatText.trim());
|
|
12046
|
+
}
|
|
12047
|
+
}
|
|
12048
|
+
}
|
|
11201
12049
|
if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
|
|
11202
12050
|
(p) => p.type === "tool" && /exitplanmode/i.test(p.name)
|
|
11203
12051
|
)) {
|
|
@@ -11679,6 +12527,82 @@ var Orchestrator = class {
|
|
|
11679
12527
|
}
|
|
11680
12528
|
return meta;
|
|
11681
12529
|
}
|
|
12530
|
+
async getPrStack(threadRef) {
|
|
12531
|
+
const thread = this.requireThread(threadRef);
|
|
12532
|
+
if (!thread.worktreePath?.trim()) return null;
|
|
12533
|
+
const stack = await getPrStack(thread.worktreePath);
|
|
12534
|
+
if (!stack) return null;
|
|
12535
|
+
const current = stack.currentIndex >= 0 ? stack.layers[stack.currentIndex] : null;
|
|
12536
|
+
const patch = {};
|
|
12537
|
+
if (stack.stackNumber != null) {
|
|
12538
|
+
const id = `gh-stack-${stack.stackNumber}`;
|
|
12539
|
+
if (thread.stackId !== id) patch.stackId = id;
|
|
12540
|
+
}
|
|
12541
|
+
if (current?.position != null && thread.stackLayer !== current.position) {
|
|
12542
|
+
patch.stackLayer = current.position;
|
|
12543
|
+
}
|
|
12544
|
+
if (current?.prUrl && current.prUrl !== thread.prUrl) patch.prUrl = current.prUrl;
|
|
12545
|
+
if (current?.title && current.title !== thread.prTitle) patch.prTitle = current.title;
|
|
12546
|
+
if (current?.branchName && current.branchName !== thread.branchName) {
|
|
12547
|
+
patch.branchName = current.branchName;
|
|
12548
|
+
}
|
|
12549
|
+
if (Object.keys(patch).length > 0) updateThread(thread.id, patch);
|
|
12550
|
+
return stack;
|
|
12551
|
+
}
|
|
12552
|
+
/** Open worktrees for all (or one) stack layers discovered from a thread. */
|
|
12553
|
+
async openPrStackLayers(threadRef, opts) {
|
|
12554
|
+
const result = await openPrStackLayers({ threadRef, layer: opts?.layer });
|
|
12555
|
+
for (const t of result.threads) {
|
|
12556
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12557
|
+
}
|
|
12558
|
+
for (const id of result.createdThreadIds) {
|
|
12559
|
+
await this.runSetupAfterCreate(id);
|
|
12560
|
+
}
|
|
12561
|
+
return { stack: result.stack, threads: result.threads };
|
|
12562
|
+
}
|
|
12563
|
+
/** Add a branch on top of the thread's stack and open its worktree. */
|
|
12564
|
+
async addStackLayer(threadRef, branchName, opts) {
|
|
12565
|
+
const result = await addStackLayerFromThread({
|
|
12566
|
+
threadRef,
|
|
12567
|
+
branchName,
|
|
12568
|
+
title: opts?.title
|
|
12569
|
+
});
|
|
12570
|
+
this.emit({
|
|
12571
|
+
type: "status_changed",
|
|
12572
|
+
threadId: result.thread.id,
|
|
12573
|
+
status: result.thread.status
|
|
12574
|
+
});
|
|
12575
|
+
if (result.createdWorktree) {
|
|
12576
|
+
await this.runSetupAfterCreate(result.thread.id);
|
|
12577
|
+
}
|
|
12578
|
+
return { stack: result.stack, thread: result.thread };
|
|
12579
|
+
}
|
|
12580
|
+
/** Initialize a stack from the current thread branch (optional extra layers). */
|
|
12581
|
+
async initStackFromThread(threadRef, opts) {
|
|
12582
|
+
const result = await initStackFromThread({
|
|
12583
|
+
threadRef,
|
|
12584
|
+
additionalBranches: opts?.additionalBranches,
|
|
12585
|
+
base: opts?.base
|
|
12586
|
+
});
|
|
12587
|
+
for (const t of result.threads) {
|
|
12588
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12589
|
+
}
|
|
12590
|
+
for (const id of result.createdThreadIds) {
|
|
12591
|
+
await this.runSetupAfterCreate(id);
|
|
12592
|
+
}
|
|
12593
|
+
return { stack: result.stack, threads: result.threads };
|
|
12594
|
+
}
|
|
12595
|
+
/** Create a new multi-layer stack with one worktree per layer. */
|
|
12596
|
+
async createPrStack(input) {
|
|
12597
|
+
const result = await createPrStack(input);
|
|
12598
|
+
for (const t of result.threads) {
|
|
12599
|
+
this.emit({ type: "status_changed", threadId: t.id, status: t.status });
|
|
12600
|
+
}
|
|
12601
|
+
for (const id of result.createdThreadIds) {
|
|
12602
|
+
await this.runSetupAfterCreate(id);
|
|
12603
|
+
}
|
|
12604
|
+
return { stack: result.stack, threads: result.threads };
|
|
12605
|
+
}
|
|
11682
12606
|
async getPrDetails(threadRef) {
|
|
11683
12607
|
const { thread, selector, cwd } = await this.withPrSelector(threadRef);
|
|
11684
12608
|
if (!selector) return null;
|
|
@@ -11741,14 +12665,9 @@ var Orchestrator = class {
|
|
|
11741
12665
|
return forkChatTab(input);
|
|
11742
12666
|
}
|
|
11743
12667
|
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
|
-
});
|
|
12668
|
+
const thread = await forkThreadWorktree(input);
|
|
11751
12669
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
12670
|
+
await this.runSetupAfterCreate(thread.id);
|
|
11752
12671
|
return thread;
|
|
11753
12672
|
}
|
|
11754
12673
|
renameThread(threadRef, title) {
|
|
@@ -11762,7 +12681,7 @@ var Orchestrator = class {
|
|
|
11762
12681
|
}
|
|
11763
12682
|
/**
|
|
11764
12683
|
* Stage OS / worktree files into composer attachments (copies external files
|
|
11765
|
-
* into `.
|
|
12684
|
+
* into `.context/attachments/` so agents can Read images and binaries).
|
|
11766
12685
|
*/
|
|
11767
12686
|
attachComposerFiles(threadRef, opts) {
|
|
11768
12687
|
const thread = this.requireThread(threadRef);
|
|
@@ -11836,7 +12755,7 @@ var Orchestrator = class {
|
|
|
11836
12755
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
11837
12756
|
return setStatus(thread.id, "idle");
|
|
11838
12757
|
}
|
|
11839
|
-
if (!(0,
|
|
12758
|
+
if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
|
|
11840
12759
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
11841
12760
|
const { execa: execa7 } = await import("execa");
|
|
11842
12761
|
const slug = thread.worktreePath.split("/").pop();
|
|
@@ -11944,13 +12863,116 @@ async function startOrchestration(opts) {
|
|
|
11944
12863
|
}
|
|
11945
12864
|
|
|
11946
12865
|
// src/index.ts
|
|
12866
|
+
init_workspace_scratch();
|
|
12867
|
+
|
|
12868
|
+
// src/plan/ask-user.ts
|
|
12869
|
+
function asRecord3(v) {
|
|
12870
|
+
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12871
|
+
}
|
|
12872
|
+
function parseOptions(raw) {
|
|
12873
|
+
if (!Array.isArray(raw)) return [];
|
|
12874
|
+
const out = [];
|
|
12875
|
+
for (const item of raw) {
|
|
12876
|
+
if (typeof item === "string" && item.trim()) {
|
|
12877
|
+
out.push({ label: item.trim() });
|
|
12878
|
+
continue;
|
|
12879
|
+
}
|
|
12880
|
+
const o = asRecord3(item);
|
|
12881
|
+
if (!o) continue;
|
|
12882
|
+
const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
|
|
12883
|
+
if (!label) continue;
|
|
12884
|
+
const description = typeof o.description === "string" && o.description.trim() ? o.description.trim() : void 0;
|
|
12885
|
+
out.push({ label, description });
|
|
12886
|
+
}
|
|
12887
|
+
return out;
|
|
12888
|
+
}
|
|
12889
|
+
function parseOneQuestion(raw) {
|
|
12890
|
+
const o = asRecord3(raw);
|
|
12891
|
+
if (!o) return null;
|
|
12892
|
+
const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
|
|
12893
|
+
if (!question) return null;
|
|
12894
|
+
const options = parseOptions(o.options);
|
|
12895
|
+
if (options.length < 1) return null;
|
|
12896
|
+
const header = typeof o.header === "string" && o.header.trim() ? o.header.trim() : void 0;
|
|
12897
|
+
const multiSelect = Boolean(o.multiSelect ?? o.multi_select);
|
|
12898
|
+
return { question, header, multiSelect, options };
|
|
12899
|
+
}
|
|
12900
|
+
function parsePlanQuestionsInput(input) {
|
|
12901
|
+
const root = asRecord3(input);
|
|
12902
|
+
if (!root) return [];
|
|
12903
|
+
const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
|
|
12904
|
+
const out = [];
|
|
12905
|
+
for (const item of list) {
|
|
12906
|
+
const q = parseOneQuestion(item);
|
|
12907
|
+
if (q) out.push(q);
|
|
12908
|
+
}
|
|
12909
|
+
return out;
|
|
12910
|
+
}
|
|
12911
|
+
var ASK_USER_TOOL_RE = /^(AskUserQuestion|ask_user|mcp__sideboard__ask_user)$/i;
|
|
12912
|
+
function isAskUserToolName(name) {
|
|
12913
|
+
if (!name) return false;
|
|
12914
|
+
const base = name.replace(/^mcp__sideboard__/i, "");
|
|
12915
|
+
return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
|
|
12916
|
+
}
|
|
12917
|
+
function extractPendingPlanQuestions(parts) {
|
|
12918
|
+
if (!parts?.length) return null;
|
|
12919
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
12920
|
+
const p = parts[i];
|
|
12921
|
+
if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
|
|
12922
|
+
const questions = parsePlanQuestionsInput(p.input);
|
|
12923
|
+
if (!questions.length) continue;
|
|
12924
|
+
return {
|
|
12925
|
+
id: p.id || `ask-${i}`,
|
|
12926
|
+
questions,
|
|
12927
|
+
source: p.name || "ask_user"
|
|
12928
|
+
};
|
|
12929
|
+
}
|
|
12930
|
+
return null;
|
|
12931
|
+
}
|
|
12932
|
+
function formatPlanQuestionAnswers(questions, answers) {
|
|
12933
|
+
const lines = ["Answers to your questions:"];
|
|
12934
|
+
for (let i = 0; i < questions.length; i++) {
|
|
12935
|
+
const q = questions[i];
|
|
12936
|
+
const a = answers.find((x) => x.questionIndex === i);
|
|
12937
|
+
const header = q.header ? `**${q.header}** \u2014 ` : "";
|
|
12938
|
+
const parts = [];
|
|
12939
|
+
if (a?.selected.length) parts.push(a.selected.join(", "));
|
|
12940
|
+
if (a?.other?.trim()) parts.push(a.other.trim());
|
|
12941
|
+
const body = parts.length ? parts.join(" \xB7 ") : "(no answer)";
|
|
12942
|
+
lines.push(`${i + 1}. ${header}${q.question}`);
|
|
12943
|
+
lines.push(` \u2192 ${body}`);
|
|
12944
|
+
}
|
|
12945
|
+
return lines.join("\n");
|
|
12946
|
+
}
|
|
12947
|
+
function formatPlanQuestionsForChat(questions) {
|
|
12948
|
+
const lines = ["### Questions for you", ""];
|
|
12949
|
+
for (let i = 0; i < questions.length; i++) {
|
|
12950
|
+
const q = questions[i];
|
|
12951
|
+
const header = q.header ? `**${q.header}** \u2014 ` : "";
|
|
12952
|
+
lines.push(`${i + 1}. ${header}${q.question}`);
|
|
12953
|
+
for (let oi = 0; oi < q.options.length; oi++) {
|
|
12954
|
+
const opt = q.options[oi];
|
|
12955
|
+
const desc = opt.description?.trim();
|
|
12956
|
+
lines.push(
|
|
12957
|
+
desc ? ` - **${opt.label}** \u2014 ${desc}` : ` - **${opt.label}**`
|
|
12958
|
+
);
|
|
12959
|
+
}
|
|
12960
|
+
lines.push("");
|
|
12961
|
+
}
|
|
12962
|
+
lines.push("_Answer in the composer below._");
|
|
12963
|
+
return lines.join("\n").trimEnd();
|
|
12964
|
+
}
|
|
12965
|
+
|
|
12966
|
+
// src/index.ts
|
|
12967
|
+
init_plan_present();
|
|
12968
|
+
init_plan_file();
|
|
11947
12969
|
init_coordinator_prompt();
|
|
11948
12970
|
|
|
11949
12971
|
// src/mcp/server.ts
|
|
11950
12972
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
11951
12973
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11952
12974
|
var import_zod = require("zod");
|
|
11953
|
-
var
|
|
12975
|
+
var import_node_path26 = require("path");
|
|
11954
12976
|
init_worktree();
|
|
11955
12977
|
init_global_workspace();
|
|
11956
12978
|
init_list_models();
|
|
@@ -11999,7 +13021,7 @@ async function startMcpServer() {
|
|
|
11999
13021
|
async () => {
|
|
12000
13022
|
const threads = orch.getThreads(true);
|
|
12001
13023
|
const lines = threads.map((t) => {
|
|
12002
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
13024
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path26.basename)(t.repoPath) || t.repoPath;
|
|
12003
13025
|
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
13026
|
});
|
|
12005
13027
|
return {
|
|
@@ -12061,6 +13083,59 @@ async function startMcpServer() {
|
|
|
12061
13083
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
12062
13084
|
}
|
|
12063
13085
|
);
|
|
13086
|
+
server.tool(
|
|
13087
|
+
"ask_user",
|
|
13088
|
+
"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.",
|
|
13089
|
+
{
|
|
13090
|
+
questions: import_zod.z.array(
|
|
13091
|
+
import_zod.z.object({
|
|
13092
|
+
question: import_zod.z.string().describe("Full question text ending with ?"),
|
|
13093
|
+
header: import_zod.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
13094
|
+
multiSelect: import_zod.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
13095
|
+
options: import_zod.z.array(
|
|
13096
|
+
import_zod.z.object({
|
|
13097
|
+
label: import_zod.z.string(),
|
|
13098
|
+
description: import_zod.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
13099
|
+
})
|
|
13100
|
+
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
13101
|
+
})
|
|
13102
|
+
).min(1).max(4).describe("1\u20134 questions")
|
|
13103
|
+
},
|
|
13104
|
+
async ({ questions }) => {
|
|
13105
|
+
const payload = {
|
|
13106
|
+
ok: true,
|
|
13107
|
+
questions,
|
|
13108
|
+
message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
|
|
13109
|
+
};
|
|
13110
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
13111
|
+
}
|
|
13112
|
+
);
|
|
13113
|
+
server.tool(
|
|
13114
|
+
"present_plan",
|
|
13115
|
+
"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.",
|
|
13116
|
+
{
|
|
13117
|
+
title: import_zod.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
13118
|
+
content: import_zod.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
13119
|
+
thread_id: import_zod.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
13120
|
+
},
|
|
13121
|
+
async ({ title, content, thread_id }) => {
|
|
13122
|
+
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
13123
|
+
let root = process.cwd();
|
|
13124
|
+
if (thread_id?.trim()) {
|
|
13125
|
+
const t = orch.getThread(thread_id.trim());
|
|
13126
|
+
if (t?.worktreePath?.trim()) root = t.worktreePath;
|
|
13127
|
+
}
|
|
13128
|
+
const path = writePlanFile2(root, content);
|
|
13129
|
+
const payload = {
|
|
13130
|
+
ok: true,
|
|
13131
|
+
path,
|
|
13132
|
+
title: title?.trim() || "Plan",
|
|
13133
|
+
content,
|
|
13134
|
+
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
13135
|
+
};
|
|
13136
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
13137
|
+
}
|
|
13138
|
+
);
|
|
12064
13139
|
server.tool(
|
|
12065
13140
|
"present_schema",
|
|
12066
13141
|
"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 +13714,126 @@ async function startMcpServer() {
|
|
|
12639
13714
|
};
|
|
12640
13715
|
}
|
|
12641
13716
|
);
|
|
13717
|
+
server.tool(
|
|
13718
|
+
"get_pr_stack",
|
|
13719
|
+
"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.",
|
|
13720
|
+
{ ref: import_zod.z.string() },
|
|
13721
|
+
async ({ ref }) => {
|
|
13722
|
+
const stack = await orch.getPrStack(ref);
|
|
13723
|
+
return {
|
|
13724
|
+
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
13725
|
+
};
|
|
13726
|
+
}
|
|
13727
|
+
);
|
|
13728
|
+
server.tool(
|
|
13729
|
+
"open_pr_stack_layers",
|
|
13730
|
+
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
13731
|
+
{
|
|
13732
|
+
ref: import_zod.z.string(),
|
|
13733
|
+
layer: import_zod.z.number().int().positive().optional()
|
|
13734
|
+
},
|
|
13735
|
+
async ({ ref, layer }) => {
|
|
13736
|
+
const result = await orch.openPrStackLayers(ref, { layer });
|
|
13737
|
+
return {
|
|
13738
|
+
content: [
|
|
13739
|
+
{
|
|
13740
|
+
type: "text",
|
|
13741
|
+
text: JSON.stringify(
|
|
13742
|
+
{
|
|
13743
|
+
stackNumber: result.stack.stackNumber,
|
|
13744
|
+
trunk: result.stack.trunk,
|
|
13745
|
+
threads: result.threads.map((t) => ({
|
|
13746
|
+
id: t.id,
|
|
13747
|
+
title: t.title,
|
|
13748
|
+
branchName: t.branchName,
|
|
13749
|
+
stackLayer: t.stackLayer,
|
|
13750
|
+
worktreePath: t.worktreePath,
|
|
13751
|
+
prUrl: t.prUrl,
|
|
13752
|
+
link: `sideboard://thread/${t.id}`
|
|
13753
|
+
}))
|
|
13754
|
+
},
|
|
13755
|
+
null,
|
|
13756
|
+
2
|
|
13757
|
+
)
|
|
13758
|
+
}
|
|
13759
|
+
]
|
|
13760
|
+
};
|
|
13761
|
+
}
|
|
13762
|
+
);
|
|
13763
|
+
server.tool(
|
|
13764
|
+
"add_stack_layer",
|
|
13765
|
+
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
13766
|
+
{
|
|
13767
|
+
ref: import_zod.z.string(),
|
|
13768
|
+
branchName: import_zod.z.string(),
|
|
13769
|
+
title: import_zod.z.string().optional()
|
|
13770
|
+
},
|
|
13771
|
+
async ({ ref, branchName, title }) => {
|
|
13772
|
+
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
13773
|
+
return {
|
|
13774
|
+
content: [
|
|
13775
|
+
{
|
|
13776
|
+
type: "text",
|
|
13777
|
+
text: JSON.stringify(
|
|
13778
|
+
{
|
|
13779
|
+
id: result.thread.id,
|
|
13780
|
+
title: result.thread.title,
|
|
13781
|
+
branchName: result.thread.branchName,
|
|
13782
|
+
stackLayer: result.thread.stackLayer,
|
|
13783
|
+
worktreePath: result.thread.worktreePath,
|
|
13784
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
13785
|
+
},
|
|
13786
|
+
null,
|
|
13787
|
+
2
|
|
13788
|
+
)
|
|
13789
|
+
}
|
|
13790
|
+
]
|
|
13791
|
+
};
|
|
13792
|
+
}
|
|
13793
|
+
);
|
|
13794
|
+
server.tool(
|
|
13795
|
+
"create_pr_stack",
|
|
13796
|
+
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
13797
|
+
{
|
|
13798
|
+
repoPath: import_zod.z.string(),
|
|
13799
|
+
branches: import_zod.z.array(import_zod.z.string()).min(1),
|
|
13800
|
+
agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
13801
|
+
base: import_zod.z.string().optional(),
|
|
13802
|
+
title: import_zod.z.string().optional()
|
|
13803
|
+
},
|
|
13804
|
+
async (args) => {
|
|
13805
|
+
const result = await orch.createPrStack({
|
|
13806
|
+
repoPath: args.repoPath,
|
|
13807
|
+
branches: args.branches,
|
|
13808
|
+
agent: args.agent,
|
|
13809
|
+
base: args.base,
|
|
13810
|
+
title: args.title
|
|
13811
|
+
});
|
|
13812
|
+
return {
|
|
13813
|
+
content: [
|
|
13814
|
+
{
|
|
13815
|
+
type: "text",
|
|
13816
|
+
text: JSON.stringify(
|
|
13817
|
+
{
|
|
13818
|
+
stackNumber: result.stack.stackNumber,
|
|
13819
|
+
trunk: result.stack.trunk,
|
|
13820
|
+
threads: result.threads.map((t) => ({
|
|
13821
|
+
id: t.id,
|
|
13822
|
+
title: t.title,
|
|
13823
|
+
branchName: t.branchName,
|
|
13824
|
+
stackLayer: t.stackLayer,
|
|
13825
|
+
worktreePath: t.worktreePath,
|
|
13826
|
+
link: `sideboard://thread/${t.id}`
|
|
13827
|
+
}))
|
|
13828
|
+
},
|
|
13829
|
+
null,
|
|
13830
|
+
2
|
|
13831
|
+
)
|
|
13832
|
+
}
|
|
13833
|
+
]
|
|
13834
|
+
};
|
|
13835
|
+
}
|
|
13836
|
+
);
|
|
12642
13837
|
server.tool(
|
|
12643
13838
|
"list_issues",
|
|
12644
13839
|
"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 +14203,7 @@ init_connected_teams();
|
|
|
13008
14203
|
init_injected_mcp();
|
|
13009
14204
|
// Annotate the CommonJS export names for ESM import in node:
|
|
13010
14205
|
0 && (module.exports = {
|
|
14206
|
+
ATTACHMENTS_DIR,
|
|
13011
14207
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
13012
14208
|
BrightsySideboardApi,
|
|
13013
14209
|
CLAUDE_MODEL_CATALOG,
|
|
@@ -13023,11 +14219,16 @@ init_injected_mcp();
|
|
|
13023
14219
|
FAMOUS_SOCCER_TEAMS,
|
|
13024
14220
|
GLOBAL_WORKSPACE_ID,
|
|
13025
14221
|
HARNESS_ENV_KEYS,
|
|
14222
|
+
LEGACY_ATTACHMENTS_DIR,
|
|
14223
|
+
LEGACY_PLAN_FILE_REL,
|
|
14224
|
+
LEGACY_REVIEW_REQUEST_PATH,
|
|
13026
14225
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
13027
14226
|
ORCHESTRATOR_AGENT_KINDS,
|
|
13028
14227
|
Orchestrator,
|
|
13029
14228
|
PASTE_ATTACH_MIN_CHARS,
|
|
13030
14229
|
PASTE_ATTACH_MIN_LINES,
|
|
14230
|
+
PLAN_FILE_NAME,
|
|
14231
|
+
PLAN_FILE_REL,
|
|
13031
14232
|
PLAN_MODE_INSTRUCTION,
|
|
13032
14233
|
REPO_REVIEW_NAME,
|
|
13033
14234
|
REPO_REVIEW_PATH,
|
|
@@ -13037,6 +14238,8 @@ init_injected_mcp();
|
|
|
13037
14238
|
SIDEBOARD_FORCE_STOP,
|
|
13038
14239
|
SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
13039
14240
|
THINKING_EFFORTS,
|
|
14241
|
+
addPrStackLayer,
|
|
14242
|
+
addStackLayerFromThread,
|
|
13040
14243
|
addWorkspace,
|
|
13041
14244
|
adoptThread,
|
|
13042
14245
|
allAdapters,
|
|
@@ -13055,6 +14258,7 @@ init_injected_mcp();
|
|
|
13055
14258
|
attachmentFromAbsolutePath,
|
|
13056
14259
|
attachmentsFromBuffers,
|
|
13057
14260
|
attachmentsFromWorktreePaths,
|
|
14261
|
+
attachmentsGitignoreBody,
|
|
13058
14262
|
autoCleanupOrphansEnabled,
|
|
13059
14263
|
autoRenameBranchEnabled,
|
|
13060
14264
|
autoRunAfterSetupEnabled,
|
|
@@ -13077,6 +14281,7 @@ init_injected_mcp();
|
|
|
13077
14281
|
caffeinateWhileRunningEnabled,
|
|
13078
14282
|
captureLoginEnv,
|
|
13079
14283
|
captureTurnBaseline,
|
|
14284
|
+
checkoutPrStackLayer,
|
|
13080
14285
|
childEnvWithAppSettings,
|
|
13081
14286
|
claudeAdapter,
|
|
13082
14287
|
claudeChromeEnabled,
|
|
@@ -13096,8 +14301,10 @@ init_injected_mcp();
|
|
|
13096
14301
|
countCacheControlBlocks,
|
|
13097
14302
|
createChatTab,
|
|
13098
14303
|
createEmptyThread,
|
|
14304
|
+
createExistingBranchWorktree,
|
|
13099
14305
|
createGlobalChat,
|
|
13100
14306
|
createOrUpdatePr,
|
|
14307
|
+
createPrStack,
|
|
13101
14308
|
createThread,
|
|
13102
14309
|
createThreadWorktree,
|
|
13103
14310
|
currentBranch,
|
|
@@ -13107,6 +14314,7 @@ init_injected_mcp();
|
|
|
13107
14314
|
deleteBranchOnPurgeEnabled,
|
|
13108
14315
|
deleteThreadRecord,
|
|
13109
14316
|
detectAgents,
|
|
14317
|
+
detectGhStack,
|
|
13110
14318
|
detectLocalMergeConflicts,
|
|
13111
14319
|
disconnectBrightsyTeam,
|
|
13112
14320
|
discoverSkills,
|
|
@@ -13122,12 +14330,15 @@ init_injected_mcp();
|
|
|
13122
14330
|
estimateThreadChars,
|
|
13123
14331
|
expandComposerPrompt,
|
|
13124
14332
|
extractGhErrorDetail,
|
|
14333
|
+
extractPendingPlanQuestions,
|
|
14334
|
+
extractPresentedPlan,
|
|
13125
14335
|
extractiveSummary,
|
|
13126
14336
|
fetchPrHead,
|
|
13127
14337
|
finalizeParts,
|
|
13128
14338
|
findInvalidCacheControlTtlOrder,
|
|
13129
14339
|
findOrphanWorktrees,
|
|
13130
14340
|
findThreadByRef,
|
|
14341
|
+
findThreadForStackLayer,
|
|
13131
14342
|
flattenTurnInput,
|
|
13132
14343
|
forkChatTab,
|
|
13133
14344
|
forkMessageSlice,
|
|
@@ -13138,6 +14349,8 @@ init_injected_mcp();
|
|
|
13138
14349
|
formatGhLandError,
|
|
13139
14350
|
formatIpcInvokeError,
|
|
13140
14351
|
formatMessagesAsTranscript,
|
|
14352
|
+
formatPlanQuestionAnswers,
|
|
14353
|
+
formatPlanQuestionsForChat,
|
|
13141
14354
|
formatRateLimitResetHint,
|
|
13142
14355
|
formatRenameBranchDirective,
|
|
13143
14356
|
formatTranscriptMarkdown,
|
|
@@ -13161,6 +14374,7 @@ init_injected_mcp();
|
|
|
13161
14374
|
getPrChecks,
|
|
13162
14375
|
getPrDetails,
|
|
13163
14376
|
getPrMeta,
|
|
14377
|
+
getPrStack,
|
|
13164
14378
|
getRepoSetupInfo,
|
|
13165
14379
|
getRunMode,
|
|
13166
14380
|
getRunScript,
|
|
@@ -13177,9 +14391,12 @@ init_injected_mcp();
|
|
|
13177
14391
|
healOrchestrationSoccerTitles,
|
|
13178
14392
|
importConductorWorkspace,
|
|
13179
14393
|
importConductorWorkspaceAsync,
|
|
14394
|
+
initPrStack,
|
|
14395
|
+
initStackFromThread,
|
|
13180
14396
|
initializeGitRepository,
|
|
13181
14397
|
inspectGitWorktree,
|
|
13182
14398
|
installAgent,
|
|
14399
|
+
isAskUserToolName,
|
|
13183
14400
|
isBrightsyConnected,
|
|
13184
14401
|
isBrightsyNdjsonLine,
|
|
13185
14402
|
isCloudCoordinatorThread,
|
|
@@ -13189,14 +14406,17 @@ init_injected_mcp();
|
|
|
13189
14406
|
isGlobalRepoPath,
|
|
13190
14407
|
isGlobalThread,
|
|
13191
14408
|
isImageFilePath,
|
|
14409
|
+
isInPrStack,
|
|
13192
14410
|
isLinearConnected,
|
|
13193
14411
|
isOrchestratorCapableAgent,
|
|
13194
14412
|
isOrchestratorThread,
|
|
13195
14413
|
isPidAlive,
|
|
13196
14414
|
isPlaceholderBranch,
|
|
14415
|
+
isPresentPlanToolName,
|
|
13197
14416
|
isSessionQuotaLimit,
|
|
13198
14417
|
isSideboardScratchPath,
|
|
13199
14418
|
isThinkingEffort,
|
|
14419
|
+
isWorkspaceScratchPath,
|
|
13200
14420
|
listAgentSetupInfo,
|
|
13201
14421
|
listBranchCommits,
|
|
13202
14422
|
listBranches,
|
|
@@ -13233,6 +14453,7 @@ init_injected_mcp();
|
|
|
13233
14453
|
mcpAllowTools,
|
|
13234
14454
|
mcpAuthWarnings,
|
|
13235
14455
|
mergePr,
|
|
14456
|
+
mergePrStack,
|
|
13236
14457
|
mergeUsage,
|
|
13237
14458
|
nextPastedTextName,
|
|
13238
14459
|
nextThinkingEffort,
|
|
@@ -13242,6 +14463,8 @@ init_injected_mcp();
|
|
|
13242
14463
|
normalizeTurnInput,
|
|
13243
14464
|
normalizeWorktreePath,
|
|
13244
14465
|
openInSystemTerminal,
|
|
14466
|
+
openPrStackLayers,
|
|
14467
|
+
openStackLayer,
|
|
13245
14468
|
opencodeAdapter,
|
|
13246
14469
|
orchestrationQuotaFallbackAgent,
|
|
13247
14470
|
orchestrationQuotaOnLimit,
|
|
@@ -13250,15 +14473,19 @@ init_injected_mcp();
|
|
|
13250
14473
|
originGhRepoEnv,
|
|
13251
14474
|
parseCursorRunnerLine,
|
|
13252
14475
|
parseForceStopMessage,
|
|
14476
|
+
parseGhStackViewJson,
|
|
13253
14477
|
parseGithubSlugFromRemoteUrl,
|
|
13254
14478
|
parseMcpList,
|
|
14479
|
+
parsePlanQuestionsInput,
|
|
13255
14480
|
parseSessionQuotaResetAt,
|
|
13256
14481
|
partsToAssistantText,
|
|
13257
14482
|
pastedTextStats,
|
|
13258
14483
|
permissionMode,
|
|
14484
|
+
planFileAbs,
|
|
13259
14485
|
previewLand,
|
|
13260
14486
|
pushBranch,
|
|
13261
14487
|
readExistingReviewRequestFile,
|
|
14488
|
+
readPlanFile,
|
|
13262
14489
|
readSkillBody,
|
|
13263
14490
|
readThread,
|
|
13264
14491
|
readWorktreeFile,
|
|
@@ -13270,6 +14497,7 @@ init_injected_mcp();
|
|
|
13270
14497
|
repoSlug,
|
|
13271
14498
|
requestReview,
|
|
13272
14499
|
requireAgent,
|
|
14500
|
+
resetGhStackDetectCache,
|
|
13273
14501
|
resolveClaudeExecutable,
|
|
13274
14502
|
resolveConductorCursorAgentId,
|
|
13275
14503
|
resolveCursorModelId,
|
|
@@ -13279,6 +14507,7 @@ init_injected_mcp();
|
|
|
13279
14507
|
resolveFilesToCopy,
|
|
13280
14508
|
resolveGhAuthToken,
|
|
13281
14509
|
resolveGithubRepoSlug,
|
|
14510
|
+
resolvePlanMarkdown,
|
|
13282
14511
|
resolvePrSelector,
|
|
13283
14512
|
resolveQuotaFallbackAgent,
|
|
13284
14513
|
resolveRepoRoot,
|
|
@@ -13306,12 +14535,16 @@ init_injected_mcp();
|
|
|
13306
14535
|
slugify,
|
|
13307
14536
|
spawnAgentTurn,
|
|
13308
14537
|
splitForCompaction,
|
|
14538
|
+
stackAgentDefaultsFrom,
|
|
14539
|
+
stackIdFrom,
|
|
14540
|
+
stackMergeReadiness,
|
|
13309
14541
|
stageAbsolutePathsAsAttachments,
|
|
13310
14542
|
stageBuffersAsAttachments,
|
|
13311
14543
|
startDevServer,
|
|
13312
14544
|
startMcpServer,
|
|
13313
14545
|
startOrchestration,
|
|
13314
14546
|
stripBrightsyNdjsonNoise,
|
|
14547
|
+
submitPrStack,
|
|
13315
14548
|
suggestSlug,
|
|
13316
14549
|
summarizeConversation,
|
|
13317
14550
|
switchBrightsyAccount,
|
|
@@ -13347,6 +14580,7 @@ init_injected_mcp();
|
|
|
13347
14580
|
worktreeNameFromPath,
|
|
13348
14581
|
worktreesRoot,
|
|
13349
14582
|
writeInjectedMcpConfig,
|
|
14583
|
+
writePlanFile,
|
|
13350
14584
|
writeThread,
|
|
13351
14585
|
writeWorktreeFile
|
|
13352
14586
|
});
|