@sideboard-ai/core 0.1.79 → 0.1.83
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 +18 -0
- package/dist/agents/cursor-runner.js +4 -0
- package/dist/{agents-EWPHOM6Y.js → agents-3YPUZME7.js} +6 -5
- package/dist/{agents-GRAPUXWW.js → agents-4KRD46KG.js} +5 -5
- package/dist/{app-settings-NILNWNNQ.js → app-settings-GVSOIJLZ.js} +1 -1
- package/dist/{app-settings-6IOQYGBK.js → app-settings-MQXL7OUF.js} +2 -1
- package/dist/{chunk-6T2SKGCS.js → chunk-DJFGX4RT.js} +3 -3
- package/dist/{chunk-HCGEFU3J.js → chunk-E2MIA7DO.js} +13 -14
- package/dist/{chunk-HSLQXL6M.js → chunk-ERJS3ZDP.js} +13 -14
- package/dist/{chunk-YVVXWWCC.js → chunk-GD2FM6FN.js} +81 -49
- package/dist/{chunk-FUUVPN4A.js → chunk-HBBXS2FR.js} +78 -49
- package/dist/chunk-IZ7RPF54.js +36 -0
- package/dist/{chunk-P33ZQ7ZC.js → chunk-JMRJ4F5B.js} +70 -25
- package/dist/{chunk-SHCR6RPU.js → chunk-JW6YFPQE.js} +83 -24
- package/dist/{chunk-PW5YHHP3.js → chunk-LVTNWH7B.js} +2 -2
- package/dist/{chunk-5LXWTU3J.js → chunk-NXXT5SE3.js} +3 -3
- package/dist/{chunk-5VTWBI3I.js → chunk-RS54WYYH.js} +2 -2
- package/dist/{chunk-XKNBSYA7.js → chunk-UHGN4KCL.js} +35 -1
- package/dist/{chunk-IFPN6TER.js → chunk-YO3CYL6B.js} +4 -1
- package/dist/{coordinator-prompt-2OVON2LQ.js → coordinator-prompt-46JE4NQR.js} +4 -3
- package/dist/{coordinator-prompt-K2R34A5T.js → coordinator-prompt-6ICA54JR.js} +3 -3
- package/dist/{global-workspace-VG44RZUH.js → global-workspace-GSLCEB5E.js} +5 -4
- package/dist/{global-workspace-RI367AM6.js → global-workspace-OBRWUPZG.js} +4 -4
- package/dist/index.cjs +268 -151
- package/dist/index.d.cts +52 -11
- package/dist/index.d.ts +52 -11
- package/dist/index.js +87 -87
- package/dist/mcp/run-stdio.cjs +249 -141
- package/dist/mcp/run-stdio.js +66 -76
- package/dist/{workspaces-TYPJNUSM.js → workspaces-7B3PTEF4.js} +6 -5
- package/dist/{workspaces-BQWQEZWE.js → workspaces-RJIWQB6J.js} +5 -5
- package/dist/{worktree-5VLLFI5W.js → worktree-HSN5LWY6.js} +3 -2
- package/dist/{worktree-UNSOCYZU.js → worktree-U3UIID2K.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -31,6 +31,43 @@ 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
|
+
var NESTED_ELECTRON_ENV_PREFIXES, STRIP_NESTED_ELECTRON_THEN_EXEC;
|
|
58
|
+
var init_nested_electron_env = __esm({
|
|
59
|
+
"src/hook/nested-electron-env.ts"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
62
|
+
STRIP_NESTED_ELECTRON_THEN_EXEC = [
|
|
63
|
+
"vars=`printenv | awk -F= '/^(ELECTRON_|CHROME_)/{print $1}'`",
|
|
64
|
+
'[ -n "$vars" ] && unset $vars',
|
|
65
|
+
"export ELECTRON_RUN_AS_NODE=1",
|
|
66
|
+
'exec "$@"'
|
|
67
|
+
].join("; ");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
34
71
|
// src/hook/settings.ts
|
|
35
72
|
function expandHome(path) {
|
|
36
73
|
if (path.startsWith("~/") || path === "~") {
|
|
@@ -1649,6 +1686,19 @@ function formatGhLandError(raw, opts) {
|
|
|
1649
1686
|
}
|
|
1650
1687
|
return detail || "Failed to create or update pull request";
|
|
1651
1688
|
}
|
|
1689
|
+
function isPrNotMergeableError(text3) {
|
|
1690
|
+
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(
|
|
1691
|
+
text3
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
function formatMergePrError(raw) {
|
|
1695
|
+
const trimmed = raw.trim();
|
|
1696
|
+
if (!trimmed) return "gh pr merge failed";
|
|
1697
|
+
if (isPrNotMergeableError(trimmed)) {
|
|
1698
|
+
return "This pull request cannot merge cleanly into the base branch. Resolve conflicts or update the branch, then retry.";
|
|
1699
|
+
}
|
|
1700
|
+
return extractGhErrorDetail(trimmed) || trimmed;
|
|
1701
|
+
}
|
|
1652
1702
|
var init_gh_errors = __esm({
|
|
1653
1703
|
"src/git/gh-errors.ts"() {
|
|
1654
1704
|
"use strict";
|
|
@@ -2855,7 +2905,7 @@ function applyAppEnvironment(target = process.env, settings = loadAppSettings())
|
|
|
2855
2905
|
}
|
|
2856
2906
|
function childEnvWithAppSettings(extra) {
|
|
2857
2907
|
const settings = loadAppSettings();
|
|
2858
|
-
const env = { ...process.env };
|
|
2908
|
+
const env = stripNestedElectronEnv({ ...process.env });
|
|
2859
2909
|
applyAppEnvironment(env, settings);
|
|
2860
2910
|
if (extra) {
|
|
2861
2911
|
for (const [k, v] of Object.entries(extra)) {
|
|
@@ -2876,6 +2926,7 @@ var init_app_settings = __esm({
|
|
|
2876
2926
|
import_node_os4 = require("os");
|
|
2877
2927
|
import_node_path10 = require("path");
|
|
2878
2928
|
init_thinking_effort();
|
|
2929
|
+
init_nested_electron_env();
|
|
2879
2930
|
init_paths();
|
|
2880
2931
|
init_private_file();
|
|
2881
2932
|
init_secret_vault();
|
|
@@ -3615,6 +3666,41 @@ ${result.stderr}`;
|
|
|
3615
3666
|
].slice(0, 20) : [];
|
|
3616
3667
|
return { conflicting, base: baseName, files };
|
|
3617
3668
|
}
|
|
3669
|
+
async function probeLocalMergeGate(cwd, gate) {
|
|
3670
|
+
const mergeable = (gate?.mergeable ?? "").toUpperCase();
|
|
3671
|
+
const mergeState = (gate?.mergeStateStatus ?? "").toUpperCase();
|
|
3672
|
+
const alreadyConflicting = mergeable === "CONFLICTING" || mergeState === "DIRTY";
|
|
3673
|
+
const inQueue = gate ? prIsInMergeQueue(gate) : false;
|
|
3674
|
+
if (alreadyConflicting || inQueue) return { gate, files: [] };
|
|
3675
|
+
try {
|
|
3676
|
+
const local = await detectLocalMergeConflicts(cwd, gate?.baseRefName ?? null);
|
|
3677
|
+
if (local.conflicting) {
|
|
3678
|
+
return {
|
|
3679
|
+
gate: {
|
|
3680
|
+
mergeable: "CONFLICTING",
|
|
3681
|
+
mergeStateStatus: "DIRTY",
|
|
3682
|
+
reviewDecision: gate?.reviewDecision ?? null,
|
|
3683
|
+
baseRefName: local.base,
|
|
3684
|
+
url: gate?.url ?? null,
|
|
3685
|
+
isInMergeQueue: false
|
|
3686
|
+
},
|
|
3687
|
+
files: local.files
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3690
|
+
if (gate && (mergeable === "UNKNOWN" || mergeState === "UNKNOWN")) {
|
|
3691
|
+
return {
|
|
3692
|
+
gate: {
|
|
3693
|
+
...gate,
|
|
3694
|
+
mergeable: gate.mergeable === "UNKNOWN" ? "MERGEABLE" : gate.mergeable,
|
|
3695
|
+
mergeStateStatus: gate.mergeStateStatus === "UNKNOWN" ? "CLEAN" : gate.mergeStateStatus
|
|
3696
|
+
},
|
|
3697
|
+
files: []
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
} catch {
|
|
3701
|
+
}
|
|
3702
|
+
return { gate, files: [] };
|
|
3703
|
+
}
|
|
3618
3704
|
async function getPrChecks(cwd, selector) {
|
|
3619
3705
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
3620
3706
|
const args = [
|
|
@@ -3650,47 +3736,9 @@ async function getPrChecks(cwd, selector) {
|
|
|
3650
3736
|
}
|
|
3651
3737
|
}
|
|
3652
3738
|
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
|
-
}
|
|
3739
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
3740
|
+
gate = probed.gate;
|
|
3741
|
+
const fileHint = probed.files.length > 0 ? ` Conflicting paths: ${probed.files.slice(0, 8).join(", ")}${probed.files.length > 8 ? "\u2026" : ""}.` : "";
|
|
3694
3742
|
if (!gate) {
|
|
3695
3743
|
return ciChecks;
|
|
3696
3744
|
}
|
|
@@ -3700,7 +3748,12 @@ async function getPrChecks(cwd, selector) {
|
|
|
3700
3748
|
const gateRows = buildMergeGateChecks(gate, {
|
|
3701
3749
|
// Avoid duplicating "blocked" when CI failures or review rows already explain it.
|
|
3702
3750
|
suppressGenericBlocked: ciFailed || hasReviewRow
|
|
3703
|
-
})
|
|
3751
|
+
}).map(
|
|
3752
|
+
(row) => fileHint && row.kind === "mergeability" && row.name === "Merge conflicts" ? {
|
|
3753
|
+
...row,
|
|
3754
|
+
description: `${row.description ?? ""}${fileHint}`.trim()
|
|
3755
|
+
} : row
|
|
3756
|
+
);
|
|
3704
3757
|
return [...gateRows, ...ciChecks];
|
|
3705
3758
|
}
|
|
3706
3759
|
async function getPrMeta(cwd, selector) {
|
|
@@ -3710,7 +3763,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3710
3763
|
"view",
|
|
3711
3764
|
selector,
|
|
3712
3765
|
"--json",
|
|
3713
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus"
|
|
3766
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus,mergeable"
|
|
3714
3767
|
];
|
|
3715
3768
|
if (slug) viewArgs.push("--repo", slug);
|
|
3716
3769
|
let { stdout, exitCode, stderr } = await gh(viewArgs, cwd, { reject: false });
|
|
@@ -3720,7 +3773,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3720
3773
|
"view",
|
|
3721
3774
|
selector,
|
|
3722
3775
|
"--json",
|
|
3723
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus"
|
|
3776
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus,mergeable"
|
|
3724
3777
|
];
|
|
3725
3778
|
if (slug) retry.push("--repo", slug);
|
|
3726
3779
|
({ stdout, exitCode, stderr } = await gh(retry, cwd, { reject: false }));
|
|
@@ -3731,16 +3784,28 @@ async function getPrMeta(cwd, selector) {
|
|
|
3731
3784
|
}
|
|
3732
3785
|
try {
|
|
3733
3786
|
const view = JSON.parse(stdout);
|
|
3787
|
+
const gate = {
|
|
3788
|
+
mergeable: typeof view.mergeable === "string" && view.mergeable ? view.mergeable : null,
|
|
3789
|
+
mergeStateStatus: typeof view.mergeStateStatus === "string" && view.mergeStateStatus ? view.mergeStateStatus : null,
|
|
3790
|
+
reviewDecision: typeof view.reviewDecision === "string" && view.reviewDecision ? view.reviewDecision : null,
|
|
3791
|
+
baseRefName: String(view.baseRefName ?? ""),
|
|
3792
|
+
url: String(view.url ?? ""),
|
|
3793
|
+
isInMergeQueue: parseInMergeQueue(view)
|
|
3794
|
+
};
|
|
3795
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
3796
|
+
const resolved = probed.gate ?? gate;
|
|
3734
3797
|
return {
|
|
3735
3798
|
number: Number(view.number),
|
|
3736
3799
|
title: String(view.title ?? ""),
|
|
3737
3800
|
url: String(view.url ?? ""),
|
|
3738
3801
|
state: String(view.state ?? ""),
|
|
3739
3802
|
isDraft: Boolean(view.isDraft),
|
|
3740
|
-
reviewDecision:
|
|
3741
|
-
baseRefName: String(view.baseRefName ?? ""),
|
|
3803
|
+
reviewDecision: resolved.reviewDecision,
|
|
3804
|
+
baseRefName: resolved.baseRefName || String(view.baseRefName ?? ""),
|
|
3742
3805
|
headRefName: String(view.headRefName ?? ""),
|
|
3743
|
-
isInMergeQueue:
|
|
3806
|
+
isInMergeQueue: Boolean(resolved.isInMergeQueue),
|
|
3807
|
+
mergeable: resolved.mergeable,
|
|
3808
|
+
mergeStateStatus: resolved.mergeStateStatus
|
|
3744
3809
|
};
|
|
3745
3810
|
} catch {
|
|
3746
3811
|
return null;
|
|
@@ -4208,7 +4273,9 @@ async function mergePr(cwd, selector, opts) {
|
|
|
4208
4273
|
if (slug) args.push("--repo", slug);
|
|
4209
4274
|
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
4210
4275
|
if (exitCode !== 0) {
|
|
4211
|
-
throw new Error(
|
|
4276
|
+
throw new Error(
|
|
4277
|
+
formatMergePrError(stderr.trim() || stdout.trim() || "gh pr merge failed")
|
|
4278
|
+
);
|
|
4212
4279
|
}
|
|
4213
4280
|
const after = await gh(viewArgs, cwd, { reject: false });
|
|
4214
4281
|
if (after.exitCode === 0 && after.stdout.trim()) {
|
|
@@ -4489,7 +4556,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
4489
4556
|
goal ? `- Goal / title: ${goal}` : null,
|
|
4490
4557
|
accountDefaultsPlaybookLine(),
|
|
4491
4558
|
`- 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.`,
|
|
4492
|
-
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn.
|
|
4559
|
+
"- 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.",
|
|
4493
4560
|
"- 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.",
|
|
4494
4561
|
"- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`.",
|
|
4495
4562
|
"- 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."
|
|
@@ -4543,9 +4610,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
4543
4610
|
orchId ? `Pass parentThreadId="${orchId}" (or omit it). Never invent another parentThreadId.` : "Pass `parentThreadId` for children (this chat's id from the turn reminder).",
|
|
4544
4611
|
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
4545
4612
|
"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.",
|
|
4546
|
-
"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
|
|
4613
|
+
"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).",
|
|
4547
4614
|
"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.",
|
|
4548
|
-
"Always ask worktree agents to commit, push, open draft PRs
|
|
4615
|
+
"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."
|
|
4549
4616
|
].join("\n");
|
|
4550
4617
|
try {
|
|
4551
4618
|
(0, import_node_fs11.writeFileSync)((0, import_node_path12.join)(dir, "CLAUDE.md"), `${body}
|
|
@@ -4558,7 +4625,6 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
4558
4625
|
}
|
|
4559
4626
|
function coordinatorSystemPrompt(opts) {
|
|
4560
4627
|
const audience = opts.audience ?? "cloud";
|
|
4561
|
-
const reposDir = sideboardReposDir();
|
|
4562
4628
|
const intro = audience === "cloud" ? [
|
|
4563
4629
|
"You are a Sideboard coordinator responding to a request from a Brightsy cloud agent (Discord, Teams, or other chat).",
|
|
4564
4630
|
"Your reply will be sent back to that cloud agent \u2014 be concise and actionable."
|
|
@@ -4576,14 +4642,8 @@ function coordinatorSystemPrompt(opts) {
|
|
|
4576
4642
|
...intro,
|
|
4577
4643
|
"You operate across ALL registered workspaces below.",
|
|
4578
4644
|
"You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
|
|
4579
|
-
|
|
4580
|
-
accountDefaultsPlaybookLine(),
|
|
4581
|
-
coordinatorGreenfieldPlaybook(reposDir),
|
|
4582
|
-
"When creating threads, pass the correct repoPath for the target workspace.",
|
|
4645
|
+
"Follow AGENTS.md / CLAUDE.md in this cwd for the fleet playbook (they are the same document).",
|
|
4583
4646
|
`YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit parentThreadId (Sideboard binds it). Never invent a uuid.`,
|
|
4584
|
-
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
4585
|
-
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 ask_git create-draft \u2192 wait_for_turn \u2192 (when ready) ask_git merge \u2192 wait_for_turn. Never target upstream. Never git/gh from this orchestration cwd.",
|
|
4586
|
-
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 ask_git create-draft.",
|
|
4587
4647
|
`Goal: ${opts.goal}`,
|
|
4588
4648
|
"Registered workspaces:",
|
|
4589
4649
|
formatWorkspaceInventory(opts.workspaces)
|
|
@@ -4610,6 +4670,7 @@ var init_coordinator_prompt = __esm({
|
|
|
4610
4670
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
4611
4671
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
4612
4672
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
4673
|
+
"- 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.",
|
|
4613
4674
|
"- 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.",
|
|
4614
4675
|
"Workspaces:",
|
|
4615
4676
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
@@ -4628,7 +4689,12 @@ var init_coordinator_prompt = __esm({
|
|
|
4628
4689
|
"- get_diff \u2014 compact diff summary",
|
|
4629
4690
|
'- 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',
|
|
4630
4691
|
"- 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.",
|
|
4631
|
-
'-
|
|
4692
|
+
'- 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.',
|
|
4693
|
+
'- 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.',
|
|
4694
|
+
"Process guides:",
|
|
4695
|
+
"- 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.",
|
|
4696
|
+
"- 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.",
|
|
4697
|
+
"- 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.",
|
|
4632
4698
|
"Human-only (do not attempt): ready-for-review land (confirm_land), purge_thread.",
|
|
4633
4699
|
"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.",
|
|
4634
4700
|
"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."
|
|
@@ -5065,6 +5131,51 @@ var init_connected_teams = __esm({
|
|
|
5065
5131
|
}
|
|
5066
5132
|
});
|
|
5067
5133
|
|
|
5134
|
+
// src/agents/usage.ts
|
|
5135
|
+
function sumOptional(a, b) {
|
|
5136
|
+
if (a == null && b == null) return void 0;
|
|
5137
|
+
return (a ?? 0) + (b ?? 0);
|
|
5138
|
+
}
|
|
5139
|
+
function fromInclusiveInputUsage(opts) {
|
|
5140
|
+
const totalInput = Number(opts.inputTokens) || 0;
|
|
5141
|
+
const outputTokens = Number(opts.outputTokens) || 0;
|
|
5142
|
+
const cached = Number(opts.cachedInputTokens) || 0;
|
|
5143
|
+
if (!totalInput && !outputTokens) return null;
|
|
5144
|
+
const cacheReadTokens = cached > 0 ? Math.min(cached, totalInput) : 0;
|
|
5145
|
+
return {
|
|
5146
|
+
inputTokens: Math.max(0, totalInput - cacheReadTokens),
|
|
5147
|
+
outputTokens,
|
|
5148
|
+
cacheReadTokens: cacheReadTokens || void 0
|
|
5149
|
+
};
|
|
5150
|
+
}
|
|
5151
|
+
function requestOccupancy(u) {
|
|
5152
|
+
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
5153
|
+
}
|
|
5154
|
+
function mergeUsage(a, b) {
|
|
5155
|
+
return {
|
|
5156
|
+
inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
|
|
5157
|
+
outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
|
|
5158
|
+
cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
|
|
5159
|
+
cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
|
|
5160
|
+
lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
|
|
5161
|
+
};
|
|
5162
|
+
}
|
|
5163
|
+
function applyTurnUsage(current, incoming, scope = "request") {
|
|
5164
|
+
if (scope === "turn") {
|
|
5165
|
+
return {
|
|
5166
|
+
...incoming,
|
|
5167
|
+
lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
|
|
5168
|
+
};
|
|
5169
|
+
}
|
|
5170
|
+
const merged = mergeUsage(current, incoming);
|
|
5171
|
+
return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
|
|
5172
|
+
}
|
|
5173
|
+
var init_usage = __esm({
|
|
5174
|
+
"src/agents/usage.ts"() {
|
|
5175
|
+
"use strict";
|
|
5176
|
+
}
|
|
5177
|
+
});
|
|
5178
|
+
|
|
5068
5179
|
// src/agents/brightsy-targets.ts
|
|
5069
5180
|
function encodeBrightsyTarget(type, id, accountId) {
|
|
5070
5181
|
if (accountId) return `team:${accountId}:${type}:${id}`;
|
|
@@ -5129,15 +5240,11 @@ var init_turn_input = __esm({
|
|
|
5129
5240
|
// src/agents/brightsy.ts
|
|
5130
5241
|
function usageFromBrightsy(usage) {
|
|
5131
5242
|
if (!usage) return null;
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
inputTokens,
|
|
5138
|
-
outputTokens,
|
|
5139
|
-
cacheReadTokens: cached || void 0
|
|
5140
|
-
};
|
|
5243
|
+
return fromInclusiveInputUsage({
|
|
5244
|
+
inputTokens: Number(usage.prompt_tokens ?? 0),
|
|
5245
|
+
outputTokens: Number(usage.completion_tokens ?? 0),
|
|
5246
|
+
cachedInputTokens: Number(usage.prompt_tokens_details?.cached_tokens ?? 0)
|
|
5247
|
+
});
|
|
5141
5248
|
}
|
|
5142
5249
|
function parseBrightsyCliLine(line) {
|
|
5143
5250
|
const trimmed = line.trim();
|
|
@@ -5353,6 +5460,7 @@ var init_brightsy = __esm({
|
|
|
5353
5460
|
init_connected_teams();
|
|
5354
5461
|
init_config();
|
|
5355
5462
|
init_app_settings();
|
|
5463
|
+
init_usage();
|
|
5356
5464
|
init_brightsy_targets();
|
|
5357
5465
|
init_error_detail();
|
|
5358
5466
|
init_turn_input();
|
|
@@ -5492,6 +5600,13 @@ var init_profile = __esm({
|
|
|
5492
5600
|
function isAsarPath(filePath) {
|
|
5493
5601
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
5494
5602
|
}
|
|
5603
|
+
function applyNodeLaunch(launch, args) {
|
|
5604
|
+
if (!launch.env.ELECTRON_RUN_AS_NODE) {
|
|
5605
|
+
return { file: launch.file, args, env: launch.env };
|
|
5606
|
+
}
|
|
5607
|
+
const wrapped = wrapElectronAsNodeLaunch(launch.file, args);
|
|
5608
|
+
return { file: wrapped.file, args: wrapped.args, env: launch.env };
|
|
5609
|
+
}
|
|
5495
5610
|
async function resolveNodeLaunch(scriptPath) {
|
|
5496
5611
|
if (isAsarPath(scriptPath)) {
|
|
5497
5612
|
return {
|
|
@@ -5512,6 +5627,7 @@ async function resolveNodeLaunch(scriptPath) {
|
|
|
5512
5627
|
var init_node_launch = __esm({
|
|
5513
5628
|
"src/agents/node-launch.ts"() {
|
|
5514
5629
|
"use strict";
|
|
5630
|
+
init_nested_electron_env();
|
|
5515
5631
|
init_run();
|
|
5516
5632
|
}
|
|
5517
5633
|
});
|
|
@@ -5638,11 +5754,11 @@ async function resolveSideboardMcpServer() {
|
|
|
5638
5754
|
if (entry) {
|
|
5639
5755
|
const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
|
|
5640
5756
|
const scriptArgs = isCli ? [entry, "mcp"] : [entry];
|
|
5641
|
-
const launch = await resolveNodeLaunch(entry);
|
|
5757
|
+
const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
|
|
5642
5758
|
return {
|
|
5643
5759
|
name: "sideboard",
|
|
5644
5760
|
command: launch.file,
|
|
5645
|
-
args:
|
|
5761
|
+
args: launch.args,
|
|
5646
5762
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
5647
5763
|
};
|
|
5648
5764
|
}
|
|
@@ -6232,14 +6348,11 @@ async function listCodexModels() {
|
|
|
6232
6348
|
}
|
|
6233
6349
|
function usageFromCodex(usage) {
|
|
6234
6350
|
if (!usage) return null;
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
outputTokens,
|
|
6241
|
-
cacheReadTokens: usage.cached_input_tokens ? Number(usage.cached_input_tokens) : void 0
|
|
6242
|
-
};
|
|
6351
|
+
return fromInclusiveInputUsage({
|
|
6352
|
+
inputTokens: Number(usage.input_tokens ?? 0),
|
|
6353
|
+
outputTokens: Number(usage.output_tokens ?? 0),
|
|
6354
|
+
cachedInputTokens: Number(usage.cached_input_tokens ?? 0)
|
|
6355
|
+
});
|
|
6243
6356
|
}
|
|
6244
6357
|
function codexConfigHasNetworkAccess() {
|
|
6245
6358
|
const candidates = [
|
|
@@ -6299,6 +6412,7 @@ var init_codex = __esm({
|
|
|
6299
6412
|
init_app_settings();
|
|
6300
6413
|
init_global_workspace();
|
|
6301
6414
|
init_error_detail();
|
|
6415
|
+
init_usage();
|
|
6302
6416
|
init_injected_mcp();
|
|
6303
6417
|
init_turn_input();
|
|
6304
6418
|
init_types();
|
|
@@ -6849,10 +6963,13 @@ var init_cursor = __esm({
|
|
|
6849
6963
|
};
|
|
6850
6964
|
const runner = cursorRunnerPath();
|
|
6851
6965
|
const isTs = runner.endsWith(".ts");
|
|
6852
|
-
const launch =
|
|
6966
|
+
const launch = applyNodeLaunch(
|
|
6967
|
+
await resolveNodeLaunch(runner),
|
|
6968
|
+
isTs ? ["--import", "tsx", runner] : [runner]
|
|
6969
|
+
);
|
|
6853
6970
|
return {
|
|
6854
6971
|
file: launch.file,
|
|
6855
|
-
args:
|
|
6972
|
+
args: launch.args,
|
|
6856
6973
|
cwd: thread.worktreePath,
|
|
6857
6974
|
stdin: JSON.stringify(req),
|
|
6858
6975
|
env: {
|
|
@@ -8141,6 +8258,9 @@ var init_cursor_recover = __esm({
|
|
|
8141
8258
|
}
|
|
8142
8259
|
});
|
|
8143
8260
|
|
|
8261
|
+
// src/mcp/run-stdio.ts
|
|
8262
|
+
init_nested_electron_env();
|
|
8263
|
+
|
|
8144
8264
|
// src/mcp/server.ts
|
|
8145
8265
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
8146
8266
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
@@ -8899,36 +9019,7 @@ function normalizeParseResult(parsed) {
|
|
|
8899
9019
|
|
|
8900
9020
|
// src/agents/spawn.ts
|
|
8901
9021
|
init_path();
|
|
8902
|
-
|
|
8903
|
-
// src/agents/usage.ts
|
|
8904
|
-
function sumOptional(a, b) {
|
|
8905
|
-
if (a == null && b == null) return void 0;
|
|
8906
|
-
return (a ?? 0) + (b ?? 0);
|
|
8907
|
-
}
|
|
8908
|
-
function requestOccupancy(u) {
|
|
8909
|
-
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
8910
|
-
}
|
|
8911
|
-
function mergeUsage(a, b) {
|
|
8912
|
-
return {
|
|
8913
|
-
inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
|
|
8914
|
-
outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
|
|
8915
|
-
cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
|
|
8916
|
-
cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
|
|
8917
|
-
lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
|
|
8918
|
-
};
|
|
8919
|
-
}
|
|
8920
|
-
function applyTurnUsage(current, incoming, scope = "request") {
|
|
8921
|
-
if (scope === "turn") {
|
|
8922
|
-
return {
|
|
8923
|
-
...incoming,
|
|
8924
|
-
lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
|
|
8925
|
-
};
|
|
8926
|
-
}
|
|
8927
|
-
const merged = mergeUsage(current, incoming);
|
|
8928
|
-
return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
|
|
8929
|
-
}
|
|
8930
|
-
|
|
8931
|
-
// src/agents/spawn.ts
|
|
9022
|
+
init_usage();
|
|
8932
9023
|
async function spawnAgentTurn(thread, input, onEvent) {
|
|
8933
9024
|
ensureAgentPath();
|
|
8934
9025
|
if (!thread.worktreePath?.trim()) {
|
|
@@ -9145,7 +9236,8 @@ function agentGitPrompt(action, opts) {
|
|
|
9145
9236
|
return "Commit, push, and open a PR in the browser.";
|
|
9146
9237
|
case "resolve-conflicts": {
|
|
9147
9238
|
const base = opts?.prBase?.trim().replace(/^refs\/heads\//, "");
|
|
9148
|
-
|
|
9239
|
+
const named = base ? ` (${base})` : "";
|
|
9240
|
+
return `Merge the remote branch${named} into your branch and resolve conflicts. Then, commit and push your changes.`;
|
|
9149
9241
|
}
|
|
9150
9242
|
case "merge":
|
|
9151
9243
|
return "Merge PR.";
|
|
@@ -9173,6 +9265,8 @@ var import_node_path18 = require("path");
|
|
|
9173
9265
|
var import_execa3 = require("execa");
|
|
9174
9266
|
var import_node_readline2 = require("readline");
|
|
9175
9267
|
init_settings();
|
|
9268
|
+
init_nested_electron_env();
|
|
9269
|
+
init_nested_electron_env();
|
|
9176
9270
|
var PORT_RANGE_SIZE = 10;
|
|
9177
9271
|
function matchSimpleGlob(pattern, name) {
|
|
9178
9272
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -9256,16 +9350,6 @@ async function captureLoginEnv() {
|
|
|
9256
9350
|
cachedLoginEnv = { ...process.env };
|
|
9257
9351
|
return { ...cachedLoginEnv };
|
|
9258
9352
|
}
|
|
9259
|
-
var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
9260
|
-
function stripNestedElectronEnv(env) {
|
|
9261
|
-
const out = { ...env };
|
|
9262
|
-
for (const key of Object.keys(out)) {
|
|
9263
|
-
if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
9264
|
-
delete out[key];
|
|
9265
|
-
}
|
|
9266
|
-
}
|
|
9267
|
-
return out;
|
|
9268
|
-
}
|
|
9269
9353
|
function buildWorkspaceScriptEnv(opts, baseEnv) {
|
|
9270
9354
|
const env = stripNestedElectronEnv({
|
|
9271
9355
|
...baseEnv ?? process.env
|
|
@@ -10416,6 +10500,10 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
10416
10500
|
|
|
10417
10501
|
**Approve** \u2014 Diff is scoped, behavior looks correct, and there are no blocking issues. Safe to merge.
|
|
10418
10502
|
</example>
|
|
10503
|
+
|
|
10504
|
+
## Growing the rules
|
|
10505
|
+
|
|
10506
|
+
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\`.
|
|
10419
10507
|
`;
|
|
10420
10508
|
|
|
10421
10509
|
// src/review/request-review.ts
|
|
@@ -12553,7 +12641,7 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
12553
12641
|
"- Prefer a concise imperative title (Conventional Commits style when it fits: feat:/fix:/chore:/docs:). Body should summarize intent, key changes, and test notes."
|
|
12554
12642
|
);
|
|
12555
12643
|
lines.push(
|
|
12556
|
-
"- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch. Never push directly to main/master or merge locally into the main checkout.
|
|
12644
|
+
"- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch. Never push directly to main/master or merge locally into the main checkout. Do not merge the PR unless this turn is a Merge PR request or the user explicitly asked. When merging, use GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
|
|
12557
12645
|
);
|
|
12558
12646
|
if (thread.prUrl) {
|
|
12559
12647
|
lines.push(
|
|
@@ -12585,19 +12673,30 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
12585
12673
|
'- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
|
|
12586
12674
|
);
|
|
12587
12675
|
lines.push(
|
|
12588
|
-
'- "
|
|
12676
|
+
'- "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.'
|
|
12589
12677
|
);
|
|
12590
12678
|
lines.push(
|
|
12591
12679
|
'- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
|
|
12592
12680
|
);
|
|
12593
12681
|
lines.push(
|
|
12594
|
-
'- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub. 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.'
|
|
12682
|
+
'- "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.'
|
|
12595
12683
|
);
|
|
12684
|
+
lines.push("");
|
|
12685
|
+
lines.push(formatProcessGuideDirective());
|
|
12596
12686
|
return lines.join("\n");
|
|
12597
12687
|
}
|
|
12598
12688
|
function formatWorktreeReminder() {
|
|
12599
12689
|
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.";
|
|
12600
12690
|
}
|
|
12691
|
+
function formatProcessGuideDirective() {
|
|
12692
|
+
return [
|
|
12693
|
+
"Process guides (recurring work only):",
|
|
12694
|
+
"- 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.",
|
|
12695
|
+
"- 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.",
|
|
12696
|
+
"- 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.",
|
|
12697
|
+
"- 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."
|
|
12698
|
+
].join("\n");
|
|
12699
|
+
}
|
|
12601
12700
|
function formatArtifactDirective() {
|
|
12602
12701
|
return [
|
|
12603
12702
|
"Sideboard side column (desktop UI):",
|
|
@@ -12615,9 +12714,22 @@ function formatArtifactDirective() {
|
|
|
12615
12714
|
"Files / media browser (CMS file manager column):",
|
|
12616
12715
|
"4) Call Sideboard MCP `present_files` with optional title, path, and datasource (brightsy|memory).",
|
|
12617
12716
|
" Opens the Files column for browse/upload/pick. Do NOT say a file manager UI is missing.",
|
|
12618
|
-
"
|
|
12717
|
+
"Multiple-choice questions (any mode \u2014 not only Plan):",
|
|
12718
|
+
"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.",
|
|
12719
|
+
"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."
|
|
12619
12720
|
].join("\n");
|
|
12620
12721
|
}
|
|
12722
|
+
function formatUiReminder() {
|
|
12723
|
+
return [
|
|
12724
|
+
"Sideboard side column (important):",
|
|
12725
|
+
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
12726
|
+
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
12727
|
+
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
12728
|
+
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
12729
|
+
"Do not say artifacts/CMS UI are unavailable.",
|
|
12730
|
+
"Multiple-choice questions: MCP ask_user (composer picker, any mode). Explain options in chat first, then wait for answers."
|
|
12731
|
+
].join(" ");
|
|
12732
|
+
}
|
|
12621
12733
|
|
|
12622
12734
|
// src/orchestrator/orchestrator.ts
|
|
12623
12735
|
init_types();
|
|
@@ -13161,14 +13273,7 @@ var Orchestrator = class {
|
|
|
13161
13273
|
SLACK_REPLY_FORMATTING
|
|
13162
13274
|
].join("\n") : null
|
|
13163
13275
|
].filter(Boolean).join("\n") : null;
|
|
13164
|
-
const artifactReminder = thread.agent !== "brightsy" ?
|
|
13165
|
-
"Sideboard side column (important):",
|
|
13166
|
-
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
13167
|
-
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
13168
|
-
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
13169
|
-
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
13170
|
-
"Do not say artifacts/CMS UI are unavailable."
|
|
13171
|
-
].join(" ") : null;
|
|
13276
|
+
const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
|
|
13172
13277
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
13173
13278
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
13174
13279
|
pendingSlackExternalReplies(thread.messages)
|
|
@@ -13862,7 +13967,9 @@ var Orchestrator = class {
|
|
|
13862
13967
|
reviewDecision: null,
|
|
13863
13968
|
baseRefName: "",
|
|
13864
13969
|
headRefName: "",
|
|
13865
|
-
isInMergeQueue: false
|
|
13970
|
+
isInMergeQueue: false,
|
|
13971
|
+
mergeable: null,
|
|
13972
|
+
mergeStateStatus: null
|
|
13866
13973
|
};
|
|
13867
13974
|
await this.persistPrMetaAndMaybeArchive(thread, metaLike);
|
|
13868
13975
|
return { url: metaLike.url, state };
|
|
@@ -15536,7 +15643,7 @@ async function startMcpServer() {
|
|
|
15536
15643
|
);
|
|
15537
15644
|
server.tool(
|
|
15538
15645
|
"ask_user",
|
|
15539
|
-
"Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (
|
|
15646
|
+
"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.",
|
|
15540
15647
|
{
|
|
15541
15648
|
questions: import_zod3.z.array(
|
|
15542
15649
|
import_zod3.z.object({
|
|
@@ -15799,7 +15906,7 @@ async function startMcpServer() {
|
|
|
15799
15906
|
);
|
|
15800
15907
|
server.tool(
|
|
15801
15908
|
"send_to_thread",
|
|
15802
|
-
|
|
15909
|
+
'Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.',
|
|
15803
15910
|
{
|
|
15804
15911
|
ref: import_zod3.z.string(),
|
|
15805
15912
|
prompt: import_zod3.z.string(),
|
|
@@ -15895,7 +16002,7 @@ async function startMcpServer() {
|
|
|
15895
16002
|
);
|
|
15896
16003
|
server.tool(
|
|
15897
16004
|
"archive_thread",
|
|
15898
|
-
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, open PRs
|
|
16005
|
+
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
|
|
15899
16006
|
{ ref: import_zod3.z.string() },
|
|
15900
16007
|
async ({ ref }) => {
|
|
15901
16008
|
const t = orch.getThread(ref);
|
|
@@ -16003,7 +16110,7 @@ async function startMcpServer() {
|
|
|
16003
16110
|
);
|
|
16004
16111
|
server.tool(
|
|
16005
16112
|
"ask_git",
|
|
16006
|
-
"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). Do not run git or gh from the orchestration cwd.",
|
|
16113
|
+
"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.",
|
|
16007
16114
|
{
|
|
16008
16115
|
ref: import_zod3.z.string().describe("Worktree thread id/ref"),
|
|
16009
16116
|
action: import_zod3.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -16304,7 +16411,7 @@ async function startMcpServer() {
|
|
|
16304
16411
|
);
|
|
16305
16412
|
server.tool(
|
|
16306
16413
|
"get_pr_stack",
|
|
16307
|
-
"Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs.",
|
|
16414
|
+
"Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs (and only merge when the user explicitly asked).",
|
|
16308
16415
|
{ ref: import_zod3.z.string() },
|
|
16309
16416
|
async ({ ref }) => {
|
|
16310
16417
|
const stack = await orch.getPrStack(ref);
|
|
@@ -16450,6 +16557,7 @@ async function startMcpServer() {
|
|
|
16450
16557
|
}
|
|
16451
16558
|
|
|
16452
16559
|
// src/mcp/run-stdio.ts
|
|
16560
|
+
dropNestedElectronEnvFromProcess();
|
|
16453
16561
|
startMcpServer().catch((err) => {
|
|
16454
16562
|
console.error(err);
|
|
16455
16563
|
process.exitCode = 1;
|