@sideboard-ai/core 0.1.81 → 0.1.84
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 +106 -53
- package/dist/agents/cursor-runner.js +96 -55
- package/dist/{agents-UQ7D6U6L.js → agents-JFXM66QA.js} +7 -6
- package/dist/{agents-EBKJ55PN.js → agents-OPTHS4IX.js} +5 -5
- package/dist/{app-settings-6IOQYGBK.js → app-settings-K6RFCRF6.js} +2 -1
- package/dist/{app-settings-NILNWNNQ.js → app-settings-PEXK5VEX.js} +1 -1
- package/dist/chunk-5KLC2MWZ.js +40 -0
- package/dist/{chunk-XKNBSYA7.js → chunk-AY53MPDE.js} +39 -1
- package/dist/{chunk-4AVU4QXO.js → chunk-BBQ6HXKS.js} +67 -17
- package/dist/{chunk-SEOICVGB.js → chunk-CBJSPTBG.js} +30 -6
- package/dist/{chunk-FUUVPN4A.js → chunk-CKBIQ54F.js} +78 -49
- package/dist/{chunk-IFPN6TER.js → chunk-I3FRXL7J.js} +4 -1
- package/dist/{chunk-CQBQWX73.js → chunk-JPHG2KCC.js} +8 -3
- package/dist/{chunk-4HJK57XU.js → chunk-NW7MEJHO.js} +8 -3
- package/dist/{chunk-K553XK7M.js → chunk-ODZ2ZOA4.js} +2 -2
- package/dist/{chunk-6F2TQFJE.js → chunk-OIEFHOP7.js} +3 -3
- package/dist/{chunk-XNECBC6T.js → chunk-R753UFSX.js} +39 -11
- package/dist/{chunk-TDJ43HUD.js → chunk-UN3LVQB2.js} +2 -2
- package/dist/{chunk-P5VDPGAN.js → chunk-WJ5TINR6.js} +3 -3
- package/dist/{chunk-YVVXWWCC.js → chunk-XW47PL6A.js} +81 -49
- package/dist/{coordinator-prompt-NYNEFKIV.js → coordinator-prompt-DOIOSLUN.js} +3 -3
- package/dist/{coordinator-prompt-JQF72JKX.js → coordinator-prompt-RCTIIZ6C.js} +4 -3
- package/dist/{global-workspace-NUMUJRWG.js → global-workspace-33BIG7KK.js} +4 -4
- package/dist/{global-workspace-DZM4ZFGU.js → global-workspace-73OAKD3C.js} +5 -4
- package/dist/index.cjs +235 -88
- package/dist/index.d.cts +36 -9
- package/dist/index.d.ts +36 -9
- package/dist/index.js +69 -45
- package/dist/mcp/run-stdio.cjs +233 -88
- package/dist/mcp/run-stdio.js +60 -43
- package/dist/{workspaces-QLRCOXAY.js → workspaces-SRITISKA.js} +6 -5
- package/dist/{workspaces-SUU2GO3A.js → workspaces-WDLY34MX.js} +5 -5
- package/dist/{worktree-5VLLFI5W.js → worktree-SGZVAWOQ.js} +3 -2
- package/dist/{worktree-UNSOCYZU.js → worktree-TUAO74SI.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -31,6 +31,46 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
+
// src/hook/nested-electron-env.ts
|
|
35
|
+
function isNestedElectronEnvKey(key) {
|
|
36
|
+
return NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
37
|
+
}
|
|
38
|
+
function stripNestedElectronEnv(env) {
|
|
39
|
+
const out = { ...env };
|
|
40
|
+
for (const key of Object.keys(out)) {
|
|
41
|
+
if (isNestedElectronEnvKey(key)) delete out[key];
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function dropNestedElectronEnvFromProcess(env = process.env) {
|
|
46
|
+
for (const key of Object.keys(env)) {
|
|
47
|
+
if (isNestedElectronEnvKey(key)) delete env[key];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function wrapElectronAsNodeLaunch(file, args) {
|
|
51
|
+
if (process.platform === "win32") return { file, args };
|
|
52
|
+
return {
|
|
53
|
+
file: "/bin/sh",
|
|
54
|
+
args: ["-c", STRIP_NESTED_ELECTRON_THEN_EXEC, "sh", file, ...args]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function isStrippedElectronLaunch(command, args) {
|
|
58
|
+
return command === "/bin/sh" && Boolean(args?.[1]?.includes("ELECTRON_RUN_AS_NODE") && args[1].includes("unset"));
|
|
59
|
+
}
|
|
60
|
+
var NESTED_ELECTRON_ENV_PREFIXES, STRIP_NESTED_ELECTRON_THEN_EXEC;
|
|
61
|
+
var init_nested_electron_env = __esm({
|
|
62
|
+
"src/hook/nested-electron-env.ts"() {
|
|
63
|
+
"use strict";
|
|
64
|
+
NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
65
|
+
STRIP_NESTED_ELECTRON_THEN_EXEC = [
|
|
66
|
+
"vars=`printenv | awk -F= '/^(ELECTRON_|CHROME_)/{print $1}'`",
|
|
67
|
+
'[ -n "$vars" ] && unset $vars',
|
|
68
|
+
"export ELECTRON_RUN_AS_NODE=1",
|
|
69
|
+
'exec "$@"'
|
|
70
|
+
].join("; ");
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
34
74
|
// src/hook/settings.ts
|
|
35
75
|
function expandHome(path) {
|
|
36
76
|
if (path.startsWith("~/") || path === "~") {
|
|
@@ -693,20 +733,36 @@ function pushTurnStderr(tail, line, maxLines = 12) {
|
|
|
693
733
|
tail.push(trimmed);
|
|
694
734
|
while (tail.length > maxLines) tail.shift();
|
|
695
735
|
}
|
|
736
|
+
function looksLikeMinifiedJsDump(line) {
|
|
737
|
+
if (line.length < 200) return false;
|
|
738
|
+
return /yield Promise\.all/.test(line) || /\(0,[A-Za-z$]\.\w+\)/.test(line) || /CURSOR_RIPGREP_PATH/.test(line);
|
|
739
|
+
}
|
|
740
|
+
function looksLikeNestedElectronCrash(line) {
|
|
741
|
+
return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
|
|
742
|
+
}
|
|
743
|
+
function clipStderr(text3, maxChars) {
|
|
744
|
+
const trimmed = text3.trim();
|
|
745
|
+
if (trimmed.length <= maxChars) return trimmed;
|
|
746
|
+
return trimmed.slice(0, maxChars);
|
|
747
|
+
}
|
|
696
748
|
function summarizeTurnStderr(tail, maxChars = 500) {
|
|
697
749
|
if (tail.length === 0) return "";
|
|
750
|
+
const cursorStartup = [...tail].reverse().find((line) => /cursor startup failed:/i.test(line));
|
|
751
|
+
if (cursorStartup) return clipStderr(cursorStartup, maxChars);
|
|
752
|
+
if (tail.some(looksLikeNestedElectronCrash)) return NESTED_ELECTRON_SUMMARY;
|
|
698
753
|
const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
|
|
699
|
-
if (moduleMissing)
|
|
700
|
-
|
|
754
|
+
if (moduleMissing) return clipStderr(moduleMissing, maxChars);
|
|
755
|
+
const useful = tail.filter((line) => !looksLikeMinifiedJsDump(line));
|
|
756
|
+
if (useful.length === 0 && tail.some(looksLikeMinifiedJsDump)) {
|
|
757
|
+
return MINIFIED_DUMP_SUMMARY;
|
|
701
758
|
}
|
|
702
|
-
const joined = tail.slice(-6).join("\n").trim();
|
|
703
|
-
|
|
704
|
-
return joined.slice(joined.length - maxChars);
|
|
759
|
+
const joined = (useful.length ? useful : tail).slice(-6).join("\n").trim();
|
|
760
|
+
return clipStderr(joined, maxChars);
|
|
705
761
|
}
|
|
706
762
|
function looksLikeInvalidAgentSession(text3) {
|
|
707
763
|
const lower = text3.trim().toLowerCase();
|
|
708
764
|
if (!lower) return false;
|
|
709
|
-
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);
|
|
765
|
+
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);
|
|
710
766
|
}
|
|
711
767
|
function looksLikeAgentFailureMessage(text3) {
|
|
712
768
|
const lower = text3.trim().toLowerCase();
|
|
@@ -746,6 +802,12 @@ function humanizeAgentFailDetail(detail) {
|
|
|
746
802
|
if (/context.*(too long|exceed)|prompt is too long|conversation too long/.test(lower)) {
|
|
747
803
|
return `${raw} \u2014 start a new chat or compact context, then retry.`;
|
|
748
804
|
}
|
|
805
|
+
if (/hascustomhostobject|electroninitializeicuandstartnode|nested chromium/i.test(lower)) {
|
|
806
|
+
return `${raw} \u2014 retry the turn; if it keeps failing, pick another agent.`;
|
|
807
|
+
}
|
|
808
|
+
if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
|
|
809
|
+
return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
|
|
810
|
+
}
|
|
749
811
|
return raw;
|
|
750
812
|
}
|
|
751
813
|
function formatTurnExitError(exitCode, stderrSummary) {
|
|
@@ -761,11 +823,13 @@ function formatTurnExitError(exitCode, stderrSummary) {
|
|
|
761
823
|
if (looksLikeAgentFailureMessage(raw)) return detail;
|
|
762
824
|
return `exit ${code}: ${detail}`;
|
|
763
825
|
}
|
|
764
|
-
var NODE_VERSION_FOOTER;
|
|
826
|
+
var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, MINIFIED_DUMP_SUMMARY;
|
|
765
827
|
var init_error_detail = __esm({
|
|
766
828
|
"src/agents/error-detail.ts"() {
|
|
767
829
|
"use strict";
|
|
768
830
|
NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
|
|
831
|
+
NESTED_ELECTRON_SUMMARY = "Cursor local agent crashed at Electron startup (nested Chromium / HasCustomHostObject)";
|
|
832
|
+
MINIFIED_DUMP_SUMMARY = "Cursor local agent crashed during startup (truncated crash dump)";
|
|
769
833
|
}
|
|
770
834
|
});
|
|
771
835
|
|
|
@@ -1649,6 +1713,19 @@ function formatGhLandError(raw, opts) {
|
|
|
1649
1713
|
}
|
|
1650
1714
|
return detail || "Failed to create or update pull request";
|
|
1651
1715
|
}
|
|
1716
|
+
function isPrNotMergeableError(text3) {
|
|
1717
|
+
return /not mergeable|cannot be cleanly created|cannot merge cleanly|Merge conflict|\bCONFLICTING\b|must be (updated|rebased)|branch is out of date|needs? to be (updated|rebased)|Resolve conflicts or update the branch/i.test(
|
|
1718
|
+
text3
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
function formatMergePrError(raw) {
|
|
1722
|
+
const trimmed = raw.trim();
|
|
1723
|
+
if (!trimmed) return "gh pr merge failed";
|
|
1724
|
+
if (isPrNotMergeableError(trimmed)) {
|
|
1725
|
+
return "This pull request cannot merge cleanly into the base branch. Resolve conflicts or update the branch, then retry.";
|
|
1726
|
+
}
|
|
1727
|
+
return extractGhErrorDetail(trimmed) || trimmed;
|
|
1728
|
+
}
|
|
1652
1729
|
var init_gh_errors = __esm({
|
|
1653
1730
|
"src/git/gh-errors.ts"() {
|
|
1654
1731
|
"use strict";
|
|
@@ -2855,7 +2932,7 @@ function applyAppEnvironment(target = process.env, settings = loadAppSettings())
|
|
|
2855
2932
|
}
|
|
2856
2933
|
function childEnvWithAppSettings(extra) {
|
|
2857
2934
|
const settings = loadAppSettings();
|
|
2858
|
-
const env = { ...process.env };
|
|
2935
|
+
const env = stripNestedElectronEnv({ ...process.env });
|
|
2859
2936
|
applyAppEnvironment(env, settings);
|
|
2860
2937
|
if (extra) {
|
|
2861
2938
|
for (const [k, v] of Object.entries(extra)) {
|
|
@@ -2876,6 +2953,7 @@ var init_app_settings = __esm({
|
|
|
2876
2953
|
import_node_os4 = require("os");
|
|
2877
2954
|
import_node_path10 = require("path");
|
|
2878
2955
|
init_thinking_effort();
|
|
2956
|
+
init_nested_electron_env();
|
|
2879
2957
|
init_paths();
|
|
2880
2958
|
init_private_file();
|
|
2881
2959
|
init_secret_vault();
|
|
@@ -3615,6 +3693,41 @@ ${result.stderr}`;
|
|
|
3615
3693
|
].slice(0, 20) : [];
|
|
3616
3694
|
return { conflicting, base: baseName, files };
|
|
3617
3695
|
}
|
|
3696
|
+
async function probeLocalMergeGate(cwd, gate) {
|
|
3697
|
+
const mergeable = (gate?.mergeable ?? "").toUpperCase();
|
|
3698
|
+
const mergeState = (gate?.mergeStateStatus ?? "").toUpperCase();
|
|
3699
|
+
const alreadyConflicting = mergeable === "CONFLICTING" || mergeState === "DIRTY";
|
|
3700
|
+
const inQueue = gate ? prIsInMergeQueue(gate) : false;
|
|
3701
|
+
if (alreadyConflicting || inQueue) return { gate, files: [] };
|
|
3702
|
+
try {
|
|
3703
|
+
const local = await detectLocalMergeConflicts(cwd, gate?.baseRefName ?? null);
|
|
3704
|
+
if (local.conflicting) {
|
|
3705
|
+
return {
|
|
3706
|
+
gate: {
|
|
3707
|
+
mergeable: "CONFLICTING",
|
|
3708
|
+
mergeStateStatus: "DIRTY",
|
|
3709
|
+
reviewDecision: gate?.reviewDecision ?? null,
|
|
3710
|
+
baseRefName: local.base,
|
|
3711
|
+
url: gate?.url ?? null,
|
|
3712
|
+
isInMergeQueue: false
|
|
3713
|
+
},
|
|
3714
|
+
files: local.files
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
if (gate && (mergeable === "UNKNOWN" || mergeState === "UNKNOWN")) {
|
|
3718
|
+
return {
|
|
3719
|
+
gate: {
|
|
3720
|
+
...gate,
|
|
3721
|
+
mergeable: gate.mergeable === "UNKNOWN" ? "MERGEABLE" : gate.mergeable,
|
|
3722
|
+
mergeStateStatus: gate.mergeStateStatus === "UNKNOWN" ? "CLEAN" : gate.mergeStateStatus
|
|
3723
|
+
},
|
|
3724
|
+
files: []
|
|
3725
|
+
};
|
|
3726
|
+
}
|
|
3727
|
+
} catch {
|
|
3728
|
+
}
|
|
3729
|
+
return { gate, files: [] };
|
|
3730
|
+
}
|
|
3618
3731
|
async function getPrChecks(cwd, selector) {
|
|
3619
3732
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
3620
3733
|
const args = [
|
|
@@ -3650,47 +3763,9 @@ async function getPrChecks(cwd, selector) {
|
|
|
3650
3763
|
}
|
|
3651
3764
|
}
|
|
3652
3765
|
let gate = await fetchPrMergeGate(cwd, selector, slug);
|
|
3653
|
-
const
|
|
3654
|
-
|
|
3655
|
-
const
|
|
3656
|
-
const inQueue = gate ? prIsInMergeQueue(gate) : false;
|
|
3657
|
-
const needsLocalProbe = !alreadyConflicting && !inQueue;
|
|
3658
|
-
if (needsLocalProbe) {
|
|
3659
|
-
try {
|
|
3660
|
-
const local = await detectLocalMergeConflicts(cwd, gate?.baseRefName ?? null);
|
|
3661
|
-
if (local.conflicting) {
|
|
3662
|
-
const fileHint = local.files.length > 0 ? ` Conflicting paths: ${local.files.slice(0, 8).join(", ")}${local.files.length > 8 ? "\u2026" : ""}.` : "";
|
|
3663
|
-
gate = {
|
|
3664
|
-
mergeable: "CONFLICTING",
|
|
3665
|
-
mergeStateStatus: "DIRTY",
|
|
3666
|
-
reviewDecision: gate?.reviewDecision ?? null,
|
|
3667
|
-
baseRefName: local.base,
|
|
3668
|
-
url: gate?.url ?? null,
|
|
3669
|
-
isInMergeQueue: false
|
|
3670
|
-
};
|
|
3671
|
-
const ciFailed2 = ciChecks.some((c) => c.bucket === "fail");
|
|
3672
|
-
const review2 = (gate.reviewDecision ?? "").toUpperCase();
|
|
3673
|
-
const hasReviewRow2 = review2 === "CHANGES_REQUESTED" || review2 === "REVIEW_REQUIRED";
|
|
3674
|
-
const gateRows2 = buildMergeGateChecks(gate, {
|
|
3675
|
-
suppressGenericBlocked: ciFailed2 || hasReviewRow2
|
|
3676
|
-
}).map(
|
|
3677
|
-
(row) => row.kind === "mergeability" && row.name === "Merge conflicts" ? {
|
|
3678
|
-
...row,
|
|
3679
|
-
description: `${row.description ?? ""}${fileHint}`.trim()
|
|
3680
|
-
} : row
|
|
3681
|
-
);
|
|
3682
|
-
return [...gateRows2, ...ciChecks];
|
|
3683
|
-
}
|
|
3684
|
-
if (gate && (mergeable === "UNKNOWN" || mergeState === "UNKNOWN")) {
|
|
3685
|
-
gate = {
|
|
3686
|
-
...gate,
|
|
3687
|
-
mergeable: gate.mergeable === "UNKNOWN" ? "MERGEABLE" : gate.mergeable,
|
|
3688
|
-
mergeStateStatus: gate.mergeStateStatus === "UNKNOWN" ? "CLEAN" : gate.mergeStateStatus
|
|
3689
|
-
};
|
|
3690
|
-
}
|
|
3691
|
-
} catch {
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3766
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
3767
|
+
gate = probed.gate;
|
|
3768
|
+
const fileHint = probed.files.length > 0 ? ` Conflicting paths: ${probed.files.slice(0, 8).join(", ")}${probed.files.length > 8 ? "\u2026" : ""}.` : "";
|
|
3694
3769
|
if (!gate) {
|
|
3695
3770
|
return ciChecks;
|
|
3696
3771
|
}
|
|
@@ -3700,7 +3775,12 @@ async function getPrChecks(cwd, selector) {
|
|
|
3700
3775
|
const gateRows = buildMergeGateChecks(gate, {
|
|
3701
3776
|
// Avoid duplicating "blocked" when CI failures or review rows already explain it.
|
|
3702
3777
|
suppressGenericBlocked: ciFailed || hasReviewRow
|
|
3703
|
-
})
|
|
3778
|
+
}).map(
|
|
3779
|
+
(row) => fileHint && row.kind === "mergeability" && row.name === "Merge conflicts" ? {
|
|
3780
|
+
...row,
|
|
3781
|
+
description: `${row.description ?? ""}${fileHint}`.trim()
|
|
3782
|
+
} : row
|
|
3783
|
+
);
|
|
3704
3784
|
return [...gateRows, ...ciChecks];
|
|
3705
3785
|
}
|
|
3706
3786
|
async function getPrMeta(cwd, selector) {
|
|
@@ -3710,7 +3790,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3710
3790
|
"view",
|
|
3711
3791
|
selector,
|
|
3712
3792
|
"--json",
|
|
3713
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus"
|
|
3793
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus,mergeable"
|
|
3714
3794
|
];
|
|
3715
3795
|
if (slug) viewArgs.push("--repo", slug);
|
|
3716
3796
|
let { stdout, exitCode, stderr } = await gh(viewArgs, cwd, { reject: false });
|
|
@@ -3720,7 +3800,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3720
3800
|
"view",
|
|
3721
3801
|
selector,
|
|
3722
3802
|
"--json",
|
|
3723
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus"
|
|
3803
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus,mergeable"
|
|
3724
3804
|
];
|
|
3725
3805
|
if (slug) retry.push("--repo", slug);
|
|
3726
3806
|
({ stdout, exitCode, stderr } = await gh(retry, cwd, { reject: false }));
|
|
@@ -3731,16 +3811,28 @@ async function getPrMeta(cwd, selector) {
|
|
|
3731
3811
|
}
|
|
3732
3812
|
try {
|
|
3733
3813
|
const view = JSON.parse(stdout);
|
|
3814
|
+
const gate = {
|
|
3815
|
+
mergeable: typeof view.mergeable === "string" && view.mergeable ? view.mergeable : null,
|
|
3816
|
+
mergeStateStatus: typeof view.mergeStateStatus === "string" && view.mergeStateStatus ? view.mergeStateStatus : null,
|
|
3817
|
+
reviewDecision: typeof view.reviewDecision === "string" && view.reviewDecision ? view.reviewDecision : null,
|
|
3818
|
+
baseRefName: String(view.baseRefName ?? ""),
|
|
3819
|
+
url: String(view.url ?? ""),
|
|
3820
|
+
isInMergeQueue: parseInMergeQueue(view)
|
|
3821
|
+
};
|
|
3822
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
3823
|
+
const resolved = probed.gate ?? gate;
|
|
3734
3824
|
return {
|
|
3735
3825
|
number: Number(view.number),
|
|
3736
3826
|
title: String(view.title ?? ""),
|
|
3737
3827
|
url: String(view.url ?? ""),
|
|
3738
3828
|
state: String(view.state ?? ""),
|
|
3739
3829
|
isDraft: Boolean(view.isDraft),
|
|
3740
|
-
reviewDecision:
|
|
3741
|
-
baseRefName: String(view.baseRefName ?? ""),
|
|
3830
|
+
reviewDecision: resolved.reviewDecision,
|
|
3831
|
+
baseRefName: resolved.baseRefName || String(view.baseRefName ?? ""),
|
|
3742
3832
|
headRefName: String(view.headRefName ?? ""),
|
|
3743
|
-
isInMergeQueue:
|
|
3833
|
+
isInMergeQueue: Boolean(resolved.isInMergeQueue),
|
|
3834
|
+
mergeable: resolved.mergeable,
|
|
3835
|
+
mergeStateStatus: resolved.mergeStateStatus
|
|
3744
3836
|
};
|
|
3745
3837
|
} catch {
|
|
3746
3838
|
return null;
|
|
@@ -4208,7 +4300,9 @@ async function mergePr(cwd, selector, opts) {
|
|
|
4208
4300
|
if (slug) args.push("--repo", slug);
|
|
4209
4301
|
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
4210
4302
|
if (exitCode !== 0) {
|
|
4211
|
-
throw new Error(
|
|
4303
|
+
throw new Error(
|
|
4304
|
+
formatMergePrError(stderr.trim() || stdout.trim() || "gh pr merge failed")
|
|
4305
|
+
);
|
|
4212
4306
|
}
|
|
4213
4307
|
const after = await gh(viewArgs, cwd, { reject: false });
|
|
4214
4308
|
if (after.exitCode === 0 && after.stdout.trim()) {
|
|
@@ -4603,6 +4697,7 @@ var init_coordinator_prompt = __esm({
|
|
|
4603
4697
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
4604
4698
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
4605
4699
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
4700
|
+
"- ask_user \u2014 multiple-choice questions in the composer (any mode, not only Plan). Explain options in chat first, include a description on every option, then wait for answers.",
|
|
4606
4701
|
"- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work. Turn OFF when the user says they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
|
|
4607
4702
|
"Workspaces:",
|
|
4608
4703
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
@@ -4622,7 +4717,11 @@ var init_coordinator_prompt = __esm({
|
|
|
4622
4717
|
'- 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',
|
|
4623
4718
|
"- 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.",
|
|
4624
4719
|
'- 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.',
|
|
4625
|
-
'- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "
|
|
4720
|
+
'- 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.',
|
|
4721
|
+
"Process guides:",
|
|
4722
|
+
"- Recurring multi-item / fan-out: if the child worktree has `.claude/skills/graph-engineering/SKILL.md`, tell the worker to follow it (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch three threads.",
|
|
4723
|
+
"- Recurring shapes: have the worktree agent write `.claude/skills/<kebab-name>/SKILL.md` (commit it) so later threads and native Claude Code / attach see it. Do not use `.sideboard/skills` for new guides. Codex/OpenCode: one line in AGENTS.md pointing at that file.",
|
|
4724
|
+
"- One-offs: no guide. Same miss across threads: edit the skill or `.sideboard/review.md`, then rerun the batch \u2014 do not patch three threads.",
|
|
4626
4725
|
"Human-only (do not attempt): ready-for-review land (confirm_land), purge_thread.",
|
|
4627
4726
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
4628
4727
|
"Bash / Read / etc: allowed for (1) inspecting target worktrees / registered repo paths from MCP, and (2) greenfield setup under ~/sideboard/repos (git clone, gh repo create, git init+remote). Never git init/clone *inside* this synthetic home cwd \u2014 emptiness here is expected, not a bug."
|
|
@@ -5528,6 +5627,18 @@ var init_profile = __esm({
|
|
|
5528
5627
|
function isAsarPath(filePath) {
|
|
5529
5628
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
5530
5629
|
}
|
|
5630
|
+
function applyNodeLaunch(launch, args) {
|
|
5631
|
+
if (!launch.env.ELECTRON_RUN_AS_NODE) {
|
|
5632
|
+
return { file: launch.file, args, env: launch.env };
|
|
5633
|
+
}
|
|
5634
|
+
const wrapped = wrapElectronAsNodeLaunch(launch.file, args);
|
|
5635
|
+
if (process.platform === "win32") {
|
|
5636
|
+
return { file: wrapped.file, args: wrapped.args, env: launch.env };
|
|
5637
|
+
}
|
|
5638
|
+
const env = { ...launch.env };
|
|
5639
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
5640
|
+
return { file: wrapped.file, args: wrapped.args, env };
|
|
5641
|
+
}
|
|
5531
5642
|
async function resolveNodeLaunch(scriptPath) {
|
|
5532
5643
|
if (isAsarPath(scriptPath)) {
|
|
5533
5644
|
return {
|
|
@@ -5548,6 +5659,7 @@ async function resolveNodeLaunch(scriptPath) {
|
|
|
5548
5659
|
var init_node_launch = __esm({
|
|
5549
5660
|
"src/agents/node-launch.ts"() {
|
|
5550
5661
|
"use strict";
|
|
5662
|
+
init_nested_electron_env();
|
|
5551
5663
|
init_run();
|
|
5552
5664
|
}
|
|
5553
5665
|
});
|
|
@@ -5674,11 +5786,11 @@ async function resolveSideboardMcpServer() {
|
|
|
5674
5786
|
if (entry) {
|
|
5675
5787
|
const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
|
|
5676
5788
|
const scriptArgs = isCli ? [entry, "mcp"] : [entry];
|
|
5677
|
-
const launch = await resolveNodeLaunch(entry);
|
|
5789
|
+
const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
|
|
5678
5790
|
return {
|
|
5679
5791
|
name: "sideboard",
|
|
5680
5792
|
command: launch.file,
|
|
5681
|
-
args:
|
|
5793
|
+
args: launch.args,
|
|
5682
5794
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
5683
5795
|
};
|
|
5684
5796
|
}
|
|
@@ -5724,10 +5836,19 @@ async function buildInjectedMcpServers(opts) {
|
|
|
5724
5836
|
function toCursorMcpServers(servers) {
|
|
5725
5837
|
const out = {};
|
|
5726
5838
|
for (const s of servers) {
|
|
5839
|
+
const env = s.env ? { ...s.env } : void 0;
|
|
5840
|
+
if (env) delete env.ELECTRON_RUN_AS_NODE;
|
|
5841
|
+
let command = s.command;
|
|
5842
|
+
let args = s.args;
|
|
5843
|
+
if (process.platform !== "win32" && !isStrippedElectronLaunch(command, args)) {
|
|
5844
|
+
const wrapped = wrapElectronAsNodeLaunch(command, args ?? []);
|
|
5845
|
+
command = wrapped.file;
|
|
5846
|
+
args = wrapped.args;
|
|
5847
|
+
}
|
|
5727
5848
|
out[s.name] = {
|
|
5728
|
-
command
|
|
5729
|
-
...
|
|
5730
|
-
...
|
|
5849
|
+
command,
|
|
5850
|
+
...args && args.length > 0 ? { args } : {},
|
|
5851
|
+
...env && Object.keys(env).length > 0 ? { env } : {}
|
|
5731
5852
|
};
|
|
5732
5853
|
}
|
|
5733
5854
|
return out;
|
|
@@ -5794,6 +5915,7 @@ var init_injected_mcp = __esm({
|
|
|
5794
5915
|
init_app_settings();
|
|
5795
5916
|
init_paths();
|
|
5796
5917
|
init_node_launch();
|
|
5918
|
+
init_nested_electron_env();
|
|
5797
5919
|
import_meta = {};
|
|
5798
5920
|
SIDEBOARD_MCP_ALLOWED_TOOLS = [
|
|
5799
5921
|
"mcp__sideboard",
|
|
@@ -6883,10 +7005,13 @@ var init_cursor = __esm({
|
|
|
6883
7005
|
};
|
|
6884
7006
|
const runner = cursorRunnerPath();
|
|
6885
7007
|
const isTs = runner.endsWith(".ts");
|
|
6886
|
-
const launch =
|
|
7008
|
+
const launch = applyNodeLaunch(
|
|
7009
|
+
await resolveNodeLaunch(runner),
|
|
7010
|
+
isTs ? ["--import", "tsx", runner] : [runner]
|
|
7011
|
+
);
|
|
6887
7012
|
return {
|
|
6888
7013
|
file: launch.file,
|
|
6889
|
-
args:
|
|
7014
|
+
args: launch.args,
|
|
6890
7015
|
cwd: thread.worktreePath,
|
|
6891
7016
|
stdin: JSON.stringify(req),
|
|
6892
7017
|
env: {
|
|
@@ -8175,6 +8300,9 @@ var init_cursor_recover = __esm({
|
|
|
8175
8300
|
}
|
|
8176
8301
|
});
|
|
8177
8302
|
|
|
8303
|
+
// src/mcp/run-stdio.ts
|
|
8304
|
+
init_nested_electron_env();
|
|
8305
|
+
|
|
8178
8306
|
// src/mcp/server.ts
|
|
8179
8307
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
8180
8308
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
@@ -9150,7 +9278,8 @@ function agentGitPrompt(action, opts) {
|
|
|
9150
9278
|
return "Commit, push, and open a PR in the browser.";
|
|
9151
9279
|
case "resolve-conflicts": {
|
|
9152
9280
|
const base = opts?.prBase?.trim().replace(/^refs\/heads\//, "");
|
|
9153
|
-
|
|
9281
|
+
const named = base ? ` (${base})` : "";
|
|
9282
|
+
return `Merge the remote branch${named} into your branch and resolve conflicts. Then, commit and push your changes.`;
|
|
9154
9283
|
}
|
|
9155
9284
|
case "merge":
|
|
9156
9285
|
return "Merge PR.";
|
|
@@ -9178,6 +9307,8 @@ var import_node_path18 = require("path");
|
|
|
9178
9307
|
var import_execa3 = require("execa");
|
|
9179
9308
|
var import_node_readline2 = require("readline");
|
|
9180
9309
|
init_settings();
|
|
9310
|
+
init_nested_electron_env();
|
|
9311
|
+
init_nested_electron_env();
|
|
9181
9312
|
var PORT_RANGE_SIZE = 10;
|
|
9182
9313
|
function matchSimpleGlob(pattern, name) {
|
|
9183
9314
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -9261,16 +9392,6 @@ async function captureLoginEnv() {
|
|
|
9261
9392
|
cachedLoginEnv = { ...process.env };
|
|
9262
9393
|
return { ...cachedLoginEnv };
|
|
9263
9394
|
}
|
|
9264
|
-
var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
9265
|
-
function stripNestedElectronEnv(env) {
|
|
9266
|
-
const out = { ...env };
|
|
9267
|
-
for (const key of Object.keys(out)) {
|
|
9268
|
-
if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
9269
|
-
delete out[key];
|
|
9270
|
-
}
|
|
9271
|
-
}
|
|
9272
|
-
return out;
|
|
9273
|
-
}
|
|
9274
9395
|
function buildWorkspaceScriptEnv(opts, baseEnv) {
|
|
9275
9396
|
const env = stripNestedElectronEnv({
|
|
9276
9397
|
...baseEnv ?? process.env
|
|
@@ -10421,6 +10542,10 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
10421
10542
|
|
|
10422
10543
|
**Approve** \u2014 Diff is scoped, behavior looks correct, and there are no blocking issues. Safe to merge.
|
|
10423
10544
|
</example>
|
|
10545
|
+
|
|
10546
|
+
## Growing the rules
|
|
10547
|
+
|
|
10548
|
+
If a blocking issue is a missing or ambiguous repo rule that will recur, say so and propose one sentence for \`.sideboard/review.md\` or a \`.claude/skills/<name>/SKILL.md\`. Do not only patch this diff when the same miss will happen again. New skills go under \`.claude/skills\` (Claude Code / attach) \u2014 not \`.sideboard/skills\`.
|
|
10424
10549
|
`;
|
|
10425
10550
|
|
|
10426
10551
|
// src/review/request-review.ts
|
|
@@ -12590,7 +12715,7 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
12590
12715
|
'- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
|
|
12591
12716
|
);
|
|
12592
12717
|
lines.push(
|
|
12593
|
-
'- "
|
|
12718
|
+
'- "Merge the remote branch (<base>) into your branch and resolve conflicts. Then, commit and push your changes." \u2192 fetch the PR base, merge it into this branch, resolve conflicts carefully, commit, and push until the PR is mergeable.'
|
|
12594
12719
|
);
|
|
12595
12720
|
lines.push(
|
|
12596
12721
|
'- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
|
|
@@ -12598,11 +12723,22 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
12598
12723
|
lines.push(
|
|
12599
12724
|
'- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub (this phrase is the explicit ask). If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.'
|
|
12600
12725
|
);
|
|
12726
|
+
lines.push("");
|
|
12727
|
+
lines.push(formatProcessGuideDirective());
|
|
12601
12728
|
return lines.join("\n");
|
|
12602
12729
|
}
|
|
12603
12730
|
function formatWorktreeReminder() {
|
|
12604
12731
|
return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout.";
|
|
12605
12732
|
}
|
|
12733
|
+
function formatProcessGuideDirective() {
|
|
12734
|
+
return [
|
|
12735
|
+
"Process guides (recurring work only):",
|
|
12736
|
+
"- If `.claude/skills/graph-engineering/SKILL.md` exists, follow it (`/graph-engineering`) for migrations, ports, batch fixes, and other fan-out. Judge first; state on disk; grow the rulebook; do not patch around it.",
|
|
12737
|
+
"- If this same shape of work will happen again, write `.claude/skills/<kebab-name>/SKILL.md` in this worktree (Claude Code project skill). Sideboard `/name`, Claude Code, and `attach` all load that path. Do not leave the method only in chat.",
|
|
12738
|
+
"- Do not write new skills under `.sideboard/skills` \u2014 native agents do not scan it. Point Codex/OpenCode at the file from `AGENTS.md`. Optional: symlink `.cursor/skills/<name>` to the Claude skill.",
|
|
12739
|
+
"- Skip a guide for a one-off. If a matching skill exists, follow it. Same miss twice \u2192 edit the skill (or `.sideboard/review.md`), do not patch around it."
|
|
12740
|
+
].join("\n");
|
|
12741
|
+
}
|
|
12606
12742
|
function formatArtifactDirective() {
|
|
12607
12743
|
return [
|
|
12608
12744
|
"Sideboard side column (desktop UI):",
|
|
@@ -12620,9 +12756,22 @@ function formatArtifactDirective() {
|
|
|
12620
12756
|
"Files / media browser (CMS file manager column):",
|
|
12621
12757
|
"4) Call Sideboard MCP `present_files` with optional title, path, and datasource (brightsy|memory).",
|
|
12622
12758
|
" Opens the Files column for browse/upload/pick. Do NOT say a file manager UI is missing.",
|
|
12623
|
-
"
|
|
12759
|
+
"Multiple-choice questions (any mode \u2014 not only Plan):",
|
|
12760
|
+
"5) Call Sideboard MCP `ask_user` when the user should pick from predefined options (approach forks, requirements, which API). First write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it). Include a description on every option. After calling, stop and wait for their next message with answers. Do not ask multiple-choice questions as plain chat bullets \u2014 use ask_user so Sideboard shows the composer picker.",
|
|
12761
|
+
"Never say artifacts, CMS UI, or the Files column are unavailable. Prefer present_schema for list/edit/publish; present_files for storage UI; html fences for standalone pages; ask_user for predefined-option questions."
|
|
12624
12762
|
].join("\n");
|
|
12625
12763
|
}
|
|
12764
|
+
function formatUiReminder() {
|
|
12765
|
+
return [
|
|
12766
|
+
"Sideboard side column (important):",
|
|
12767
|
+
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
12768
|
+
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
12769
|
+
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
12770
|
+
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
12771
|
+
"Do not say artifacts/CMS UI are unavailable.",
|
|
12772
|
+
"Multiple-choice questions: MCP ask_user (composer picker, any mode). Explain options in chat first, then wait for answers."
|
|
12773
|
+
].join(" ");
|
|
12774
|
+
}
|
|
12626
12775
|
|
|
12627
12776
|
// src/orchestrator/orchestrator.ts
|
|
12628
12777
|
init_types();
|
|
@@ -13166,14 +13315,7 @@ var Orchestrator = class {
|
|
|
13166
13315
|
SLACK_REPLY_FORMATTING
|
|
13167
13316
|
].join("\n") : null
|
|
13168
13317
|
].filter(Boolean).join("\n") : null;
|
|
13169
|
-
const artifactReminder = thread.agent !== "brightsy" ?
|
|
13170
|
-
"Sideboard side column (important):",
|
|
13171
|
-
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
13172
|
-
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
13173
|
-
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
13174
|
-
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
13175
|
-
"Do not say artifacts/CMS UI are unavailable."
|
|
13176
|
-
].join(" ") : null;
|
|
13318
|
+
const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
|
|
13177
13319
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
13178
13320
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
13179
13321
|
pendingSlackExternalReplies(thread.messages)
|
|
@@ -13312,7 +13454,7 @@ var Orchestrator = class {
|
|
|
13312
13454
|
}
|
|
13313
13455
|
let lastStderr = summarizeTurnStderr(stderrTail);
|
|
13314
13456
|
let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
13315
|
-
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "
|
|
13457
|
+
if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "brightsy") {
|
|
13316
13458
|
updateThread(threadId, { sessionId: null });
|
|
13317
13459
|
pushTurnStderr(
|
|
13318
13460
|
stderrTail,
|
|
@@ -13867,7 +14009,9 @@ var Orchestrator = class {
|
|
|
13867
14009
|
reviewDecision: null,
|
|
13868
14010
|
baseRefName: "",
|
|
13869
14011
|
headRefName: "",
|
|
13870
|
-
isInMergeQueue: false
|
|
14012
|
+
isInMergeQueue: false,
|
|
14013
|
+
mergeable: null,
|
|
14014
|
+
mergeStateStatus: null
|
|
13871
14015
|
};
|
|
13872
14016
|
await this.persistPrMetaAndMaybeArchive(thread, metaLike);
|
|
13873
14017
|
return { url: metaLike.url, state };
|
|
@@ -15541,7 +15685,7 @@ async function startMcpServer() {
|
|
|
15541
15685
|
);
|
|
15542
15686
|
server.tool(
|
|
15543
15687
|
"ask_user",
|
|
15544
|
-
"Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (
|
|
15688
|
+
"Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (any mode \u2014 not only Plan). Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. Use when the user should pick from predefined options (approach forks, requirements, which API) \u2014 not for \u201Cis the plan ready?\u201D. After calling, stop and wait for their next message with answers.",
|
|
15545
15689
|
{
|
|
15546
15690
|
questions: import_zod3.z.array(
|
|
15547
15691
|
import_zod3.z.object({
|
|
@@ -16455,6 +16599,7 @@ async function startMcpServer() {
|
|
|
16455
16599
|
}
|
|
16456
16600
|
|
|
16457
16601
|
// src/mcp/run-stdio.ts
|
|
16602
|
+
dropNestedElectronEnvFromProcess();
|
|
16458
16603
|
startMcpServer().catch((err) => {
|
|
16459
16604
|
console.error(err);
|
|
16460
16605
|
process.exitCode = 1;
|