@sideboard-ai/core 0.1.158 → 0.1.160
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/{abletime-GWZE6OYM.js → abletime-AGF54TNA.js} +2 -2
- package/dist/{abletime-BEIDJG7W.js → abletime-BMS53RIZ.js} +2 -2
- package/dist/{agents-RGE7PSUY.js → agents-6UOXBORU.js} +4 -4
- package/dist/{agents-E3ZPWZRS.js → agents-RD774UEV.js} +4 -4
- package/dist/{app-settings-VRQI3FIE.js → app-settings-BRLZQA7N.js} +13 -17
- package/dist/{app-settings-H3INSR67.js → app-settings-JJQDWPEK.js} +13 -17
- package/dist/{chunk-EPQIOAAY.js → chunk-7OWRFJK6.js} +7 -5
- package/dist/{chunk-KYKNFJK5.js → chunk-BQ4PEXIF.js} +39 -20
- package/dist/{chunk-3BWVOUQG.js → chunk-CVZ4KXVP.js} +127 -113
- package/dist/{chunk-3SUZMPGL.js → chunk-HZROPNYL.js} +12 -13
- package/dist/{chunk-DVJBO3M7.js → chunk-ILXKJ4ZD.js} +1 -1
- package/dist/{chunk-A7QXLYBN.js → chunk-IO5WGZRR.js} +3 -13
- package/dist/{chunk-N27GVFZY.js → chunk-KJFFQAMA.js} +1 -1
- package/dist/{chunk-D7EGJ3EI.js → chunk-LTXS26K5.js} +127 -113
- package/dist/{chunk-CVR4RJ6G.js → chunk-QANLIT2E.js} +12 -13
- package/dist/{chunk-FBFIOVDH.js → chunk-QYKADGPP.js} +38 -19
- package/dist/{chunk-BLRHO7XQ.js → chunk-RQNXLIXO.js} +10 -6
- package/dist/{chunk-LQMFCS54.js → chunk-UUEBNUEG.js} +3 -13
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-X3LXE5Q4.js} +3 -3
- package/dist/{coordinator-prompt-GZESIQNH.js → coordinator-prompt-YYUXC7EM.js} +3 -3
- package/dist/{global-workspace-REI55FZ6.js → global-workspace-E6S5XQZF.js} +3 -3
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-SPR2Y4YQ.js} +3 -3
- package/dist/index.cjs +599 -475
- package/dist/index.d.cts +62 -57
- package/dist/index.d.ts +62 -57
- package/dist/index.js +325 -230
- package/dist/mcp/run-stdio.cjs +528 -402
- package/dist/mcp/run-stdio.js +265 -163
- package/dist/{orchestrator-ZVW2AC6B.js → orchestrator-BSVSOTBK.js} +5 -5
- package/dist/{orchestrator-J727WIJY.js → orchestrator-KVKPIUVU.js} +5 -5
- package/dist/{workspaces-NYSM2EPX.js → workspaces-MZELP4BI.js} +3 -3
- package/dist/{workspaces-OCEPZXFD.js → workspaces-OK2SNNQF.js} +3 -3
- package/dist/{worktree-VTMBZKY6.js → worktree-BX53JZHO.js} +2 -2
- package/dist/{worktree-T4SWFSTT.js → worktree-IY7YCEGH.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1086,8 +1086,8 @@ ${input.permalink}` : "";
|
|
|
1086
1086
|
|
|
1087
1087
|
${body}${link}`;
|
|
1088
1088
|
}
|
|
1089
|
-
function isSlackExternalReplyPrompt(
|
|
1090
|
-
return
|
|
1089
|
+
function isSlackExternalReplyPrompt(text7) {
|
|
1090
|
+
return text7.startsWith("Slack reply from ") && text7.includes("not a command");
|
|
1091
1091
|
}
|
|
1092
1092
|
function pendingSlackExternalReplies(messages) {
|
|
1093
1093
|
let i = messages.length - 1;
|
|
@@ -1128,9 +1128,9 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
1128
1128
|
function listSlackOutboundWatches() {
|
|
1129
1129
|
return pruneWatches(readStore3());
|
|
1130
1130
|
}
|
|
1131
|
-
function formatOwnerSlackFyi(userName,
|
|
1131
|
+
function formatOwnerSlackFyi(userName, text7) {
|
|
1132
1132
|
const who = userName.trim() || "Someone";
|
|
1133
|
-
const body =
|
|
1133
|
+
const body = text7.trim() || "(no text)";
|
|
1134
1134
|
return `${who} replied in Slack:
|
|
1135
1135
|
${body}`;
|
|
1136
1136
|
}
|
|
@@ -1145,7 +1145,7 @@ async function relayExternalReply(opts) {
|
|
|
1145
1145
|
const thread = readThread(threadId);
|
|
1146
1146
|
if (!thread || thread.status === "archived") return "ignored";
|
|
1147
1147
|
try {
|
|
1148
|
-
const
|
|
1148
|
+
const text7 = formatSlackExternalReplyPrompt({
|
|
1149
1149
|
userName: opts.reply.userName,
|
|
1150
1150
|
kind: opts.watch.kind,
|
|
1151
1151
|
toLabel: opts.watch.toLabel,
|
|
@@ -1154,7 +1154,7 @@ async function relayExternalReply(opts) {
|
|
|
1154
1154
|
});
|
|
1155
1155
|
appendMessage(threadId, {
|
|
1156
1156
|
role: "agent",
|
|
1157
|
-
text:
|
|
1157
|
+
text: text7,
|
|
1158
1158
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
1159
1159
|
});
|
|
1160
1160
|
} catch {
|
|
@@ -1468,20 +1468,20 @@ function looksLikeMinifiedJsDump(line) {
|
|
|
1468
1468
|
function looksLikeNestedElectronCrash(line) {
|
|
1469
1469
|
return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
|
|
1470
1470
|
}
|
|
1471
|
-
function looksLikeNativeEventLoopCrash(
|
|
1472
|
-
return /uv_run|uv__io_poll|SpinEventLoopInternal/i.test(
|
|
1471
|
+
function looksLikeNativeEventLoopCrash(text7) {
|
|
1472
|
+
return /uv_run|uv__io_poll|SpinEventLoopInternal/i.test(text7);
|
|
1473
1473
|
}
|
|
1474
|
-
function looksLikeV8Oom(
|
|
1474
|
+
function looksLikeV8Oom(text7) {
|
|
1475
1475
|
return /javascript heap|reached heap limit|OOMErrorHandler|FatalProcessOutOfMemory|Allocation failed - JavaScript heap/i.test(
|
|
1476
|
-
|
|
1476
|
+
text7
|
|
1477
1477
|
);
|
|
1478
1478
|
}
|
|
1479
|
-
function looksLikeHomebrewLibuvCrash(
|
|
1480
|
-
if (!looksLikeNativeEventLoopCrash(
|
|
1481
|
-
return /Cellar\/(?:libuv|node)|libuv\.\d+\.dylib/i.test(
|
|
1479
|
+
function looksLikeHomebrewLibuvCrash(text7) {
|
|
1480
|
+
if (!looksLikeNativeEventLoopCrash(text7)) return false;
|
|
1481
|
+
return /Cellar\/(?:libuv|node)|libuv\.\d+\.dylib/i.test(text7);
|
|
1482
1482
|
}
|
|
1483
|
-
function clipStderr(
|
|
1484
|
-
const trimmed =
|
|
1483
|
+
function clipStderr(text7, maxChars) {
|
|
1484
|
+
const trimmed = text7.trim();
|
|
1485
1485
|
if (trimmed.length <= maxChars) return trimmed;
|
|
1486
1486
|
return trimmed.slice(0, maxChars);
|
|
1487
1487
|
}
|
|
@@ -1515,16 +1515,16 @@ function summarizeTurnStderr(tail, maxChars = 500) {
|
|
|
1515
1515
|
const joined = (useful.length ? useful : tail).slice(-6).join("\n").trim();
|
|
1516
1516
|
return clipStderr(joined, maxChars);
|
|
1517
1517
|
}
|
|
1518
|
-
function looksLikeInvalidAgentSession(
|
|
1519
|
-
const lower =
|
|
1518
|
+
function looksLikeInvalidAgentSession(text7) {
|
|
1519
|
+
const lower = text7.trim().toLowerCase();
|
|
1520
1520
|
if (!lower) return false;
|
|
1521
1521
|
return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower) || /\brun\b.{0,80}\bnot found for agent\b/.test(lower);
|
|
1522
1522
|
}
|
|
1523
|
-
function looksLikeRetryableRunnerCrash(
|
|
1524
|
-
if (looksLikeAgentFailureMessage(
|
|
1525
|
-
if (looksLikeInvalidAgentSession(
|
|
1526
|
-
if (looksLikeV8Oom(
|
|
1527
|
-
const lower =
|
|
1523
|
+
function looksLikeRetryableRunnerCrash(text7) {
|
|
1524
|
+
if (looksLikeAgentFailureMessage(text7)) return false;
|
|
1525
|
+
if (looksLikeInvalidAgentSession(text7)) return false;
|
|
1526
|
+
if (looksLikeV8Oom(text7)) return false;
|
|
1527
|
+
const lower = text7.trim().toLowerCase();
|
|
1528
1528
|
if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
|
|
1529
1529
|
if (!lower) return true;
|
|
1530
1530
|
return /uv_run|spineventloopinternal|libuv|homebrew node \+ shared libuv|cursor runner crashed in node|hascustomhostobject|electroninitializeicuandstartnode|nested chromium|truncated crash dump|connection stalled|network request failed|cursor startup failed:.+\(retryable\)|sig(?:segv|abrt|ill)|segmentation fault|illegal instruction|fatal error/.test(
|
|
@@ -1536,12 +1536,12 @@ function shouldRetryFailedAgentTurn(detail, opts) {
|
|
|
1536
1536
|
if (looksLikeV8Oom(detail) && opts.hasSession) return true;
|
|
1537
1537
|
return looksLikeRetryableRunnerCrash(detail);
|
|
1538
1538
|
}
|
|
1539
|
-
function looksLikeAgentFailureMessage(
|
|
1540
|
-
const lower =
|
|
1539
|
+
function looksLikeAgentFailureMessage(text7) {
|
|
1540
|
+
const lower = text7.trim().toLowerCase();
|
|
1541
1541
|
if (!lower) return false;
|
|
1542
1542
|
return /you've hit your|hit your (session|weekly|opus) limit|usage limit/.test(lower) || /credit balance is too low|out of credits|insufficient.?quota|quota.?exceeded/.test(lower) || /invalid user api key|invalid api key|not logged in|not authenticated|unauthorized/.test(
|
|
1543
1543
|
lower
|
|
1544
|
-
) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower) || /\[resource_exhausted\]|resource_exhausted/.test(lower) || /findFilesWithRipgrep/.test(
|
|
1544
|
+
) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower) || /\[resource_exhausted\]|resource_exhausted/.test(lower) || /findFilesWithRipgrep/.test(text7);
|
|
1545
1545
|
}
|
|
1546
1546
|
function fallbackTurnFailDetail(assistantText) {
|
|
1547
1547
|
const t = assistantText.trim();
|
|
@@ -1822,8 +1822,8 @@ var init_github_agent_auth = __esm({
|
|
|
1822
1822
|
});
|
|
1823
1823
|
|
|
1824
1824
|
// src/git/stale-lock.ts
|
|
1825
|
-
function isIndexLockError(
|
|
1826
|
-
return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(
|
|
1825
|
+
function isIndexLockError(text7) {
|
|
1826
|
+
return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(text7);
|
|
1827
1827
|
}
|
|
1828
1828
|
function clearStaleIndexLock(gitDir, maxAgeMs = STALE_INDEX_LOCK_MS, now = Date.now()) {
|
|
1829
1829
|
const lockPath = (0, import_node_path13.join)(gitDir, "index.lock");
|
|
@@ -2802,12 +2802,12 @@ var init_worktree_labels = __esm({
|
|
|
2802
2802
|
});
|
|
2803
2803
|
|
|
2804
2804
|
// src/git/gh-errors.ts
|
|
2805
|
-
function isGhRateLimitError(
|
|
2806
|
-
return /API rate limit (already )?exceeded/i.test(
|
|
2805
|
+
function isGhRateLimitError(text7) {
|
|
2806
|
+
return /API rate limit (already )?exceeded/i.test(text7) || /rate limit exceeded/i.test(text7);
|
|
2807
2807
|
}
|
|
2808
|
-
function isGhPrBodyTooLongError(
|
|
2808
|
+
function isGhPrBodyTooLongError(text7) {
|
|
2809
2809
|
return /body is too long|body.{0,20}too long|maximum is 65536|exceeds the maximum allowed size|request (entity |is )?too large|payload too large|413 Request Entity Too Large/i.test(
|
|
2810
|
-
|
|
2810
|
+
text7
|
|
2811
2811
|
);
|
|
2812
2812
|
}
|
|
2813
2813
|
function clampGithubPrBody(body, max = GITHUB_PR_BODY_SAFE_CHARS) {
|
|
@@ -2827,21 +2827,21 @@ function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
|
|
|
2827
2827
|
const hours = Math.ceil(mins / 60);
|
|
2828
2828
|
return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
2829
2829
|
}
|
|
2830
|
-
function omitGhBodyArg(
|
|
2831
|
-
return
|
|
2830
|
+
function omitGhBodyArg(text7) {
|
|
2831
|
+
return text7.replace(
|
|
2832
2832
|
/--body(?:-file)?(\s+|=)(?:'[^']*'|"[^"]*"|\S+)/g,
|
|
2833
2833
|
"--body <omitted>"
|
|
2834
2834
|
);
|
|
2835
2835
|
}
|
|
2836
|
-
function capGhErrorText(
|
|
2837
|
-
const t =
|
|
2836
|
+
function capGhErrorText(text7, max = 400) {
|
|
2837
|
+
const t = text7.trim();
|
|
2838
2838
|
if (t.length <= max) return t;
|
|
2839
2839
|
return `${t.slice(0, max - 1).trimEnd()}\u2026`;
|
|
2840
2840
|
}
|
|
2841
|
-
function extractGhErrorDetail(
|
|
2842
|
-
const trimmed = omitGhBodyArg(
|
|
2841
|
+
function extractGhErrorDetail(text7) {
|
|
2842
|
+
const trimmed = omitGhBodyArg(text7.trim());
|
|
2843
2843
|
if (!trimmed) return "";
|
|
2844
|
-
if (isGhPrBodyTooLongError(
|
|
2844
|
+
if (isGhPrBodyTooLongError(text7) || isGhPrBodyTooLongError(trimmed)) {
|
|
2845
2845
|
return "GraphQL: body is too long (GitHub PR body / request size limit).";
|
|
2846
2846
|
}
|
|
2847
2847
|
const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
|
|
@@ -2881,9 +2881,9 @@ function formatGhLandError(raw, opts) {
|
|
|
2881
2881
|
}
|
|
2882
2882
|
return detail || "Failed to create or update pull request";
|
|
2883
2883
|
}
|
|
2884
|
-
function isPrNotMergeableError(
|
|
2884
|
+
function isPrNotMergeableError(text7) {
|
|
2885
2885
|
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(
|
|
2886
|
-
|
|
2886
|
+
text7
|
|
2887
2887
|
);
|
|
2888
2888
|
}
|
|
2889
2889
|
function formatMergePrError(raw) {
|
|
@@ -2971,27 +2971,27 @@ var init_worktree_exclude = __esm({
|
|
|
2971
2971
|
});
|
|
2972
2972
|
|
|
2973
2973
|
// src/store/account-profile.ts
|
|
2974
|
-
function
|
|
2975
|
-
const known =
|
|
2974
|
+
function legacyRoleLabel(role) {
|
|
2975
|
+
const known = LEGACY_ROLE_LABELS[role];
|
|
2976
2976
|
if (known) return known;
|
|
2977
2977
|
return role.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2978
2978
|
}
|
|
2979
|
-
function
|
|
2979
|
+
function normalizeLegacyRole(value) {
|
|
2980
2980
|
const key = (value ?? "").trim().toLowerCase().replace(/[_\s]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2981
2981
|
if (key === "both") return void 0;
|
|
2982
2982
|
if (key.length < 2 || key.length > 40) return void 0;
|
|
2983
2983
|
if (!/^[a-z][a-z0-9-]*$/.test(key)) return void 0;
|
|
2984
2984
|
return key;
|
|
2985
2985
|
}
|
|
2986
|
-
function
|
|
2986
|
+
function legacyRoleLabels(roles, legacyRole) {
|
|
2987
2987
|
const raw = Array.isArray(roles) ? roles : [];
|
|
2988
2988
|
const out = [];
|
|
2989
2989
|
const seen = /* @__PURE__ */ new Set();
|
|
2990
2990
|
const push = (value) => {
|
|
2991
|
-
const role =
|
|
2992
|
-
if (!role || seen.has(role) || out.length >=
|
|
2991
|
+
const role = normalizeLegacyRole(typeof value === "string" ? value : "");
|
|
2992
|
+
if (!role || seen.has(role) || out.length >= 16) return;
|
|
2993
2993
|
seen.add(role);
|
|
2994
|
-
out.push(role);
|
|
2994
|
+
out.push(legacyRoleLabel(role));
|
|
2995
2995
|
};
|
|
2996
2996
|
for (const item of raw) push(item);
|
|
2997
2997
|
if (out.length === 0) push(legacyRole);
|
|
@@ -3001,117 +3001,83 @@ function normalizeProfileNotes(value) {
|
|
|
3001
3001
|
if (typeof value !== "string") return "";
|
|
3002
3002
|
return value.replace(/\r\n/g, "\n").trim().slice(0, PROFILE_NOTES_MAX);
|
|
3003
3003
|
}
|
|
3004
|
-
function
|
|
3005
|
-
const
|
|
3006
|
-
const
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
if (seen.has(key)) continue;
|
|
3012
|
-
seen.add(key);
|
|
3013
|
-
out.push(hint);
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
return out;
|
|
3017
|
-
}
|
|
3018
|
-
function teamHintsForCustomRole(role) {
|
|
3019
|
-
const slug = role.trim().toLowerCase();
|
|
3020
|
-
if (!slug) return [];
|
|
3021
|
-
if (slug.endsWith("-team") || slug.endsWith("-squad")) return [slug];
|
|
3022
|
-
return [`${slug}-team`, slug];
|
|
3004
|
+
function foldLegacyRolesIntoNotes(roles, legacyRole, notes) {
|
|
3005
|
+
const text7 = normalizeProfileNotes(notes);
|
|
3006
|
+
const labels = legacyRoleLabels(roles, legacyRole);
|
|
3007
|
+
if (!labels.length) return text7;
|
|
3008
|
+
if (text7.toLowerCase().startsWith("roles:")) return text7;
|
|
3009
|
+
const prefix = `Roles: ${labels.join(", ")}`;
|
|
3010
|
+
return text7 ? `${prefix}. ${text7}` : prefix;
|
|
3023
3011
|
}
|
|
3024
3012
|
function resolveAccountProfile(input) {
|
|
3025
3013
|
return resolveViewerProfile(input);
|
|
3026
3014
|
}
|
|
3027
3015
|
function resolveViewerProfile(account, project) {
|
|
3028
|
-
const
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
const projectNotes =
|
|
3034
|
-
|
|
3016
|
+
const accountNotes = foldLegacyRolesIntoNotes(
|
|
3017
|
+
account?.roles,
|
|
3018
|
+
account?.role,
|
|
3019
|
+
account?.notes
|
|
3020
|
+
);
|
|
3021
|
+
const projectNotes = foldLegacyRolesIntoNotes(
|
|
3022
|
+
project?.roles,
|
|
3023
|
+
project?.role,
|
|
3024
|
+
project?.notes
|
|
3025
|
+
);
|
|
3035
3026
|
return {
|
|
3036
|
-
|
|
3037
|
-
roleLabels: roles.map((role) => accountRoleLabel(role)),
|
|
3038
|
-
reviewTeamHints: reviewTeamHintsForRoles(roles),
|
|
3039
|
-
notes,
|
|
3027
|
+
notes: [accountNotes, projectNotes].filter(Boolean).join("\n"),
|
|
3040
3028
|
accountNotes,
|
|
3041
|
-
projectNotes
|
|
3042
|
-
rolesFromProject
|
|
3029
|
+
projectNotes
|
|
3043
3030
|
};
|
|
3044
3031
|
}
|
|
3045
|
-
function preferTeamsForRole(viewerTeams, hints) {
|
|
3046
|
-
const teams = viewerTeams.map((t) => t.trim()).filter(Boolean);
|
|
3047
|
-
const want = hints.map((t) => t.trim()).filter(Boolean);
|
|
3048
|
-
if (!want.length) return teams;
|
|
3049
|
-
if (!teams.length) return want;
|
|
3050
|
-
const hintKeys = want.map((t) => t.toLowerCase());
|
|
3051
|
-
const matched = teams.filter((team) => {
|
|
3052
|
-
const key = team.toLowerCase();
|
|
3053
|
-
return hintKeys.some((hint) => key === hint || key.includes(hint) || hint.includes(key));
|
|
3054
|
-
});
|
|
3055
|
-
return matched.length ? matched : teams;
|
|
3056
|
-
}
|
|
3057
3032
|
function compactNotes(notes, max = 160) {
|
|
3058
3033
|
const oneLine = notes.replace(/\s+/g, " ").trim();
|
|
3059
3034
|
if (oneLine.length <= max) return oneLine;
|
|
3060
3035
|
return `${oneLine.slice(0, max - 1).trimEnd()}\u2026`;
|
|
3061
3036
|
}
|
|
3062
3037
|
function formatAccountProfilePlaybookLine(profile) {
|
|
3063
|
-
if (
|
|
3064
|
-
|
|
3065
|
-
if (profile.roleLabels.length) {
|
|
3066
|
-
parts.push(
|
|
3067
|
-
`roles ${profile.roleLabels.join(", ")} (tickets to work on / PRs to review; prefer those teams)`
|
|
3068
|
-
);
|
|
3069
|
-
}
|
|
3070
|
-
if (profile.notes) {
|
|
3071
|
-
parts.push(`notes: ${compactNotes(profile.notes)}`);
|
|
3072
|
-
}
|
|
3073
|
-
return `- Viewer profile: ${parts.join(". ")}. A team review request is not a claim \u2014 only an individual reviewer is. When they ask to find work, use these notes with list_issues (tickets) and list_prs(queue=review) (reviews) and show the options. Only create_thread and start when they also asked to start.`;
|
|
3038
|
+
if (!profile.accountNotes) return "";
|
|
3039
|
+
return `- Account context (Settings \u2192 Agents): ${compactNotes(profile.accountNotes)}. When they ask to find work, use this with list_issues (tickets) and list_prs(queue=review) (reviews) and show the options. Only create_thread and start when they also asked to start. To change it, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context(scope=account, confirmed=true).`;
|
|
3074
3040
|
}
|
|
3075
3041
|
function formatProjectProfilePlaybookLines(projects) {
|
|
3076
3042
|
const lines = projects.map((project) => {
|
|
3077
|
-
const roles = (project.roleLabels ?? []).filter(Boolean);
|
|
3078
3043
|
const notes = normalizeProfileNotes(project.notes);
|
|
3079
|
-
if (!
|
|
3080
|
-
|
|
3081
|
-
if (roles.length) bits.push(`roles ${roles.join(", ")}`);
|
|
3082
|
-
if (notes) bits.push(compactNotes(notes, 120));
|
|
3083
|
-
return `- ${project.name}: ${bits.join(". ")}`;
|
|
3044
|
+
if (!notes) return "";
|
|
3045
|
+
return `- ${project.name}: ${compactNotes(notes, 120)}`;
|
|
3084
3046
|
}).filter(Boolean);
|
|
3085
3047
|
if (!lines.length) return "";
|
|
3086
|
-
return [
|
|
3087
|
-
"\
|
|
3088
|
-
|
|
3048
|
+
return [
|
|
3049
|
+
"Project context (Settings \u2192 Projects; adds to account context):",
|
|
3050
|
+
...lines
|
|
3051
|
+
].join("\n");
|
|
3089
3052
|
}
|
|
3090
3053
|
function formatWorkspaceProfileSuffix(profile) {
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3054
|
+
if (!profile.projectNotes) return "";
|
|
3055
|
+
return ` context:${compactNotes(profile.projectNotes, 80)}`;
|
|
3056
|
+
}
|
|
3057
|
+
function formatViewerContextDirective(profile) {
|
|
3058
|
+
const account = profile.accountNotes || "(empty)";
|
|
3059
|
+
const project = profile.projectNotes || "(empty \u2014 account context applies)";
|
|
3060
|
+
return [
|
|
3061
|
+
"Viewer context (Settings \u2192 Agents / Projects) \u2014 how to find this user\u2019s tickets and review PRs:",
|
|
3062
|
+
`- Account: ${compactNotes(profile.accountNotes || account, 240)}`,
|
|
3063
|
+
`- This project: ${compactNotes(profile.projectNotes || project, 240)}`,
|
|
3064
|
+
"To change these, show the proposed text in chat, call ask_user (Save this context / Do not save), wait for the answer, then update_viewer_context with confirmed=true. Never write context without confirmation. get_viewer_context reads the current values."
|
|
3065
|
+
].join("\n");
|
|
3095
3066
|
}
|
|
3096
|
-
|
|
3067
|
+
function formatViewerContextReminder() {
|
|
3068
|
+
return "Viewer context: get_viewer_context to read; update_viewer_context only after ask_user confirms (Save this context / Do not save).";
|
|
3069
|
+
}
|
|
3070
|
+
var PROFILE_NOTES_MAX, LEGACY_ROLE_LABELS;
|
|
3097
3071
|
var init_account_profile = __esm({
|
|
3098
3072
|
"src/store/account-profile.ts"() {
|
|
3099
3073
|
"use strict";
|
|
3100
|
-
ACCOUNT_ROLE_PRESETS = ["engineering", "design", "product"];
|
|
3101
|
-
ACCOUNT_ROLES = ACCOUNT_ROLE_PRESETS;
|
|
3102
|
-
ACCOUNT_ROLE_MAX = 16;
|
|
3103
3074
|
PROFILE_NOTES_MAX = 2e3;
|
|
3104
|
-
|
|
3075
|
+
LEGACY_ROLE_LABELS = {
|
|
3105
3076
|
engineering: "Engineering",
|
|
3106
3077
|
design: "Design",
|
|
3107
3078
|
product: "Product",
|
|
3108
3079
|
qa: "QA"
|
|
3109
3080
|
};
|
|
3110
|
-
ROLE_TEAM_HINTS = {
|
|
3111
|
-
engineering: ["engineering-team", "engineering", "eng-team"],
|
|
3112
|
-
design: ["design-team", "design"],
|
|
3113
|
-
product: ["product-team", "product"]
|
|
3114
|
-
};
|
|
3115
3081
|
}
|
|
3116
3082
|
});
|
|
3117
3083
|
|
|
@@ -3209,16 +3175,11 @@ var init_issue_source_labels = __esm({
|
|
|
3209
3175
|
// src/store/app-settings.ts
|
|
3210
3176
|
var app_settings_exports = {};
|
|
3211
3177
|
__export(app_settings_exports, {
|
|
3212
|
-
ACCOUNT_ROLES: () => ACCOUNT_ROLES,
|
|
3213
|
-
ACCOUNT_ROLE_LABELS: () => ACCOUNT_ROLE_LABELS,
|
|
3214
|
-
ACCOUNT_ROLE_MAX: () => ACCOUNT_ROLE_MAX,
|
|
3215
|
-
ACCOUNT_ROLE_PRESETS: () => ACCOUNT_ROLE_PRESETS,
|
|
3216
3178
|
FOLLOW_UP_BEHAVIORS: () => FOLLOW_UP_BEHAVIORS,
|
|
3217
3179
|
GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
|
|
3218
3180
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
3219
3181
|
ISSUE_SOURCE_LABELS: () => ISSUE_SOURCE_LABELS,
|
|
3220
3182
|
PROFILE_NOTES_MAX: () => PROFILE_NOTES_MAX,
|
|
3221
|
-
accountRoleLabel: () => accountRoleLabel,
|
|
3222
3183
|
appSettingsPath: () => appSettingsPath,
|
|
3223
3184
|
applyAppEnvironment: () => applyAppEnvironment,
|
|
3224
3185
|
autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
|
|
@@ -3242,9 +3203,12 @@ __export(app_settings_exports, {
|
|
|
3242
3203
|
emptyPublicIntegrations: () => emptyPublicIntegrations,
|
|
3243
3204
|
ensureSlackDeviceIdentity: () => ensureSlackDeviceIdentity,
|
|
3244
3205
|
findProjectProfileKey: () => findProjectProfileKey,
|
|
3206
|
+
foldLegacyRolesIntoNotes: () => foldLegacyRolesIntoNotes,
|
|
3245
3207
|
followUpBehavior: () => followUpBehavior,
|
|
3246
3208
|
formatAccountProfilePlaybookLine: () => formatAccountProfilePlaybookLine,
|
|
3247
3209
|
formatProjectProfilePlaybookLines: () => formatProjectProfilePlaybookLines,
|
|
3210
|
+
formatViewerContextDirective: () => formatViewerContextDirective,
|
|
3211
|
+
formatViewerContextReminder: () => formatViewerContextReminder,
|
|
3248
3212
|
formatWorkspaceProfileSuffix: () => formatWorkspaceProfileSuffix,
|
|
3249
3213
|
getAbleTimeAccessToken: () => getAbleTimeAccessToken,
|
|
3250
3214
|
getAbleTimeHost: () => getAbleTimeHost,
|
|
@@ -3265,7 +3229,7 @@ __export(app_settings_exports, {
|
|
|
3265
3229
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
3266
3230
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
3267
3231
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
3268
|
-
|
|
3232
|
+
readViewerContext: () => readViewerContext,
|
|
3269
3233
|
resolveAccountProfile: () => resolveAccountProfile,
|
|
3270
3234
|
resolveAccountProfileFromSettings: () => resolveAccountProfileFromSettings,
|
|
3271
3235
|
resolveAgentExecutable: () => resolveAgentExecutable,
|
|
@@ -3275,7 +3239,6 @@ __export(app_settings_exports, {
|
|
|
3275
3239
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
3276
3240
|
resolveViewerProfile: () => resolveViewerProfile,
|
|
3277
3241
|
resolveViewerProfileForRepo: () => resolveViewerProfileForRepo,
|
|
3278
|
-
reviewTeamHintsForRoles: () => reviewTeamHintsForRoles,
|
|
3279
3242
|
saveAbleTimeConnection: () => saveAbleTimeConnection,
|
|
3280
3243
|
saveAppSettings: () => saveAppSettings,
|
|
3281
3244
|
saveLinearOAuth: () => saveLinearOAuth,
|
|
@@ -3292,7 +3255,8 @@ __export(app_settings_exports, {
|
|
|
3292
3255
|
updateDefaultsSettings: () => updateDefaultsSettings,
|
|
3293
3256
|
updateIntegrationsSettings: () => updateIntegrationsSettings,
|
|
3294
3257
|
updateOpencodeSettings: () => updateOpencodeSettings,
|
|
3295
|
-
updateProjectProfileSettings: () => updateProjectProfileSettings
|
|
3258
|
+
updateProjectProfileSettings: () => updateProjectProfileSettings,
|
|
3259
|
+
writeViewerContext: () => writeViewerContext
|
|
3296
3260
|
});
|
|
3297
3261
|
function emptyPublicIntegrations() {
|
|
3298
3262
|
return {
|
|
@@ -3550,9 +3514,7 @@ function normalizeDefaults(raw) {
|
|
|
3550
3514
|
if (typeof source.fast === "boolean") {
|
|
3551
3515
|
out.fast = source.fast;
|
|
3552
3516
|
}
|
|
3553
|
-
const
|
|
3554
|
-
if (roles.length) out.roles = roles;
|
|
3555
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
3517
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
3556
3518
|
if (notes) out.notes = notes;
|
|
3557
3519
|
return out;
|
|
3558
3520
|
}
|
|
@@ -3563,11 +3525,9 @@ function normalizeProjectProfile(raw) {
|
|
|
3563
3525
|
if (!raw || typeof raw !== "object") return void 0;
|
|
3564
3526
|
const source = raw;
|
|
3565
3527
|
const out = {};
|
|
3566
|
-
const
|
|
3567
|
-
if (roles.length) out.roles = roles;
|
|
3568
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
3528
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
3569
3529
|
if (notes) out.notes = notes;
|
|
3570
|
-
return out.
|
|
3530
|
+
return out.notes ? out : void 0;
|
|
3571
3531
|
}
|
|
3572
3532
|
function normalizeProjects(raw) {
|
|
3573
3533
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -3584,14 +3544,19 @@ function findProjectProfileKey(projects, repoPath) {
|
|
|
3584
3544
|
const want = profileRepoKey(repoPath ?? "");
|
|
3585
3545
|
if (!want) return void 0;
|
|
3586
3546
|
if (projects[want]) return want;
|
|
3547
|
+
let best;
|
|
3548
|
+
let bestLen = 0;
|
|
3587
3549
|
for (const key of Object.keys(projects)) {
|
|
3588
3550
|
const stored = profileRepoKey(key);
|
|
3589
3551
|
if (!stored) continue;
|
|
3590
|
-
if (stored === want || want.startsWith(`${stored}/`)
|
|
3591
|
-
|
|
3552
|
+
if (stored === want || want.startsWith(`${stored}/`)) {
|
|
3553
|
+
if (stored.length > bestLen) {
|
|
3554
|
+
best = key;
|
|
3555
|
+
bestLen = stored.length;
|
|
3556
|
+
}
|
|
3592
3557
|
}
|
|
3593
3558
|
}
|
|
3594
|
-
return
|
|
3559
|
+
return best;
|
|
3595
3560
|
}
|
|
3596
3561
|
function normalizeAdvanced(raw) {
|
|
3597
3562
|
if (!raw || typeof raw !== "object") return {};
|
|
@@ -4126,17 +4091,13 @@ function updateDefaultsSettings(patch) {
|
|
|
4126
4091
|
defaults.fast = Boolean(patch.fast);
|
|
4127
4092
|
}
|
|
4128
4093
|
}
|
|
4129
|
-
if ("roles" in patch) {
|
|
4130
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
4131
|
-
if (!roles.length) delete defaults.roles;
|
|
4132
|
-
else defaults.roles = roles;
|
|
4133
|
-
delete defaults.role;
|
|
4134
|
-
}
|
|
4135
4094
|
if ("notes" in patch) {
|
|
4136
4095
|
const notes = normalizeProfileNotes(patch.notes);
|
|
4137
4096
|
if (!notes) delete defaults.notes;
|
|
4138
4097
|
else defaults.notes = notes;
|
|
4139
4098
|
}
|
|
4099
|
+
delete defaults.roles;
|
|
4100
|
+
delete defaults.role;
|
|
4140
4101
|
return saveAppSettings({ ...current, defaults });
|
|
4141
4102
|
}
|
|
4142
4103
|
function updateProjectProfileSettings(repoPath, patch) {
|
|
@@ -4146,17 +4107,14 @@ function updateProjectProfileSettings(repoPath, patch) {
|
|
|
4146
4107
|
const projects = { ...current.projects };
|
|
4147
4108
|
const existing = projects[key] ?? {};
|
|
4148
4109
|
const next = { ...existing };
|
|
4149
|
-
if ("roles" in patch) {
|
|
4150
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
4151
|
-
if (!roles.length) delete next.roles;
|
|
4152
|
-
else next.roles = roles;
|
|
4153
|
-
}
|
|
4154
4110
|
if ("notes" in patch) {
|
|
4155
4111
|
const notes = normalizeProfileNotes(patch.notes);
|
|
4156
4112
|
if (!notes) delete next.notes;
|
|
4157
4113
|
else next.notes = notes;
|
|
4158
4114
|
}
|
|
4159
|
-
|
|
4115
|
+
delete next.roles;
|
|
4116
|
+
delete next.role;
|
|
4117
|
+
if (!next.notes) delete projects[key];
|
|
4160
4118
|
else projects[key] = next;
|
|
4161
4119
|
return saveAppSettings({ ...current, projects });
|
|
4162
4120
|
}
|
|
@@ -4181,6 +4139,60 @@ function resolveViewerProfileForRepo(repoPath, settings = loadAppSettings()) {
|
|
|
4181
4139
|
const project = key ? settings.projects[key] : void 0;
|
|
4182
4140
|
return resolveViewerProfile(settings.defaults, project);
|
|
4183
4141
|
}
|
|
4142
|
+
function readViewerContext(repoPath) {
|
|
4143
|
+
const settings = loadAppSettings();
|
|
4144
|
+
const key = findProjectProfileKey(settings.projects, repoPath);
|
|
4145
|
+
const resolved = resolveViewerProfile(
|
|
4146
|
+
settings.defaults,
|
|
4147
|
+
key ? settings.projects[key] : void 0
|
|
4148
|
+
);
|
|
4149
|
+
return {
|
|
4150
|
+
account: resolved.accountNotes,
|
|
4151
|
+
project: resolved.projectNotes,
|
|
4152
|
+
combined: resolved.notes,
|
|
4153
|
+
repoPath: key ?? (repoPath?.trim() ? profileRepoKey(repoPath) : null)
|
|
4154
|
+
};
|
|
4155
|
+
}
|
|
4156
|
+
function writeViewerContext(input) {
|
|
4157
|
+
const proposed = normalizeProfileNotes(input.context);
|
|
4158
|
+
if (input.scope === "project") {
|
|
4159
|
+
const settings = loadAppSettings();
|
|
4160
|
+
const key = findProjectProfileKey(settings.projects, input.repoPath) ?? profileRepoKey(input.repoPath ?? "");
|
|
4161
|
+
if (!key) {
|
|
4162
|
+
return {
|
|
4163
|
+
ok: false,
|
|
4164
|
+
error: "repoPath required",
|
|
4165
|
+
message: "Pass repoPath (or run from a registered worktree) when updating project context.",
|
|
4166
|
+
current: "",
|
|
4167
|
+
proposed
|
|
4168
|
+
};
|
|
4169
|
+
}
|
|
4170
|
+
const current2 = resolveViewerProfileForRepo(key, settings).projectNotes;
|
|
4171
|
+
if (!input.confirmed) {
|
|
4172
|
+
return {
|
|
4173
|
+
ok: false,
|
|
4174
|
+
error: "Confirmation required",
|
|
4175
|
+
message: CONFIRM_PROJECT,
|
|
4176
|
+
current: current2,
|
|
4177
|
+
proposed
|
|
4178
|
+
};
|
|
4179
|
+
}
|
|
4180
|
+
updateProjectProfileSettings(key, { notes: proposed || null });
|
|
4181
|
+
return { ok: true, scope: "project", context: proposed, repoPath: key };
|
|
4182
|
+
}
|
|
4183
|
+
const current = resolveAccountProfileFromSettings().accountNotes;
|
|
4184
|
+
if (!input.confirmed) {
|
|
4185
|
+
return {
|
|
4186
|
+
ok: false,
|
|
4187
|
+
error: "Confirmation required",
|
|
4188
|
+
message: CONFIRM_ACCOUNT,
|
|
4189
|
+
current,
|
|
4190
|
+
proposed
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4193
|
+
updateDefaultsSettings({ notes: proposed || null });
|
|
4194
|
+
return { ok: true, scope: "account", context: proposed };
|
|
4195
|
+
}
|
|
4184
4196
|
function resolveThreadDefaults(settings = loadAppSettings()) {
|
|
4185
4197
|
return {
|
|
4186
4198
|
agent: getDefaultAgent(settings),
|
|
@@ -4527,7 +4539,7 @@ function childEnvWithAppSettings(extra) {
|
|
|
4527
4539
|
function harnessEnvKey(harness) {
|
|
4528
4540
|
return HARNESS_ENV_KEYS[harness];
|
|
4529
4541
|
}
|
|
4530
|
-
var import_node_crypto3, import_node_fs14, import_node_os5, import_node_path16, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, FOLLOW_UP_BEHAVIORS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
|
|
4542
|
+
var import_node_crypto3, import_node_fs14, import_node_os5, import_node_path16, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, FOLLOW_UP_BEHAVIORS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, CONFIRM_ACCOUNT, CONFIRM_PROJECT, DEFAULT_CLI_BIN;
|
|
4531
4543
|
var init_app_settings = __esm({
|
|
4532
4544
|
"src/store/app-settings.ts"() {
|
|
4533
4545
|
"use strict";
|
|
@@ -4578,6 +4590,8 @@ var init_app_settings = __esm({
|
|
|
4578
4590
|
projects: {},
|
|
4579
4591
|
advanced: {}
|
|
4580
4592
|
};
|
|
4593
|
+
CONFIRM_ACCOUNT = "Show the user the proposed account context, call ask_user (Save this context / Do not save), wait for their answer, then retry with confirmed=true.";
|
|
4594
|
+
CONFIRM_PROJECT = "Show the user the proposed project context, call ask_user (Save this context / Do not save), wait for their answer, then retry with confirmed=true.";
|
|
4581
4595
|
DEFAULT_CLI_BIN = {
|
|
4582
4596
|
claude: "claude",
|
|
4583
4597
|
codex: "codex",
|
|
@@ -5787,14 +5801,7 @@ async function listPrs(repoPath, opts) {
|
|
|
5787
5801
|
const limit = resolved.limit ?? 200;
|
|
5788
5802
|
const reviewerKey = (resolved.reviewer ?? "").trim().toLowerCase();
|
|
5789
5803
|
const needsUnclaimed = reviewerKey === "unassigned" || reviewerKey === "none" || reviewerKey === "null";
|
|
5790
|
-
|
|
5791
|
-
try {
|
|
5792
|
-
profile = resolveViewerProfileForRepo(repoPath);
|
|
5793
|
-
} catch {
|
|
5794
|
-
profile = resolveViewerProfile();
|
|
5795
|
-
}
|
|
5796
|
-
const fetchedTeams = needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : [];
|
|
5797
|
-
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? preferTeamsForRole(fetchedTeams, profile.reviewTeamHints) : void 0);
|
|
5804
|
+
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : void 0);
|
|
5798
5805
|
const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
|
|
5799
5806
|
const args = buildGhPrListArgs({
|
|
5800
5807
|
slug,
|
|
@@ -7419,16 +7426,15 @@ function accountRolePlaybookLine() {
|
|
|
7419
7426
|
return "";
|
|
7420
7427
|
}
|
|
7421
7428
|
}
|
|
7422
|
-
function
|
|
7429
|
+
function accountContextReminderLine() {
|
|
7423
7430
|
let profile;
|
|
7424
7431
|
try {
|
|
7425
7432
|
profile = resolveAccountProfileFromSettings();
|
|
7426
7433
|
} catch {
|
|
7427
7434
|
return "";
|
|
7428
7435
|
}
|
|
7429
|
-
if (
|
|
7430
|
-
|
|
7431
|
-
return `- Viewer profile: ${roles}. Use Settings notes with list_issues / list_prs when they ask to find work.`;
|
|
7436
|
+
if (!profile.accountNotes) return "";
|
|
7437
|
+
return "- Account context is set. Use get_viewer_context / Settings notes with list_issues / list_prs when they ask to find work. Update only after ask_user confirms.";
|
|
7432
7438
|
}
|
|
7433
7439
|
function projectProfilePlaybookBlock() {
|
|
7434
7440
|
let settings;
|
|
@@ -7445,7 +7451,6 @@ function projectProfilePlaybookBlock() {
|
|
|
7445
7451
|
const key = path.replace(/\/+$/, "");
|
|
7446
7452
|
return {
|
|
7447
7453
|
name: names.get(key) ?? key.split("/").filter(Boolean).pop() ?? path,
|
|
7448
|
-
roleLabels: resolved.rolesFromProject ? resolved.roleLabels : [],
|
|
7449
7454
|
notes: resolved.projectNotes
|
|
7450
7455
|
};
|
|
7451
7456
|
});
|
|
@@ -7459,7 +7464,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
7459
7464
|
`- YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit it.`,
|
|
7460
7465
|
goal ? `- Goal / title: ${goal}` : null,
|
|
7461
7466
|
accountDefaultsPlaybookLine(),
|
|
7462
|
-
|
|
7467
|
+
accountContextReminderLine() || null,
|
|
7463
7468
|
"- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_threads. Link chats as `[Title](sideboard://thread/<id>)`. Merge only if the user asked. If a child is stopped/error/broken, it did not finish \u2014 resume or tell the user."
|
|
7464
7469
|
].filter(Boolean).join("\n");
|
|
7465
7470
|
}
|
|
@@ -7513,7 +7518,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
7513
7518
|
"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.",
|
|
7514
7519
|
"Typical flow (Home board): list_board \u2192 create_thread (sourceType=ticket|pr|branch) \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft \u2192 wait_for_turn. Merge only if the user explicitly asked (`ask_git` merge).",
|
|
7515
7520
|
"Typical flow (branch / explicit source): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
7516
|
-
"Typical flow (find work): list_workspaces \u2192 pick repo(s) matching
|
|
7521
|
+
"Typical flow (find work): list_workspaces \u2192 pick repo(s) matching account / project context \u2192 Sideboard list_issues / linear_* (tickets) or list_prs(queue=review) (reviews) \u2192 show the options. Never wait on Claude Linear MCP. Only create_thread + send_to_thread when they asked to start (e.g. \u201Cfind me work and start it\u201D) \u2014 then wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
7517
7522
|
'Typical flow (review inbox): list_workspaces \u2192 list_prs(queue=review, limit=N) \u2192 show those PRs (ticket ids in the title when present). create_thread sourceType=pr only when they asked to start / do the work. Do not list_issues for "tickets to review".',
|
|
7518
7523
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
7519
7524
|
"Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). If the user gave a goal (Greptile 5/5, CI green), pass that goal through so they watch-fix-push until it lands \u2014 do not start that loop on a plain push, and do not tell the human to poll. Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
|
|
@@ -7567,10 +7572,10 @@ var init_coordinator_prompt = __esm({
|
|
|
7567
7572
|
"Role: you oversee Sideboard worktree agents across registered repos. You do not live inside one of those worktrees.",
|
|
7568
7573
|
"Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
|
|
7569
7574
|
"Discover:",
|
|
7570
|
-
"- list_workspaces \u2014 registered repos (path + github slug +
|
|
7575
|
+
"- list_workspaces \u2014 registered repos (path + github slug + project context when set). Use account / project context to pick the right repo for tickets or reviews.",
|
|
7571
7576
|
"- list_board \u2014 Home Kanban of worktrees (New / Draft / Review / Merged; one card per checkout). Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
|
|
7572
|
-
'- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_thread sourceType=pr. That is open non-draft PRs labeled eng-review with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored.
|
|
7573
|
-
"- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo)
|
|
7577
|
+
'- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_thread sourceType=pr. That is open non-draft PRs labeled eng-review with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored. Use Settings \u2192 Agents / Projects context (roles, teams, labels \u2014 freeform text; project adds to account) to prefer the right queues. queue=mine is review-requested:@me; queue=approved|changes uses those labels. Also: state, label, reviewer=me|unassigned|login, query, limit default 40 max 250; raise limit or tighten when truncated. list_issues (query, assignee=me|unassigned|all|user, limit default 40 max 250) lists Linear, AbleTime, or GitHub tickets \u2014 do not use it for that review-inbox ask. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).',
|
|
7578
|
+
"- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo) context. Tickets \u2192 Sideboard list_issues / linear_* (Account Linear). Reviews \u2192 list_prs(queue=review). Do not call Claude Linear MCP or any other vendor Linear MCP \u2014 those HTTP connectors hang or flap on the first turn. If a vendor MCP is down or reconnecting, ignore it and keep going with Sideboard tools. Show the options \u2014 do not create_thread or start unless they also asked to start (e.g. \u201Cfind me work and start it\u201D). Do not start this unprompted. To change that context, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context with confirmed=true. Never write it without confirmation.",
|
|
7574
7579
|
"- linear_* (when Linear is connected) \u2014 list_teams for team key/states; get/create/update/comment with ENG-123. Worktree agents have the same Account tools \u2014 prefer they comment, update status, and create spin-offs (parent=) on their own ticket. Scope errors: reconnect Linear in Account settings. Prefer these over any Linear MCP the CLI may still list.",
|
|
7575
7580
|
"- github_* \u2014 get/comment/update/create GitHub issues via Account `gh` (#123). Worktree agents have these too. Prefer over any vendor GitHub MCP. Pass parent= for spin-offs.",
|
|
7576
7581
|
"- abletime_* (when AbleTime is connected) \u2014 orientation first; get/comment/update/create (parent= for spin-offs); ensure_task when work has no ticket (or create_thread from the default branch auto-creates one). Worktree agents have the same Account tools.",
|
|
@@ -7580,7 +7585,8 @@ var init_coordinator_prompt = __esm({
|
|
|
7580
7585
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
7581
7586
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
7582
7587
|
"- list_threads / get_thread \u2014 live thread list (parent id + last message preview). get_thread on this orchestration chat lists child worktree agents (status + lastText). Also includes usage / lastTurnUsage.",
|
|
7583
|
-
"- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API). Never for hellos, check-ins, or invented \u201Cwhat should we do?\u201D menus \u2014 reply in chat. Explain options first, description on every option, then wait.",
|
|
7588
|
+
"- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API, Save this context / Do not save). Never for hellos, check-ins, or invented \u201Cwhat should we do?\u201D menus \u2014 reply in chat. Explain options first, description on every option, then wait.",
|
|
7589
|
+
"- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_workspaces (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
|
|
7584
7590
|
"- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
|
|
7585
7591
|
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to an orchestration chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Jobs fire only while Sideboard.app is running; sleep skips until wake. Overnight/unattended runs: ask the user to enable Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or call set_caffeinate.",
|
|
7586
7592
|
"Workspaces:",
|
|
@@ -8607,10 +8613,10 @@ var init_brightsy = __esm({
|
|
|
8607
8613
|
});
|
|
8608
8614
|
|
|
8609
8615
|
// src/agents/claude-mcp.ts
|
|
8610
|
-
function parseMcpList(
|
|
8616
|
+
function parseMcpList(text7) {
|
|
8611
8617
|
const servers = [];
|
|
8612
8618
|
const seen = /* @__PURE__ */ new Set();
|
|
8613
|
-
for (const raw of
|
|
8619
|
+
for (const raw of text7.split("\n")) {
|
|
8614
8620
|
const line = raw.trim();
|
|
8615
8621
|
if (!line || /^Checking MCP/i.test(line)) continue;
|
|
8616
8622
|
const m = line.match(/^(.+?):\s+\S+/);
|
|
@@ -8765,8 +8771,8 @@ function isSubagentToolName(name) {
|
|
|
8765
8771
|
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
8766
8772
|
return /connectedAgentRequest/i.test(n);
|
|
8767
8773
|
}
|
|
8768
|
-
function isInternalAgentStatusText(
|
|
8769
|
-
const t =
|
|
8774
|
+
function isInternalAgentStatusText(text7) {
|
|
8775
|
+
const t = text7.trim();
|
|
8770
8776
|
if (!t) return false;
|
|
8771
8777
|
if (/waiting for gate to pass/i.test(t)) return true;
|
|
8772
8778
|
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
@@ -8780,9 +8786,9 @@ function lastTextPart(parts, type) {
|
|
|
8780
8786
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
8781
8787
|
const p = parts[i];
|
|
8782
8788
|
if (p.type !== type) continue;
|
|
8783
|
-
const
|
|
8784
|
-
if (!
|
|
8785
|
-
return
|
|
8789
|
+
const text7 = p.text.trim();
|
|
8790
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
8791
|
+
return text7;
|
|
8786
8792
|
}
|
|
8787
8793
|
return "";
|
|
8788
8794
|
}
|
|
@@ -8803,7 +8809,7 @@ function liveActivitySummary(parts, opts) {
|
|
|
8803
8809
|
);
|
|
8804
8810
|
const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
|
|
8805
8811
|
const thinking = lastTextPart(parts, "thinking");
|
|
8806
|
-
const
|
|
8812
|
+
const text7 = lastTextPart(parts, "text");
|
|
8807
8813
|
if (runningSubs.length > 0) {
|
|
8808
8814
|
const heads = runningSubs.map(toolLabel);
|
|
8809
8815
|
const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
|
|
@@ -8813,7 +8819,7 @@ function liveActivitySummary(parts, opts) {
|
|
|
8813
8819
|
if (runningTop) return toolLabel(runningTop);
|
|
8814
8820
|
if (runningPoll) return toolLabel(runningPoll);
|
|
8815
8821
|
if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
|
|
8816
|
-
if (
|
|
8822
|
+
if (text7) return "Writing reply\u2026";
|
|
8817
8823
|
const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
|
|
8818
8824
|
if (last) {
|
|
8819
8825
|
const label = toolLabel(last);
|
|
@@ -8841,9 +8847,9 @@ function toolFilePath(input) {
|
|
|
8841
8847
|
if (!input) return void 0;
|
|
8842
8848
|
return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
8843
8849
|
}
|
|
8844
|
-
function countLines(
|
|
8845
|
-
if (!
|
|
8846
|
-
return
|
|
8850
|
+
function countLines(text7) {
|
|
8851
|
+
if (!text7) return 0;
|
|
8852
|
+
return text7.split("\n").length;
|
|
8847
8853
|
}
|
|
8848
8854
|
function diffFromInput(input) {
|
|
8849
8855
|
if (!input) return {};
|
|
@@ -9068,9 +9074,9 @@ function partsToAssistantText(parts) {
|
|
|
9068
9074
|
(p) => p.type === "text" && !messagePartParentId(p)
|
|
9069
9075
|
).map((p) => p.text).join("").trim();
|
|
9070
9076
|
}
|
|
9071
|
-
function stripBrightsyNdjsonNoise(
|
|
9072
|
-
if (!
|
|
9073
|
-
let out =
|
|
9077
|
+
function stripBrightsyNdjsonNoise(text7) {
|
|
9078
|
+
if (!text7 || !text7.includes('"type"')) return text7;
|
|
9079
|
+
let out = text7;
|
|
9074
9080
|
out = out.replace(
|
|
9075
9081
|
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
9076
9082
|
""
|
|
@@ -9393,8 +9399,8 @@ function isBrightsyConnected() {
|
|
|
9393
9399
|
return false;
|
|
9394
9400
|
}
|
|
9395
9401
|
}
|
|
9396
|
-
function promptMentionsBrightsy(
|
|
9397
|
-
return BRIGHTSY_WORD.test(
|
|
9402
|
+
function promptMentionsBrightsy(text7) {
|
|
9403
|
+
return BRIGHTSY_WORD.test(text7 ?? "");
|
|
9398
9404
|
}
|
|
9399
9405
|
function isBrightsyMcpToolName(name) {
|
|
9400
9406
|
const n = name.toLowerCase();
|
|
@@ -9670,7 +9676,9 @@ var init_injected_mcp = __esm({
|
|
|
9670
9676
|
"mcp__sideboard__present_files",
|
|
9671
9677
|
"mcp__sideboard__ask_user",
|
|
9672
9678
|
"mcp__sideboard__present_plan",
|
|
9673
|
-
"mcp__sideboard__wait_for_job"
|
|
9679
|
+
"mcp__sideboard__wait_for_job",
|
|
9680
|
+
"mcp__sideboard__get_viewer_context",
|
|
9681
|
+
"mcp__sideboard__update_viewer_context"
|
|
9674
9682
|
];
|
|
9675
9683
|
SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
9676
9684
|
SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
@@ -10003,9 +10011,9 @@ function compactMetadataFromClaude(obj) {
|
|
|
10003
10011
|
return { trigger, postTokens };
|
|
10004
10012
|
}
|
|
10005
10013
|
function parseIssuesJson(raw) {
|
|
10006
|
-
const
|
|
10007
|
-
const candidates = [
|
|
10008
|
-
const match =
|
|
10014
|
+
const text7 = raw.trim();
|
|
10015
|
+
const candidates = [text7];
|
|
10016
|
+
const match = text7.match(/\[[\s\S]*\]/);
|
|
10009
10017
|
if (match) candidates.push(match[0]);
|
|
10010
10018
|
for (const c of candidates) {
|
|
10011
10019
|
try {
|
|
@@ -10273,8 +10281,8 @@ var init_claude = __esm({
|
|
|
10273
10281
|
if (errorDetail) {
|
|
10274
10282
|
events.push({ type: "stderr", data: errorDetail });
|
|
10275
10283
|
} else {
|
|
10276
|
-
const
|
|
10277
|
-
if (typeof
|
|
10284
|
+
const text7 = obj.result;
|
|
10285
|
+
if (typeof text7 === "string" && text7) events.push({ type: "stdout", data: text7 });
|
|
10278
10286
|
}
|
|
10279
10287
|
const usage = usageFromClaude(obj.usage);
|
|
10280
10288
|
if (usage) {
|
|
@@ -10351,9 +10359,9 @@ function asObject(value) {
|
|
|
10351
10359
|
function listMcpNamesFromJsonMap(raw, key) {
|
|
10352
10360
|
return Object.keys(asObject(asObject(raw)[key])).filter((n) => n.trim());
|
|
10353
10361
|
}
|
|
10354
|
-
function listMcpNamesFromCodexToml(
|
|
10362
|
+
function listMcpNamesFromCodexToml(text7) {
|
|
10355
10363
|
try {
|
|
10356
|
-
const parsed = (0, import_smol_toml2.parse)(
|
|
10364
|
+
const parsed = (0, import_smol_toml2.parse)(text7);
|
|
10357
10365
|
return Object.keys(asObject(parsed.mcp_servers)).filter((n) => n.trim());
|
|
10358
10366
|
} catch {
|
|
10359
10367
|
return [];
|
|
@@ -10484,8 +10492,8 @@ function codexConfigHasNetworkAccess() {
|
|
|
10484
10492
|
];
|
|
10485
10493
|
for (const path of candidates) {
|
|
10486
10494
|
if (!(0, import_node_fs28.existsSync)(path)) continue;
|
|
10487
|
-
const
|
|
10488
|
-
if (/network_access\s*=\s*true/.test(
|
|
10495
|
+
const text7 = (0, import_node_fs28.readFileSync)(path, "utf8");
|
|
10496
|
+
if (/network_access\s*=\s*true/.test(text7)) return true;
|
|
10489
10497
|
}
|
|
10490
10498
|
return false;
|
|
10491
10499
|
}
|
|
@@ -10498,8 +10506,8 @@ function unwrapCodexMcpResult(result) {
|
|
|
10498
10506
|
const texts = [];
|
|
10499
10507
|
for (const item of rec.content) {
|
|
10500
10508
|
if (!item || typeof item !== "object") continue;
|
|
10501
|
-
const
|
|
10502
|
-
if (typeof
|
|
10509
|
+
const text7 = item.text;
|
|
10510
|
+
if (typeof text7 === "string") texts.push(text7);
|
|
10503
10511
|
}
|
|
10504
10512
|
if (texts.length) return texts.join("\n");
|
|
10505
10513
|
}
|
|
@@ -10741,8 +10749,8 @@ var init_codex = __esm({
|
|
|
10741
10749
|
if (msg) nested.push(msg);
|
|
10742
10750
|
}
|
|
10743
10751
|
}
|
|
10744
|
-
for (const
|
|
10745
|
-
events.push({ type: "stdout", data:
|
|
10752
|
+
for (const text7 of nested) {
|
|
10753
|
+
events.push({ type: "stdout", data: text7, parentId: item.id });
|
|
10746
10754
|
}
|
|
10747
10755
|
events.push({
|
|
10748
10756
|
type: "tool_result",
|
|
@@ -11293,8 +11301,8 @@ var init_opencode = __esm({
|
|
|
11293
11301
|
return { type: "session_id", data: sid };
|
|
11294
11302
|
}
|
|
11295
11303
|
if (obj.type === "text") {
|
|
11296
|
-
const
|
|
11297
|
-
if (
|
|
11304
|
+
const text7 = obj.part?.text ?? obj.text;
|
|
11305
|
+
if (text7) return { type: "stdout", data: text7 };
|
|
11298
11306
|
}
|
|
11299
11307
|
if (obj.type === "tool_use") {
|
|
11300
11308
|
const part = obj.part;
|
|
@@ -11573,8 +11581,8 @@ var init_list_models = __esm({
|
|
|
11573
11581
|
});
|
|
11574
11582
|
|
|
11575
11583
|
// src/agents/session-quota.ts
|
|
11576
|
-
function isSessionQuotaLimit(
|
|
11577
|
-
const lower =
|
|
11584
|
+
function isSessionQuotaLimit(text7) {
|
|
11585
|
+
const lower = text7.trim().toLowerCase();
|
|
11578
11586
|
if (!lower) return false;
|
|
11579
11587
|
if (/credit balance is too low|out of credits|insufficient.?quota|billing/.test(lower)) {
|
|
11580
11588
|
return false;
|
|
@@ -11582,10 +11590,10 @@ function isSessionQuotaLimit(text6) {
|
|
|
11582
11590
|
if (/prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower)) {
|
|
11583
11591
|
return false;
|
|
11584
11592
|
}
|
|
11585
|
-
return /you've hit your/.test(lower) || /hit your (session|weekly|opus) limit/.test(lower) || /usage limit/.test(lower) || /rate.?limit|too many requests|\b429\b/.test(lower) && /reset/i.test(
|
|
11593
|
+
return /you've hit your/.test(lower) || /hit your (session|weekly|opus) limit/.test(lower) || /usage limit/.test(lower) || /rate.?limit|too many requests|\b429\b/.test(lower) && /reset/i.test(text7);
|
|
11586
11594
|
}
|
|
11587
|
-
function parseSessionQuotaResetAt(
|
|
11588
|
-
const absolute =
|
|
11595
|
+
function parseSessionQuotaResetAt(text7, now = /* @__PURE__ */ new Date()) {
|
|
11596
|
+
const absolute = text7.match(
|
|
11589
11597
|
/resets\s+(?:at\s+)?(\d{1,2}):(\d{2})\s*(am|pm)(?:\s*\(([^)]+)\))?/i
|
|
11590
11598
|
);
|
|
11591
11599
|
if (absolute) {
|
|
@@ -11603,7 +11611,7 @@ function parseSessionQuotaResetAt(text6, now = /* @__PURE__ */ new Date()) {
|
|
|
11603
11611
|
}
|
|
11604
11612
|
return at;
|
|
11605
11613
|
}
|
|
11606
|
-
const relative =
|
|
11614
|
+
const relative = text7.match(
|
|
11607
11615
|
/resets\s+in\s+(\d+)\s*(minutes?|hours?|days?)/i
|
|
11608
11616
|
);
|
|
11609
11617
|
if (relative) {
|
|
@@ -11903,8 +11911,8 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
11903
11911
|
onEvent({ type: "exit", data: exitCode });
|
|
11904
11912
|
const finalized = finalizeParts(parts);
|
|
11905
11913
|
const rawText = assistantText.trim() || partsToAssistantText(finalized);
|
|
11906
|
-
const
|
|
11907
|
-
return { exitCode, sessionId, assistantText:
|
|
11914
|
+
const text7 = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
|
|
11915
|
+
return { exitCode, sessionId, assistantText: text7, parts: finalized, usage };
|
|
11908
11916
|
});
|
|
11909
11917
|
return {
|
|
11910
11918
|
pid: child.pid,
|
|
@@ -12163,9 +12171,9 @@ async function tryClaudeSummary(transcript, opts) {
|
|
|
12163
12171
|
{ cwd: opts?.cwd, reject: false }
|
|
12164
12172
|
);
|
|
12165
12173
|
if (exitCode !== 0) return null;
|
|
12166
|
-
const
|
|
12167
|
-
if (
|
|
12168
|
-
return
|
|
12174
|
+
const text7 = stdout.trim();
|
|
12175
|
+
if (text7.length < 40) return null;
|
|
12176
|
+
return text7;
|
|
12169
12177
|
} catch {
|
|
12170
12178
|
return null;
|
|
12171
12179
|
}
|
|
@@ -12214,9 +12222,9 @@ function extractiveSummary(transcript) {
|
|
|
12214
12222
|
);
|
|
12215
12223
|
return parts.join("\n");
|
|
12216
12224
|
}
|
|
12217
|
-
function clipSummary(
|
|
12218
|
-
if (
|
|
12219
|
-
return `${
|
|
12225
|
+
function clipSummary(text7) {
|
|
12226
|
+
if (text7.length <= MAX_SUMMARY_CHARS) return text7;
|
|
12227
|
+
return `${text7.slice(0, MAX_SUMMARY_CHARS)}
|
|
12220
12228
|
|
|
12221
12229
|
[\u2026summary truncated\u2026]`;
|
|
12222
12230
|
}
|
|
@@ -12370,8 +12378,8 @@ function findLastBrightsyContextSummary(messages) {
|
|
|
12370
12378
|
continue;
|
|
12371
12379
|
}
|
|
12372
12380
|
if (part.status === "error") continue;
|
|
12373
|
-
const
|
|
12374
|
-
if (
|
|
12381
|
+
const text7 = extractBrightsyContextSummary(part.result);
|
|
12382
|
+
if (text7) return { index: i, text: text7 };
|
|
12375
12383
|
}
|
|
12376
12384
|
}
|
|
12377
12385
|
return null;
|
|
@@ -13775,8 +13783,8 @@ function listRunningDetachedJobs(worktreePath) {
|
|
|
13775
13783
|
}
|
|
13776
13784
|
return running.sort();
|
|
13777
13785
|
}
|
|
13778
|
-
function looksLikeDeferredDonePromise(
|
|
13779
|
-
const t = (
|
|
13786
|
+
function looksLikeDeferredDonePromise(text7) {
|
|
13787
|
+
const t = (text7 ?? "").trim();
|
|
13780
13788
|
if (!t) return false;
|
|
13781
13789
|
return /\b(i['’]?ll|i will)\s+let you know\b/i.test(t) || /\blet you know when\b/i.test(t) || /\b(check back|ping me)\s+when\b/i.test(t) || /\bi(?:['’]ll| will)\s+(report|update you)\s+when\b/i.test(t);
|
|
13782
13790
|
}
|
|
@@ -14480,11 +14488,11 @@ function unwrapToolResult(result) {
|
|
|
14480
14488
|
return typeof item.text === "string" ? item.text : "";
|
|
14481
14489
|
}).filter(Boolean);
|
|
14482
14490
|
if (texts.length === 1) {
|
|
14483
|
-
const
|
|
14491
|
+
const text7 = texts[0];
|
|
14484
14492
|
try {
|
|
14485
|
-
return JSON.parse(
|
|
14493
|
+
return JSON.parse(text7);
|
|
14486
14494
|
} catch {
|
|
14487
|
-
return
|
|
14495
|
+
return text7;
|
|
14488
14496
|
}
|
|
14489
14497
|
}
|
|
14490
14498
|
if (texts.length > 1) return texts.join("\n");
|
|
@@ -15237,8 +15245,8 @@ function summarizeTurnLive(parts) {
|
|
|
15237
15245
|
const interesting = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? lastTool;
|
|
15238
15246
|
const lastToolLabel = interesting ? interesting.description || toolDescription(interesting.name, interesting.input) || interesting.name : void 0;
|
|
15239
15247
|
const thinking = lastText(parts, "thinking");
|
|
15240
|
-
const
|
|
15241
|
-
const excerptRaw = thinking ||
|
|
15248
|
+
const text7 = lastText(parts, "text");
|
|
15249
|
+
const excerptRaw = thinking || text7;
|
|
15242
15250
|
const excerpt = excerptRaw.length > 280 ? `${excerptRaw.slice(-280)}` : excerptRaw || void 0;
|
|
15243
15251
|
const summary = liveActivitySummary(parts);
|
|
15244
15252
|
return {
|
|
@@ -15474,8 +15482,8 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
15474
15482
|
);
|
|
15475
15483
|
const recent = from.messages.slice(-8).map((m) => {
|
|
15476
15484
|
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
15477
|
-
const
|
|
15478
|
-
return
|
|
15485
|
+
const text7 = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
15486
|
+
return text7 ? `- ${role}: ${text7}` : null;
|
|
15479
15487
|
}).filter(Boolean);
|
|
15480
15488
|
const body = [
|
|
15481
15489
|
`# Orchestration handoff`,
|
|
@@ -15572,13 +15580,13 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
15572
15580
|
for (const hash of hashes) {
|
|
15573
15581
|
const agentsFile = (0, import_node_path39.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
15574
15582
|
if (!(0, import_node_fs41.existsSync)(agentsFile)) continue;
|
|
15575
|
-
let
|
|
15583
|
+
let text7;
|
|
15576
15584
|
try {
|
|
15577
|
-
|
|
15585
|
+
text7 = (0, import_node_fs41.readFileSync)(agentsFile, "utf8");
|
|
15578
15586
|
} catch {
|
|
15579
15587
|
continue;
|
|
15580
15588
|
}
|
|
15581
|
-
for (const line of
|
|
15589
|
+
for (const line of text7.split("\n")) {
|
|
15582
15590
|
const trimmed = line.trim();
|
|
15583
15591
|
if (!trimmed) continue;
|
|
15584
15592
|
try {
|
|
@@ -16374,13 +16382,13 @@ function formatStat(files) {
|
|
|
16374
16382
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
16375
16383
|
}
|
|
16376
16384
|
function emptyScopeStats() {
|
|
16377
|
-
const
|
|
16385
|
+
const z8 = emptyStat();
|
|
16378
16386
|
return {
|
|
16379
|
-
commits:
|
|
16380
|
-
uncommitted:
|
|
16381
|
-
staged:
|
|
16382
|
-
unstaged:
|
|
16383
|
-
last_turn:
|
|
16387
|
+
commits: z8,
|
|
16388
|
+
uncommitted: z8,
|
|
16389
|
+
staged: z8,
|
|
16390
|
+
unstaged: z8,
|
|
16391
|
+
last_turn: z8
|
|
16384
16392
|
};
|
|
16385
16393
|
}
|
|
16386
16394
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -17760,11 +17768,11 @@ function resolvePlanMarkdown(opts) {
|
|
|
17760
17768
|
source: "exit_plan"
|
|
17761
17769
|
};
|
|
17762
17770
|
}
|
|
17763
|
-
const
|
|
17764
|
-
if (
|
|
17771
|
+
const text7 = opts.text?.trim();
|
|
17772
|
+
if (text7 && text7.length >= 80) {
|
|
17765
17773
|
return {
|
|
17766
17774
|
title: "Plan",
|
|
17767
|
-
content:
|
|
17775
|
+
content: text7,
|
|
17768
17776
|
path: PLAN_FILE_REL,
|
|
17769
17777
|
source: "text"
|
|
17770
17778
|
};
|
|
@@ -19175,11 +19183,11 @@ var init_orchestrator = __esm({
|
|
|
19175
19183
|
return results;
|
|
19176
19184
|
}
|
|
19177
19185
|
/** Edit the text of a not-yet-started queued message. */
|
|
19178
|
-
async editQueuedMessage(threadRef, index,
|
|
19186
|
+
async editQueuedMessage(threadRef, index, text7) {
|
|
19179
19187
|
const thread = this.requireThread(threadRef);
|
|
19180
19188
|
return withThreadLock(thread.id, async () => {
|
|
19181
19189
|
const current = this.requireThread(thread.id);
|
|
19182
|
-
const trimmed =
|
|
19190
|
+
const trimmed = text7.trim();
|
|
19183
19191
|
if (!trimmed || index < 0 || index >= current.queue.length) {
|
|
19184
19192
|
return current;
|
|
19185
19193
|
}
|
|
@@ -19365,6 +19373,7 @@ var init_orchestrator = __esm({
|
|
|
19365
19373
|
ticketId: issueTicket?.id,
|
|
19366
19374
|
ticketProvider: issueTicket?.provider
|
|
19367
19375
|
}) : null;
|
|
19376
|
+
const viewerContextReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatViewerContextReminder() : null;
|
|
19368
19377
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
19369
19378
|
pendingSlackExternalReplies(thread.messages)
|
|
19370
19379
|
);
|
|
@@ -19374,6 +19383,7 @@ var init_orchestrator = __esm({
|
|
|
19374
19383
|
worktreeReminder,
|
|
19375
19384
|
optionalServicesReminder,
|
|
19376
19385
|
issueToolsReminder,
|
|
19386
|
+
viewerContextReminder,
|
|
19377
19387
|
artifactReminder,
|
|
19378
19388
|
longRunningReminder,
|
|
19379
19389
|
slackReplyContext,
|
|
@@ -19414,6 +19424,18 @@ var init_orchestrator = __esm({
|
|
|
19414
19424
|
ticketId: freshIssueTicket?.id,
|
|
19415
19425
|
ticketProvider: freshIssueTicket?.provider
|
|
19416
19426
|
});
|
|
19427
|
+
let viewerContextDirective = null;
|
|
19428
|
+
if (!isBrightsy && !isOrchestration) {
|
|
19429
|
+
try {
|
|
19430
|
+
viewerContextDirective = formatViewerContextDirective(
|
|
19431
|
+
resolveViewerProfileForRepo(fresh.repoPath || fresh.worktreePath)
|
|
19432
|
+
);
|
|
19433
|
+
} catch {
|
|
19434
|
+
viewerContextDirective = formatViewerContextDirective(
|
|
19435
|
+
resolveViewerProfileForRepo()
|
|
19436
|
+
);
|
|
19437
|
+
}
|
|
19438
|
+
}
|
|
19417
19439
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
19418
19440
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
19419
19441
|
customPrompt: settings?.prompts?.renameBranch
|
|
@@ -19445,6 +19467,7 @@ var init_orchestrator = __esm({
|
|
|
19445
19467
|
worktreeDirective,
|
|
19446
19468
|
optionalServicesDirective,
|
|
19447
19469
|
issueToolsDirective,
|
|
19470
|
+
viewerContextDirective,
|
|
19448
19471
|
artifactDirective,
|
|
19449
19472
|
longRunningDirective,
|
|
19450
19473
|
renameBranchDirective,
|
|
@@ -19555,6 +19578,7 @@ var init_orchestrator = __esm({
|
|
|
19555
19578
|
worktreeDirective,
|
|
19556
19579
|
optionalServicesDirective,
|
|
19557
19580
|
issueToolsDirective,
|
|
19581
|
+
viewerContextDirective,
|
|
19558
19582
|
artifactDirective,
|
|
19559
19583
|
longRunningDirective,
|
|
19560
19584
|
renameBranchDirective,
|
|
@@ -20089,13 +20113,13 @@ var init_orchestrator = __esm({
|
|
|
20089
20113
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
20090
20114
|
const lastError = thread.lastError ?? null;
|
|
20091
20115
|
const rawText = (lastAgent?.text ?? "").trim();
|
|
20092
|
-
const
|
|
20116
|
+
const text7 = (rawText && !isInternalAgentStatusText(rawText) ? rawText : "") || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
|
|
20093
20117
|
const stillRunning = this.threadLooksLive(thread);
|
|
20094
20118
|
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
20095
20119
|
const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
|
|
20096
20120
|
const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
|
|
20097
20121
|
return {
|
|
20098
|
-
text:
|
|
20122
|
+
text: text7,
|
|
20099
20123
|
status: thread.status,
|
|
20100
20124
|
sessionId: thread.sessionId,
|
|
20101
20125
|
lastError,
|
|
@@ -20739,7 +20763,7 @@ init_nested_electron_env();
|
|
|
20739
20763
|
// src/mcp/server.ts
|
|
20740
20764
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
20741
20765
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
20742
|
-
var
|
|
20766
|
+
var import_zod7 = require("zod");
|
|
20743
20767
|
var import_node_crypto12 = require("crypto");
|
|
20744
20768
|
var import_node_path50 = require("path");
|
|
20745
20769
|
init_orchestrator();
|
|
@@ -21600,9 +21624,9 @@ init_message_parts();
|
|
|
21600
21624
|
function lastMessagePreview(messages, max = 160) {
|
|
21601
21625
|
if (!messages?.length) return null;
|
|
21602
21626
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
21603
|
-
const
|
|
21604
|
-
if (!
|
|
21605
|
-
const flat =
|
|
21627
|
+
const text7 = messages[i]?.text?.trim();
|
|
21628
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
21629
|
+
const flat = text7.replace(/\s+/g, " ");
|
|
21606
21630
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
21607
21631
|
}
|
|
21608
21632
|
return null;
|
|
@@ -21836,8 +21860,8 @@ function labelGithubUrl(url) {
|
|
|
21836
21860
|
}
|
|
21837
21861
|
return { kind: "other", label: "GitHub", url: raw };
|
|
21838
21862
|
}
|
|
21839
|
-
function appendGithubLink(
|
|
21840
|
-
const body =
|
|
21863
|
+
function appendGithubLink(text7, githubUrl) {
|
|
21864
|
+
const body = text7.trimEnd();
|
|
21841
21865
|
if (!githubUrl?.trim()) return body;
|
|
21842
21866
|
const labeled = labelGithubUrl(githubUrl);
|
|
21843
21867
|
if (!labeled) {
|
|
@@ -22709,6 +22733,107 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
22709
22733
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
22710
22734
|
}
|
|
22711
22735
|
|
|
22736
|
+
// src/mcp/viewer-context-tools.ts
|
|
22737
|
+
var import_zod5 = require("zod");
|
|
22738
|
+
init_worktree();
|
|
22739
|
+
init_app_settings();
|
|
22740
|
+
init_workspaces2();
|
|
22741
|
+
function text5(payload, isError = false) {
|
|
22742
|
+
return mcpJson(payload, isError);
|
|
22743
|
+
}
|
|
22744
|
+
function matchRegisteredWorkspace(repoPath) {
|
|
22745
|
+
const hint = (repoPath ?? "").trim().replace(/\/+$/, "");
|
|
22746
|
+
if (!hint) return null;
|
|
22747
|
+
let best = null;
|
|
22748
|
+
for (const w of listWorkspaces()) {
|
|
22749
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
22750
|
+
if (stored === hint || hint.startsWith(`${stored}/`)) {
|
|
22751
|
+
if (!best || stored.length > best.length) best = stored;
|
|
22752
|
+
}
|
|
22753
|
+
}
|
|
22754
|
+
return best;
|
|
22755
|
+
}
|
|
22756
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
22757
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
22758
|
+
let root = hint;
|
|
22759
|
+
try {
|
|
22760
|
+
root = await resolveRepoRoot(hint);
|
|
22761
|
+
} catch {
|
|
22762
|
+
root = hint.replace(/\/+$/, "");
|
|
22763
|
+
}
|
|
22764
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
22765
|
+
}
|
|
22766
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_workspaces. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
22767
|
+
function registerViewerContextTools(server) {
|
|
22768
|
+
server.tool(
|
|
22769
|
+
"get_viewer_context",
|
|
22770
|
+
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from list_workspaces for project context. Worktree turns may omit repoPath (uses cwd).",
|
|
22771
|
+
{
|
|
22772
|
+
repoPath: import_zod5.z.string().optional().describe(
|
|
22773
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
22774
|
+
)
|
|
22775
|
+
},
|
|
22776
|
+
async ({ repoPath }) => {
|
|
22777
|
+
try {
|
|
22778
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
22779
|
+
return text5(readViewerContext(path ?? void 0));
|
|
22780
|
+
} catch (err) {
|
|
22781
|
+
return text5(
|
|
22782
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
22783
|
+
true
|
|
22784
|
+
);
|
|
22785
|
+
}
|
|
22786
|
+
}
|
|
22787
|
+
);
|
|
22788
|
+
server.tool(
|
|
22789
|
+
"update_viewer_context",
|
|
22790
|
+
"Replace account or project user context (Settings \u2192 Agents / Projects). Never call until the user confirmed. Show the proposed text, ask_user (Save this context / Do not save), wait, then pass confirmed=true. Empty context clears that field.",
|
|
22791
|
+
{
|
|
22792
|
+
scope: import_zod5.z.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
22793
|
+
context: import_zod5.z.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
22794
|
+
confirmed: import_zod5.z.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
22795
|
+
repoPath: import_zod5.z.string().optional().describe(
|
|
22796
|
+
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
22797
|
+
)
|
|
22798
|
+
},
|
|
22799
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
22800
|
+
try {
|
|
22801
|
+
if (scope === "project") {
|
|
22802
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
22803
|
+
if (!path) {
|
|
22804
|
+
return text5(
|
|
22805
|
+
{
|
|
22806
|
+
ok: false,
|
|
22807
|
+
error: "repoPath required",
|
|
22808
|
+
message: PROJECT_PATH_HINT
|
|
22809
|
+
},
|
|
22810
|
+
true
|
|
22811
|
+
);
|
|
22812
|
+
}
|
|
22813
|
+
const result2 = writeViewerContext({
|
|
22814
|
+
scope,
|
|
22815
|
+
context,
|
|
22816
|
+
repoPath: path,
|
|
22817
|
+
confirmed: confirmed === true
|
|
22818
|
+
});
|
|
22819
|
+
return text5(result2, !result2.ok);
|
|
22820
|
+
}
|
|
22821
|
+
const result = writeViewerContext({
|
|
22822
|
+
scope,
|
|
22823
|
+
context,
|
|
22824
|
+
confirmed: confirmed === true
|
|
22825
|
+
});
|
|
22826
|
+
return text5(result, !result.ok);
|
|
22827
|
+
} catch (err) {
|
|
22828
|
+
return text5(
|
|
22829
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
22830
|
+
true
|
|
22831
|
+
);
|
|
22832
|
+
}
|
|
22833
|
+
}
|
|
22834
|
+
);
|
|
22835
|
+
}
|
|
22836
|
+
|
|
22712
22837
|
// src/mcp/pr-list.ts
|
|
22713
22838
|
function compactPrRow(pr) {
|
|
22714
22839
|
return {
|
|
@@ -22748,17 +22873,17 @@ init_list_prs();
|
|
|
22748
22873
|
init_app_settings();
|
|
22749
22874
|
|
|
22750
22875
|
// src/mcp/schedule-tools.ts
|
|
22751
|
-
var
|
|
22876
|
+
var import_zod6 = require("zod");
|
|
22752
22877
|
init_schedules();
|
|
22753
22878
|
init_schedule_runner();
|
|
22754
|
-
function
|
|
22879
|
+
function text6(payload, isError = false) {
|
|
22755
22880
|
return {
|
|
22756
22881
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
22757
22882
|
...isError ? { isError: true } : {}
|
|
22758
22883
|
};
|
|
22759
22884
|
}
|
|
22760
22885
|
function fail5(err) {
|
|
22761
|
-
return
|
|
22886
|
+
return text6(
|
|
22762
22887
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
22763
22888
|
true
|
|
22764
22889
|
);
|
|
@@ -22783,7 +22908,7 @@ function registerScheduleTools(server) {
|
|
|
22783
22908
|
{},
|
|
22784
22909
|
async () => {
|
|
22785
22910
|
try {
|
|
22786
|
-
return
|
|
22911
|
+
return text6({ schedules: listSchedules() });
|
|
22787
22912
|
} catch (err) {
|
|
22788
22913
|
return fail5(err);
|
|
22789
22914
|
}
|
|
@@ -22793,17 +22918,17 @@ function registerScheduleTools(server) {
|
|
|
22793
22918
|
"create_schedule",
|
|
22794
22919
|
"Create a local schedule that, when due, sends a prompt to an orchestration chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=self to continue this coordinator. Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without threadId open a new chat each run. Overnight/unattended runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate. Sideboard.app must be running for the job to fire.",
|
|
22795
22920
|
{
|
|
22796
|
-
prompt:
|
|
22797
|
-
name:
|
|
22798
|
-
at:
|
|
22799
|
-
every:
|
|
22800
|
-
cron:
|
|
22801
|
-
tz:
|
|
22802
|
-
threadId:
|
|
22921
|
+
prompt: import_zod6.z.string().describe("User message / goal queued when the schedule fires"),
|
|
22922
|
+
name: import_zod6.z.string().optional(),
|
|
22923
|
+
at: import_zod6.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
22924
|
+
every: import_zod6.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
22925
|
+
cron: import_zod6.z.string().optional().describe("5-field cron expression"),
|
|
22926
|
+
tz: import_zod6.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
22927
|
+
threadId: import_zod6.z.string().optional().describe(
|
|
22803
22928
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
22804
22929
|
),
|
|
22805
|
-
agent:
|
|
22806
|
-
model:
|
|
22930
|
+
agent: import_zod6.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
22931
|
+
model: import_zod6.z.string().optional()
|
|
22807
22932
|
},
|
|
22808
22933
|
async (args) => {
|
|
22809
22934
|
try {
|
|
@@ -22823,7 +22948,7 @@ function registerScheduleTools(server) {
|
|
|
22823
22948
|
model: args.model,
|
|
22824
22949
|
createdBy: "mcp"
|
|
22825
22950
|
});
|
|
22826
|
-
return
|
|
22951
|
+
return text6({
|
|
22827
22952
|
schedule,
|
|
22828
22953
|
hint: "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Overnight runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate."
|
|
22829
22954
|
});
|
|
@@ -22836,17 +22961,17 @@ function registerScheduleTools(server) {
|
|
|
22836
22961
|
"update_schedule",
|
|
22837
22962
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
22838
22963
|
{
|
|
22839
|
-
id:
|
|
22840
|
-
prompt:
|
|
22841
|
-
name:
|
|
22842
|
-
at:
|
|
22843
|
-
every:
|
|
22844
|
-
cron:
|
|
22845
|
-
tz:
|
|
22846
|
-
threadId:
|
|
22847
|
-
agent:
|
|
22848
|
-
model:
|
|
22849
|
-
enabled:
|
|
22964
|
+
id: import_zod6.z.string(),
|
|
22965
|
+
prompt: import_zod6.z.string().optional(),
|
|
22966
|
+
name: import_zod6.z.string().optional(),
|
|
22967
|
+
at: import_zod6.z.string().optional(),
|
|
22968
|
+
every: import_zod6.z.string().optional(),
|
|
22969
|
+
cron: import_zod6.z.string().optional(),
|
|
22970
|
+
tz: import_zod6.z.string().optional(),
|
|
22971
|
+
threadId: import_zod6.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
22972
|
+
agent: import_zod6.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
22973
|
+
model: import_zod6.z.string().optional(),
|
|
22974
|
+
enabled: import_zod6.z.boolean().optional()
|
|
22850
22975
|
},
|
|
22851
22976
|
async (args) => {
|
|
22852
22977
|
try {
|
|
@@ -22875,7 +23000,7 @@ function registerScheduleTools(server) {
|
|
|
22875
23000
|
enabled: args.enabled,
|
|
22876
23001
|
model: args.model
|
|
22877
23002
|
});
|
|
22878
|
-
return
|
|
23003
|
+
return text6({ schedule });
|
|
22879
23004
|
} catch (err) {
|
|
22880
23005
|
return fail5(err);
|
|
22881
23006
|
}
|
|
@@ -22884,11 +23009,11 @@ function registerScheduleTools(server) {
|
|
|
22884
23009
|
server.tool(
|
|
22885
23010
|
"delete_schedule",
|
|
22886
23011
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
22887
|
-
{ id:
|
|
23012
|
+
{ id: import_zod6.z.string() },
|
|
22888
23013
|
async ({ id }) => {
|
|
22889
23014
|
try {
|
|
22890
23015
|
deleteSchedule(id);
|
|
22891
|
-
return
|
|
23016
|
+
return text6({ ok: true, id });
|
|
22892
23017
|
} catch (err) {
|
|
22893
23018
|
return fail5(err);
|
|
22894
23019
|
}
|
|
@@ -22897,14 +23022,14 @@ function registerScheduleTools(server) {
|
|
|
22897
23022
|
server.tool(
|
|
22898
23023
|
"run_schedule",
|
|
22899
23024
|
"Fire a schedule now (does not wait for nextRunAt). Queues the prompt or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
|
|
22900
|
-
{ id:
|
|
23025
|
+
{ id: import_zod6.z.string() },
|
|
22901
23026
|
async ({ id }) => {
|
|
22902
23027
|
try {
|
|
22903
23028
|
if (!getSchedule(id)) {
|
|
22904
23029
|
return fail5(new Error(`Schedule not found: ${id}`));
|
|
22905
23030
|
}
|
|
22906
23031
|
const schedule = await fireSchedule(id);
|
|
22907
|
-
return
|
|
23032
|
+
return text6({ schedule });
|
|
22908
23033
|
} catch (err) {
|
|
22909
23034
|
return fail5(err);
|
|
22910
23035
|
}
|
|
@@ -23273,13 +23398,14 @@ async function startMcpServer() {
|
|
|
23273
23398
|
version: "0.1.0"
|
|
23274
23399
|
});
|
|
23275
23400
|
const worktreeProfile = sideboardMcpProfile() === "worktree";
|
|
23401
|
+
registerViewerContextTools(server);
|
|
23276
23402
|
if (worktreeProfile) {
|
|
23277
23403
|
registerConnectedIssueVendorTools(server);
|
|
23278
23404
|
}
|
|
23279
23405
|
if (!worktreeProfile) {
|
|
23280
23406
|
server.tool(
|
|
23281
23407
|
"list_workspaces",
|
|
23282
|
-
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and
|
|
23408
|
+
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and project context when set \u2014 use path as repoPath for list_board/list_branches/list_prs/list_issues/create_thread. Account / project context (Settings \u2192 Agents / Projects) says which tickets and review PRs belong to the user.",
|
|
23283
23409
|
{},
|
|
23284
23410
|
async () => {
|
|
23285
23411
|
const workspaces = orch.listWorkspaces();
|
|
@@ -23328,13 +23454,13 @@ async function startMcpServer() {
|
|
|
23328
23454
|
"list_board",
|
|
23329
23455
|
"Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
|
|
23330
23456
|
{
|
|
23331
|
-
query:
|
|
23332
|
-
repoPath:
|
|
23333
|
-
kind:
|
|
23334
|
-
column:
|
|
23457
|
+
query: import_zod7.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
23458
|
+
repoPath: import_zod7.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
23459
|
+
kind: import_zod7.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
23460
|
+
column: import_zod7.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
23335
23461
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
23336
23462
|
),
|
|
23337
|
-
limit:
|
|
23463
|
+
limit: import_zod7.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
23338
23464
|
},
|
|
23339
23465
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
23340
23466
|
const workspaces = orch.listWorkspaces();
|
|
@@ -23372,7 +23498,7 @@ async function startMcpServer() {
|
|
|
23372
23498
|
server.tool(
|
|
23373
23499
|
"get_thread",
|
|
23374
23500
|
"Get a compact thread summary by id/ref. Includes last message preview, parentThreadId, and child worktree threads (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (thread billed token + costUsd totals when providers reported cost) and lastTurnUsage.",
|
|
23375
|
-
{ ref:
|
|
23501
|
+
{ ref: import_zod7.z.string() },
|
|
23376
23502
|
async ({ ref }) => {
|
|
23377
23503
|
const t = orch.getThread(ref);
|
|
23378
23504
|
if (!t) {
|
|
@@ -23414,17 +23540,17 @@ async function startMcpServer() {
|
|
|
23414
23540
|
"present_artifact",
|
|
23415
23541
|
"Show a document or live log in Sideboard\u2019s side column. For html/svg/markdown/react, pass the FULL document and do not also fence that same body in chat. For type=log, pass only NEW lines (same artifact_id appends). Prefer type=log for long-running job output \u2014 do not resend HTML. type=react is a single default-export component (JSX/TSX); only react/react-dom imports.",
|
|
23416
23542
|
{
|
|
23417
|
-
title:
|
|
23418
|
-
type:
|
|
23543
|
+
title: import_zod7.z.string().describe("Short title shown in the artifact pane header"),
|
|
23544
|
+
type: import_zod7.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
23419
23545
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
23420
23546
|
),
|
|
23421
|
-
content:
|
|
23547
|
+
content: import_zod7.z.string().describe(
|
|
23422
23548
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
23423
23549
|
),
|
|
23424
|
-
artifact_id:
|
|
23425
|
-
status:
|
|
23426
|
-
phase:
|
|
23427
|
-
mode:
|
|
23550
|
+
artifact_id: import_zod7.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
23551
|
+
status: import_zod7.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
23552
|
+
phase: import_zod7.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
23553
|
+
mode: import_zod7.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
23428
23554
|
},
|
|
23429
23555
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
23430
23556
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23445,15 +23571,15 @@ async function startMcpServer() {
|
|
|
23445
23571
|
"ask_user",
|
|
23446
23572
|
"Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
|
|
23447
23573
|
{
|
|
23448
|
-
questions:
|
|
23449
|
-
|
|
23450
|
-
question:
|
|
23451
|
-
header:
|
|
23452
|
-
multiSelect:
|
|
23453
|
-
options:
|
|
23454
|
-
|
|
23455
|
-
label:
|
|
23456
|
-
description:
|
|
23574
|
+
questions: import_zod7.z.array(
|
|
23575
|
+
import_zod7.z.object({
|
|
23576
|
+
question: import_zod7.z.string().describe("Full question text ending with ?"),
|
|
23577
|
+
header: import_zod7.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
23578
|
+
multiSelect: import_zod7.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
23579
|
+
options: import_zod7.z.array(
|
|
23580
|
+
import_zod7.z.object({
|
|
23581
|
+
label: import_zod7.z.string(),
|
|
23582
|
+
description: import_zod7.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
23457
23583
|
})
|
|
23458
23584
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
23459
23585
|
})
|
|
@@ -23472,9 +23598,9 @@ async function startMcpServer() {
|
|
|
23472
23598
|
"present_plan",
|
|
23473
23599
|
"Save the implementation plan as markdown to .context/attachments/plan.md and show it in Sideboard chat for user approval (Copy / Hand off / Approve). Call this when the plan is ready \u2014 required in plan mode. Pass the full plan body in content. Then Claude should call ExitPlanMode.",
|
|
23474
23600
|
{
|
|
23475
|
-
title:
|
|
23476
|
-
content:
|
|
23477
|
-
thread_id:
|
|
23601
|
+
title: import_zod7.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
23602
|
+
content: import_zod7.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
23603
|
+
thread_id: import_zod7.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
23478
23604
|
},
|
|
23479
23605
|
async ({ title, content, thread_id }) => {
|
|
23480
23606
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -23497,15 +23623,15 @@ async function startMcpServer() {
|
|
|
23497
23623
|
"present_schema",
|
|
23498
23624
|
"Open Sideboard\u2019s schema-driven side column (filterable table and/or form) when the user needs to filter, edit, publish, or persist records. Do not call this just to re-display rows you already wrote as a markdown table. If the user asks for an editable / interactive table, call this even if chat already showed those rows. Pass JSON Schema + optional schemaUi. Prefer datasource=inline with embedded resource/records. Use datasource=brightsy with resource_id only when the user is logged into Brightsy.",
|
|
23499
23625
|
{
|
|
23500
|
-
title:
|
|
23501
|
-
mode:
|
|
23502
|
-
datasource:
|
|
23503
|
-
resource_id:
|
|
23504
|
-
record_id:
|
|
23505
|
-
resource:
|
|
23506
|
-
record:
|
|
23507
|
-
records:
|
|
23508
|
-
pane_id:
|
|
23626
|
+
title: import_zod7.z.string().describe("Pane title"),
|
|
23627
|
+
mode: import_zod7.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
23628
|
+
datasource: import_zod7.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
23629
|
+
resource_id: import_zod7.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
23630
|
+
record_id: import_zod7.z.string().optional().describe("Record id when opening form mode"),
|
|
23631
|
+
resource: import_zod7.z.record(import_zod7.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
23632
|
+
record: import_zod7.z.record(import_zod7.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
23633
|
+
records: import_zod7.z.array(import_zod7.z.record(import_zod7.z.unknown())).optional().describe("Inline records for table mode"),
|
|
23634
|
+
pane_id: import_zod7.z.string().optional().describe("Stable pane id across updates")
|
|
23509
23635
|
},
|
|
23510
23636
|
async (args) => {
|
|
23511
23637
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23526,10 +23652,10 @@ async function startMcpServer() {
|
|
|
23526
23652
|
"present_files",
|
|
23527
23653
|
"Open Sideboard\u2019s Files column (CMS-style file manager: browse, upload, pick). Use datasource=brightsy when the user is logged into Brightsy account storage; datasource=memory for a session-local demo store. Prefer this over claiming a file manager UI is unavailable. Pair with present_schema when editing records that need media.",
|
|
23528
23654
|
{
|
|
23529
|
-
title:
|
|
23530
|
-
datasource:
|
|
23531
|
-
path:
|
|
23532
|
-
pane_id:
|
|
23655
|
+
title: import_zod7.z.string().optional().describe("Pane title (default: Files)"),
|
|
23656
|
+
datasource: import_zod7.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
23657
|
+
path: import_zod7.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
23658
|
+
pane_id: import_zod7.z.string().optional().describe("Stable pane id across updates")
|
|
23533
23659
|
},
|
|
23534
23660
|
async (args) => {
|
|
23535
23661
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23548,8 +23674,8 @@ async function startMcpServer() {
|
|
|
23548
23674
|
"wait_for_job",
|
|
23549
23675
|
"Wait on a detached long job (tests, pack, deploy) started with detached-job.js. MCP clients kill tools around 60s, so this returns within 45s. stillRunning is the source of truth \u2014 if true, present_artifact type=log with content=delta (same artifact_id) and call wait_for_job again. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
23550
23676
|
{
|
|
23551
|
-
id:
|
|
23552
|
-
timeoutMs:
|
|
23677
|
+
id: import_zod7.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
23678
|
+
timeoutMs: import_zod7.z.number().optional()
|
|
23553
23679
|
},
|
|
23554
23680
|
async ({ id, timeoutMs }) => {
|
|
23555
23681
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
@@ -23570,7 +23696,7 @@ async function startMcpServer() {
|
|
|
23570
23696
|
"set_caffeinate",
|
|
23571
23697
|
"Keep this Mac awake with caffeinate (like Claude Code) across turns \u2014 independent of Settings toggles. Turn ON when the user will be away from the keyboard, is driving work from Slack, or asks you to keep the machine awake. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the Mac awake. Closing or archiving this orchestration chat also releases the hold. macOS only.",
|
|
23572
23698
|
{
|
|
23573
|
-
enabled:
|
|
23699
|
+
enabled: import_zod7.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
23574
23700
|
},
|
|
23575
23701
|
async ({ enabled }) => {
|
|
23576
23702
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -23608,18 +23734,18 @@ async function startMcpServer() {
|
|
|
23608
23734
|
"create_thread",
|
|
23609
23735
|
`Create a worktree thread (chat) from branch, pr, or ticket. A ticket, PR, or named branch may have only one live worktree \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. Creating from the default branch still opens a new isolated worktree. Pass repoPath from list_workspaces. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Then use send_to_thread to chat.`,
|
|
23610
23736
|
{
|
|
23611
|
-
sourceType:
|
|
23612
|
-
sourceRef:
|
|
23613
|
-
agent:
|
|
23614
|
-
model:
|
|
23737
|
+
sourceType: import_zod7.z.enum(["branch", "pr", "ticket"]),
|
|
23738
|
+
sourceRef: import_zod7.z.string(),
|
|
23739
|
+
agent: import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
23740
|
+
model: import_zod7.z.string().nullable().optional().describe(
|
|
23615
23741
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
23616
23742
|
),
|
|
23617
|
-
repoPath:
|
|
23618
|
-
title:
|
|
23619
|
-
cowboy:
|
|
23743
|
+
repoPath: import_zod7.z.string(),
|
|
23744
|
+
title: import_zod7.z.string().optional(),
|
|
23745
|
+
cowboy: import_zod7.z.boolean().optional().describe(
|
|
23620
23746
|
"If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
|
|
23621
23747
|
),
|
|
23622
|
-
parentThreadId:
|
|
23748
|
+
parentThreadId: import_zod7.z.string().optional().describe(
|
|
23623
23749
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
23624
23750
|
)
|
|
23625
23751
|
},
|
|
@@ -23635,10 +23761,10 @@ async function startMcpServer() {
|
|
|
23635
23761
|
"start_board_card",
|
|
23636
23762
|
"Same as create_thread for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second worktree when one already matches \u2014 returns that thread (alreadyStarted). Then send_to_thread.",
|
|
23637
23763
|
{
|
|
23638
|
-
kind:
|
|
23639
|
-
ref:
|
|
23640
|
-
repoPath:
|
|
23641
|
-
title:
|
|
23764
|
+
kind: import_zod7.z.enum(["ticket", "pr", "branch"]),
|
|
23765
|
+
ref: import_zod7.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
23766
|
+
repoPath: import_zod7.z.string().describe("Workspace path from list_workspaces / list_board"),
|
|
23767
|
+
title: import_zod7.z.string().optional()
|
|
23642
23768
|
},
|
|
23643
23769
|
async ({ kind, ref, repoPath, title }) => {
|
|
23644
23770
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -23742,9 +23868,9 @@ async function startMcpServer() {
|
|
|
23742
23868
|
"send_to_thread",
|
|
23743
23869
|
'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. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not force_stop to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that stops the child mid-thought. Call wait_for_turn again instead.',
|
|
23744
23870
|
{
|
|
23745
|
-
ref:
|
|
23746
|
-
prompt:
|
|
23747
|
-
force_stop:
|
|
23871
|
+
ref: import_zod7.z.string(),
|
|
23872
|
+
prompt: import_zod7.z.string(),
|
|
23873
|
+
force_stop: import_zod7.z.boolean().optional()
|
|
23748
23874
|
},
|
|
23749
23875
|
async ({ ref, prompt, force_stop }) => {
|
|
23750
23876
|
if (force_stop) {
|
|
@@ -23773,8 +23899,8 @@ async function startMcpServer() {
|
|
|
23773
23899
|
"wait_for_turn",
|
|
23774
23900
|
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
|
|
23775
23901
|
{
|
|
23776
|
-
ref:
|
|
23777
|
-
timeoutMs:
|
|
23902
|
+
ref: import_zod7.z.string(),
|
|
23903
|
+
timeoutMs: import_zod7.z.number().optional()
|
|
23778
23904
|
},
|
|
23779
23905
|
async ({ ref, timeoutMs }) => {
|
|
23780
23906
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -23804,7 +23930,7 @@ async function startMcpServer() {
|
|
|
23804
23930
|
server.tool(
|
|
23805
23931
|
"get_turn_result",
|
|
23806
23932
|
"Assistant message when the turn finished, or live progress while stillRunning. Not the full transcript. Includes usage for the last agent turn (tokens + costUsd when reported).",
|
|
23807
|
-
{ ref:
|
|
23933
|
+
{ ref: import_zod7.z.string() },
|
|
23808
23934
|
async ({ ref }) => {
|
|
23809
23935
|
const result = orch.getTurnResult(ref);
|
|
23810
23936
|
return {
|
|
@@ -23825,8 +23951,8 @@ async function startMcpServer() {
|
|
|
23825
23951
|
"stop_thread",
|
|
23826
23952
|
"Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true.",
|
|
23827
23953
|
{
|
|
23828
|
-
ref:
|
|
23829
|
-
force:
|
|
23954
|
+
ref: import_zod7.z.string(),
|
|
23955
|
+
force: import_zod7.z.boolean().optional()
|
|
23830
23956
|
},
|
|
23831
23957
|
async ({ ref, force }) => {
|
|
23832
23958
|
const t = orch.getThread(ref);
|
|
@@ -23856,7 +23982,7 @@ async function startMcpServer() {
|
|
|
23856
23982
|
server.tool(
|
|
23857
23983
|
"archive_thread",
|
|
23858
23984
|
"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.",
|
|
23859
|
-
{ ref:
|
|
23985
|
+
{ ref: import_zod7.z.string() },
|
|
23860
23986
|
async ({ ref }) => {
|
|
23861
23987
|
const t = orch.getThread(ref);
|
|
23862
23988
|
if (!t) {
|
|
@@ -23889,7 +24015,7 @@ async function startMcpServer() {
|
|
|
23889
24015
|
server.tool(
|
|
23890
24016
|
"restore_thread",
|
|
23891
24017
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
23892
|
-
{ ref:
|
|
24018
|
+
{ ref: import_zod7.z.string() },
|
|
23893
24019
|
async ({ ref }) => {
|
|
23894
24020
|
try {
|
|
23895
24021
|
const restored = await orch.restore(ref);
|
|
@@ -23915,8 +24041,8 @@ async function startMcpServer() {
|
|
|
23915
24041
|
"get_diff",
|
|
23916
24042
|
"Compact diff summary (capped hunks, paginated)",
|
|
23917
24043
|
{
|
|
23918
|
-
ref:
|
|
23919
|
-
maxFiles:
|
|
24044
|
+
ref: import_zod7.z.string(),
|
|
24045
|
+
maxFiles: import_zod7.z.number().optional()
|
|
23920
24046
|
},
|
|
23921
24047
|
async ({ ref, maxFiles }) => {
|
|
23922
24048
|
const summary = await orch.diffSummary(ref);
|
|
@@ -23936,7 +24062,7 @@ async function startMcpServer() {
|
|
|
23936
24062
|
server.tool(
|
|
23937
24063
|
"get_pr_checks",
|
|
23938
24064
|
"Snapshot of GitHub PR checks for a worktree thread (`gh pr checks` plus merge/review gates). null = no PR. If the user gave a goal (Greptile 5/5, CI green), the worktree agent watches with `gh pr checks --watch` via /long-running \u2014 this tool is a snapshot, not a waiter. Coordinators: do not run gh from the orchestration cwd.",
|
|
23939
|
-
{ ref:
|
|
24065
|
+
{ ref: import_zod7.z.string().describe("Worktree thread id/ref") },
|
|
23940
24066
|
async ({ ref }) => {
|
|
23941
24067
|
try {
|
|
23942
24068
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -23952,7 +24078,7 @@ async function startMcpServer() {
|
|
|
23952
24078
|
server.tool(
|
|
23953
24079
|
"request_review",
|
|
23954
24080
|
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
|
|
23955
|
-
{ ref:
|
|
24081
|
+
{ ref: import_zod7.z.string().describe("Worktree thread id/ref to review") },
|
|
23956
24082
|
async ({ ref }) => {
|
|
23957
24083
|
try {
|
|
23958
24084
|
const tab = await orch.requestReview(ref);
|
|
@@ -23981,8 +24107,8 @@ async function startMcpServer() {
|
|
|
23981
24107
|
"ask_git",
|
|
23982
24108
|
"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` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). 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. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
|
|
23983
24109
|
{
|
|
23984
|
-
ref:
|
|
23985
|
-
action:
|
|
24110
|
+
ref: import_zod7.z.string().describe("Worktree thread id/ref"),
|
|
24111
|
+
action: import_zod7.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
23986
24112
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
23987
24113
|
)
|
|
23988
24114
|
},
|
|
@@ -24027,7 +24153,7 @@ async function startMcpServer() {
|
|
|
24027
24153
|
}
|
|
24028
24154
|
}
|
|
24029
24155
|
);
|
|
24030
|
-
const agentEnum =
|
|
24156
|
+
const agentEnum = import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
24031
24157
|
server.tool(
|
|
24032
24158
|
"list_models",
|
|
24033
24159
|
"List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
|
|
@@ -24050,11 +24176,11 @@ async function startMcpServer() {
|
|
|
24050
24176
|
"fork_worktree",
|
|
24051
24177
|
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
24052
24178
|
{
|
|
24053
|
-
ref:
|
|
24054
|
-
through_index:
|
|
24179
|
+
ref: import_zod7.z.string().describe("Worktree thread id/ref to fork"),
|
|
24180
|
+
through_index: import_zod7.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
24055
24181
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
24056
|
-
model:
|
|
24057
|
-
title:
|
|
24182
|
+
model: import_zod7.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
24183
|
+
title: import_zod7.z.string().optional()
|
|
24058
24184
|
},
|
|
24059
24185
|
async ({ ref, through_index, agent, model, title }) => {
|
|
24060
24186
|
try {
|
|
@@ -24095,11 +24221,11 @@ async function startMcpServer() {
|
|
|
24095
24221
|
"fork_chat",
|
|
24096
24222
|
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
24097
24223
|
{
|
|
24098
|
-
ref:
|
|
24099
|
-
through_index:
|
|
24224
|
+
ref: import_zod7.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
24225
|
+
through_index: import_zod7.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
24100
24226
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
24101
|
-
model:
|
|
24102
|
-
title:
|
|
24227
|
+
model: import_zod7.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
24228
|
+
title: import_zod7.z.string().optional()
|
|
24103
24229
|
},
|
|
24104
24230
|
async ({ ref, through_index, agent, model, title }) => {
|
|
24105
24231
|
try {
|
|
@@ -24145,8 +24271,8 @@ async function startMcpServer() {
|
|
|
24145
24271
|
"run_dev_script",
|
|
24146
24272
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
24147
24273
|
{
|
|
24148
|
-
ref:
|
|
24149
|
-
name:
|
|
24274
|
+
ref: import_zod7.z.string(),
|
|
24275
|
+
name: import_zod7.z.string().optional()
|
|
24150
24276
|
},
|
|
24151
24277
|
async ({ ref, name }) => {
|
|
24152
24278
|
const result = await orch.startDev(ref, name);
|
|
@@ -24168,7 +24294,7 @@ async function startMcpServer() {
|
|
|
24168
24294
|
server.tool(
|
|
24169
24295
|
"list_run_scripts",
|
|
24170
24296
|
"List named run scripts available for a thread",
|
|
24171
|
-
{ ref:
|
|
24297
|
+
{ ref: import_zod7.z.string() },
|
|
24172
24298
|
async ({ ref }) => {
|
|
24173
24299
|
const scripts = orch.listThreadRunScripts(ref);
|
|
24174
24300
|
const active = orch.getActiveRuns(ref);
|
|
@@ -24186,8 +24312,8 @@ async function startMcpServer() {
|
|
|
24186
24312
|
"stop_dev_script",
|
|
24187
24313
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
24188
24314
|
{
|
|
24189
|
-
ref:
|
|
24190
|
-
name:
|
|
24315
|
+
ref: import_zod7.z.string(),
|
|
24316
|
+
name: import_zod7.z.string().optional()
|
|
24191
24317
|
},
|
|
24192
24318
|
async ({ ref, name }) => {
|
|
24193
24319
|
orch.stopDev(ref, name);
|
|
@@ -24197,7 +24323,7 @@ async function startMcpServer() {
|
|
|
24197
24323
|
server.tool(
|
|
24198
24324
|
"run_setup",
|
|
24199
24325
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
24200
|
-
{ ref:
|
|
24326
|
+
{ ref: import_zod7.z.string() },
|
|
24201
24327
|
async ({ ref }) => {
|
|
24202
24328
|
const result = await orch.runSetup(ref);
|
|
24203
24329
|
return {
|
|
@@ -24208,7 +24334,7 @@ async function startMcpServer() {
|
|
|
24208
24334
|
server.tool(
|
|
24209
24335
|
"add_workspace",
|
|
24210
24336
|
"Register a git repo as a Sideboard workspace",
|
|
24211
|
-
{ repoPath:
|
|
24337
|
+
{ repoPath: import_zod7.z.string() },
|
|
24212
24338
|
async ({ repoPath }) => {
|
|
24213
24339
|
const ws = await orch.addWorkspace(repoPath);
|
|
24214
24340
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -24217,7 +24343,7 @@ async function startMcpServer() {
|
|
|
24217
24343
|
server.tool(
|
|
24218
24344
|
"remove_workspace",
|
|
24219
24345
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
24220
|
-
{ repoPath:
|
|
24346
|
+
{ repoPath: import_zod7.z.string() },
|
|
24221
24347
|
async ({ repoPath }) => {
|
|
24222
24348
|
orch.removeWorkspace(repoPath);
|
|
24223
24349
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -24227,12 +24353,12 @@ async function startMcpServer() {
|
|
|
24227
24353
|
"fanout",
|
|
24228
24354
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
24229
24355
|
{
|
|
24230
|
-
prompt:
|
|
24231
|
-
agents:
|
|
24232
|
-
repoPath:
|
|
24233
|
-
sourceType:
|
|
24234
|
-
sourceRef:
|
|
24235
|
-
title:
|
|
24356
|
+
prompt: import_zod7.z.string(),
|
|
24357
|
+
agents: import_zod7.z.array(import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
24358
|
+
repoPath: import_zod7.z.string(),
|
|
24359
|
+
sourceType: import_zod7.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
24360
|
+
sourceRef: import_zod7.z.string().optional(),
|
|
24361
|
+
title: import_zod7.z.string().optional()
|
|
24236
24362
|
},
|
|
24237
24363
|
async (args) => {
|
|
24238
24364
|
const threads = await orch.bestOfN(args);
|
|
@@ -24259,8 +24385,8 @@ async function startMcpServer() {
|
|
|
24259
24385
|
"list_branches",
|
|
24260
24386
|
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
24261
24387
|
{
|
|
24262
|
-
repoPath:
|
|
24263
|
-
unmergedOnly:
|
|
24388
|
+
repoPath: import_zod7.z.string(),
|
|
24389
|
+
unmergedOnly: import_zod7.z.boolean().optional()
|
|
24264
24390
|
},
|
|
24265
24391
|
async ({ repoPath, unmergedOnly }) => {
|
|
24266
24392
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -24281,19 +24407,19 @@ async function startMcpServer() {
|
|
|
24281
24407
|
"list_prs",
|
|
24282
24408
|
'List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_workspaces. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled eng-review with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_thread with sourceType=pr.',
|
|
24283
24409
|
{
|
|
24284
|
-
repoPath:
|
|
24285
|
-
query:
|
|
24286
|
-
queue:
|
|
24410
|
+
repoPath: import_zod7.z.string(),
|
|
24411
|
+
query: import_zod7.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
24412
|
+
queue: import_zod7.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
24287
24413
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
24288
24414
|
),
|
|
24289
|
-
state:
|
|
24290
|
-
label:
|
|
24415
|
+
state: import_zod7.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
24416
|
+
label: import_zod7.z.string().optional().describe(
|
|
24291
24417
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
24292
24418
|
),
|
|
24293
|
-
reviewer:
|
|
24419
|
+
reviewer: import_zod7.z.string().optional().describe(
|
|
24294
24420
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
24295
24421
|
),
|
|
24296
|
-
limit:
|
|
24422
|
+
limit: import_zod7.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
24297
24423
|
},
|
|
24298
24424
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
24299
24425
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -24325,7 +24451,7 @@ async function startMcpServer() {
|
|
|
24325
24451
|
server.tool(
|
|
24326
24452
|
"get_pr_stack",
|
|
24327
24453
|
"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).",
|
|
24328
|
-
{ ref:
|
|
24454
|
+
{ ref: import_zod7.z.string() },
|
|
24329
24455
|
async ({ ref }) => {
|
|
24330
24456
|
const stack = await orch.getPrStack(ref);
|
|
24331
24457
|
return {
|
|
@@ -24337,8 +24463,8 @@ async function startMcpServer() {
|
|
|
24337
24463
|
"open_pr_stack_layers",
|
|
24338
24464
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
24339
24465
|
{
|
|
24340
|
-
ref:
|
|
24341
|
-
layer:
|
|
24466
|
+
ref: import_zod7.z.string(),
|
|
24467
|
+
layer: import_zod7.z.number().int().positive().optional()
|
|
24342
24468
|
},
|
|
24343
24469
|
async ({ ref, layer }) => {
|
|
24344
24470
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -24372,9 +24498,9 @@ async function startMcpServer() {
|
|
|
24372
24498
|
"add_stack_layer",
|
|
24373
24499
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
24374
24500
|
{
|
|
24375
|
-
ref:
|
|
24376
|
-
branchName:
|
|
24377
|
-
title:
|
|
24501
|
+
ref: import_zod7.z.string(),
|
|
24502
|
+
branchName: import_zod7.z.string(),
|
|
24503
|
+
title: import_zod7.z.string().optional()
|
|
24378
24504
|
},
|
|
24379
24505
|
async ({ ref, branchName, title }) => {
|
|
24380
24506
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -24403,11 +24529,11 @@ async function startMcpServer() {
|
|
|
24403
24529
|
"create_pr_stack",
|
|
24404
24530
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
24405
24531
|
{
|
|
24406
|
-
repoPath:
|
|
24407
|
-
branches:
|
|
24408
|
-
agent:
|
|
24409
|
-
base:
|
|
24410
|
-
title:
|
|
24532
|
+
repoPath: import_zod7.z.string(),
|
|
24533
|
+
branches: import_zod7.z.array(import_zod7.z.string()).min(1),
|
|
24534
|
+
agent: import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
24535
|
+
base: import_zod7.z.string().optional(),
|
|
24536
|
+
title: import_zod7.z.string().optional()
|
|
24411
24537
|
},
|
|
24412
24538
|
async (args) => {
|
|
24413
24539
|
const result = await orch.createPrStack({
|
|
@@ -24446,10 +24572,10 @@ async function startMcpServer() {
|
|
|
24446
24572
|
"list_issues",
|
|
24447
24573
|
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Then create_thread with sourceType=ticket.",
|
|
24448
24574
|
{
|
|
24449
|
-
repoPath:
|
|
24450
|
-
query:
|
|
24451
|
-
assignee:
|
|
24452
|
-
limit:
|
|
24575
|
+
repoPath: import_zod7.z.string(),
|
|
24576
|
+
query: import_zod7.z.string().optional().describe("Search title, identifier, or description"),
|
|
24577
|
+
assignee: import_zod7.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
24578
|
+
limit: import_zod7.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
24453
24579
|
},
|
|
24454
24580
|
async ({ repoPath, query, assignee, limit }) => {
|
|
24455
24581
|
const root = await resolveRepoRoot(repoPath);
|