@sideboard-ai/core 0.1.99 → 0.1.103
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 +22 -22
- package/dist/agents/cursor-runner.js +2 -2
- package/dist/{agents-ESJKIQQA.js → agents-UT5GCJH3.js} +8 -8
- package/dist/{agents-3MWWWSMF.js → agents-XZBAKBO2.js} +9 -9
- package/dist/{app-settings-BRF7VKTQ.js → app-settings-EKJM2344.js} +2 -2
- package/dist/{app-settings-ZXPAB3QN.js → app-settings-VDJK2BZ2.js} +2 -2
- package/dist/{caffeinate-hold-U3QJBV67.js → caffeinate-hold-NBRUWYXO.js} +2 -2
- package/dist/{caffeinate-hold-I7SG6UQR.js → caffeinate-hold-X3JNPR42.js} +2 -2
- package/dist/{chunk-UYQYK2RY.js → chunk-27MCESES.js} +3 -3
- package/dist/{chunk-QSLE4VEM.js → chunk-2GO3YKBA.js} +4 -4
- package/dist/{chunk-DKHGWYWR.js → chunk-7YKXHBIW.js} +1 -1
- package/dist/{chunk-JL4SJ6V7.js → chunk-BD2ICC4D.js} +4 -0
- package/dist/{chunk-NZPGMNFC.js → chunk-DCOZABNT.js} +1 -1
- package/dist/{chunk-CIRXAYWS.js → chunk-DHAGMJIK.js} +55 -28
- package/dist/{chunk-XUWDLRAE.js → chunk-ETMRBJKA.js} +11 -11
- package/dist/{chunk-MBP3XG57.js → chunk-H6LP57EF.js} +7 -7
- package/dist/{chunk-FO67IJTY.js → chunk-HHLZWO7U.js} +5 -5
- package/dist/{chunk-GXSYI7FH.js → chunk-HUKCGRAT.js} +209 -59
- package/dist/{chunk-3WA37BRI.js → chunk-KGSVLZV6.js} +11 -4
- package/dist/{chunk-7KDBDMNC.js → chunk-KLBOWJ74.js} +1 -1
- package/dist/{chunk-FYS2BULQ.js → chunk-LANDJIAI.js} +213 -63
- package/dist/{chunk-OANJQTVG.js → chunk-ND6AO7C5.js} +3 -3
- package/dist/{chunk-HI2OTFFR.js → chunk-OW34MVDN.js} +5 -5
- package/dist/{chunk-JOF3XIEM.js → chunk-P3BQ5DOL.js} +11 -4
- package/dist/{chunk-B4R2VB2A.js → chunk-QZQEXME2.js} +4 -0
- package/dist/{chunk-R7BQBSDT.js → chunk-WA3DVVWG.js} +55 -28
- package/dist/{chunk-6LFV4VFI.js → chunk-WSYCND2U.js} +1 -1
- package/dist/{chunk-FT2SQOL4.js → chunk-XHZJJWLI.js} +1 -1
- package/dist/{chunk-XH2GS2LO.js → chunk-YJADJH5U.js} +11 -11
- package/dist/{connected-teams-SWHJWK2A.js → connected-teams-J4I5AKMR.js} +2 -2
- package/dist/{connected-teams-TLVKZPJP.js → connected-teams-M5XNXRI5.js} +2 -2
- package/dist/{coordinator-prompt-OQOOD5ET.js → coordinator-prompt-3DFZRMXE.js} +5 -5
- package/dist/{coordinator-prompt-AKEY4WSO.js → coordinator-prompt-UWHSD65V.js} +5 -5
- package/dist/{cursor-recover-S5ORPW34.js → cursor-recover-JBTIDARH.js} +1 -1
- package/dist/{cursor-recover-G34BYKL2.js → cursor-recover-ZUJG6XRQ.js} +1 -1
- package/dist/{global-workspace-WMF3BJP5.js → global-workspace-KJQS5S2C.js} +6 -6
- package/dist/{global-workspace-RSQXRLT7.js → global-workspace-UR4E3CCG.js} +6 -6
- package/dist/index.cjs +851 -232
- package/dist/index.d.cts +39 -6
- package/dist/index.d.ts +39 -6
- package/dist/index.js +561 -138
- package/dist/mcp/run-stdio.cjs +651 -256
- package/dist/mcp/run-stdio.js +345 -143
- package/dist/{paths-TZX4L5X7.js → paths-QCI3MTO5.js} +3 -1
- package/dist/{paths-JQM2XRAD.js → paths-UMEDHOS5.js} +3 -1
- package/dist/{thread-store-XAKWCV2E.js → thread-store-4OUEQ2DB.js} +2 -2
- package/dist/{thread-store-V7NOOXKE.js → thread-store-DQJGDKIJ.js} +2 -2
- package/dist/{workspaces-MZVQHRSJ.js → workspaces-NTIVI6DI.js} +7 -7
- package/dist/{workspaces-4ZY4QPWQ.js → workspaces-TKJXPNV4.js} +7 -7
- package/dist/{worktree-EO5QAGJU.js → worktree-NKWN5ZTS.js} +4 -4
- package/dist/{worktree-DA4BOV7G.js → worktree-PEUPFAKL.js} +4 -4
- package/package.json +2 -2
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -416,6 +416,7 @@ __export(paths_exports, {
|
|
|
416
416
|
sideboardReposDir: () => sideboardReposDir,
|
|
417
417
|
sideboardWorkspacesDir: () => sideboardWorkspacesDir,
|
|
418
418
|
threadFilePath: () => threadFilePath,
|
|
419
|
+
threadLivePath: () => threadLivePath,
|
|
419
420
|
threadLockPath: () => threadLockPath,
|
|
420
421
|
threadsDir: () => threadsDir,
|
|
421
422
|
worktreesRoot: () => worktreesRoot
|
|
@@ -467,6 +468,9 @@ function worktreesRoot(repoPath) {
|
|
|
467
468
|
function threadFilePath(id) {
|
|
468
469
|
return (0, import_node_path5.join)(threadsDir(), `${id}.json`);
|
|
469
470
|
}
|
|
471
|
+
function threadLivePath(id) {
|
|
472
|
+
return (0, import_node_path5.join)(threadsDir(), `${id}.live.json`);
|
|
473
|
+
}
|
|
470
474
|
function threadLockPath(id) {
|
|
471
475
|
return (0, import_node_path5.join)(locksDir(), `${id}.lock`);
|
|
472
476
|
}
|
|
@@ -759,8 +763,11 @@ function writeThread(thread) {
|
|
|
759
763
|
function idPath(id) {
|
|
760
764
|
return id;
|
|
761
765
|
}
|
|
766
|
+
function isThreadRecordFile(name) {
|
|
767
|
+
return name.endsWith(".json") && !name.endsWith(".live.json");
|
|
768
|
+
}
|
|
762
769
|
function listThreads(opts) {
|
|
763
|
-
const files = (0, import_node_fs7.readdirSync)(threadsDir()).filter(
|
|
770
|
+
const files = (0, import_node_fs7.readdirSync)(threadsDir()).filter(isThreadRecordFile);
|
|
764
771
|
const threads = files.map((f) => {
|
|
765
772
|
try {
|
|
766
773
|
return normalizeThread(
|
|
@@ -769,7 +776,9 @@ function listThreads(opts) {
|
|
|
769
776
|
} catch {
|
|
770
777
|
return null;
|
|
771
778
|
}
|
|
772
|
-
}).filter(
|
|
779
|
+
}).filter(
|
|
780
|
+
(t) => t !== null && typeof t.id === "string" && t.id.length > 0
|
|
781
|
+
).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
773
782
|
if (opts?.includeArchived) return threads;
|
|
774
783
|
return threads.filter((t) => t.status !== "archived");
|
|
775
784
|
}
|
|
@@ -801,7 +810,9 @@ function setStatus(id, status, lastError) {
|
|
|
801
810
|
}
|
|
802
811
|
function findThreadByRef(ref) {
|
|
803
812
|
const all = listThreads({ includeArchived: true });
|
|
804
|
-
return all.find(
|
|
813
|
+
return all.find(
|
|
814
|
+
(t) => t.id === ref || t.id.startsWith(ref) || t.branchName === ref || t.title === ref
|
|
815
|
+
) ?? null;
|
|
805
816
|
}
|
|
806
817
|
var import_node_crypto2, import_node_fs7, import_proper_lockfile;
|
|
807
818
|
var init_thread_store = __esm({
|
|
@@ -883,6 +894,13 @@ function looksLikeMinifiedJsDump(line) {
|
|
|
883
894
|
function looksLikeNestedElectronCrash(line) {
|
|
884
895
|
return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
|
|
885
896
|
}
|
|
897
|
+
function looksLikeHomebrewLibuvCrash(line) {
|
|
898
|
+
const uvRun = /uv_run/i.test(line);
|
|
899
|
+
const spin = /SpinEventLoopInternal/i.test(line);
|
|
900
|
+
const homebrewUv = /Cellar\/libuv|libuv\.\d\.dylib/i.test(line);
|
|
901
|
+
const homebrewNode = /Cellar\/node\//i.test(line);
|
|
902
|
+
return uvRun && (homebrewUv || spin || homebrewNode) || spin && (homebrewUv || homebrewNode);
|
|
903
|
+
}
|
|
886
904
|
function clipStderr(text3, maxChars) {
|
|
887
905
|
const trimmed = text3.trim();
|
|
888
906
|
if (trimmed.length <= maxChars) return trimmed;
|
|
@@ -893,6 +911,7 @@ function summarizeTurnStderr(tail, maxChars = 500) {
|
|
|
893
911
|
const cursorStartup = [...tail].reverse().find((line) => /cursor startup failed:/i.test(line));
|
|
894
912
|
if (cursorStartup) return clipStderr(cursorStartup, maxChars);
|
|
895
913
|
if (tail.some(looksLikeNestedElectronCrash)) return NESTED_ELECTRON_SUMMARY;
|
|
914
|
+
if (tail.some(looksLikeHomebrewLibuvCrash)) return HOMEBREW_LIBUV_SUMMARY;
|
|
896
915
|
if (tail.some(
|
|
897
916
|
(line) => /\[resource_exhausted\]|resource_exhausted/i.test(line) || /findFilesWithRipgrep/.test(line)
|
|
898
917
|
)) {
|
|
@@ -915,6 +934,20 @@ function looksLikeInvalidAgentSession(text3) {
|
|
|
915
934
|
if (!lower) return false;
|
|
916
935
|
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) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower);
|
|
917
936
|
}
|
|
937
|
+
function looksLikeRetryableRunnerCrash(text3) {
|
|
938
|
+
if (looksLikeAgentFailureMessage(text3)) return false;
|
|
939
|
+
if (looksLikeInvalidAgentSession(text3)) return false;
|
|
940
|
+
const lower = text3.trim().toLowerCase();
|
|
941
|
+
if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
|
|
942
|
+
if (!lower) return true;
|
|
943
|
+
return /uv_run|spineventloopinternal|libuv|homebrew node \+ shared libuv|hascustomhostobject|electroninitializeicuandstartnode|nested chromium|truncated crash dump|sig(?:segv|abrt|ill)|segmentation fault|illegal instruction|fatal error/.test(
|
|
944
|
+
lower
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
function shouldRetryFailedAgentTurn(detail, opts) {
|
|
948
|
+
if (looksLikeInvalidAgentSession(detail) && opts.hasSession) return true;
|
|
949
|
+
return looksLikeRetryableRunnerCrash(detail);
|
|
950
|
+
}
|
|
918
951
|
function looksLikeAgentFailureMessage(text3) {
|
|
919
952
|
const lower = text3.trim().toLowerCase();
|
|
920
953
|
if (!lower) return false;
|
|
@@ -959,11 +992,22 @@ function humanizeAgentFailDetail(detail) {
|
|
|
959
992
|
if (/hascustomhostobject|electroninitializeicuandstartnode|nested chromium/i.test(lower)) {
|
|
960
993
|
return `${raw} \u2014 retry the turn; if it keeps failing, pick another agent.`;
|
|
961
994
|
}
|
|
995
|
+
if (/homebrew node \+ shared libuv|uv_run|spineventloopinternal/i.test(lower)) {
|
|
996
|
+
return /brew install node@22/i.test(raw) ? raw : `${raw} \u2014 install Node 22 LTS (\`brew install node@22\`) and retry.`;
|
|
997
|
+
}
|
|
962
998
|
if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
|
|
963
999
|
return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
|
|
964
1000
|
}
|
|
965
1001
|
return raw;
|
|
966
1002
|
}
|
|
1003
|
+
function turnFailChatText(opts) {
|
|
1004
|
+
const chat = opts.assistantText.trim();
|
|
1005
|
+
if (chat) return chat;
|
|
1006
|
+
if (opts.exitCode === 0) return "";
|
|
1007
|
+
const detail = opts.detail.trim();
|
|
1008
|
+
if (detail) return humanizeAgentFailDetail(detail);
|
|
1009
|
+
return formatTurnExitError(opts.exitCode ?? 1, "");
|
|
1010
|
+
}
|
|
967
1011
|
function formatTurnExitError(exitCode, stderrSummary) {
|
|
968
1012
|
const code = exitCode ?? 1;
|
|
969
1013
|
const raw = stderrSummary.trim();
|
|
@@ -977,12 +1021,13 @@ function formatTurnExitError(exitCode, stderrSummary) {
|
|
|
977
1021
|
if (looksLikeAgentFailureMessage(raw)) return detail;
|
|
978
1022
|
return `exit ${code}: ${detail}`;
|
|
979
1023
|
}
|
|
980
|
-
var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, MINIFIED_DUMP_SUMMARY;
|
|
1024
|
+
var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, HOMEBREW_LIBUV_SUMMARY, MINIFIED_DUMP_SUMMARY;
|
|
981
1025
|
var init_error_detail = __esm({
|
|
982
1026
|
"src/agents/error-detail.ts"() {
|
|
983
1027
|
"use strict";
|
|
984
1028
|
NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
|
|
985
1029
|
NESTED_ELECTRON_SUMMARY = "Cursor local agent crashed at Electron startup (nested Chromium / HasCustomHostObject)";
|
|
1030
|
+
HOMEBREW_LIBUV_SUMMARY = "Cursor runner crashed in Node (Homebrew Node + shared libuv). Install Node 22 LTS (`brew install node@22`) and retry.";
|
|
986
1031
|
MINIFIED_DUMP_SUMMARY = "Cursor local agent crashed during startup (truncated crash dump)";
|
|
987
1032
|
}
|
|
988
1033
|
});
|
|
@@ -1764,7 +1809,7 @@ var init_teams = __esm({
|
|
|
1764
1809
|
|
|
1765
1810
|
// src/git/worktree-labels.ts
|
|
1766
1811
|
function normalizeWorktreePath(worktreePath) {
|
|
1767
|
-
const trimmed = worktreePath.replace(/\/+$/, "") || "/";
|
|
1812
|
+
const trimmed = (worktreePath ?? "").replace(/\/+$/, "") || "/";
|
|
1768
1813
|
const absolute = trimmed.startsWith("/");
|
|
1769
1814
|
const parts = trimmed.split("/").filter((p) => p && p !== ".");
|
|
1770
1815
|
const out = [];
|
|
@@ -3355,6 +3400,42 @@ var init_git_auth_mode = __esm({
|
|
|
3355
3400
|
}
|
|
3356
3401
|
});
|
|
3357
3402
|
|
|
3403
|
+
// src/util/enqueue-by-key.ts
|
|
3404
|
+
function enqueueByKey(key, fn) {
|
|
3405
|
+
const prev = tails.get(key) ?? Promise.resolve();
|
|
3406
|
+
const next = prev.then(fn, fn);
|
|
3407
|
+
tails.set(
|
|
3408
|
+
key,
|
|
3409
|
+
next.then(
|
|
3410
|
+
() => void 0,
|
|
3411
|
+
() => void 0
|
|
3412
|
+
)
|
|
3413
|
+
);
|
|
3414
|
+
return next;
|
|
3415
|
+
}
|
|
3416
|
+
var tails;
|
|
3417
|
+
var init_enqueue_by_key = __esm({
|
|
3418
|
+
"src/util/enqueue-by-key.ts"() {
|
|
3419
|
+
"use strict";
|
|
3420
|
+
tails = /* @__PURE__ */ new Map();
|
|
3421
|
+
}
|
|
3422
|
+
});
|
|
3423
|
+
|
|
3424
|
+
// src/git/repo-git-lock.ts
|
|
3425
|
+
function repoLockKey(repoPath) {
|
|
3426
|
+
const trimmed = repoPath.replace(/\/+$/, "");
|
|
3427
|
+
return `git-worktree:${trimmed || "/"}`;
|
|
3428
|
+
}
|
|
3429
|
+
function withRepoGitLock(repoPath, fn) {
|
|
3430
|
+
return enqueueByKey(repoLockKey(repoPath), fn);
|
|
3431
|
+
}
|
|
3432
|
+
var init_repo_git_lock = __esm({
|
|
3433
|
+
"src/git/repo-git-lock.ts"() {
|
|
3434
|
+
"use strict";
|
|
3435
|
+
init_enqueue_by_key();
|
|
3436
|
+
}
|
|
3437
|
+
});
|
|
3438
|
+
|
|
3358
3439
|
// src/git/pr-gates.ts
|
|
3359
3440
|
function prIsInMergeQueue(gate) {
|
|
3360
3441
|
if (gate.isInMergeQueue) return true;
|
|
@@ -4508,12 +4589,13 @@ async function createThreadWorktree(opts) {
|
|
|
4508
4589
|
`Invalid git reference: ${opts.sourceRef} (and fetch did not resolve it)`
|
|
4509
4590
|
);
|
|
4510
4591
|
}
|
|
4511
|
-
const
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4592
|
+
const added = await withRepoGitLock(opts.repoPath, async () => {
|
|
4593
|
+
const add = await git(
|
|
4594
|
+
["worktree", "add", "-b", branchName, worktreePath, startPoint],
|
|
4595
|
+
opts.repoPath,
|
|
4596
|
+
{ reject: false, timeoutMs: 6e4 }
|
|
4597
|
+
);
|
|
4598
|
+
if (add.exitCode === 0) return branchName;
|
|
4517
4599
|
const err = `${add.stderr}
|
|
4518
4600
|
${add.stdout}`;
|
|
4519
4601
|
if (/already used by worktree|already exists|checked out/i.test(err)) {
|
|
@@ -4524,17 +4606,14 @@ ${add.stdout}`;
|
|
|
4524
4606
|
opts.repoPath,
|
|
4525
4607
|
{ reject: false, timeoutMs: 6e4 }
|
|
4526
4608
|
);
|
|
4527
|
-
if (retry.exitCode === 0)
|
|
4528
|
-
branchName = alt;
|
|
4529
|
-
await ensureGhPreferOrigin(worktreePath);
|
|
4530
|
-
return { branchName, worktreePath };
|
|
4531
|
-
}
|
|
4609
|
+
if (retry.exitCode === 0) return alt;
|
|
4532
4610
|
}
|
|
4533
4611
|
}
|
|
4534
4612
|
throw new Error(
|
|
4535
4613
|
`Failed to create worktree: ${add.stderr.trim() || add.stdout.trim() || `exit ${add.exitCode}`}`
|
|
4536
4614
|
);
|
|
4537
|
-
}
|
|
4615
|
+
});
|
|
4616
|
+
branchName = added;
|
|
4538
4617
|
await ensureGhPreferOrigin(worktreePath);
|
|
4539
4618
|
return { branchName, worktreePath };
|
|
4540
4619
|
}
|
|
@@ -4558,12 +4637,13 @@ async function createExistingBranchWorktree(opts) {
|
|
|
4558
4637
|
);
|
|
4559
4638
|
}
|
|
4560
4639
|
const startPoint = await resolveWorktreeStartPoint(opts.repoPath, branchName);
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4640
|
+
await withRepoGitLock(opts.repoPath, async () => {
|
|
4641
|
+
const add = await git(
|
|
4642
|
+
["worktree", "add", worktreePath, startPoint],
|
|
4643
|
+
opts.repoPath,
|
|
4644
|
+
{ reject: false }
|
|
4645
|
+
);
|
|
4646
|
+
if (add.exitCode === 0) return;
|
|
4567
4647
|
const retry = await git(
|
|
4568
4648
|
["worktree", "add", worktreePath, branchName],
|
|
4569
4649
|
opts.repoPath,
|
|
@@ -4574,7 +4654,7 @@ async function createExistingBranchWorktree(opts) {
|
|
|
4574
4654
|
`Failed to create worktree for ${branchName}: ${retry.stderr.trim() || add.stderr.trim() || retry.stdout.trim() || add.stdout.trim() || `exit ${add.exitCode}`}`
|
|
4575
4655
|
);
|
|
4576
4656
|
}
|
|
4577
|
-
}
|
|
4657
|
+
});
|
|
4578
4658
|
const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
|
|
4579
4659
|
reject: false
|
|
4580
4660
|
});
|
|
@@ -4585,12 +4665,14 @@ async function createExistingBranchWorktree(opts) {
|
|
|
4585
4665
|
return { branchName, worktreePath };
|
|
4586
4666
|
}
|
|
4587
4667
|
async function removeWorktree(repoPath, worktreePath, opts) {
|
|
4588
|
-
await
|
|
4589
|
-
|
|
4668
|
+
await withRepoGitLock(repoPath, async () => {
|
|
4669
|
+
await git(["worktree", "remove", "--force", worktreePath], repoPath, {
|
|
4670
|
+
reject: false
|
|
4671
|
+
});
|
|
4672
|
+
if (opts?.deleteBranch) {
|
|
4673
|
+
await git(["branch", "-D", opts.deleteBranch], repoPath, { reject: false });
|
|
4674
|
+
}
|
|
4590
4675
|
});
|
|
4591
|
-
if (opts?.deleteBranch) {
|
|
4592
|
-
await git(["branch", "-D", opts.deleteBranch], repoPath, { reject: false });
|
|
4593
|
-
}
|
|
4594
4676
|
}
|
|
4595
4677
|
async function listWorktrees(repoPath) {
|
|
4596
4678
|
const { stdout } = await git(
|
|
@@ -4882,6 +4964,7 @@ var init_worktree = __esm({
|
|
|
4882
4964
|
init_gh_errors();
|
|
4883
4965
|
init_git_auth_mode();
|
|
4884
4966
|
init_run();
|
|
4967
|
+
init_repo_git_lock();
|
|
4885
4968
|
init_app_settings();
|
|
4886
4969
|
init_pr_gates();
|
|
4887
4970
|
init_stack();
|
|
@@ -4984,7 +5067,7 @@ function coordinatorGreenfieldPlaybook(reposDir) {
|
|
|
4984
5067
|
"- Examples:",
|
|
4985
5068
|
` - Clone: \`git clone <url> ${reposDir}/<name>\``,
|
|
4986
5069
|
` - New GitHub repo: \`gh repo create <owner>/<name> --private --clone -- ${reposDir}/<name>\` (or mkdir + git init + gh repo create + remote add + push)`,
|
|
4987
|
-
"- Then: add_workspace with that absolute path \u2192 create_thread (repoPath + parentThreadId) \u2192 send_to_thread (build) \u2192 wait_for_turn \u2192 ask_git create-draft (or send_to_thread `gh pr create --draft -R <origin-owner/name>`).",
|
|
5070
|
+
"- Then: add_workspace with that absolute path \u2192 create_thread (repoPath + parentThreadId) \u2192 send_to_thread (build) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft (or send_to_thread `gh pr create --draft -R <origin-owner/name>`).",
|
|
4988
5071
|
"- Always target the child worktree's **origin** (`github:` slug from list_workspaces / `git remote get-url origin` in that worktree). Never open PRs against `upstream`.",
|
|
4989
5072
|
"- Do coding work in the child worktree thread, not by editing files in this home cwd."
|
|
4990
5073
|
].join("\n");
|
|
@@ -5007,8 +5090,8 @@ function coordinatorTurnReminder(opts) {
|
|
|
5007
5090
|
goal ? `- Goal / title: ${goal}` : null,
|
|
5008
5091
|
accountDefaultsPlaybookLine(),
|
|
5009
5092
|
`- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
|
|
5010
|
-
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn. Commit/push/draft PR with ask_git on the child, then wait_for_turn \u2014 never git/gh from this cwd. Call ask_git merge only if the user explicitly asked to merge.",
|
|
5011
|
-
"- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 ask_git create-draft.",
|
|
5093
|
+
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn. If stillRunning, the child is working (progress is tools/thinking) \u2014 wait_for_turn again; do not ping it. If status is error, lastError/text is the failure \u2014 adapt (switch agent, tell the user). Commit/push/draft PR with ask_git on the child, then wait_for_turn \u2014 never git/gh from this cwd. Call ask_git merge only if the user explicitly asked to merge.",
|
|
5094
|
+
"- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
5012
5095
|
"- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`.",
|
|
5013
5096
|
"- If they will wait on Slack or leave the Mac, call set_caffeinate enabled=true. When they say they are done / wrapping up / going to sleep, call set_caffeinate enabled=false. Closing this chat also turns it off."
|
|
5014
5097
|
].filter(Boolean).join("\n");
|
|
@@ -5061,8 +5144,8 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
5061
5144
|
orchId ? `Pass parentThreadId="${orchId}" (or omit it). Never invent another parentThreadId.` : "Pass `parentThreadId` for children (this chat's id from the turn reminder).",
|
|
5062
5145
|
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
5063
5146
|
"Never pass `agent=codex` when you yourself are Codex \u2014 nested Codex deadlocks on shared ~/.codex locks. Omit agent (Account default) or use cursor/claude.",
|
|
5064
|
-
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 ask_git create-draft \u2192 wait_for_turn. Merge only if the user explicitly asked (`ask_git` merge).",
|
|
5065
|
-
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 ask_git create-draft.",
|
|
5147
|
+
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft \u2192 wait_for_turn. Merge only if the user explicitly asked (`ask_git` merge).",
|
|
5148
|
+
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
5066
5149
|
"Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
|
|
5067
5150
|
].join("\n");
|
|
5068
5151
|
try {
|
|
@@ -5130,7 +5213,7 @@ var init_coordinator_prompt = __esm({
|
|
|
5130
5213
|
"- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
|
|
5131
5214
|
"- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
|
|
5132
5215
|
"- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
|
|
5133
|
-
"- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply",
|
|
5216
|
+
"- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply. wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success.",
|
|
5134
5217
|
"- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
|
|
5135
5218
|
"- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
|
|
5136
5219
|
"Setup / run:",
|
|
@@ -5138,8 +5221,8 @@ var init_coordinator_prompt = __esm({
|
|
|
5138
5221
|
"- list_run_scripts / run_dev_script / stop_dev_script \u2014 start/stop named run scripts",
|
|
5139
5222
|
"Inspect / review / PRs:",
|
|
5140
5223
|
"- get_diff \u2014 compact diff summary",
|
|
5141
|
-
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review changes in this workspace."); then wait_for_turn / get_turn_result on the returned id',
|
|
5142
|
-
"- ask_git \u2014 commit & push, open a draft PR, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it queues the worktree agent \u2014 then wait_for_turn. Prefer this over paraphrasing.",
|
|
5224
|
+
'- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id',
|
|
5225
|
+
"- ask_git \u2014 commit & push, open a draft PR, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it queues the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing.",
|
|
5143
5226
|
'- Merge (`ask_git` action=merge / send_to_thread "Merge PR.") only when the user explicitly asked to merge that PR. Do not merge because the work looks done, CI is green, or a typical flow includes it.',
|
|
5144
5227
|
'- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Merge the remote branch (main) into your branch and resolve conflicts. Then, commit and push your changes.", "Merge PR." (draft PRs: `gh pr create --draft -R <origin-owner/name>` using the workspace `github:` slug \u2014 never upstream). Never run git/gh from this orchestration cwd, and never merge the PR yourself.',
|
|
5145
5228
|
"Process guides:",
|
|
@@ -6046,6 +6129,55 @@ var init_profile = __esm({
|
|
|
6046
6129
|
}
|
|
6047
6130
|
});
|
|
6048
6131
|
|
|
6132
|
+
// src/agents/packaged-runtime.ts
|
|
6133
|
+
function electronResourcesPath() {
|
|
6134
|
+
const resources = process.resourcesPath;
|
|
6135
|
+
if (typeof resources !== "string" || !resources) return null;
|
|
6136
|
+
return resources;
|
|
6137
|
+
}
|
|
6138
|
+
function packagedCursorRuntimeDir() {
|
|
6139
|
+
const resources = electronResourcesPath();
|
|
6140
|
+
if (!resources) return null;
|
|
6141
|
+
const dir = (0, import_node_path19.join)(resources, "cursor-runtime");
|
|
6142
|
+
if (!(0, import_node_fs18.existsSync)((0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
|
|
6143
|
+
return dir;
|
|
6144
|
+
}
|
|
6145
|
+
function packagedCursorRunnerPath() {
|
|
6146
|
+
const dir = packagedCursorRuntimeDir();
|
|
6147
|
+
return dir ? (0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
|
|
6148
|
+
}
|
|
6149
|
+
function packagedMcpDir() {
|
|
6150
|
+
const resources = electronResourcesPath();
|
|
6151
|
+
if (!resources) return null;
|
|
6152
|
+
const dir = (0, import_node_path19.join)(resources, "sideboard-mcp");
|
|
6153
|
+
if (!(0, import_node_fs18.existsSync)((0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
|
|
6154
|
+
return dir;
|
|
6155
|
+
}
|
|
6156
|
+
function packagedMcpStdioPath() {
|
|
6157
|
+
const dir = packagedMcpDir();
|
|
6158
|
+
return dir ? (0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
|
|
6159
|
+
}
|
|
6160
|
+
function packagedBundledNodePath() {
|
|
6161
|
+
const resources = electronResourcesPath();
|
|
6162
|
+
if (!resources) return null;
|
|
6163
|
+
const bin = (0, import_node_path19.join)(resources, "node", "bin", "node");
|
|
6164
|
+
if (!(0, import_node_fs18.existsSync)(bin)) return null;
|
|
6165
|
+
return bin;
|
|
6166
|
+
}
|
|
6167
|
+
function packagedCursorRipgrepCandidate(platformPkg, binName) {
|
|
6168
|
+
const dir = packagedCursorRuntimeDir();
|
|
6169
|
+
if (!dir) return null;
|
|
6170
|
+
return (0, import_node_path19.join)(dir, "node_modules", platformPkg, "bin", binName);
|
|
6171
|
+
}
|
|
6172
|
+
var import_node_fs18, import_node_path19;
|
|
6173
|
+
var init_packaged_runtime = __esm({
|
|
6174
|
+
"src/agents/packaged-runtime.ts"() {
|
|
6175
|
+
"use strict";
|
|
6176
|
+
import_node_fs18 = require("fs");
|
|
6177
|
+
import_node_path19 = require("path");
|
|
6178
|
+
}
|
|
6179
|
+
});
|
|
6180
|
+
|
|
6049
6181
|
// src/agents/node-launch.ts
|
|
6050
6182
|
function isAsarPath(filePath) {
|
|
6051
6183
|
if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
|
|
@@ -6055,24 +6187,129 @@ function unpackedAsarPath(filePath) {
|
|
|
6055
6187
|
if (!isAsarPath(filePath)) return null;
|
|
6056
6188
|
const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
|
|
6057
6189
|
if (unpacked === filePath) return null;
|
|
6058
|
-
return (0,
|
|
6190
|
+
return (0, import_node_fs19.existsSync)(unpacked) ? unpacked : null;
|
|
6059
6191
|
}
|
|
6060
6192
|
function nodeReadableScriptPath(scriptPath) {
|
|
6061
6193
|
return unpackedAsarPath(scriptPath) ?? scriptPath;
|
|
6062
6194
|
}
|
|
6063
|
-
|
|
6064
|
-
const
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6195
|
+
function parseNodeMajor(version) {
|
|
6196
|
+
const match = /^v?(\d+)/.exec(version.trim());
|
|
6197
|
+
if (!match) return null;
|
|
6198
|
+
const major = Number(match[1]);
|
|
6199
|
+
return Number.isInteger(major) ? major : null;
|
|
6200
|
+
}
|
|
6201
|
+
function scoreNodeForAgentRuntime(candidate) {
|
|
6202
|
+
const major = parseNodeMajor(candidate.version);
|
|
6203
|
+
if (major == null) return Number.NEGATIVE_INFINITY;
|
|
6204
|
+
if (major < 20) return major - 100;
|
|
6205
|
+
const posix = candidate.path.replace(/\\/g, "/");
|
|
6206
|
+
let score = 0;
|
|
6207
|
+
if (major % 2 === 0) {
|
|
6208
|
+
score += 1e3 + major * 10;
|
|
6209
|
+
} else {
|
|
6210
|
+
score += major;
|
|
6211
|
+
}
|
|
6212
|
+
if (/\/Cellar\/node\/\d/.test(posix) && !/\/Cellar\/node@\d+/.test(posix)) {
|
|
6213
|
+
score -= 50;
|
|
6214
|
+
}
|
|
6215
|
+
return score;
|
|
6216
|
+
}
|
|
6217
|
+
function pickPreferredNode(candidates) {
|
|
6218
|
+
let best = null;
|
|
6219
|
+
let bestScore = Number.NEGATIVE_INFINITY;
|
|
6220
|
+
for (const candidate of candidates) {
|
|
6221
|
+
const score = scoreNodeForAgentRuntime(candidate);
|
|
6222
|
+
if (score > bestScore) {
|
|
6223
|
+
bestScore = score;
|
|
6224
|
+
best = candidate;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
return best;
|
|
6228
|
+
}
|
|
6229
|
+
function versionDirNodeBins(root, toBin) {
|
|
6230
|
+
if (!(0, import_node_fs19.existsSync)(root)) return [];
|
|
6231
|
+
try {
|
|
6232
|
+
return (0, import_node_fs19.readdirSync)(root).map(toBin);
|
|
6233
|
+
} catch {
|
|
6234
|
+
return [];
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
function defaultNodeBinCandidates(home = (0, import_node_os7.homedir)()) {
|
|
6238
|
+
const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
|
|
6239
|
+
(prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path20.join)(prefix, "opt", `node@${major}`, "bin", "node"))
|
|
6240
|
+
);
|
|
6241
|
+
return [
|
|
6242
|
+
...kegs,
|
|
6243
|
+
"/opt/homebrew/bin/node",
|
|
6244
|
+
"/usr/local/bin/node",
|
|
6245
|
+
(0, import_node_path20.join)(home, ".local/share/fnm/aliases/default/bin/node"),
|
|
6246
|
+
(0, import_node_path20.join)(home, ".nvm/current/bin/node"),
|
|
6247
|
+
(0, import_node_path20.join)(home, ".volta/bin/node"),
|
|
6248
|
+
(0, import_node_path20.join)(home, ".asdf/shims/node"),
|
|
6249
|
+
(0, import_node_path20.join)(home, ".local/share/mise/shims/node"),
|
|
6250
|
+
...versionDirNodeBins(
|
|
6251
|
+
(0, import_node_path20.join)(home, ".nvm", "versions", "node"),
|
|
6252
|
+
(name) => (0, import_node_path20.join)(home, ".nvm", "versions", "node", name, "bin", "node")
|
|
6253
|
+
),
|
|
6254
|
+
...versionDirNodeBins(
|
|
6255
|
+
(0, import_node_path20.join)(home, ".local/share/fnm", "node-versions"),
|
|
6256
|
+
(name) => (0, import_node_path20.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
|
|
6257
|
+
),
|
|
6258
|
+
...versionDirNodeBins(
|
|
6259
|
+
(0, import_node_path20.join)(home, ".volta", "tools", "image", "node"),
|
|
6260
|
+
(name) => (0, import_node_path20.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
|
|
6261
|
+
)
|
|
6071
6262
|
];
|
|
6072
|
-
|
|
6073
|
-
|
|
6263
|
+
}
|
|
6264
|
+
function uniqueExistingNodeBins(paths) {
|
|
6265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6266
|
+
const out = [];
|
|
6267
|
+
for (const raw of paths) {
|
|
6268
|
+
const p = raw.trim();
|
|
6269
|
+
if (!p || !(0, import_node_fs19.existsSync)(p) || isElectronLikeCommand(p)) continue;
|
|
6270
|
+
let key = p;
|
|
6271
|
+
try {
|
|
6272
|
+
key = (0, import_node_fs19.realpathSync)(p);
|
|
6273
|
+
} catch {
|
|
6274
|
+
continue;
|
|
6275
|
+
}
|
|
6276
|
+
if (seen.has(key)) continue;
|
|
6277
|
+
seen.add(key);
|
|
6278
|
+
out.push(p);
|
|
6074
6279
|
}
|
|
6075
|
-
return
|
|
6280
|
+
return out;
|
|
6281
|
+
}
|
|
6282
|
+
async function whichAllNode() {
|
|
6283
|
+
if (process.platform === "win32") {
|
|
6284
|
+
const located = await run("where", ["node"], { reject: false, timeoutMs: 5e3 });
|
|
6285
|
+
if (located.exitCode !== 0) return [];
|
|
6286
|
+
return located.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6287
|
+
}
|
|
6288
|
+
const all = await run("which", ["-a", "node"], { reject: false, timeoutMs: 5e3 });
|
|
6289
|
+
if (all.exitCode === 0 && all.stdout.trim()) {
|
|
6290
|
+
return all.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6291
|
+
}
|
|
6292
|
+
const single = await run("which", ["node"], { reject: false, timeoutMs: 5e3 });
|
|
6293
|
+
if (single.exitCode !== 0 || !single.stdout.trim()) return [];
|
|
6294
|
+
return [single.stdout.trim()];
|
|
6295
|
+
}
|
|
6296
|
+
async function probeNodeVersion(bin) {
|
|
6297
|
+
const probed = await run(bin, ["-v"], { reject: false, timeoutMs: 4e3 });
|
|
6298
|
+
if (probed.exitCode !== 0) return null;
|
|
6299
|
+
const version = probed.stdout.trim().split(/\r?\n/).find(Boolean);
|
|
6300
|
+
return version || null;
|
|
6301
|
+
}
|
|
6302
|
+
async function findSystemNode() {
|
|
6303
|
+
const fromPath = await whichAllNode();
|
|
6304
|
+
const bins = uniqueExistingNodeBins([...fromPath, ...defaultNodeBinCandidates()]);
|
|
6305
|
+
if (bins.length === 0) return null;
|
|
6306
|
+
const probed = await Promise.all(
|
|
6307
|
+
bins.map(async (path) => {
|
|
6308
|
+
const version = await probeNodeVersion(path);
|
|
6309
|
+
return version ? { path, version } : null;
|
|
6310
|
+
})
|
|
6311
|
+
);
|
|
6312
|
+
return pickPreferredNode(probed.filter((c) => c != null));
|
|
6076
6313
|
}
|
|
6077
6314
|
function applyNodeLaunch(launch, args) {
|
|
6078
6315
|
const readableArgs = args.map(nodeReadableScriptPath);
|
|
@@ -6090,9 +6327,13 @@ function applyNodeLaunch(launch, args) {
|
|
|
6090
6327
|
async function resolveNodeLaunch(scriptPath) {
|
|
6091
6328
|
const script = nodeReadableScriptPath(scriptPath);
|
|
6092
6329
|
if (!isAsarPath(script)) {
|
|
6093
|
-
const
|
|
6094
|
-
if (
|
|
6095
|
-
return { file:
|
|
6330
|
+
const bundled = packagedBundledNodePath();
|
|
6331
|
+
if (bundled && !isElectronLikeCommand(bundled)) {
|
|
6332
|
+
return { file: bundled, env: {} };
|
|
6333
|
+
}
|
|
6334
|
+
const node = await findSystemNode();
|
|
6335
|
+
if (node) {
|
|
6336
|
+
return { file: node.path, env: {}, nodeVersion: node.version };
|
|
6096
6337
|
}
|
|
6097
6338
|
}
|
|
6098
6339
|
return {
|
|
@@ -6100,61 +6341,17 @@ async function resolveNodeLaunch(scriptPath) {
|
|
|
6100
6341
|
env: { ELECTRON_RUN_AS_NODE: "1" }
|
|
6101
6342
|
};
|
|
6102
6343
|
}
|
|
6103
|
-
var
|
|
6344
|
+
var import_node_fs19, import_node_os7, import_node_path20, PREFERRED_LTS_MAJORS;
|
|
6104
6345
|
var init_node_launch = __esm({
|
|
6105
6346
|
"src/agents/node-launch.ts"() {
|
|
6106
6347
|
"use strict";
|
|
6107
|
-
|
|
6348
|
+
import_node_fs19 = require("fs");
|
|
6108
6349
|
import_node_os7 = require("os");
|
|
6109
|
-
|
|
6350
|
+
import_node_path20 = require("path");
|
|
6110
6351
|
init_nested_electron_env();
|
|
6111
6352
|
init_run();
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
"/usr/local/bin/node"
|
|
6115
|
-
];
|
|
6116
|
-
}
|
|
6117
|
-
});
|
|
6118
|
-
|
|
6119
|
-
// src/agents/packaged-runtime.ts
|
|
6120
|
-
function electronResourcesPath() {
|
|
6121
|
-
const resources = process.resourcesPath;
|
|
6122
|
-
if (typeof resources !== "string" || !resources) return null;
|
|
6123
|
-
return resources;
|
|
6124
|
-
}
|
|
6125
|
-
function packagedCursorRuntimeDir() {
|
|
6126
|
-
const resources = electronResourcesPath();
|
|
6127
|
-
if (!resources) return null;
|
|
6128
|
-
const dir = (0, import_node_path20.join)(resources, "cursor-runtime");
|
|
6129
|
-
if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
|
|
6130
|
-
return dir;
|
|
6131
|
-
}
|
|
6132
|
-
function packagedCursorRunnerPath() {
|
|
6133
|
-
const dir = packagedCursorRuntimeDir();
|
|
6134
|
-
return dir ? (0, import_node_path20.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
|
|
6135
|
-
}
|
|
6136
|
-
function packagedMcpDir() {
|
|
6137
|
-
const resources = electronResourcesPath();
|
|
6138
|
-
if (!resources) return null;
|
|
6139
|
-
const dir = (0, import_node_path20.join)(resources, "sideboard-mcp");
|
|
6140
|
-
if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
|
|
6141
|
-
return dir;
|
|
6142
|
-
}
|
|
6143
|
-
function packagedMcpStdioPath() {
|
|
6144
|
-
const dir = packagedMcpDir();
|
|
6145
|
-
return dir ? (0, import_node_path20.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
|
|
6146
|
-
}
|
|
6147
|
-
function packagedCursorRipgrepCandidate(platformPkg, binName) {
|
|
6148
|
-
const dir = packagedCursorRuntimeDir();
|
|
6149
|
-
if (!dir) return null;
|
|
6150
|
-
return (0, import_node_path20.join)(dir, "node_modules", platformPkg, "bin", binName);
|
|
6151
|
-
}
|
|
6152
|
-
var import_node_fs19, import_node_path20;
|
|
6153
|
-
var init_packaged_runtime = __esm({
|
|
6154
|
-
"src/agents/packaged-runtime.ts"() {
|
|
6155
|
-
"use strict";
|
|
6156
|
-
import_node_fs19 = require("fs");
|
|
6157
|
-
import_node_path20 = require("path");
|
|
6353
|
+
init_packaged_runtime();
|
|
6354
|
+
PREFERRED_LTS_MAJORS = [24, 22, 20];
|
|
6158
6355
|
}
|
|
6159
6356
|
});
|
|
6160
6357
|
|
|
@@ -8645,17 +8842,17 @@ __export(plan_file_exports, {
|
|
|
8645
8842
|
});
|
|
8646
8843
|
function ensureAttachmentsGitignore2(worktreePath) {
|
|
8647
8844
|
const gitignoreAbs = (0, import_node_path36.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
8648
|
-
if ((0,
|
|
8649
|
-
(0,
|
|
8650
|
-
(0,
|
|
8845
|
+
if ((0, import_node_fs40.existsSync)(gitignoreAbs)) return;
|
|
8846
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path36.dirname)(gitignoreAbs), { recursive: true });
|
|
8847
|
+
(0, import_node_fs40.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
8651
8848
|
}
|
|
8652
8849
|
function planFileAbs(worktreePath) {
|
|
8653
8850
|
return (0, import_node_path36.join)(worktreePath, PLAN_FILE_REL);
|
|
8654
8851
|
}
|
|
8655
8852
|
function readTextIfPresent2(abs) {
|
|
8656
|
-
if (!(0,
|
|
8853
|
+
if (!(0, import_node_fs40.existsSync)(abs)) return null;
|
|
8657
8854
|
try {
|
|
8658
|
-
const content = (0,
|
|
8855
|
+
const content = (0, import_node_fs40.readFileSync)(abs, "utf8");
|
|
8659
8856
|
return content.trim() ? content : null;
|
|
8660
8857
|
} catch {
|
|
8661
8858
|
return null;
|
|
@@ -8667,16 +8864,16 @@ function readPlanFile(worktreePath) {
|
|
|
8667
8864
|
function writePlanFile(worktreePath, content) {
|
|
8668
8865
|
ensureAttachmentsGitignore2(worktreePath);
|
|
8669
8866
|
const abs = planFileAbs(worktreePath);
|
|
8670
|
-
(0,
|
|
8867
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path36.dirname)(abs), { recursive: true });
|
|
8671
8868
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
8672
|
-
(0,
|
|
8869
|
+
(0, import_node_fs40.writeFileSync)(abs, body, "utf8");
|
|
8673
8870
|
return PLAN_FILE_REL;
|
|
8674
8871
|
}
|
|
8675
|
-
var
|
|
8872
|
+
var import_node_fs40, import_node_path36;
|
|
8676
8873
|
var init_plan_file = __esm({
|
|
8677
8874
|
"src/plan/plan-file.ts"() {
|
|
8678
8875
|
"use strict";
|
|
8679
|
-
|
|
8876
|
+
import_node_fs40 = require("fs");
|
|
8680
8877
|
import_node_path36 = require("path");
|
|
8681
8878
|
init_workspace_scratch();
|
|
8682
8879
|
init_plan_present();
|
|
@@ -8732,9 +8929,9 @@ function uniqueIds(ids) {
|
|
|
8732
8929
|
}
|
|
8733
8930
|
function readHold() {
|
|
8734
8931
|
const path = caffeinateHoldPath();
|
|
8735
|
-
if (!(0,
|
|
8932
|
+
if (!(0, import_node_fs41.existsSync)(path)) return null;
|
|
8736
8933
|
try {
|
|
8737
|
-
const parsed = JSON.parse((0,
|
|
8934
|
+
const parsed = JSON.parse((0, import_node_fs41.readFileSync)(path, "utf8"));
|
|
8738
8935
|
if (typeof parsed?.pid === "number" && parsed.pid > 0) {
|
|
8739
8936
|
return {
|
|
8740
8937
|
pid: parsed.pid,
|
|
@@ -8756,7 +8953,7 @@ function writeHold(pid, threadIds) {
|
|
|
8756
8953
|
}
|
|
8757
8954
|
function clearHold() {
|
|
8758
8955
|
try {
|
|
8759
|
-
(0,
|
|
8956
|
+
(0, import_node_fs41.unlinkSync)(caffeinateHoldPath());
|
|
8760
8957
|
} catch {
|
|
8761
8958
|
}
|
|
8762
8959
|
}
|
|
@@ -8841,12 +9038,12 @@ function releaseCaffeinateHoldForThread(threadId) {
|
|
|
8841
9038
|
}
|
|
8842
9039
|
return setCaffeinateHold(false, { threadId: id });
|
|
8843
9040
|
}
|
|
8844
|
-
var import_node_child_process4,
|
|
9041
|
+
var import_node_child_process4, import_node_fs41, import_node_path37, hooks;
|
|
8845
9042
|
var init_caffeinate_hold = __esm({
|
|
8846
9043
|
"src/store/caffeinate-hold.ts"() {
|
|
8847
9044
|
"use strict";
|
|
8848
9045
|
import_node_child_process4 = require("child_process");
|
|
8849
|
-
|
|
9046
|
+
import_node_fs41 = require("fs");
|
|
8850
9047
|
import_node_path37 = require("path");
|
|
8851
9048
|
init_paths();
|
|
8852
9049
|
init_private_file();
|
|
@@ -8863,9 +9060,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
8863
9060
|
const agentId = opts.agentId.trim();
|
|
8864
9061
|
if (!agentId) return null;
|
|
8865
9062
|
const runsPath = (0, import_node_path38.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
|
|
8866
|
-
if (!(0,
|
|
9063
|
+
if (!(0, import_node_fs42.existsSync)(runsPath)) return null;
|
|
8867
9064
|
try {
|
|
8868
|
-
const lines = (0,
|
|
9065
|
+
const lines = (0, import_node_fs42.readFileSync)(runsPath, "utf8").split("\n");
|
|
8869
9066
|
let best = null;
|
|
8870
9067
|
for (const line of lines) {
|
|
8871
9068
|
const trimmed = line.trim();
|
|
@@ -8891,11 +9088,11 @@ function recoverFinishedCursorRun(opts) {
|
|
|
8891
9088
|
return null;
|
|
8892
9089
|
}
|
|
8893
9090
|
}
|
|
8894
|
-
var
|
|
9091
|
+
var import_node_fs42, import_node_path38;
|
|
8895
9092
|
var init_cursor_recover = __esm({
|
|
8896
9093
|
"src/agents/cursor-recover.ts"() {
|
|
8897
9094
|
"use strict";
|
|
8898
|
-
|
|
9095
|
+
import_node_fs42 = require("fs");
|
|
8899
9096
|
import_node_path38 = require("path");
|
|
8900
9097
|
init_paths();
|
|
8901
9098
|
}
|
|
@@ -9419,7 +9616,7 @@ async function refreshSlackReplyBadges(opts) {
|
|
|
9419
9616
|
}
|
|
9420
9617
|
|
|
9421
9618
|
// src/orchestrator/orchestrator.ts
|
|
9422
|
-
var
|
|
9619
|
+
var import_node_fs43 = require("fs");
|
|
9423
9620
|
init_error_detail();
|
|
9424
9621
|
|
|
9425
9622
|
// src/agents/spawn.ts
|
|
@@ -9673,7 +9870,7 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
9673
9870
|
const intervalMs = opts?.intervalMs ?? 32;
|
|
9674
9871
|
let pending = null;
|
|
9675
9872
|
let timer = null;
|
|
9676
|
-
const
|
|
9873
|
+
const flush2 = () => {
|
|
9677
9874
|
if (timer) {
|
|
9678
9875
|
clearTimeout(timer);
|
|
9679
9876
|
timer = null;
|
|
@@ -9685,13 +9882,13 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
9685
9882
|
};
|
|
9686
9883
|
const schedule = () => {
|
|
9687
9884
|
if (timer) return;
|
|
9688
|
-
timer = setTimeout(
|
|
9885
|
+
timer = setTimeout(flush2, intervalMs);
|
|
9689
9886
|
timer.unref?.();
|
|
9690
9887
|
};
|
|
9691
9888
|
return {
|
|
9692
9889
|
push(event) {
|
|
9693
9890
|
if (!isTextEvent(event)) {
|
|
9694
|
-
|
|
9891
|
+
flush2();
|
|
9695
9892
|
emit(event);
|
|
9696
9893
|
return;
|
|
9697
9894
|
}
|
|
@@ -9699,12 +9896,12 @@ function createAgentStreamCoalescer(emit, opts) {
|
|
|
9699
9896
|
if (pending && pending.type === event.type) {
|
|
9700
9897
|
pending.data += event.data;
|
|
9701
9898
|
} else {
|
|
9702
|
-
|
|
9899
|
+
flush2();
|
|
9703
9900
|
pending = { type: event.type, data: event.data };
|
|
9704
9901
|
}
|
|
9705
9902
|
schedule();
|
|
9706
9903
|
},
|
|
9707
|
-
flush
|
|
9904
|
+
flush: flush2
|
|
9708
9905
|
};
|
|
9709
9906
|
}
|
|
9710
9907
|
|
|
@@ -11020,8 +11217,11 @@ function worktreeBindingFrom(from) {
|
|
|
11020
11217
|
};
|
|
11021
11218
|
}
|
|
11022
11219
|
function threadsSharingWorktree(worktreePath) {
|
|
11220
|
+
if (!worktreePath) return [];
|
|
11023
11221
|
const key = normalizeWorktreePath(worktreePath);
|
|
11024
|
-
return listThreads({ includeArchived: true }).filter(
|
|
11222
|
+
return listThreads({ includeArchived: true }).filter(
|
|
11223
|
+
(t) => Boolean(t.worktreePath) && sameWorktreePath(t.worktreePath, key) && t.status !== "archived"
|
|
11224
|
+
).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
11025
11225
|
}
|
|
11026
11226
|
function formatTranscriptMarkdown(title, messages) {
|
|
11027
11227
|
const body = formatMessagesAsTranscript(messages, { tools: "full" });
|
|
@@ -11092,9 +11292,132 @@ function forkChatTab(input) {
|
|
|
11092
11292
|
return tab;
|
|
11093
11293
|
}
|
|
11094
11294
|
|
|
11295
|
+
// src/orchestrator/orchestrator.ts
|
|
11296
|
+
init_enqueue_by_key();
|
|
11297
|
+
init_run();
|
|
11298
|
+
init_repo_git_lock();
|
|
11299
|
+
|
|
11300
|
+
// src/store/turn-live.ts
|
|
11301
|
+
var import_node_fs32 = require("fs");
|
|
11302
|
+
init_paths();
|
|
11303
|
+
var buffers = /* @__PURE__ */ new Map();
|
|
11304
|
+
var FLUSH_MS = 800;
|
|
11305
|
+
var MAX_PARTS = 80;
|
|
11306
|
+
function trimParts(parts) {
|
|
11307
|
+
if (parts.length <= MAX_PARTS) return parts;
|
|
11308
|
+
return parts.slice(-Math.floor(MAX_PARTS / 2));
|
|
11309
|
+
}
|
|
11310
|
+
function lastText(parts, type) {
|
|
11311
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
11312
|
+
const p = parts[i];
|
|
11313
|
+
if (p.type === type && p.text.trim()) return p.text.trim();
|
|
11314
|
+
}
|
|
11315
|
+
return "";
|
|
11316
|
+
}
|
|
11317
|
+
function summarizeTurnLive(parts) {
|
|
11318
|
+
const tools = parts.filter(
|
|
11319
|
+
(p) => p.type === "tool"
|
|
11320
|
+
);
|
|
11321
|
+
const lastTool = tools[tools.length - 1];
|
|
11322
|
+
const lastToolLabel = lastTool ? lastTool.description || toolDescription(lastTool.name, lastTool.input) || lastTool.name : void 0;
|
|
11323
|
+
const running = lastTool?.status === "running";
|
|
11324
|
+
const thinking = lastText(parts, "thinking");
|
|
11325
|
+
const text3 = lastText(parts, "text");
|
|
11326
|
+
const excerptRaw = thinking || text3;
|
|
11327
|
+
const excerpt = excerptRaw.length > 280 ? `${excerptRaw.slice(-280)}` : excerptRaw || void 0;
|
|
11328
|
+
let summary = "Working\u2026";
|
|
11329
|
+
if (lastToolLabel) {
|
|
11330
|
+
const verb = running ? lastToolLabel : `Finished ${lastToolLabel}`;
|
|
11331
|
+
summary = tools.length > 1 ? `${verb} (${tools.length} tools)` : verb;
|
|
11332
|
+
} else if (thinking) {
|
|
11333
|
+
summary = "Thinking\u2026";
|
|
11334
|
+
} else if (text3) {
|
|
11335
|
+
summary = "Writing reply\u2026";
|
|
11336
|
+
}
|
|
11337
|
+
return {
|
|
11338
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11339
|
+
summary,
|
|
11340
|
+
lastTool: lastToolLabel,
|
|
11341
|
+
toolCount: tools.length,
|
|
11342
|
+
excerpt
|
|
11343
|
+
};
|
|
11344
|
+
}
|
|
11345
|
+
function flush(threadId) {
|
|
11346
|
+
const buf = buffers.get(threadId);
|
|
11347
|
+
if (!buf) return;
|
|
11348
|
+
if (buf.timer) {
|
|
11349
|
+
clearTimeout(buf.timer);
|
|
11350
|
+
buf.timer = null;
|
|
11351
|
+
}
|
|
11352
|
+
writeTurnLive(threadId, summarizeTurnLive(buf.parts));
|
|
11353
|
+
}
|
|
11354
|
+
function scheduleFlush(threadId) {
|
|
11355
|
+
const buf = buffers.get(threadId);
|
|
11356
|
+
if (!buf) return;
|
|
11357
|
+
if (buf.timer) return;
|
|
11358
|
+
buf.timer = setTimeout(() => {
|
|
11359
|
+
const live = buffers.get(threadId);
|
|
11360
|
+
if (live) live.timer = null;
|
|
11361
|
+
flush(threadId);
|
|
11362
|
+
}, FLUSH_MS);
|
|
11363
|
+
}
|
|
11364
|
+
function noteTurnLiveEvent(threadId, event) {
|
|
11365
|
+
if (event.type === "session_id" || event.type === "usage" || event.type === "stderr") {
|
|
11366
|
+
return;
|
|
11367
|
+
}
|
|
11368
|
+
const prev = buffers.get(threadId) ?? { parts: [], timer: null };
|
|
11369
|
+
prev.parts = trimParts(applyAgentEvent(prev.parts, event));
|
|
11370
|
+
buffers.set(threadId, prev);
|
|
11371
|
+
if (event.type === "exit") {
|
|
11372
|
+
flush(threadId);
|
|
11373
|
+
const cur = buffers.get(threadId);
|
|
11374
|
+
if (cur?.timer) clearTimeout(cur.timer);
|
|
11375
|
+
buffers.delete(threadId);
|
|
11376
|
+
return;
|
|
11377
|
+
}
|
|
11378
|
+
const immediate = event.type === "tool_use" || event.type === "tool_result";
|
|
11379
|
+
if (immediate) flush(threadId);
|
|
11380
|
+
else scheduleFlush(threadId);
|
|
11381
|
+
}
|
|
11382
|
+
function writeTurnLive(threadId, progress) {
|
|
11383
|
+
const path = threadLivePath(threadId);
|
|
11384
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
11385
|
+
try {
|
|
11386
|
+
(0, import_node_fs32.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
|
|
11387
|
+
(0, import_node_fs32.renameSync)(tmp, path);
|
|
11388
|
+
} catch {
|
|
11389
|
+
try {
|
|
11390
|
+
(0, import_node_fs32.unlinkSync)(tmp);
|
|
11391
|
+
} catch {
|
|
11392
|
+
}
|
|
11393
|
+
}
|
|
11394
|
+
}
|
|
11395
|
+
function readTurnLive(threadId) {
|
|
11396
|
+
const path = threadLivePath(threadId);
|
|
11397
|
+
if (!(0, import_node_fs32.existsSync)(path)) return null;
|
|
11398
|
+
try {
|
|
11399
|
+
const raw = JSON.parse((0, import_node_fs32.readFileSync)(path, "utf8"));
|
|
11400
|
+
if (!raw || typeof raw.summary !== "string") return null;
|
|
11401
|
+
return raw;
|
|
11402
|
+
} catch {
|
|
11403
|
+
return null;
|
|
11404
|
+
}
|
|
11405
|
+
}
|
|
11406
|
+
function clearTurnLive(threadId) {
|
|
11407
|
+
const buf = buffers.get(threadId);
|
|
11408
|
+
if (buf?.timer) clearTimeout(buf.timer);
|
|
11409
|
+
buffers.delete(threadId);
|
|
11410
|
+
const path = threadLivePath(threadId);
|
|
11411
|
+
if (!(0, import_node_fs32.existsSync)(path)) return;
|
|
11412
|
+
try {
|
|
11413
|
+
(0, import_node_fs32.unlinkSync)(path);
|
|
11414
|
+
} catch {
|
|
11415
|
+
}
|
|
11416
|
+
}
|
|
11417
|
+
|
|
11095
11418
|
// src/review/request-review.ts
|
|
11096
11419
|
var import_node_crypto5 = require("crypto");
|
|
11097
|
-
var
|
|
11420
|
+
var import_node_fs33 = require("fs");
|
|
11098
11421
|
var import_node_path30 = require("path");
|
|
11099
11422
|
init_global_workspace();
|
|
11100
11423
|
init_thread_store();
|
|
@@ -11243,9 +11566,9 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
11243
11566
|
return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
|
|
11244
11567
|
}
|
|
11245
11568
|
function readTextIfPresent(abs) {
|
|
11246
|
-
if (!(0,
|
|
11569
|
+
if (!(0, import_node_fs33.existsSync)(abs)) return null;
|
|
11247
11570
|
try {
|
|
11248
|
-
const content = (0,
|
|
11571
|
+
const content = (0, import_node_fs33.readFileSync)(abs, "utf8");
|
|
11249
11572
|
return content.trim() ? content : null;
|
|
11250
11573
|
} catch {
|
|
11251
11574
|
return null;
|
|
@@ -11253,9 +11576,9 @@ function readTextIfPresent(abs) {
|
|
|
11253
11576
|
}
|
|
11254
11577
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
11255
11578
|
const gitignoreAbs = (0, import_node_path30.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
11256
|
-
if ((0,
|
|
11257
|
-
(0,
|
|
11258
|
-
(0,
|
|
11579
|
+
if ((0, import_node_fs33.existsSync)(gitignoreAbs)) return;
|
|
11580
|
+
(0, import_node_fs33.mkdirSync)((0, import_node_path30.dirname)(gitignoreAbs), { recursive: true });
|
|
11581
|
+
(0, import_node_fs33.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
11259
11582
|
}
|
|
11260
11583
|
function resolveReviewGuidelines(worktreePath) {
|
|
11261
11584
|
const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
|
|
@@ -11289,8 +11612,8 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
11289
11612
|
};
|
|
11290
11613
|
}
|
|
11291
11614
|
ensureAttachmentsGitignore(worktreePath);
|
|
11292
|
-
(0,
|
|
11293
|
-
(0,
|
|
11615
|
+
(0, import_node_fs33.mkdirSync)((0, import_node_path30.dirname)(localAbs), { recursive: true });
|
|
11616
|
+
(0, import_node_fs33.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
11294
11617
|
return {
|
|
11295
11618
|
path: REVIEW_REQUEST_PATH,
|
|
11296
11619
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -11490,7 +11813,7 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
11490
11813
|
|
|
11491
11814
|
// src/threads/adopt.ts
|
|
11492
11815
|
var import_node_child_process3 = require("child_process");
|
|
11493
|
-
var
|
|
11816
|
+
var import_node_fs34 = require("fs");
|
|
11494
11817
|
var import_node_os10 = require("os");
|
|
11495
11818
|
var import_node_path31 = require("path");
|
|
11496
11819
|
var import_node_module4 = require("module");
|
|
@@ -11521,21 +11844,21 @@ function mapAgentType(raw) {
|
|
|
11521
11844
|
return null;
|
|
11522
11845
|
}
|
|
11523
11846
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
11524
|
-
if (!workspacePath || !(0,
|
|
11847
|
+
if (!workspacePath || !(0, import_node_fs34.existsSync)(CURSOR_SDK_STORE)) return null;
|
|
11525
11848
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
11526
11849
|
let best = null;
|
|
11527
11850
|
let hashes;
|
|
11528
11851
|
try {
|
|
11529
|
-
hashes = (0,
|
|
11852
|
+
hashes = (0, import_node_fs34.readdirSync)(CURSOR_SDK_STORE);
|
|
11530
11853
|
} catch {
|
|
11531
11854
|
return null;
|
|
11532
11855
|
}
|
|
11533
11856
|
for (const hash of hashes) {
|
|
11534
11857
|
const agentsFile = (0, import_node_path31.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
11535
|
-
if (!(0,
|
|
11858
|
+
if (!(0, import_node_fs34.existsSync)(agentsFile)) continue;
|
|
11536
11859
|
let text3;
|
|
11537
11860
|
try {
|
|
11538
|
-
text3 = (0,
|
|
11861
|
+
text3 = (0, import_node_fs34.readFileSync)(agentsFile, "utf8");
|
|
11539
11862
|
} catch {
|
|
11540
11863
|
continue;
|
|
11541
11864
|
}
|
|
@@ -11559,7 +11882,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
11559
11882
|
return best?.agentId ?? null;
|
|
11560
11883
|
}
|
|
11561
11884
|
async function adoptThread(input) {
|
|
11562
|
-
if (!(0,
|
|
11885
|
+
if (!(0, import_node_fs34.existsSync)(input.worktreePath)) {
|
|
11563
11886
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
11564
11887
|
}
|
|
11565
11888
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -11583,18 +11906,18 @@ async function adoptThread(input) {
|
|
|
11583
11906
|
return thread;
|
|
11584
11907
|
}
|
|
11585
11908
|
function listConductorWorkspaces() {
|
|
11586
|
-
if (!(0,
|
|
11909
|
+
if (!(0, import_node_fs34.existsSync)(CONDUCTOR_DB)) {
|
|
11587
11910
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
11588
11911
|
}
|
|
11589
|
-
const tmp = (0,
|
|
11912
|
+
const tmp = (0, import_node_fs34.mkdtempSync)((0, import_node_path31.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
|
|
11590
11913
|
const snapshot = (0, import_node_path31.join)(tmp, "conductor.db");
|
|
11591
11914
|
try {
|
|
11592
|
-
(0,
|
|
11915
|
+
(0, import_node_fs34.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
11593
11916
|
for (const suffix of ["-wal", "-shm"]) {
|
|
11594
11917
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
11595
|
-
if ((0,
|
|
11918
|
+
if ((0, import_node_fs34.existsSync)(src)) {
|
|
11596
11919
|
try {
|
|
11597
|
-
(0,
|
|
11920
|
+
(0, import_node_fs34.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
11598
11921
|
} catch {
|
|
11599
11922
|
}
|
|
11600
11923
|
}
|
|
@@ -11670,22 +11993,22 @@ function listConductorWorkspaces() {
|
|
|
11670
11993
|
db.close();
|
|
11671
11994
|
}
|
|
11672
11995
|
} finally {
|
|
11673
|
-
(0,
|
|
11996
|
+
(0, import_node_fs34.rmSync)(tmp, { recursive: true, force: true });
|
|
11674
11997
|
}
|
|
11675
11998
|
}
|
|
11676
11999
|
function importConductorWorkspace(workspaceId) {
|
|
11677
|
-
if (!(0,
|
|
12000
|
+
if (!(0, import_node_fs34.existsSync)(CONDUCTOR_DB)) {
|
|
11678
12001
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
11679
12002
|
}
|
|
11680
|
-
const tmp = (0,
|
|
12003
|
+
const tmp = (0, import_node_fs34.mkdtempSync)((0, import_node_path31.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
|
|
11681
12004
|
const snapshot = (0, import_node_path31.join)(tmp, "conductor.db");
|
|
11682
12005
|
try {
|
|
11683
|
-
(0,
|
|
12006
|
+
(0, import_node_fs34.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
11684
12007
|
for (const suffix of ["-wal", "-shm"]) {
|
|
11685
12008
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
11686
|
-
if ((0,
|
|
12009
|
+
if ((0, import_node_fs34.existsSync)(src)) {
|
|
11687
12010
|
try {
|
|
11688
|
-
(0,
|
|
12011
|
+
(0, import_node_fs34.copyFileSync)(src, `${snapshot}${suffix}`);
|
|
11689
12012
|
} catch {
|
|
11690
12013
|
}
|
|
11691
12014
|
}
|
|
@@ -11703,7 +12026,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
11703
12026
|
).get(workspaceId);
|
|
11704
12027
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
11705
12028
|
const worktreePath = String(row.workspacePath);
|
|
11706
|
-
if (!(0,
|
|
12029
|
+
if (!(0, import_node_fs34.existsSync)(worktreePath)) {
|
|
11707
12030
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
11708
12031
|
}
|
|
11709
12032
|
let sessionId = null;
|
|
@@ -11766,7 +12089,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
11766
12089
|
db.close();
|
|
11767
12090
|
}
|
|
11768
12091
|
} finally {
|
|
11769
|
-
(0,
|
|
12092
|
+
(0, import_node_fs34.rmSync)(tmp, { recursive: true, force: true });
|
|
11770
12093
|
}
|
|
11771
12094
|
}
|
|
11772
12095
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
@@ -11774,7 +12097,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
11774
12097
|
}
|
|
11775
12098
|
|
|
11776
12099
|
// src/threads/stack-layers.ts
|
|
11777
|
-
var
|
|
12100
|
+
var import_node_fs35 = require("fs");
|
|
11778
12101
|
init_run();
|
|
11779
12102
|
init_stack();
|
|
11780
12103
|
init_worktree();
|
|
@@ -11842,7 +12165,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
11842
12165
|
let createdWorktree = false;
|
|
11843
12166
|
const trees = await listWorktrees(repoPath);
|
|
11844
12167
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
11845
|
-
if (checkedOut?.path && (0,
|
|
12168
|
+
if (checkedOut?.path && (0, import_node_fs35.existsSync)(checkedOut.path)) {
|
|
11846
12169
|
if (input.reuseExistingWorktree !== false) {
|
|
11847
12170
|
worktreePath = checkedOut.path;
|
|
11848
12171
|
} else {
|
|
@@ -11984,7 +12307,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
11984
12307
|
async function createPrStack(input, onSetupLine) {
|
|
11985
12308
|
await requireAgent(input.agent);
|
|
11986
12309
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
11987
|
-
if (!(0,
|
|
12310
|
+
if (!(0, import_node_fs35.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
11988
12311
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
11989
12312
|
const status = await detectGhStack(repoPath);
|
|
11990
12313
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -12051,7 +12374,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
12051
12374
|
}
|
|
12052
12375
|
}
|
|
12053
12376
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
12054
|
-
if (!claimed.has(bootstrap.worktreePath) && (0,
|
|
12377
|
+
if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs35.existsSync)(bootstrap.worktreePath)) {
|
|
12055
12378
|
try {
|
|
12056
12379
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
12057
12380
|
deleteBranch: bootstrap.branchName
|
|
@@ -12066,12 +12389,12 @@ async function createPrStack(input, onSetupLine) {
|
|
|
12066
12389
|
init_worktree();
|
|
12067
12390
|
|
|
12068
12391
|
// src/diff/diff.ts
|
|
12069
|
-
var
|
|
12392
|
+
var import_node_fs36 = require("fs");
|
|
12070
12393
|
var import_node_path32 = require("path");
|
|
12071
12394
|
init_run();
|
|
12072
12395
|
init_worktree();
|
|
12073
12396
|
async function inspectGitWorktree(worktreePath) {
|
|
12074
|
-
if (!worktreePath || !(0,
|
|
12397
|
+
if (!worktreePath || !(0, import_node_fs36.existsSync)(worktreePath)) return "missing_worktree";
|
|
12075
12398
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
12076
12399
|
reject: false
|
|
12077
12400
|
});
|
|
@@ -12079,7 +12402,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
12079
12402
|
return "ok";
|
|
12080
12403
|
}
|
|
12081
12404
|
async function initializeGitRepository(worktreePath) {
|
|
12082
|
-
if (!worktreePath || !(0,
|
|
12405
|
+
if (!worktreePath || !(0, import_node_fs36.existsSync)(worktreePath)) {
|
|
12083
12406
|
throw new Error("Worktree not found");
|
|
12084
12407
|
}
|
|
12085
12408
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -12217,9 +12540,9 @@ new file mode 100644
|
|
|
12217
12540
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
12218
12541
|
const abs = (0, import_node_path32.join)(worktreePath, path);
|
|
12219
12542
|
try {
|
|
12220
|
-
const st = (0,
|
|
12543
|
+
const st = (0, import_node_fs36.statSync)(abs);
|
|
12221
12544
|
if (st.isFile() && st.size > maxHunk) {
|
|
12222
|
-
const buf = (0,
|
|
12545
|
+
const buf = (0, import_node_fs36.readFileSync)(abs).subarray(0, maxHunk);
|
|
12223
12546
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
12224
12547
|
}
|
|
12225
12548
|
} catch {
|
|
@@ -12721,7 +13044,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
12721
13044
|
assertSafeRelativePath(relativePath);
|
|
12722
13045
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
12723
13046
|
const abs = (0, import_node_path32.join)(worktreePath, relativePath);
|
|
12724
|
-
const st = (0,
|
|
13047
|
+
const st = (0, import_node_fs36.statSync)(abs);
|
|
12725
13048
|
if (!st.isFile()) {
|
|
12726
13049
|
throw new Error(`Not a file: ${relativePath}`);
|
|
12727
13050
|
}
|
|
@@ -12730,7 +13053,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
12730
13053
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
12731
13054
|
);
|
|
12732
13055
|
}
|
|
12733
|
-
const buf = (0,
|
|
13056
|
+
const buf = (0, import_node_fs36.readFileSync)(abs);
|
|
12734
13057
|
return {
|
|
12735
13058
|
path: relativePath,
|
|
12736
13059
|
contentBase64: buf.toString("base64"),
|
|
@@ -12741,11 +13064,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
|
12741
13064
|
assertSafeRelativePath(relativePath);
|
|
12742
13065
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
12743
13066
|
const abs = (0, import_node_path32.join)(worktreePath, relativePath);
|
|
12744
|
-
const st = (0,
|
|
13067
|
+
const st = (0, import_node_fs36.statSync)(abs);
|
|
12745
13068
|
if (!st.isFile()) {
|
|
12746
13069
|
throw new Error(`Not a file: ${relativePath}`);
|
|
12747
13070
|
}
|
|
12748
|
-
const buf = (0,
|
|
13071
|
+
const buf = (0, import_node_fs36.readFileSync)(abs);
|
|
12749
13072
|
if (isImageRelativePath(relativePath)) {
|
|
12750
13073
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
12751
13074
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -12789,8 +13112,8 @@ function assertSafeRelativePath(relativePath) {
|
|
|
12789
13112
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
12790
13113
|
assertSafeRelativePath(relativePath);
|
|
12791
13114
|
const abs = (0, import_node_path32.join)(worktreePath, relativePath);
|
|
12792
|
-
(0,
|
|
12793
|
-
(0,
|
|
13115
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path32.dirname)(abs), { recursive: true });
|
|
13116
|
+
(0, import_node_fs36.writeFileSync)(abs, content, "utf8");
|
|
12794
13117
|
return { path: relativePath };
|
|
12795
13118
|
}
|
|
12796
13119
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -12894,7 +13217,7 @@ async function confirmLand(thread, opts) {
|
|
|
12894
13217
|
}
|
|
12895
13218
|
|
|
12896
13219
|
// src/skills/discover.ts
|
|
12897
|
-
var
|
|
13220
|
+
var import_node_fs37 = require("fs");
|
|
12898
13221
|
var import_node_os11 = require("os");
|
|
12899
13222
|
var import_node_path33 = require("path");
|
|
12900
13223
|
function toCommand(name) {
|
|
@@ -12928,7 +13251,7 @@ function parseFrontmatter(content) {
|
|
|
12928
13251
|
}
|
|
12929
13252
|
function readSkill(skillMd, source) {
|
|
12930
13253
|
try {
|
|
12931
|
-
const content = (0,
|
|
13254
|
+
const content = (0, import_node_fs37.readFileSync)(skillMd, "utf8");
|
|
12932
13255
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
12933
13256
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
12934
13257
|
const name = fmName || dirName;
|
|
@@ -12947,19 +13270,19 @@ function readSkill(skillMd, source) {
|
|
|
12947
13270
|
}
|
|
12948
13271
|
}
|
|
12949
13272
|
function scanSkillsDir(dir, source, out) {
|
|
12950
|
-
if (!(0,
|
|
13273
|
+
if (!(0, import_node_fs37.existsSync)(dir)) return;
|
|
12951
13274
|
let entries;
|
|
12952
13275
|
try {
|
|
12953
|
-
entries = (0,
|
|
13276
|
+
entries = (0, import_node_fs37.readdirSync)(dir);
|
|
12954
13277
|
} catch {
|
|
12955
13278
|
return;
|
|
12956
13279
|
}
|
|
12957
13280
|
for (const entry of entries) {
|
|
12958
13281
|
if (entry.startsWith(".")) continue;
|
|
12959
13282
|
const skillMd = (0, import_node_path33.join)(dir, entry, "SKILL.md");
|
|
12960
|
-
if (!(0,
|
|
13283
|
+
if (!(0, import_node_fs37.existsSync)(skillMd)) continue;
|
|
12961
13284
|
try {
|
|
12962
|
-
if (!(0,
|
|
13285
|
+
if (!(0, import_node_fs37.statSync)(skillMd).isFile()) continue;
|
|
12963
13286
|
} catch {
|
|
12964
13287
|
continue;
|
|
12965
13288
|
}
|
|
@@ -12968,12 +13291,12 @@ function scanSkillsDir(dir, source, out) {
|
|
|
12968
13291
|
}
|
|
12969
13292
|
}
|
|
12970
13293
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
12971
|
-
if (!(0,
|
|
13294
|
+
if (!(0, import_node_fs37.existsSync)(pluginsRoot)) return;
|
|
12972
13295
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
12973
13296
|
if (depth > 7) return;
|
|
12974
13297
|
let entries;
|
|
12975
13298
|
try {
|
|
12976
|
-
entries = (0,
|
|
13299
|
+
entries = (0, import_node_fs37.readdirSync)(dir);
|
|
12977
13300
|
} catch {
|
|
12978
13301
|
return;
|
|
12979
13302
|
}
|
|
@@ -12985,7 +13308,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
12985
13308
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
12986
13309
|
const full = (0, import_node_path33.join)(dir, entry);
|
|
12987
13310
|
try {
|
|
12988
|
-
if (!(0,
|
|
13311
|
+
if (!(0, import_node_fs37.statSync)(full).isDirectory()) continue;
|
|
12989
13312
|
} catch {
|
|
12990
13313
|
continue;
|
|
12991
13314
|
}
|
|
@@ -13025,7 +13348,7 @@ function discoverSkills(worktreePath) {
|
|
|
13025
13348
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
13026
13349
|
}
|
|
13027
13350
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
13028
|
-
const raw = (0,
|
|
13351
|
+
const raw = (0, import_node_fs37.readFileSync)(skillPath, "utf8");
|
|
13029
13352
|
if (raw.startsWith("---")) {
|
|
13030
13353
|
const end = raw.indexOf("\n---", 3);
|
|
13031
13354
|
if (end >= 0) {
|
|
@@ -13118,7 +13441,7 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
13118
13441
|
}
|
|
13119
13442
|
|
|
13120
13443
|
// src/composer/stage-files.ts
|
|
13121
|
-
var
|
|
13444
|
+
var import_node_fs38 = require("fs");
|
|
13122
13445
|
var import_node_path34 = require("path");
|
|
13123
13446
|
var import_node_crypto7 = require("crypto");
|
|
13124
13447
|
init_workspace_scratch();
|
|
@@ -13156,21 +13479,21 @@ function imageMimeType(filePath) {
|
|
|
13156
13479
|
}
|
|
13157
13480
|
function ensureAttachmentsDir(worktreePath) {
|
|
13158
13481
|
const dir = (0, import_node_path34.join)(worktreePath, ATTACHMENTS_DIR);
|
|
13159
|
-
(0,
|
|
13482
|
+
(0, import_node_fs38.mkdirSync)(dir, { recursive: true });
|
|
13160
13483
|
const gi = (0, import_node_path34.join)(dir, ".gitignore");
|
|
13161
|
-
if (!(0,
|
|
13162
|
-
(0,
|
|
13484
|
+
if (!(0, import_node_fs38.existsSync)(gi)) {
|
|
13485
|
+
(0, import_node_fs38.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
13163
13486
|
}
|
|
13164
13487
|
return dir;
|
|
13165
13488
|
}
|
|
13166
13489
|
function uniqueAttachmentName(dir, originalName) {
|
|
13167
13490
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
13168
|
-
if (!(0,
|
|
13491
|
+
if (!(0, import_node_fs38.existsSync)((0, import_node_path34.join)(dir, safe))) return safe;
|
|
13169
13492
|
const ext = (0, import_node_path34.extname)(safe);
|
|
13170
13493
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
13171
13494
|
for (let i = 1; i < 1e4; i++) {
|
|
13172
13495
|
const candidate = `${stem}-${i}${ext}`;
|
|
13173
|
-
if (!(0,
|
|
13496
|
+
if (!(0, import_node_fs38.existsSync)((0, import_node_path34.join)(dir, candidate))) return candidate;
|
|
13174
13497
|
}
|
|
13175
13498
|
return `${stem}-${(0, import_node_crypto7.randomUUID)()}${ext}`;
|
|
13176
13499
|
}
|
|
@@ -13228,13 +13551,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
13228
13551
|
for (const abs of absolutePaths) {
|
|
13229
13552
|
const originalName = (0, import_node_path34.basename)(abs);
|
|
13230
13553
|
try {
|
|
13231
|
-
const st = (0,
|
|
13554
|
+
const st = (0, import_node_fs38.statSync)(abs);
|
|
13232
13555
|
if (!st.isFile()) continue;
|
|
13233
13556
|
const name = uniqueAttachmentName(dir, originalName);
|
|
13234
13557
|
const destAbs = (0, import_node_path34.join)(dir, name);
|
|
13235
|
-
(0,
|
|
13558
|
+
(0, import_node_fs38.copyFileSync)(abs, destAbs);
|
|
13236
13559
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
13237
|
-
const buf = (0,
|
|
13560
|
+
const buf = (0, import_node_fs38.readFileSync)(destAbs);
|
|
13238
13561
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
13239
13562
|
} catch (err) {
|
|
13240
13563
|
out.push({
|
|
@@ -13247,17 +13570,17 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
13247
13570
|
}
|
|
13248
13571
|
return out;
|
|
13249
13572
|
}
|
|
13250
|
-
function stageBuffersAsAttachments(worktreePath,
|
|
13251
|
-
if (
|
|
13573
|
+
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
13574
|
+
if (buffers2.length === 0) return [];
|
|
13252
13575
|
const dir = ensureAttachmentsDir(worktreePath);
|
|
13253
13576
|
const out = [];
|
|
13254
|
-
for (const item of
|
|
13577
|
+
for (const item of buffers2) {
|
|
13255
13578
|
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
13256
13579
|
try {
|
|
13257
13580
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
13258
13581
|
const name = uniqueAttachmentName(dir, originalName);
|
|
13259
13582
|
const destAbs = (0, import_node_path34.join)(dir, name);
|
|
13260
|
-
(0,
|
|
13583
|
+
(0, import_node_fs38.writeFileSync)(destAbs, buf);
|
|
13261
13584
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
13262
13585
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
13263
13586
|
} catch (err) {
|
|
@@ -13286,9 +13609,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
13286
13609
|
const name = (0, import_node_path34.basename)(rel);
|
|
13287
13610
|
try {
|
|
13288
13611
|
const abs = (0, import_node_path34.join)(worktreePath, rel);
|
|
13289
|
-
const st = (0,
|
|
13612
|
+
const st = (0, import_node_fs38.statSync)(abs);
|
|
13290
13613
|
if (!st.isFile()) continue;
|
|
13291
|
-
const buf = (0,
|
|
13614
|
+
const buf = (0, import_node_fs38.readFileSync)(abs);
|
|
13292
13615
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
13293
13616
|
} catch (err) {
|
|
13294
13617
|
out.push({
|
|
@@ -13303,7 +13626,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
13303
13626
|
}
|
|
13304
13627
|
|
|
13305
13628
|
// src/agents/instructions.ts
|
|
13306
|
-
var
|
|
13629
|
+
var import_node_fs39 = require("fs");
|
|
13307
13630
|
var import_node_path35 = require("path");
|
|
13308
13631
|
init_git_auth_mode();
|
|
13309
13632
|
init_worktree_labels();
|
|
@@ -13507,6 +13830,15 @@ function isPidAlive(pid) {
|
|
|
13507
13830
|
return false;
|
|
13508
13831
|
}
|
|
13509
13832
|
}
|
|
13833
|
+
async function waitForPidExit(pid, timeoutMs) {
|
|
13834
|
+
const start = Date.now();
|
|
13835
|
+
while (isPidAlive(pid)) {
|
|
13836
|
+
if (Date.now() - start >= timeoutMs) return false;
|
|
13837
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
13838
|
+
}
|
|
13839
|
+
return true;
|
|
13840
|
+
}
|
|
13841
|
+
var STALE_AGENT_PID_WAIT_MS = 2500;
|
|
13510
13842
|
function writeLiveStatus(threadId, status, lastError) {
|
|
13511
13843
|
const latest = readThread(threadId);
|
|
13512
13844
|
if (!latest || latest.status === "archived") return latest;
|
|
@@ -13584,7 +13916,7 @@ var Orchestrator = class {
|
|
|
13584
13916
|
}
|
|
13585
13917
|
continue;
|
|
13586
13918
|
}
|
|
13587
|
-
if (!(0,
|
|
13919
|
+
if (!(0, import_node_fs43.existsSync)(thread.worktreePath)) {
|
|
13588
13920
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
13589
13921
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
13590
13922
|
continue;
|
|
@@ -13905,14 +14237,10 @@ var Orchestrator = class {
|
|
|
13905
14237
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
13906
14238
|
const livePid = current.agentPid;
|
|
13907
14239
|
const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
|
|
13908
|
-
if (inFlight) {
|
|
14240
|
+
if (inFlight || foreignLive) {
|
|
13909
14241
|
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
13910
|
-
} else {
|
|
13911
|
-
if (foreignLive) {
|
|
13912
|
-
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
13913
|
-
}
|
|
13914
|
-
void this.drainQueue(thread.id);
|
|
13915
14242
|
}
|
|
14243
|
+
void this.drainQueue(thread.id);
|
|
13916
14244
|
return this.requireThread(thread.id);
|
|
13917
14245
|
}
|
|
13918
14246
|
async drainQueue(threadId) {
|
|
@@ -13941,7 +14269,18 @@ var Orchestrator = class {
|
|
|
13941
14269
|
}
|
|
13942
14270
|
const livePid = thread.agentPid;
|
|
13943
14271
|
if (typeof livePid === "number" && livePid > 0 && isPidAlive(livePid)) {
|
|
13944
|
-
|
|
14272
|
+
const exited = await waitForPidExit(livePid, STALE_AGENT_PID_WAIT_MS);
|
|
14273
|
+
if (!exited && isPidAlive(livePid)) {
|
|
14274
|
+
try {
|
|
14275
|
+
process.kill(livePid, "SIGKILL");
|
|
14276
|
+
} catch {
|
|
14277
|
+
}
|
|
14278
|
+
await waitForPidExit(livePid, 400);
|
|
14279
|
+
try {
|
|
14280
|
+
updateThread(threadId, { agentPid: null });
|
|
14281
|
+
} catch {
|
|
14282
|
+
}
|
|
14283
|
+
}
|
|
13945
14284
|
continue;
|
|
13946
14285
|
}
|
|
13947
14286
|
const prompt = thread.queue[0];
|
|
@@ -14098,6 +14437,7 @@ var Orchestrator = class {
|
|
|
14098
14437
|
{ cachedPrefix, prompt: agentPrompt },
|
|
14099
14438
|
(event) => {
|
|
14100
14439
|
this.emit({ type: "turn_output", threadId, event });
|
|
14440
|
+
noteTurnLiveEvent(threadId, event);
|
|
14101
14441
|
if (event.type === "session_id") {
|
|
14102
14442
|
updateThread(threadId, { sessionId: event.data });
|
|
14103
14443
|
}
|
|
@@ -14161,19 +14501,16 @@ var Orchestrator = class {
|
|
|
14161
14501
|
}
|
|
14162
14502
|
let lastStderr = summarizeTurnStderr(stderrTail);
|
|
14163
14503
|
let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
14164
|
-
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) &&
|
|
14504
|
+
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
|
|
14505
|
+
hasSession: Boolean(this.requireThread(threadId).sessionId)
|
|
14506
|
+
})) {
|
|
14165
14507
|
updateThread(threadId, { sessionId: null });
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
"Agent session missing \u2014 starting a fresh session"
|
|
14169
|
-
);
|
|
14508
|
+
const retryNote = looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
|
|
14509
|
+
pushTurnStderr(stderrTail, retryNote);
|
|
14170
14510
|
this.emit({
|
|
14171
14511
|
type: "turn_output",
|
|
14172
14512
|
threadId,
|
|
14173
|
-
event: {
|
|
14174
|
-
type: "stderr",
|
|
14175
|
-
data: "Agent session missing \u2014 starting a fresh session"
|
|
14176
|
-
}
|
|
14513
|
+
event: { type: "stderr", data: retryNote }
|
|
14177
14514
|
});
|
|
14178
14515
|
const retryThread = this.requireThread(threadId);
|
|
14179
14516
|
const prior = retryThread.messages.slice(0, -1);
|
|
@@ -14222,10 +14559,7 @@ var Orchestrator = class {
|
|
|
14222
14559
|
lastStderr = summarizeTurnStderr(stderrTail);
|
|
14223
14560
|
detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
14224
14561
|
}
|
|
14225
|
-
let chatText = assistantText;
|
|
14226
|
-
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
14227
|
-
chatText = humanizeAgentFailDetail(detail);
|
|
14228
|
-
}
|
|
14562
|
+
let chatText = this.stoppedTurns.has(threadId) ? assistantText : turnFailChatText({ exitCode, assistantText, detail });
|
|
14229
14563
|
if (chatText || parts.length > 0) {
|
|
14230
14564
|
appendMessage(threadId, {
|
|
14231
14565
|
role: "agent",
|
|
@@ -14308,6 +14642,7 @@ var Orchestrator = class {
|
|
|
14308
14642
|
this.processes.delete(`${threadId}:agent`);
|
|
14309
14643
|
this.stoppedTurns.delete(threadId);
|
|
14310
14644
|
this.runningCount = Math.max(0, this.runningCount - 1);
|
|
14645
|
+
clearTurnLive(threadId);
|
|
14311
14646
|
try {
|
|
14312
14647
|
updateThread(threadId, { agentPid: null });
|
|
14313
14648
|
} catch {
|
|
@@ -14581,51 +14916,56 @@ var Orchestrator = class {
|
|
|
14581
14916
|
}
|
|
14582
14917
|
return created;
|
|
14583
14918
|
}
|
|
14584
|
-
async waitForTurn(threadRef, timeoutMs = 6e5) {
|
|
14919
|
+
async waitForTurn(threadRef, timeoutMs = 6e5, opts) {
|
|
14585
14920
|
const thread = this.requireThread(threadRef);
|
|
14921
|
+
if (!["running", "queued"].includes(thread.status)) {
|
|
14922
|
+
return thread;
|
|
14923
|
+
}
|
|
14586
14924
|
const start = Date.now();
|
|
14587
14925
|
return new Promise((resolve, reject) => {
|
|
14588
|
-
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14926
|
+
let settled = false;
|
|
14927
|
+
let timer;
|
|
14928
|
+
const finish = (fn) => {
|
|
14929
|
+
if (settled) return;
|
|
14930
|
+
settled = true;
|
|
14931
|
+
if (timer) clearInterval(timer);
|
|
14932
|
+
off();
|
|
14933
|
+
fn();
|
|
14934
|
+
};
|
|
14592
14935
|
const off = this.on((event) => {
|
|
14593
14936
|
if (event.type === "turn_finished" && event.threadId === thread.id) {
|
|
14594
|
-
off();
|
|
14595
14937
|
const latest = readThread(thread.id);
|
|
14596
14938
|
if (!latest) {
|
|
14597
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
14939
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
14598
14940
|
return;
|
|
14599
14941
|
}
|
|
14600
|
-
resolve(latest);
|
|
14942
|
+
finish(() => resolve(latest));
|
|
14601
14943
|
}
|
|
14602
14944
|
if (event.type === "error" && event.threadId === thread.id) {
|
|
14603
|
-
off();
|
|
14604
14945
|
const latest = readThread(thread.id);
|
|
14605
14946
|
if (!latest) {
|
|
14606
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
14947
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
14607
14948
|
return;
|
|
14608
14949
|
}
|
|
14609
|
-
resolve(latest);
|
|
14950
|
+
finish(() => resolve(latest));
|
|
14610
14951
|
}
|
|
14611
14952
|
});
|
|
14612
|
-
|
|
14613
|
-
if (Date.now() - start > timeoutMs) {
|
|
14614
|
-
clearInterval(timer);
|
|
14615
|
-
off();
|
|
14616
|
-
reject(new Error("wait_for_turn timed out"));
|
|
14617
|
-
}
|
|
14953
|
+
timer = setInterval(() => {
|
|
14618
14954
|
const current = readThread(thread.id);
|
|
14619
14955
|
if (!current) {
|
|
14620
|
-
|
|
14621
|
-
off();
|
|
14622
|
-
reject(new Error(`Thread not found: ${thread.id}`));
|
|
14956
|
+
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
14623
14957
|
return;
|
|
14624
14958
|
}
|
|
14625
14959
|
if (!["running", "queued"].includes(current.status)) {
|
|
14626
|
-
|
|
14627
|
-
|
|
14628
|
-
|
|
14960
|
+
finish(() => resolve(current));
|
|
14961
|
+
return;
|
|
14962
|
+
}
|
|
14963
|
+
if (Date.now() - start > timeoutMs) {
|
|
14964
|
+
if (opts?.resolveIfStillRunning) {
|
|
14965
|
+
finish(() => resolve(current));
|
|
14966
|
+
} else {
|
|
14967
|
+
finish(() => reject(new Error("wait_for_turn timed out")));
|
|
14968
|
+
}
|
|
14629
14969
|
}
|
|
14630
14970
|
}, 200);
|
|
14631
14971
|
});
|
|
@@ -14633,10 +14973,18 @@ var Orchestrator = class {
|
|
|
14633
14973
|
getTurnResult(threadRef) {
|
|
14634
14974
|
const thread = this.requireThread(threadRef);
|
|
14635
14975
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
14976
|
+
const lastError = thread.lastError ?? null;
|
|
14977
|
+
const text3 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
|
|
14978
|
+
const stillRunning = thread.status === "running" || thread.status === "queued";
|
|
14979
|
+
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
14636
14980
|
return {
|
|
14637
|
-
text:
|
|
14981
|
+
text: text3,
|
|
14638
14982
|
status: thread.status,
|
|
14639
|
-
sessionId: thread.sessionId
|
|
14983
|
+
sessionId: thread.sessionId,
|
|
14984
|
+
lastError,
|
|
14985
|
+
stillRunning,
|
|
14986
|
+
progress: live?.summary ?? null,
|
|
14987
|
+
lastActivityAt: live?.updatedAt ?? null
|
|
14640
14988
|
};
|
|
14641
14989
|
}
|
|
14642
14990
|
assertNotGlobal(thread, action) {
|
|
@@ -15062,7 +15410,19 @@ var Orchestrator = class {
|
|
|
15062
15410
|
const thread = this.requireThread(threadRef);
|
|
15063
15411
|
return threadsSharingWorktree(thread.worktreePath);
|
|
15064
15412
|
}
|
|
15413
|
+
/**
|
|
15414
|
+
* Last-tab teardown must see siblings already archived. Serialize per
|
|
15415
|
+
* worktree so parallel archive/purge of sibling tabs still removes once.
|
|
15416
|
+
*/
|
|
15417
|
+
enqueueWorktreeTeardown(thread, fn) {
|
|
15418
|
+
const key = isGlobalThread(thread) ? `archive:global:${thread.id}` : `archive:wt:${normalizeWorktreePath(thread.worktreePath)}`;
|
|
15419
|
+
return enqueueByKey(key, fn);
|
|
15420
|
+
}
|
|
15065
15421
|
async archive(threadRef) {
|
|
15422
|
+
const thread = this.requireThread(threadRef);
|
|
15423
|
+
return this.enqueueWorktreeTeardown(thread, () => this.archiveUnlocked(threadRef));
|
|
15424
|
+
}
|
|
15425
|
+
async archiveUnlocked(threadRef) {
|
|
15066
15426
|
const thread = this.requireThread(threadRef);
|
|
15067
15427
|
this.stop(thread.id);
|
|
15068
15428
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -15098,6 +15458,10 @@ var Orchestrator = class {
|
|
|
15098
15458
|
return archived;
|
|
15099
15459
|
}
|
|
15100
15460
|
async purge(threadRef, opts) {
|
|
15461
|
+
const thread = this.requireThread(threadRef);
|
|
15462
|
+
await this.enqueueWorktreeTeardown(thread, () => this.purgeUnlocked(threadRef, opts));
|
|
15463
|
+
}
|
|
15464
|
+
async purgeUnlocked(threadRef, opts) {
|
|
15101
15465
|
const thread = this.requireThread(threadRef);
|
|
15102
15466
|
this.stop(thread.id);
|
|
15103
15467
|
this.releaseOrchestratorCaffeinate(thread);
|
|
@@ -15132,13 +15496,12 @@ var Orchestrator = class {
|
|
|
15132
15496
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
15133
15497
|
return restored2;
|
|
15134
15498
|
}
|
|
15135
|
-
if (!(0,
|
|
15499
|
+
if (!(0, import_node_fs43.existsSync)(thread.worktreePath)) {
|
|
15136
15500
|
const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
|
|
15137
|
-
const { execa: execa7 } = await import("execa");
|
|
15138
15501
|
const slug = thread.worktreePath.split("/").pop();
|
|
15139
15502
|
const dest = thread.worktreePath;
|
|
15140
|
-
await
|
|
15141
|
-
|
|
15503
|
+
await withRepoGitLock(thread.repoPath, async () => {
|
|
15504
|
+
await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
|
|
15142
15505
|
});
|
|
15143
15506
|
void createThreadWorktree2;
|
|
15144
15507
|
void slug;
|
|
@@ -15702,6 +16065,15 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
15702
16065
|
// src/mcp/server.ts
|
|
15703
16066
|
init_profile();
|
|
15704
16067
|
|
|
16068
|
+
// src/mcp/wait-for-turn.ts
|
|
16069
|
+
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
16070
|
+
function mcpWaitForTurnTimeoutMs(requested) {
|
|
16071
|
+
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
16072
|
+
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
16073
|
+
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
16074
|
+
}
|
|
16075
|
+
var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
|
|
16076
|
+
|
|
15705
16077
|
// src/mcp/slack-tools.ts
|
|
15706
16078
|
var import_zod = require("zod");
|
|
15707
16079
|
|
|
@@ -16373,7 +16745,9 @@ async function startMcpServer() {
|
|
|
16373
16745
|
const threads = orch.getThreads(true);
|
|
16374
16746
|
const lines = threads.map((t) => {
|
|
16375
16747
|
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path39.basename)(t.repoPath) || t.repoPath;
|
|
16376
|
-
|
|
16748
|
+
const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
|
|
16749
|
+
const progress = live?.summary ? ` ${live.summary}` : "";
|
|
16750
|
+
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}` : ""}${progress}`;
|
|
16377
16751
|
});
|
|
16378
16752
|
return {
|
|
16379
16753
|
content: [{ type: "text", text: lines.join("\n") || "(no threads)" }]
|
|
@@ -16382,13 +16756,14 @@ async function startMcpServer() {
|
|
|
16382
16756
|
);
|
|
16383
16757
|
server.tool(
|
|
16384
16758
|
"get_thread",
|
|
16385
|
-
"Get a compact thread summary by id/ref",
|
|
16759
|
+
"Get a compact thread summary by id/ref. While running, includes progress (last tool/thinking) and lastActivityAt.",
|
|
16386
16760
|
{ ref: import_zod3.z.string() },
|
|
16387
16761
|
async ({ ref }) => {
|
|
16388
16762
|
const t = orch.getThread(ref);
|
|
16389
16763
|
if (!t) {
|
|
16390
16764
|
return { content: [{ type: "text", text: `Thread not found: ${ref}` }], isError: true };
|
|
16391
16765
|
}
|
|
16766
|
+
const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
|
|
16392
16767
|
const summary = {
|
|
16393
16768
|
id: t.id,
|
|
16394
16769
|
title: t.title,
|
|
@@ -16403,7 +16778,10 @@ async function startMcpServer() {
|
|
|
16403
16778
|
messageCount: t.messages.length,
|
|
16404
16779
|
devPort: t.devPort,
|
|
16405
16780
|
prUrl: t.prUrl,
|
|
16406
|
-
lastError: t.lastError ?? null
|
|
16781
|
+
lastError: t.lastError ?? null,
|
|
16782
|
+
stillRunning: t.status === "running" || t.status === "queued",
|
|
16783
|
+
progress: live?.summary ?? null,
|
|
16784
|
+
lastActivityAt: live?.updatedAt ?? null
|
|
16407
16785
|
};
|
|
16408
16786
|
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
16409
16787
|
}
|
|
@@ -16729,13 +17107,15 @@ async function startMcpServer() {
|
|
|
16729
17107
|
);
|
|
16730
17108
|
server.tool(
|
|
16731
17109
|
"wait_for_turn",
|
|
16732
|
-
"
|
|
17110
|
+
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. If stillRunning is true, progress is tools/thinking \u2014 call wait_for_turn again. Do not send a check-in prompt or assume a hang. On status error, lastError/text is the failure.",
|
|
16733
17111
|
{
|
|
16734
17112
|
ref: import_zod3.z.string(),
|
|
16735
17113
|
timeoutMs: import_zod3.z.number().optional()
|
|
16736
17114
|
},
|
|
16737
17115
|
async ({ ref, timeoutMs }) => {
|
|
16738
|
-
const thread = await orch.waitForTurn(ref, timeoutMs
|
|
17116
|
+
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
17117
|
+
resolveIfStillRunning: true
|
|
17118
|
+
});
|
|
16739
17119
|
const result = orch.getTurnResult(thread.id);
|
|
16740
17120
|
return {
|
|
16741
17121
|
content: [
|
|
@@ -16744,7 +17124,12 @@ async function startMcpServer() {
|
|
|
16744
17124
|
text: JSON.stringify({
|
|
16745
17125
|
id: thread.id,
|
|
16746
17126
|
status: result.status,
|
|
16747
|
-
text: result.text
|
|
17127
|
+
text: result.text,
|
|
17128
|
+
lastError: result.lastError,
|
|
17129
|
+
stillRunning: result.stillRunning,
|
|
17130
|
+
progress: result.progress,
|
|
17131
|
+
lastActivityAt: result.lastActivityAt,
|
|
17132
|
+
hint: result.stillRunning ? MCP_WAIT_STILL_RUNNING_HINT : void 0
|
|
16748
17133
|
})
|
|
16749
17134
|
}
|
|
16750
17135
|
]
|
|
@@ -16753,11 +17138,21 @@ async function startMcpServer() {
|
|
|
16753
17138
|
);
|
|
16754
17139
|
server.tool(
|
|
16755
17140
|
"get_turn_result",
|
|
16756
|
-
"
|
|
17141
|
+
"Assistant message when the turn finished, or live progress while stillRunning. Not the full transcript.",
|
|
16757
17142
|
{ ref: import_zod3.z.string() },
|
|
16758
17143
|
async ({ ref }) => {
|
|
16759
17144
|
const result = orch.getTurnResult(ref);
|
|
16760
|
-
return {
|
|
17145
|
+
return {
|
|
17146
|
+
content: [
|
|
17147
|
+
{
|
|
17148
|
+
type: "text",
|
|
17149
|
+
text: JSON.stringify({
|
|
17150
|
+
...result,
|
|
17151
|
+
hint: result.stillRunning ? MCP_WAIT_STILL_RUNNING_HINT : void 0
|
|
17152
|
+
})
|
|
17153
|
+
}
|
|
17154
|
+
]
|
|
17155
|
+
};
|
|
16761
17156
|
}
|
|
16762
17157
|
);
|
|
16763
17158
|
server.tool(
|
|
@@ -16874,7 +17269,7 @@ async function startMcpServer() {
|
|
|
16874
17269
|
);
|
|
16875
17270
|
server.tool(
|
|
16876
17271
|
"request_review",
|
|
16877
|
-
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
|
|
17272
|
+
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
|
|
16878
17273
|
{ ref: import_zod3.z.string().describe("Worktree thread id/ref to review") },
|
|
16879
17274
|
async ({ ref }) => {
|
|
16880
17275
|
try {
|
|
@@ -16902,7 +17297,7 @@ async function startMcpServer() {
|
|
|
16902
17297
|
);
|
|
16903
17298
|
server.tool(
|
|
16904
17299
|
"ask_git",
|
|
16905
|
-
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn. Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
|
|
17300
|
+
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
|
|
16906
17301
|
{
|
|
16907
17302
|
ref: import_zod3.z.string().describe("Worktree thread id/ref"),
|
|
16908
17303
|
action: import_zod3.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -16953,7 +17348,7 @@ async function startMcpServer() {
|
|
|
16953
17348
|
);
|
|
16954
17349
|
server.tool(
|
|
16955
17350
|
"fork_worktree",
|
|
16956
|
-
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
17351
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
16957
17352
|
{
|
|
16958
17353
|
ref: import_zod3.z.string().describe("Worktree thread id/ref to fork"),
|
|
16959
17354
|
through_index: import_zod3.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
@@ -16998,7 +17393,7 @@ async function startMcpServer() {
|
|
|
16998
17393
|
);
|
|
16999
17394
|
server.tool(
|
|
17000
17395
|
"fork_chat",
|
|
17001
|
-
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
17396
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
17002
17397
|
{
|
|
17003
17398
|
ref: import_zod3.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
17004
17399
|
through_index: import_zod3.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|