@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/index.cjs
CHANGED
|
@@ -385,6 +385,38 @@ var init_paths = __esm({
|
|
|
385
385
|
}
|
|
386
386
|
});
|
|
387
387
|
|
|
388
|
+
// src/hook/nested-electron-env.ts
|
|
389
|
+
function isNestedElectronEnvKey(key) {
|
|
390
|
+
return NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
391
|
+
}
|
|
392
|
+
function stripNestedElectronEnv(env) {
|
|
393
|
+
const out = { ...env };
|
|
394
|
+
for (const key of Object.keys(out)) {
|
|
395
|
+
if (isNestedElectronEnvKey(key)) delete out[key];
|
|
396
|
+
}
|
|
397
|
+
return out;
|
|
398
|
+
}
|
|
399
|
+
function wrapElectronAsNodeLaunch(file, args) {
|
|
400
|
+
if (process.platform === "win32") return { file, args };
|
|
401
|
+
return {
|
|
402
|
+
file: "/bin/sh",
|
|
403
|
+
args: ["-c", STRIP_NESTED_ELECTRON_THEN_EXEC, "sh", file, ...args]
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
var NESTED_ELECTRON_ENV_PREFIXES, STRIP_NESTED_ELECTRON_THEN_EXEC;
|
|
407
|
+
var init_nested_electron_env = __esm({
|
|
408
|
+
"src/hook/nested-electron-env.ts"() {
|
|
409
|
+
"use strict";
|
|
410
|
+
NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
411
|
+
STRIP_NESTED_ELECTRON_THEN_EXEC = [
|
|
412
|
+
"vars=`printenv | awk -F= '/^(ELECTRON_|CHROME_)/{print $1}'`",
|
|
413
|
+
'[ -n "$vars" ] && unset $vars',
|
|
414
|
+
"export ELECTRON_RUN_AS_NODE=1",
|
|
415
|
+
'exec "$@"'
|
|
416
|
+
].join("; ");
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
388
420
|
// src/store/private-file.ts
|
|
389
421
|
function writePrivateFile(path2, contents) {
|
|
390
422
|
(0, import_node_fs3.mkdirSync)((0, import_node_path4.dirname)(path2), { recursive: true });
|
|
@@ -1499,7 +1531,7 @@ function applyAppEnvironment(target = process.env, settings = loadAppSettings())
|
|
|
1499
1531
|
}
|
|
1500
1532
|
function childEnvWithAppSettings(extra) {
|
|
1501
1533
|
const settings = loadAppSettings();
|
|
1502
|
-
const env = { ...process.env };
|
|
1534
|
+
const env = stripNestedElectronEnv({ ...process.env });
|
|
1503
1535
|
applyAppEnvironment(env, settings);
|
|
1504
1536
|
if (extra) {
|
|
1505
1537
|
for (const [k, v] of Object.entries(extra)) {
|
|
@@ -1520,6 +1552,7 @@ var init_app_settings = __esm({
|
|
|
1520
1552
|
import_node_os3 = require("os");
|
|
1521
1553
|
import_node_path6 = require("path");
|
|
1522
1554
|
init_thinking_effort();
|
|
1555
|
+
init_nested_electron_env();
|
|
1523
1556
|
init_paths();
|
|
1524
1557
|
init_private_file();
|
|
1525
1558
|
init_secret_vault();
|
|
@@ -2852,11 +2885,25 @@ function formatGhLandError(raw, opts) {
|
|
|
2852
2885
|
}
|
|
2853
2886
|
return detail || "Failed to create or update pull request";
|
|
2854
2887
|
}
|
|
2888
|
+
function isPrNotMergeableError(text3) {
|
|
2889
|
+
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(
|
|
2890
|
+
text3
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
function formatMergePrError(raw) {
|
|
2894
|
+
const trimmed = raw.trim();
|
|
2895
|
+
if (!trimmed) return "gh pr merge failed";
|
|
2896
|
+
if (isPrNotMergeableError(trimmed)) {
|
|
2897
|
+
return "This pull request cannot merge cleanly into the base branch. Resolve conflicts or update the branch, then retry.";
|
|
2898
|
+
}
|
|
2899
|
+
return extractGhErrorDetail(trimmed) || trimmed;
|
|
2900
|
+
}
|
|
2855
2901
|
function formatIpcInvokeError(err) {
|
|
2856
2902
|
let msg = err instanceof Error ? err.message : String(err);
|
|
2857
2903
|
msg = msg.replace(/^Error invoking remote method '[^']+':\s*/i, "");
|
|
2858
2904
|
msg = msg.replace(/^ExecaError:\s*/i, "");
|
|
2859
2905
|
msg = msg.replace(/^Error:\s*/i, "");
|
|
2906
|
+
if (isPrNotMergeableError(msg)) return formatMergePrError(msg);
|
|
2860
2907
|
return formatGhLandError(msg);
|
|
2861
2908
|
}
|
|
2862
2909
|
var init_gh_errors = __esm({
|
|
@@ -3839,6 +3886,41 @@ ${result.stderr}`;
|
|
|
3839
3886
|
].slice(0, 20) : [];
|
|
3840
3887
|
return { conflicting, base: baseName, files };
|
|
3841
3888
|
}
|
|
3889
|
+
async function probeLocalMergeGate(cwd, gate) {
|
|
3890
|
+
const mergeable = (gate?.mergeable ?? "").toUpperCase();
|
|
3891
|
+
const mergeState = (gate?.mergeStateStatus ?? "").toUpperCase();
|
|
3892
|
+
const alreadyConflicting = mergeable === "CONFLICTING" || mergeState === "DIRTY";
|
|
3893
|
+
const inQueue = gate ? prIsInMergeQueue(gate) : false;
|
|
3894
|
+
if (alreadyConflicting || inQueue) return { gate, files: [] };
|
|
3895
|
+
try {
|
|
3896
|
+
const local = await detectLocalMergeConflicts(cwd, gate?.baseRefName ?? null);
|
|
3897
|
+
if (local.conflicting) {
|
|
3898
|
+
return {
|
|
3899
|
+
gate: {
|
|
3900
|
+
mergeable: "CONFLICTING",
|
|
3901
|
+
mergeStateStatus: "DIRTY",
|
|
3902
|
+
reviewDecision: gate?.reviewDecision ?? null,
|
|
3903
|
+
baseRefName: local.base,
|
|
3904
|
+
url: gate?.url ?? null,
|
|
3905
|
+
isInMergeQueue: false
|
|
3906
|
+
},
|
|
3907
|
+
files: local.files
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3910
|
+
if (gate && (mergeable === "UNKNOWN" || mergeState === "UNKNOWN")) {
|
|
3911
|
+
return {
|
|
3912
|
+
gate: {
|
|
3913
|
+
...gate,
|
|
3914
|
+
mergeable: gate.mergeable === "UNKNOWN" ? "MERGEABLE" : gate.mergeable,
|
|
3915
|
+
mergeStateStatus: gate.mergeStateStatus === "UNKNOWN" ? "CLEAN" : gate.mergeStateStatus
|
|
3916
|
+
},
|
|
3917
|
+
files: []
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
} catch {
|
|
3921
|
+
}
|
|
3922
|
+
return { gate, files: [] };
|
|
3923
|
+
}
|
|
3842
3924
|
async function getPrChecks(cwd, selector) {
|
|
3843
3925
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
3844
3926
|
const args = [
|
|
@@ -3874,47 +3956,9 @@ async function getPrChecks(cwd, selector) {
|
|
|
3874
3956
|
}
|
|
3875
3957
|
}
|
|
3876
3958
|
let gate = await fetchPrMergeGate(cwd, selector, slug);
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
const
|
|
3880
|
-
const inQueue = gate ? prIsInMergeQueue(gate) : false;
|
|
3881
|
-
const needsLocalProbe = !alreadyConflicting && !inQueue;
|
|
3882
|
-
if (needsLocalProbe) {
|
|
3883
|
-
try {
|
|
3884
|
-
const local = await detectLocalMergeConflicts(cwd, gate?.baseRefName ?? null);
|
|
3885
|
-
if (local.conflicting) {
|
|
3886
|
-
const fileHint = local.files.length > 0 ? ` Conflicting paths: ${local.files.slice(0, 8).join(", ")}${local.files.length > 8 ? "\u2026" : ""}.` : "";
|
|
3887
|
-
gate = {
|
|
3888
|
-
mergeable: "CONFLICTING",
|
|
3889
|
-
mergeStateStatus: "DIRTY",
|
|
3890
|
-
reviewDecision: gate?.reviewDecision ?? null,
|
|
3891
|
-
baseRefName: local.base,
|
|
3892
|
-
url: gate?.url ?? null,
|
|
3893
|
-
isInMergeQueue: false
|
|
3894
|
-
};
|
|
3895
|
-
const ciFailed2 = ciChecks.some((c) => c.bucket === "fail");
|
|
3896
|
-
const review2 = (gate.reviewDecision ?? "").toUpperCase();
|
|
3897
|
-
const hasReviewRow2 = review2 === "CHANGES_REQUESTED" || review2 === "REVIEW_REQUIRED";
|
|
3898
|
-
const gateRows2 = buildMergeGateChecks(gate, {
|
|
3899
|
-
suppressGenericBlocked: ciFailed2 || hasReviewRow2
|
|
3900
|
-
}).map(
|
|
3901
|
-
(row) => row.kind === "mergeability" && row.name === "Merge conflicts" ? {
|
|
3902
|
-
...row,
|
|
3903
|
-
description: `${row.description ?? ""}${fileHint}`.trim()
|
|
3904
|
-
} : row
|
|
3905
|
-
);
|
|
3906
|
-
return [...gateRows2, ...ciChecks];
|
|
3907
|
-
}
|
|
3908
|
-
if (gate && (mergeable === "UNKNOWN" || mergeState === "UNKNOWN")) {
|
|
3909
|
-
gate = {
|
|
3910
|
-
...gate,
|
|
3911
|
-
mergeable: gate.mergeable === "UNKNOWN" ? "MERGEABLE" : gate.mergeable,
|
|
3912
|
-
mergeStateStatus: gate.mergeStateStatus === "UNKNOWN" ? "CLEAN" : gate.mergeStateStatus
|
|
3913
|
-
};
|
|
3914
|
-
}
|
|
3915
|
-
} catch {
|
|
3916
|
-
}
|
|
3917
|
-
}
|
|
3959
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
3960
|
+
gate = probed.gate;
|
|
3961
|
+
const fileHint = probed.files.length > 0 ? ` Conflicting paths: ${probed.files.slice(0, 8).join(", ")}${probed.files.length > 8 ? "\u2026" : ""}.` : "";
|
|
3918
3962
|
if (!gate) {
|
|
3919
3963
|
return ciChecks;
|
|
3920
3964
|
}
|
|
@@ -3924,7 +3968,12 @@ async function getPrChecks(cwd, selector) {
|
|
|
3924
3968
|
const gateRows = buildMergeGateChecks(gate, {
|
|
3925
3969
|
// Avoid duplicating "blocked" when CI failures or review rows already explain it.
|
|
3926
3970
|
suppressGenericBlocked: ciFailed || hasReviewRow
|
|
3927
|
-
})
|
|
3971
|
+
}).map(
|
|
3972
|
+
(row) => fileHint && row.kind === "mergeability" && row.name === "Merge conflicts" ? {
|
|
3973
|
+
...row,
|
|
3974
|
+
description: `${row.description ?? ""}${fileHint}`.trim()
|
|
3975
|
+
} : row
|
|
3976
|
+
);
|
|
3928
3977
|
return [...gateRows, ...ciChecks];
|
|
3929
3978
|
}
|
|
3930
3979
|
async function getPrMeta(cwd, selector) {
|
|
@@ -3934,7 +3983,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3934
3983
|
"view",
|
|
3935
3984
|
selector,
|
|
3936
3985
|
"--json",
|
|
3937
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus"
|
|
3986
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus,mergeable"
|
|
3938
3987
|
];
|
|
3939
3988
|
if (slug) viewArgs.push("--repo", slug);
|
|
3940
3989
|
let { stdout, exitCode, stderr } = await gh(viewArgs, cwd, { reject: false });
|
|
@@ -3944,7 +3993,7 @@ async function getPrMeta(cwd, selector) {
|
|
|
3944
3993
|
"view",
|
|
3945
3994
|
selector,
|
|
3946
3995
|
"--json",
|
|
3947
|
-
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus"
|
|
3996
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus,mergeable"
|
|
3948
3997
|
];
|
|
3949
3998
|
if (slug) retry.push("--repo", slug);
|
|
3950
3999
|
({ stdout, exitCode, stderr } = await gh(retry, cwd, { reject: false }));
|
|
@@ -3955,16 +4004,28 @@ async function getPrMeta(cwd, selector) {
|
|
|
3955
4004
|
}
|
|
3956
4005
|
try {
|
|
3957
4006
|
const view = JSON.parse(stdout);
|
|
4007
|
+
const gate = {
|
|
4008
|
+
mergeable: typeof view.mergeable === "string" && view.mergeable ? view.mergeable : null,
|
|
4009
|
+
mergeStateStatus: typeof view.mergeStateStatus === "string" && view.mergeStateStatus ? view.mergeStateStatus : null,
|
|
4010
|
+
reviewDecision: typeof view.reviewDecision === "string" && view.reviewDecision ? view.reviewDecision : null,
|
|
4011
|
+
baseRefName: String(view.baseRefName ?? ""),
|
|
4012
|
+
url: String(view.url ?? ""),
|
|
4013
|
+
isInMergeQueue: parseInMergeQueue(view)
|
|
4014
|
+
};
|
|
4015
|
+
const probed = await probeLocalMergeGate(cwd, gate);
|
|
4016
|
+
const resolved = probed.gate ?? gate;
|
|
3958
4017
|
return {
|
|
3959
4018
|
number: Number(view.number),
|
|
3960
4019
|
title: String(view.title ?? ""),
|
|
3961
4020
|
url: String(view.url ?? ""),
|
|
3962
4021
|
state: String(view.state ?? ""),
|
|
3963
4022
|
isDraft: Boolean(view.isDraft),
|
|
3964
|
-
reviewDecision:
|
|
3965
|
-
baseRefName: String(view.baseRefName ?? ""),
|
|
4023
|
+
reviewDecision: resolved.reviewDecision,
|
|
4024
|
+
baseRefName: resolved.baseRefName || String(view.baseRefName ?? ""),
|
|
3966
4025
|
headRefName: String(view.headRefName ?? ""),
|
|
3967
|
-
isInMergeQueue:
|
|
4026
|
+
isInMergeQueue: Boolean(resolved.isInMergeQueue),
|
|
4027
|
+
mergeable: resolved.mergeable,
|
|
4028
|
+
mergeStateStatus: resolved.mergeStateStatus
|
|
3968
4029
|
};
|
|
3969
4030
|
} catch {
|
|
3970
4031
|
return null;
|
|
@@ -4432,7 +4493,9 @@ async function mergePr(cwd, selector, opts) {
|
|
|
4432
4493
|
if (slug) args.push("--repo", slug);
|
|
4433
4494
|
const { exitCode, stderr, stdout } = await gh(args, cwd, { reject: false });
|
|
4434
4495
|
if (exitCode !== 0) {
|
|
4435
|
-
throw new Error(
|
|
4496
|
+
throw new Error(
|
|
4497
|
+
formatMergePrError(stderr.trim() || stdout.trim() || "gh pr merge failed")
|
|
4498
|
+
);
|
|
4436
4499
|
}
|
|
4437
4500
|
const after = await gh(viewArgs, cwd, { reject: false });
|
|
4438
4501
|
if (after.exitCode === 0 && after.stdout.trim()) {
|
|
@@ -4655,7 +4718,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
4655
4718
|
goal ? `- Goal / title: ${goal}` : null,
|
|
4656
4719
|
accountDefaultsPlaybookLine(),
|
|
4657
4720
|
`- 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.`,
|
|
4658
|
-
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn.
|
|
4721
|
+
"- 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.",
|
|
4659
4722
|
"- 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.",
|
|
4660
4723
|
"- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`.",
|
|
4661
4724
|
"- 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."
|
|
@@ -4709,9 +4772,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
4709
4772
|
orchId ? `Pass parentThreadId="${orchId}" (or omit it). Never invent another parentThreadId.` : "Pass `parentThreadId` for children (this chat's id from the turn reminder).",
|
|
4710
4773
|
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
4711
4774
|
"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.",
|
|
4712
|
-
"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
|
|
4775
|
+
"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).",
|
|
4713
4776
|
"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.",
|
|
4714
|
-
"Always ask worktree agents to commit, push, open draft PRs
|
|
4777
|
+
"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."
|
|
4715
4778
|
].join("\n");
|
|
4716
4779
|
try {
|
|
4717
4780
|
(0, import_node_fs10.writeFileSync)((0, import_node_path10.join)(dir, "CLAUDE.md"), `${body}
|
|
@@ -4724,7 +4787,6 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
4724
4787
|
}
|
|
4725
4788
|
function coordinatorSystemPrompt(opts) {
|
|
4726
4789
|
const audience = opts.audience ?? "cloud";
|
|
4727
|
-
const reposDir = sideboardReposDir();
|
|
4728
4790
|
const intro = audience === "cloud" ? [
|
|
4729
4791
|
"You are a Sideboard coordinator responding to a request from a Brightsy cloud agent (Discord, Teams, or other chat).",
|
|
4730
4792
|
"Your reply will be sent back to that cloud agent \u2014 be concise and actionable."
|
|
@@ -4742,14 +4804,8 @@ function coordinatorSystemPrompt(opts) {
|
|
|
4742
4804
|
...intro,
|
|
4743
4805
|
"You operate across ALL registered workspaces below.",
|
|
4744
4806
|
"You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
|
|
4745
|
-
|
|
4746
|
-
accountDefaultsPlaybookLine(),
|
|
4747
|
-
coordinatorGreenfieldPlaybook(reposDir),
|
|
4748
|
-
"When creating threads, pass the correct repoPath for the target workspace.",
|
|
4807
|
+
"Follow AGENTS.md / CLAUDE.md in this cwd for the fleet playbook (they are the same document).",
|
|
4749
4808
|
`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.`,
|
|
4750
|
-
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
4751
|
-
"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.",
|
|
4752
|
-
"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.",
|
|
4753
4809
|
`Goal: ${opts.goal}`,
|
|
4754
4810
|
"Registered workspaces:",
|
|
4755
4811
|
formatWorkspaceInventory(opts.workspaces)
|
|
@@ -4776,6 +4832,7 @@ var init_coordinator_prompt = __esm({
|
|
|
4776
4832
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
4777
4833
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
4778
4834
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
4835
|
+
"- 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.",
|
|
4779
4836
|
"- 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.",
|
|
4780
4837
|
"Workspaces:",
|
|
4781
4838
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
@@ -4794,7 +4851,12 @@ var init_coordinator_prompt = __esm({
|
|
|
4794
4851
|
"- get_diff \u2014 compact diff summary",
|
|
4795
4852
|
'- 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',
|
|
4796
4853
|
"- 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.",
|
|
4797
|
-
'-
|
|
4854
|
+
'- 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.',
|
|
4855
|
+
'- 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.',
|
|
4856
|
+
"Process guides:",
|
|
4857
|
+
"- 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.",
|
|
4858
|
+
"- 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.",
|
|
4859
|
+
"- 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.",
|
|
4798
4860
|
"Human-only (do not attempt): ready-for-review land (confirm_land), purge_thread.",
|
|
4799
4861
|
"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.",
|
|
4800
4862
|
"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."
|
|
@@ -5488,6 +5550,58 @@ var init_connected_teams = __esm({
|
|
|
5488
5550
|
}
|
|
5489
5551
|
});
|
|
5490
5552
|
|
|
5553
|
+
// src/agents/usage.ts
|
|
5554
|
+
function sumOptional(a, b) {
|
|
5555
|
+
if (a == null && b == null) return void 0;
|
|
5556
|
+
return (a ?? 0) + (b ?? 0);
|
|
5557
|
+
}
|
|
5558
|
+
function fromInclusiveInputUsage(opts) {
|
|
5559
|
+
const totalInput = Number(opts.inputTokens) || 0;
|
|
5560
|
+
const outputTokens = Number(opts.outputTokens) || 0;
|
|
5561
|
+
const cached = Number(opts.cachedInputTokens) || 0;
|
|
5562
|
+
if (!totalInput && !outputTokens) return null;
|
|
5563
|
+
const cacheReadTokens = cached > 0 ? Math.min(cached, totalInput) : 0;
|
|
5564
|
+
return {
|
|
5565
|
+
inputTokens: Math.max(0, totalInput - cacheReadTokens),
|
|
5566
|
+
outputTokens,
|
|
5567
|
+
cacheReadTokens: cacheReadTokens || void 0
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5570
|
+
function requestOccupancy(u) {
|
|
5571
|
+
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
5572
|
+
}
|
|
5573
|
+
function mergeUsage(a, b) {
|
|
5574
|
+
return {
|
|
5575
|
+
inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
|
|
5576
|
+
outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
|
|
5577
|
+
cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
|
|
5578
|
+
cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
|
|
5579
|
+
lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
function applyTurnUsage(current, incoming, scope = "request") {
|
|
5583
|
+
if (scope === "turn") {
|
|
5584
|
+
return {
|
|
5585
|
+
...incoming,
|
|
5586
|
+
lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
|
|
5587
|
+
};
|
|
5588
|
+
}
|
|
5589
|
+
const merged = mergeUsage(current, incoming);
|
|
5590
|
+
return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
|
|
5591
|
+
}
|
|
5592
|
+
function totalTokens(u) {
|
|
5593
|
+
return u.inputTokens + u.outputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
5594
|
+
}
|
|
5595
|
+
function contextTokens(u) {
|
|
5596
|
+
if (u.lastRequestTokens != null && u.lastRequestTokens > 0) return u.lastRequestTokens;
|
|
5597
|
+
return requestOccupancy(u);
|
|
5598
|
+
}
|
|
5599
|
+
var init_usage = __esm({
|
|
5600
|
+
"src/agents/usage.ts"() {
|
|
5601
|
+
"use strict";
|
|
5602
|
+
}
|
|
5603
|
+
});
|
|
5604
|
+
|
|
5491
5605
|
// src/agents/brightsy-targets.ts
|
|
5492
5606
|
function encodeBrightsyTarget(type, id, accountId) {
|
|
5493
5607
|
if (accountId) return `team:${accountId}:${type}:${id}`;
|
|
@@ -5723,15 +5837,11 @@ var init_turn_input = __esm({
|
|
|
5723
5837
|
// src/agents/brightsy.ts
|
|
5724
5838
|
function usageFromBrightsy(usage) {
|
|
5725
5839
|
if (!usage) return null;
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
inputTokens,
|
|
5732
|
-
outputTokens,
|
|
5733
|
-
cacheReadTokens: cached || void 0
|
|
5734
|
-
};
|
|
5840
|
+
return fromInclusiveInputUsage({
|
|
5841
|
+
inputTokens: Number(usage.prompt_tokens ?? 0),
|
|
5842
|
+
outputTokens: Number(usage.completion_tokens ?? 0),
|
|
5843
|
+
cachedInputTokens: Number(usage.prompt_tokens_details?.cached_tokens ?? 0)
|
|
5844
|
+
});
|
|
5735
5845
|
}
|
|
5736
5846
|
function parseBrightsyCliLine(line) {
|
|
5737
5847
|
const trimmed = line.trim();
|
|
@@ -5947,6 +6057,7 @@ var init_brightsy = __esm({
|
|
|
5947
6057
|
init_connected_teams();
|
|
5948
6058
|
init_config();
|
|
5949
6059
|
init_app_settings();
|
|
6060
|
+
init_usage();
|
|
5950
6061
|
init_brightsy_targets();
|
|
5951
6062
|
init_error_detail();
|
|
5952
6063
|
init_turn_input();
|
|
@@ -6093,6 +6204,13 @@ var init_profile = __esm({
|
|
|
6093
6204
|
function isAsarPath(filePath) {
|
|
6094
6205
|
return /\.asar([/\\]|$)/.test(filePath);
|
|
6095
6206
|
}
|
|
6207
|
+
function applyNodeLaunch(launch, args) {
|
|
6208
|
+
if (!launch.env.ELECTRON_RUN_AS_NODE) {
|
|
6209
|
+
return { file: launch.file, args, env: launch.env };
|
|
6210
|
+
}
|
|
6211
|
+
const wrapped = wrapElectronAsNodeLaunch(launch.file, args);
|
|
6212
|
+
return { file: wrapped.file, args: wrapped.args, env: launch.env };
|
|
6213
|
+
}
|
|
6096
6214
|
async function resolveNodeLaunch(scriptPath) {
|
|
6097
6215
|
if (isAsarPath(scriptPath)) {
|
|
6098
6216
|
return {
|
|
@@ -6113,6 +6231,7 @@ async function resolveNodeLaunch(scriptPath) {
|
|
|
6113
6231
|
var init_node_launch = __esm({
|
|
6114
6232
|
"src/agents/node-launch.ts"() {
|
|
6115
6233
|
"use strict";
|
|
6234
|
+
init_nested_electron_env();
|
|
6116
6235
|
init_run();
|
|
6117
6236
|
}
|
|
6118
6237
|
});
|
|
@@ -6239,11 +6358,11 @@ async function resolveSideboardMcpServer() {
|
|
|
6239
6358
|
if (entry) {
|
|
6240
6359
|
const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
|
|
6241
6360
|
const scriptArgs = isCli ? [entry, "mcp"] : [entry];
|
|
6242
|
-
const launch = await resolveNodeLaunch(entry);
|
|
6361
|
+
const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
|
|
6243
6362
|
return {
|
|
6244
6363
|
name: "sideboard",
|
|
6245
6364
|
command: launch.file,
|
|
6246
|
-
args:
|
|
6365
|
+
args: launch.args,
|
|
6247
6366
|
...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
|
|
6248
6367
|
};
|
|
6249
6368
|
}
|
|
@@ -6840,14 +6959,11 @@ async function listCodexModels() {
|
|
|
6840
6959
|
}
|
|
6841
6960
|
function usageFromCodex(usage) {
|
|
6842
6961
|
if (!usage) return null;
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
outputTokens,
|
|
6849
|
-
cacheReadTokens: usage.cached_input_tokens ? Number(usage.cached_input_tokens) : void 0
|
|
6850
|
-
};
|
|
6962
|
+
return fromInclusiveInputUsage({
|
|
6963
|
+
inputTokens: Number(usage.input_tokens ?? 0),
|
|
6964
|
+
outputTokens: Number(usage.output_tokens ?? 0),
|
|
6965
|
+
cachedInputTokens: Number(usage.cached_input_tokens ?? 0)
|
|
6966
|
+
});
|
|
6851
6967
|
}
|
|
6852
6968
|
function codexConfigHasNetworkAccess() {
|
|
6853
6969
|
const candidates = [
|
|
@@ -6907,6 +7023,7 @@ var init_codex = __esm({
|
|
|
6907
7023
|
init_app_settings();
|
|
6908
7024
|
init_global_workspace();
|
|
6909
7025
|
init_error_detail();
|
|
7026
|
+
init_usage();
|
|
6910
7027
|
init_injected_mcp();
|
|
6911
7028
|
init_turn_input();
|
|
6912
7029
|
init_types();
|
|
@@ -7457,10 +7574,13 @@ var init_cursor = __esm({
|
|
|
7457
7574
|
};
|
|
7458
7575
|
const runner = cursorRunnerPath();
|
|
7459
7576
|
const isTs = runner.endsWith(".ts");
|
|
7460
|
-
const launch =
|
|
7577
|
+
const launch = applyNodeLaunch(
|
|
7578
|
+
await resolveNodeLaunch(runner),
|
|
7579
|
+
isTs ? ["--import", "tsx", runner] : [runner]
|
|
7580
|
+
);
|
|
7461
7581
|
return {
|
|
7462
7582
|
file: launch.file,
|
|
7463
|
-
args:
|
|
7583
|
+
args: launch.args,
|
|
7464
7584
|
cwd: thread.worktreePath,
|
|
7465
7585
|
stdin: JSON.stringify(req),
|
|
7466
7586
|
env: {
|
|
@@ -8534,6 +8654,7 @@ __export(index_exports, {
|
|
|
8534
8654
|
REVIEW_REQUEST_NAME: () => REVIEW_REQUEST_NAME,
|
|
8535
8655
|
REVIEW_REQUEST_PATH: () => REVIEW_REQUEST_PATH,
|
|
8536
8656
|
REVIEW_REQUEST_PREFILL: () => REVIEW_REQUEST_PREFILL,
|
|
8657
|
+
REVIEW_REQUEST_TEMPLATE: () => REVIEW_REQUEST_TEMPLATE,
|
|
8537
8658
|
SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
|
|
8538
8659
|
SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
8539
8660
|
SIDEBOARD_MCP_PROFILE_ENV: () => SIDEBOARD_MCP_PROFILE_ENV,
|
|
@@ -8682,9 +8803,11 @@ __export(index_exports, {
|
|
|
8682
8803
|
formatGhLandError: () => formatGhLandError,
|
|
8683
8804
|
formatGitAuthModeDirective: () => formatGitAuthModeDirective,
|
|
8684
8805
|
formatIpcInvokeError: () => formatIpcInvokeError,
|
|
8806
|
+
formatMergePrError: () => formatMergePrError,
|
|
8685
8807
|
formatMessagesAsTranscript: () => formatMessagesAsTranscript,
|
|
8686
8808
|
formatPlanQuestionAnswers: () => formatPlanQuestionAnswers,
|
|
8687
8809
|
formatPlanQuestionsForChat: () => formatPlanQuestionsForChat,
|
|
8810
|
+
formatProcessGuideDirective: () => formatProcessGuideDirective,
|
|
8688
8811
|
formatRateLimitResetHint: () => formatRateLimitResetHint,
|
|
8689
8812
|
formatRenameBranchDirective: () => formatRenameBranchDirective,
|
|
8690
8813
|
formatSlackExternalReplyPrompt: () => formatSlackExternalReplyPrompt,
|
|
@@ -8692,9 +8815,11 @@ __export(index_exports, {
|
|
|
8692
8815
|
formatSlackRepliesForTurn: () => formatSlackRepliesForTurn,
|
|
8693
8816
|
formatSlackSignedReply: () => formatSlackSignedReply,
|
|
8694
8817
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
8818
|
+
formatUiReminder: () => formatUiReminder,
|
|
8695
8819
|
formatWorkspaceInventory: () => formatWorkspaceInventory,
|
|
8696
8820
|
formatWorktreeDirective: () => formatWorktreeDirective,
|
|
8697
8821
|
formatWorktreeReminder: () => formatWorktreeReminder,
|
|
8822
|
+
fromInclusiveInputUsage: () => fromInclusiveInputUsage,
|
|
8698
8823
|
getAdapter: () => getAdapter,
|
|
8699
8824
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
8700
8825
|
getBrightsySession: () => getBrightsySession,
|
|
@@ -8765,6 +8890,7 @@ __export(index_exports, {
|
|
|
8765
8890
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
8766
8891
|
isPidAlive: () => isPidAlive,
|
|
8767
8892
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
8893
|
+
isPrNotMergeableError: () => isPrNotMergeableError,
|
|
8768
8894
|
isPresentPlanToolName: () => isPresentPlanToolName,
|
|
8769
8895
|
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
8770
8896
|
isSideboardScratchPath: () => isSideboardScratchPath,
|
|
@@ -9031,7 +9157,8 @@ function agentGitPrompt(action, opts) {
|
|
|
9031
9157
|
return "Commit, push, and open a PR in the browser.";
|
|
9032
9158
|
case "resolve-conflicts": {
|
|
9033
9159
|
const base = opts?.prBase?.trim().replace(/^refs\/heads\//, "");
|
|
9034
|
-
|
|
9160
|
+
const named = base ? ` (${base})` : "";
|
|
9161
|
+
return `Merge the remote branch${named} into your branch and resolve conflicts. Then, commit and push your changes.`;
|
|
9035
9162
|
}
|
|
9036
9163
|
case "merge":
|
|
9037
9164
|
return "Merge PR.";
|
|
@@ -9992,43 +10119,7 @@ function normalizeParseResult(parsed) {
|
|
|
9992
10119
|
|
|
9993
10120
|
// src/agents/spawn.ts
|
|
9994
10121
|
init_path();
|
|
9995
|
-
|
|
9996
|
-
// src/agents/usage.ts
|
|
9997
|
-
function sumOptional(a, b) {
|
|
9998
|
-
if (a == null && b == null) return void 0;
|
|
9999
|
-
return (a ?? 0) + (b ?? 0);
|
|
10000
|
-
}
|
|
10001
|
-
function requestOccupancy(u) {
|
|
10002
|
-
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
10003
|
-
}
|
|
10004
|
-
function mergeUsage(a, b) {
|
|
10005
|
-
return {
|
|
10006
|
-
inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
|
|
10007
|
-
outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
|
|
10008
|
-
cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
|
|
10009
|
-
cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
|
|
10010
|
-
lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
|
|
10011
|
-
};
|
|
10012
|
-
}
|
|
10013
|
-
function applyTurnUsage(current, incoming, scope = "request") {
|
|
10014
|
-
if (scope === "turn") {
|
|
10015
|
-
return {
|
|
10016
|
-
...incoming,
|
|
10017
|
-
lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
|
|
10018
|
-
};
|
|
10019
|
-
}
|
|
10020
|
-
const merged = mergeUsage(current, incoming);
|
|
10021
|
-
return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
|
|
10022
|
-
}
|
|
10023
|
-
function totalTokens(u) {
|
|
10024
|
-
return u.inputTokens + u.outputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
10025
|
-
}
|
|
10026
|
-
function contextTokens(u) {
|
|
10027
|
-
if (u.lastRequestTokens != null && u.lastRequestTokens > 0) return u.lastRequestTokens;
|
|
10028
|
-
return requestOccupancy(u);
|
|
10029
|
-
}
|
|
10030
|
-
|
|
10031
|
-
// src/agents/spawn.ts
|
|
10122
|
+
init_usage();
|
|
10032
10123
|
async function spawnAgentTurn(thread, input, onEvent) {
|
|
10033
10124
|
ensureAgentPath();
|
|
10034
10125
|
if (!thread.worktreePath?.trim()) {
|
|
@@ -10146,6 +10237,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
10146
10237
|
}
|
|
10147
10238
|
|
|
10148
10239
|
// src/index.ts
|
|
10240
|
+
init_usage();
|
|
10149
10241
|
init_claude_mcp();
|
|
10150
10242
|
|
|
10151
10243
|
// src/agents/instructions.ts
|
|
@@ -10210,7 +10302,7 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
10210
10302
|
"- Prefer a concise imperative title (Conventional Commits style when it fits: feat:/fix:/chore:/docs:). Body should summarize intent, key changes, and test notes."
|
|
10211
10303
|
);
|
|
10212
10304
|
lines.push(
|
|
10213
|
-
"- 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.
|
|
10305
|
+
"- 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`)."
|
|
10214
10306
|
);
|
|
10215
10307
|
if (thread.prUrl) {
|
|
10216
10308
|
lines.push(
|
|
@@ -10242,19 +10334,30 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
10242
10334
|
'- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
|
|
10243
10335
|
);
|
|
10244
10336
|
lines.push(
|
|
10245
|
-
'- "
|
|
10337
|
+
'- "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.'
|
|
10246
10338
|
);
|
|
10247
10339
|
lines.push(
|
|
10248
10340
|
'- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
|
|
10249
10341
|
);
|
|
10250
10342
|
lines.push(
|
|
10251
|
-
'- "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.'
|
|
10343
|
+
'- "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.'
|
|
10252
10344
|
);
|
|
10345
|
+
lines.push("");
|
|
10346
|
+
lines.push(formatProcessGuideDirective());
|
|
10253
10347
|
return lines.join("\n");
|
|
10254
10348
|
}
|
|
10255
10349
|
function formatWorktreeReminder() {
|
|
10256
10350
|
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.";
|
|
10257
10351
|
}
|
|
10352
|
+
function formatProcessGuideDirective() {
|
|
10353
|
+
return [
|
|
10354
|
+
"Process guides (recurring work only):",
|
|
10355
|
+
"- 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.",
|
|
10356
|
+
"- 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.",
|
|
10357
|
+
"- 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.",
|
|
10358
|
+
"- 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."
|
|
10359
|
+
].join("\n");
|
|
10360
|
+
}
|
|
10258
10361
|
function formatArtifactDirective() {
|
|
10259
10362
|
return [
|
|
10260
10363
|
"Sideboard side column (desktop UI):",
|
|
@@ -10272,9 +10375,22 @@ function formatArtifactDirective() {
|
|
|
10272
10375
|
"Files / media browser (CMS file manager column):",
|
|
10273
10376
|
"4) Call Sideboard MCP `present_files` with optional title, path, and datasource (brightsy|memory).",
|
|
10274
10377
|
" Opens the Files column for browse/upload/pick. Do NOT say a file manager UI is missing.",
|
|
10275
|
-
"
|
|
10378
|
+
"Multiple-choice questions (any mode \u2014 not only Plan):",
|
|
10379
|
+
"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.",
|
|
10380
|
+
"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."
|
|
10276
10381
|
].join("\n");
|
|
10277
10382
|
}
|
|
10383
|
+
function formatUiReminder() {
|
|
10384
|
+
return [
|
|
10385
|
+
"Sideboard side column (important):",
|
|
10386
|
+
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
10387
|
+
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
10388
|
+
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
10389
|
+
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
10390
|
+
"Do not say artifacts/CMS UI are unavailable.",
|
|
10391
|
+
"Multiple-choice questions: MCP ask_user (composer picker, any mode). Explain options in chat first, then wait for answers."
|
|
10392
|
+
].join(" ");
|
|
10393
|
+
}
|
|
10278
10394
|
var FILES_BY_AGENT = {
|
|
10279
10395
|
claude: [
|
|
10280
10396
|
"CLAUDE.md",
|
|
@@ -10291,10 +10407,11 @@ var FILES_BY_AGENT = {
|
|
|
10291
10407
|
var MAX_CHARS_PER_FILE = 48e3;
|
|
10292
10408
|
function loadAgentInstructions(worktreePath, agent) {
|
|
10293
10409
|
const candidates = FILES_BY_AGENT[agent] ?? FILES_BY_AGENT.claude;
|
|
10294
|
-
const
|
|
10410
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
10411
|
+
const seenBodies = /* @__PURE__ */ new Set();
|
|
10295
10412
|
const out = [];
|
|
10296
10413
|
for (const rel of candidates) {
|
|
10297
|
-
if (
|
|
10414
|
+
if (seenPaths.has(rel)) continue;
|
|
10298
10415
|
const abs = (0, import_node_path17.join)(worktreePath, rel);
|
|
10299
10416
|
if (!(0, import_node_fs20.existsSync)(abs)) continue;
|
|
10300
10417
|
try {
|
|
@@ -10306,7 +10423,10 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
10306
10423
|
|
|
10307
10424
|
\u2026(truncated)`;
|
|
10308
10425
|
}
|
|
10309
|
-
|
|
10426
|
+
const body = content.trim().replace(/\r\n/g, "\n");
|
|
10427
|
+
if (seenBodies.has(body)) continue;
|
|
10428
|
+
seenPaths.add(rel);
|
|
10429
|
+
seenBodies.add(body);
|
|
10310
10430
|
out.push({ relativePath: rel, content });
|
|
10311
10431
|
} catch {
|
|
10312
10432
|
}
|
|
@@ -10384,6 +10504,8 @@ var import_node_path18 = require("path");
|
|
|
10384
10504
|
var import_execa3 = require("execa");
|
|
10385
10505
|
var import_node_readline2 = require("readline");
|
|
10386
10506
|
init_settings();
|
|
10507
|
+
init_nested_electron_env();
|
|
10508
|
+
init_nested_electron_env();
|
|
10387
10509
|
var PORT_RANGE_SIZE = 10;
|
|
10388
10510
|
function matchSimpleGlob(pattern, name) {
|
|
10389
10511
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -10467,16 +10589,6 @@ async function captureLoginEnv() {
|
|
|
10467
10589
|
cachedLoginEnv = { ...process.env };
|
|
10468
10590
|
return { ...cachedLoginEnv };
|
|
10469
10591
|
}
|
|
10470
|
-
var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
|
|
10471
|
-
function stripNestedElectronEnv(env) {
|
|
10472
|
-
const out = { ...env };
|
|
10473
|
-
for (const key of Object.keys(out)) {
|
|
10474
|
-
if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
10475
|
-
delete out[key];
|
|
10476
|
-
}
|
|
10477
|
-
}
|
|
10478
|
-
return out;
|
|
10479
|
-
}
|
|
10480
10592
|
function buildWorkspaceScriptEnv(opts, baseEnv) {
|
|
10481
10593
|
const env = stripNestedElectronEnv({
|
|
10482
10594
|
...baseEnv ?? process.env
|
|
@@ -14267,6 +14379,10 @@ File: src/client/frontends/desktop/core/UserData.ts
|
|
|
14267
14379
|
|
|
14268
14380
|
**Approve** \u2014 Diff is scoped, behavior looks correct, and there are no blocking issues. Safe to merge.
|
|
14269
14381
|
</example>
|
|
14382
|
+
|
|
14383
|
+
## Growing the rules
|
|
14384
|
+
|
|
14385
|
+
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\`.
|
|
14270
14386
|
`;
|
|
14271
14387
|
|
|
14272
14388
|
// src/review/request-review.ts
|
|
@@ -15067,14 +15183,7 @@ var Orchestrator = class {
|
|
|
15067
15183
|
SLACK_REPLY_FORMATTING
|
|
15068
15184
|
].join("\n") : null
|
|
15069
15185
|
].filter(Boolean).join("\n") : null;
|
|
15070
|
-
const artifactReminder = thread.agent !== "brightsy" ?
|
|
15071
|
-
"Sideboard side column (important):",
|
|
15072
|
-
"There is no claude.ai Artifact tool here \u2014 that is normal.",
|
|
15073
|
-
"HTML/SVG/markdown: ```html fence or MCP present_artifact.",
|
|
15074
|
-
"CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
|
|
15075
|
-
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
15076
|
-
"Do not say artifacts/CMS UI are unavailable."
|
|
15077
|
-
].join(" ") : null;
|
|
15186
|
+
const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
|
|
15078
15187
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
15079
15188
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
15080
15189
|
pendingSlackExternalReplies(thread.messages)
|
|
@@ -15768,7 +15877,9 @@ var Orchestrator = class {
|
|
|
15768
15877
|
reviewDecision: null,
|
|
15769
15878
|
baseRefName: "",
|
|
15770
15879
|
headRefName: "",
|
|
15771
|
-
isInMergeQueue: false
|
|
15880
|
+
isInMergeQueue: false,
|
|
15881
|
+
mergeable: null,
|
|
15882
|
+
mergeStateStatus: null
|
|
15772
15883
|
};
|
|
15773
15884
|
await this.persistPrMetaAndMaybeArchive(thread, metaLike);
|
|
15774
15885
|
return { url: metaLike.url, state };
|
|
@@ -17130,7 +17241,7 @@ async function startMcpServer() {
|
|
|
17130
17241
|
);
|
|
17131
17242
|
server.tool(
|
|
17132
17243
|
"ask_user",
|
|
17133
|
-
"Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (
|
|
17244
|
+
"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.",
|
|
17134
17245
|
{
|
|
17135
17246
|
questions: import_zod3.z.array(
|
|
17136
17247
|
import_zod3.z.object({
|
|
@@ -17393,7 +17504,7 @@ async function startMcpServer() {
|
|
|
17393
17504
|
);
|
|
17394
17505
|
server.tool(
|
|
17395
17506
|
"send_to_thread",
|
|
17396
|
-
|
|
17507
|
+
'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.',
|
|
17397
17508
|
{
|
|
17398
17509
|
ref: import_zod3.z.string(),
|
|
17399
17510
|
prompt: import_zod3.z.string(),
|
|
@@ -17489,7 +17600,7 @@ async function startMcpServer() {
|
|
|
17489
17600
|
);
|
|
17490
17601
|
server.tool(
|
|
17491
17602
|
"archive_thread",
|
|
17492
|
-
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, open PRs
|
|
17603
|
+
"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.",
|
|
17493
17604
|
{ ref: import_zod3.z.string() },
|
|
17494
17605
|
async ({ ref }) => {
|
|
17495
17606
|
const t = orch.getThread(ref);
|
|
@@ -17597,7 +17708,7 @@ async function startMcpServer() {
|
|
|
17597
17708
|
);
|
|
17598
17709
|
server.tool(
|
|
17599
17710
|
"ask_git",
|
|
17600
|
-
"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.",
|
|
17711
|
+
"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.",
|
|
17601
17712
|
{
|
|
17602
17713
|
ref: import_zod3.z.string().describe("Worktree thread id/ref"),
|
|
17603
17714
|
action: import_zod3.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -17898,7 +18009,7 @@ async function startMcpServer() {
|
|
|
17898
18009
|
);
|
|
17899
18010
|
server.tool(
|
|
17900
18011
|
"get_pr_stack",
|
|
17901
|
-
"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.",
|
|
18012
|
+
"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).",
|
|
17902
18013
|
{ ref: import_zod3.z.string() },
|
|
17903
18014
|
async ({ ref }) => {
|
|
17904
18015
|
const stack = await orch.getPrStack(ref);
|
|
@@ -20007,6 +20118,7 @@ async function startSlackRelayServer(opts) {
|
|
|
20007
20118
|
REVIEW_REQUEST_NAME,
|
|
20008
20119
|
REVIEW_REQUEST_PATH,
|
|
20009
20120
|
REVIEW_REQUEST_PREFILL,
|
|
20121
|
+
REVIEW_REQUEST_TEMPLATE,
|
|
20010
20122
|
SIDEBOARD_FORCE_STOP,
|
|
20011
20123
|
SIDEBOARD_MCP_ALLOWED_TOOLS,
|
|
20012
20124
|
SIDEBOARD_MCP_PROFILE_ENV,
|
|
@@ -20155,9 +20267,11 @@ async function startSlackRelayServer(opts) {
|
|
|
20155
20267
|
formatGhLandError,
|
|
20156
20268
|
formatGitAuthModeDirective,
|
|
20157
20269
|
formatIpcInvokeError,
|
|
20270
|
+
formatMergePrError,
|
|
20158
20271
|
formatMessagesAsTranscript,
|
|
20159
20272
|
formatPlanQuestionAnswers,
|
|
20160
20273
|
formatPlanQuestionsForChat,
|
|
20274
|
+
formatProcessGuideDirective,
|
|
20161
20275
|
formatRateLimitResetHint,
|
|
20162
20276
|
formatRenameBranchDirective,
|
|
20163
20277
|
formatSlackExternalReplyPrompt,
|
|
@@ -20165,9 +20279,11 @@ async function startSlackRelayServer(opts) {
|
|
|
20165
20279
|
formatSlackRepliesForTurn,
|
|
20166
20280
|
formatSlackSignedReply,
|
|
20167
20281
|
formatTranscriptMarkdown,
|
|
20282
|
+
formatUiReminder,
|
|
20168
20283
|
formatWorkspaceInventory,
|
|
20169
20284
|
formatWorktreeDirective,
|
|
20170
20285
|
formatWorktreeReminder,
|
|
20286
|
+
fromInclusiveInputUsage,
|
|
20171
20287
|
getAdapter,
|
|
20172
20288
|
getAgentSetupInfo,
|
|
20173
20289
|
getBrightsySession,
|
|
@@ -20238,6 +20354,7 @@ async function startSlackRelayServer(opts) {
|
|
|
20238
20354
|
isOrchestratorThread,
|
|
20239
20355
|
isPidAlive,
|
|
20240
20356
|
isPlaceholderBranch,
|
|
20357
|
+
isPrNotMergeableError,
|
|
20241
20358
|
isPresentPlanToolName,
|
|
20242
20359
|
isSessionQuotaLimit,
|
|
20243
20360
|
isSideboardScratchPath,
|