@sideboard-ai/core 0.1.158 → 0.1.159
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-BEIDJG7W.js → abletime-HOQBDQC4.js} +2 -2
- package/dist/{abletime-GWZE6OYM.js → abletime-LK7GV556.js} +2 -2
- package/dist/{agents-E3ZPWZRS.js → agents-5HY76BCR.js} +4 -4
- package/dist/{agents-RGE7PSUY.js → agents-67ZBH5OE.js} +4 -4
- package/dist/{app-settings-VRQI3FIE.js → app-settings-BJ5GFAE5.js} +13 -17
- package/dist/{app-settings-H3INSR67.js → app-settings-YWJ4MVYM.js} +13 -17
- package/dist/{chunk-DVJBO3M7.js → chunk-AGLPYXDT.js} +1 -1
- package/dist/{chunk-EPQIOAAY.js → chunk-DWUOKF3W.js} +7 -5
- package/dist/{chunk-D7EGJ3EI.js → chunk-EQIJ5MHR.js} +119 -110
- package/dist/{chunk-3BWVOUQG.js → chunk-FLG4A5FJ.js} +119 -110
- package/dist/{chunk-LQMFCS54.js → chunk-KEDHHXKW.js} +3 -13
- package/dist/{chunk-A7QXLYBN.js → chunk-MEZXWELT.js} +3 -13
- package/dist/{chunk-CVR4RJ6G.js → chunk-O7RWWPMR.js} +12 -13
- package/dist/{chunk-KYKNFJK5.js → chunk-OYOXLY6B.js} +39 -20
- package/dist/{chunk-3SUZMPGL.js → chunk-SIMFKJIB.js} +12 -13
- package/dist/{chunk-FBFIOVDH.js → chunk-TGU3LSDG.js} +38 -19
- package/dist/{chunk-N27GVFZY.js → chunk-UBMCEZRE.js} +1 -1
- package/dist/{chunk-BLRHO7XQ.js → chunk-V4R6L2ZL.js} +10 -6
- package/dist/{coordinator-prompt-GZESIQNH.js → coordinator-prompt-7NKY7QRK.js} +3 -3
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-CNI6AK65.js} +3 -3
- package/dist/{global-workspace-REI55FZ6.js → global-workspace-FKAJZHO5.js} +3 -3
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-IJYCUO4M.js} +3 -3
- package/dist/index.cjs +588 -472
- package/dist/index.d.cts +62 -57
- package/dist/index.d.ts +62 -57
- package/dist/index.js +322 -230
- package/dist/mcp/run-stdio.cjs +517 -399
- package/dist/mcp/run-stdio.js +262 -163
- package/dist/{orchestrator-ZVW2AC6B.js → orchestrator-KFLS5BTP.js} +5 -5
- package/dist/{orchestrator-J727WIJY.js → orchestrator-NSVP7DJH.js} +5 -5
- package/dist/{workspaces-NYSM2EPX.js → workspaces-D2M3AM2M.js} +3 -3
- package/dist/{workspaces-OCEPZXFD.js → workspaces-OFSDJ7PZ.js} +3 -3
- package/dist/{worktree-VTMBZKY6.js → worktree-BQE5KLRY.js} +2 -2
- package/dist/{worktree-T4SWFSTT.js → worktree-EA6AD6D6.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -578,27 +578,27 @@ var init_nested_electron_env = __esm({
|
|
|
578
578
|
});
|
|
579
579
|
|
|
580
580
|
// src/store/account-profile.ts
|
|
581
|
-
function
|
|
582
|
-
const known =
|
|
581
|
+
function legacyRoleLabel(role) {
|
|
582
|
+
const known = LEGACY_ROLE_LABELS[role];
|
|
583
583
|
if (known) return known;
|
|
584
584
|
return role.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
585
585
|
}
|
|
586
|
-
function
|
|
586
|
+
function normalizeLegacyRole(value) {
|
|
587
587
|
const key = (value ?? "").trim().toLowerCase().replace(/[_\s]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
588
588
|
if (key === "both") return void 0;
|
|
589
589
|
if (key.length < 2 || key.length > 40) return void 0;
|
|
590
590
|
if (!/^[a-z][a-z0-9-]*$/.test(key)) return void 0;
|
|
591
591
|
return key;
|
|
592
592
|
}
|
|
593
|
-
function
|
|
593
|
+
function legacyRoleLabels(roles, legacyRole) {
|
|
594
594
|
const raw = Array.isArray(roles) ? roles : [];
|
|
595
595
|
const out = [];
|
|
596
596
|
const seen = /* @__PURE__ */ new Set();
|
|
597
597
|
const push = (value) => {
|
|
598
|
-
const role =
|
|
599
|
-
if (!role || seen.has(role) || out.length >=
|
|
598
|
+
const role = normalizeLegacyRole(typeof value === "string" ? value : "");
|
|
599
|
+
if (!role || seen.has(role) || out.length >= 16) return;
|
|
600
600
|
seen.add(role);
|
|
601
|
-
out.push(role);
|
|
601
|
+
out.push(legacyRoleLabel(role));
|
|
602
602
|
};
|
|
603
603
|
for (const item of raw) push(item);
|
|
604
604
|
if (out.length === 0) push(legacyRole);
|
|
@@ -608,117 +608,83 @@ function normalizeProfileNotes(value) {
|
|
|
608
608
|
if (typeof value !== "string") return "";
|
|
609
609
|
return value.replace(/\r\n/g, "\n").trim().slice(0, PROFILE_NOTES_MAX);
|
|
610
610
|
}
|
|
611
|
-
function
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (seen.has(key)) continue;
|
|
619
|
-
seen.add(key);
|
|
620
|
-
out.push(hint);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return out;
|
|
624
|
-
}
|
|
625
|
-
function teamHintsForCustomRole(role) {
|
|
626
|
-
const slug = role.trim().toLowerCase();
|
|
627
|
-
if (!slug) return [];
|
|
628
|
-
if (slug.endsWith("-team") || slug.endsWith("-squad")) return [slug];
|
|
629
|
-
return [`${slug}-team`, slug];
|
|
611
|
+
function foldLegacyRolesIntoNotes(roles, legacyRole, notes) {
|
|
612
|
+
const text7 = normalizeProfileNotes(notes);
|
|
613
|
+
const labels = legacyRoleLabels(roles, legacyRole);
|
|
614
|
+
if (!labels.length) return text7;
|
|
615
|
+
if (text7.toLowerCase().startsWith("roles:")) return text7;
|
|
616
|
+
const prefix = `Roles: ${labels.join(", ")}`;
|
|
617
|
+
return text7 ? `${prefix}. ${text7}` : prefix;
|
|
630
618
|
}
|
|
631
619
|
function resolveAccountProfile(input) {
|
|
632
620
|
return resolveViewerProfile(input);
|
|
633
621
|
}
|
|
634
622
|
function resolveViewerProfile(account, project) {
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const projectNotes =
|
|
641
|
-
|
|
623
|
+
const accountNotes = foldLegacyRolesIntoNotes(
|
|
624
|
+
account?.roles,
|
|
625
|
+
account?.role,
|
|
626
|
+
account?.notes
|
|
627
|
+
);
|
|
628
|
+
const projectNotes = foldLegacyRolesIntoNotes(
|
|
629
|
+
project?.roles,
|
|
630
|
+
project?.role,
|
|
631
|
+
project?.notes
|
|
632
|
+
);
|
|
642
633
|
return {
|
|
643
|
-
|
|
644
|
-
roleLabels: roles.map((role) => accountRoleLabel(role)),
|
|
645
|
-
reviewTeamHints: reviewTeamHintsForRoles(roles),
|
|
646
|
-
notes,
|
|
634
|
+
notes: [accountNotes, projectNotes].filter(Boolean).join("\n"),
|
|
647
635
|
accountNotes,
|
|
648
|
-
projectNotes
|
|
649
|
-
rolesFromProject
|
|
636
|
+
projectNotes
|
|
650
637
|
};
|
|
651
638
|
}
|
|
652
|
-
function preferTeamsForRole(viewerTeams, hints) {
|
|
653
|
-
const teams = viewerTeams.map((t) => t.trim()).filter(Boolean);
|
|
654
|
-
const want = hints.map((t) => t.trim()).filter(Boolean);
|
|
655
|
-
if (!want.length) return teams;
|
|
656
|
-
if (!teams.length) return want;
|
|
657
|
-
const hintKeys = want.map((t) => t.toLowerCase());
|
|
658
|
-
const matched = teams.filter((team) => {
|
|
659
|
-
const key = team.toLowerCase();
|
|
660
|
-
return hintKeys.some((hint) => key === hint || key.includes(hint) || hint.includes(key));
|
|
661
|
-
});
|
|
662
|
-
return matched.length ? matched : teams;
|
|
663
|
-
}
|
|
664
639
|
function compactNotes(notes, max = 160) {
|
|
665
640
|
const oneLine = notes.replace(/\s+/g, " ").trim();
|
|
666
641
|
if (oneLine.length <= max) return oneLine;
|
|
667
642
|
return `${oneLine.slice(0, max - 1).trimEnd()}\u2026`;
|
|
668
643
|
}
|
|
669
644
|
function formatAccountProfilePlaybookLine(profile) {
|
|
670
|
-
if (
|
|
671
|
-
|
|
672
|
-
if (profile.roleLabels.length) {
|
|
673
|
-
parts.push(
|
|
674
|
-
`roles ${profile.roleLabels.join(", ")} (tickets to work on / PRs to review; prefer those teams)`
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
if (profile.notes) {
|
|
678
|
-
parts.push(`notes: ${compactNotes(profile.notes)}`);
|
|
679
|
-
}
|
|
680
|
-
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.`;
|
|
645
|
+
if (!profile.accountNotes) return "";
|
|
646
|
+
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).`;
|
|
681
647
|
}
|
|
682
648
|
function formatProjectProfilePlaybookLines(projects) {
|
|
683
649
|
const lines = projects.map((project) => {
|
|
684
|
-
const roles = (project.roleLabels ?? []).filter(Boolean);
|
|
685
650
|
const notes = normalizeProfileNotes(project.notes);
|
|
686
|
-
if (!
|
|
687
|
-
|
|
688
|
-
if (roles.length) bits.push(`roles ${roles.join(", ")}`);
|
|
689
|
-
if (notes) bits.push(compactNotes(notes, 120));
|
|
690
|
-
return `- ${project.name}: ${bits.join(". ")}`;
|
|
651
|
+
if (!notes) return "";
|
|
652
|
+
return `- ${project.name}: ${compactNotes(notes, 120)}`;
|
|
691
653
|
}).filter(Boolean);
|
|
692
654
|
if (!lines.length) return "";
|
|
693
|
-
return [
|
|
694
|
-
"\
|
|
695
|
-
|
|
655
|
+
return [
|
|
656
|
+
"Project context (Settings \u2192 Projects; adds to account context):",
|
|
657
|
+
...lines
|
|
658
|
+
].join("\n");
|
|
696
659
|
}
|
|
697
660
|
function formatWorkspaceProfileSuffix(profile) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (profile.projectNotes) bits.push(`notes:${compactNotes(profile.projectNotes, 80)}`);
|
|
701
|
-
return bits.length ? ` ${bits.join(" ")}` : "";
|
|
661
|
+
if (!profile.projectNotes) return "";
|
|
662
|
+
return ` context:${compactNotes(profile.projectNotes, 80)}`;
|
|
702
663
|
}
|
|
703
|
-
|
|
664
|
+
function formatViewerContextDirective(profile) {
|
|
665
|
+
const account = profile.accountNotes || "(empty)";
|
|
666
|
+
const project = profile.projectNotes || "(empty \u2014 account context applies)";
|
|
667
|
+
return [
|
|
668
|
+
"Viewer context (Settings \u2192 Agents / Projects) \u2014 how to find this user\u2019s tickets and review PRs:",
|
|
669
|
+
`- Account: ${compactNotes(profile.accountNotes || account, 240)}`,
|
|
670
|
+
`- This project: ${compactNotes(profile.projectNotes || project, 240)}`,
|
|
671
|
+
"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."
|
|
672
|
+
].join("\n");
|
|
673
|
+
}
|
|
674
|
+
function formatViewerContextReminder() {
|
|
675
|
+
return "Viewer context: get_viewer_context to read; update_viewer_context only after ask_user confirms (Save this context / Do not save).";
|
|
676
|
+
}
|
|
677
|
+
var PROFILE_NOTES_MAX, LEGACY_ROLE_LABELS;
|
|
704
678
|
var init_account_profile = __esm({
|
|
705
679
|
"src/store/account-profile.ts"() {
|
|
706
680
|
"use strict";
|
|
707
|
-
ACCOUNT_ROLE_PRESETS = ["engineering", "design", "product"];
|
|
708
|
-
ACCOUNT_ROLES = ACCOUNT_ROLE_PRESETS;
|
|
709
|
-
ACCOUNT_ROLE_MAX = 16;
|
|
710
681
|
PROFILE_NOTES_MAX = 2e3;
|
|
711
|
-
|
|
682
|
+
LEGACY_ROLE_LABELS = {
|
|
712
683
|
engineering: "Engineering",
|
|
713
684
|
design: "Design",
|
|
714
685
|
product: "Product",
|
|
715
686
|
qa: "QA"
|
|
716
687
|
};
|
|
717
|
-
ROLE_TEAM_HINTS = {
|
|
718
|
-
engineering: ["engineering-team", "engineering", "eng-team"],
|
|
719
|
-
design: ["design-team", "design"],
|
|
720
|
-
product: ["product-team", "product"]
|
|
721
|
-
};
|
|
722
688
|
}
|
|
723
689
|
});
|
|
724
690
|
|
|
@@ -982,16 +948,11 @@ var init_issue_source_labels = __esm({
|
|
|
982
948
|
// src/store/app-settings.ts
|
|
983
949
|
var app_settings_exports = {};
|
|
984
950
|
__export(app_settings_exports, {
|
|
985
|
-
ACCOUNT_ROLES: () => ACCOUNT_ROLES,
|
|
986
|
-
ACCOUNT_ROLE_LABELS: () => ACCOUNT_ROLE_LABELS,
|
|
987
|
-
ACCOUNT_ROLE_MAX: () => ACCOUNT_ROLE_MAX,
|
|
988
|
-
ACCOUNT_ROLE_PRESETS: () => ACCOUNT_ROLE_PRESETS,
|
|
989
951
|
FOLLOW_UP_BEHAVIORS: () => FOLLOW_UP_BEHAVIORS,
|
|
990
952
|
GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
|
|
991
953
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
992
954
|
ISSUE_SOURCE_LABELS: () => ISSUE_SOURCE_LABELS,
|
|
993
955
|
PROFILE_NOTES_MAX: () => PROFILE_NOTES_MAX,
|
|
994
|
-
accountRoleLabel: () => accountRoleLabel,
|
|
995
956
|
appSettingsPath: () => appSettingsPath,
|
|
996
957
|
applyAppEnvironment: () => applyAppEnvironment,
|
|
997
958
|
autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
|
|
@@ -1015,9 +976,12 @@ __export(app_settings_exports, {
|
|
|
1015
976
|
emptyPublicIntegrations: () => emptyPublicIntegrations,
|
|
1016
977
|
ensureSlackDeviceIdentity: () => ensureSlackDeviceIdentity,
|
|
1017
978
|
findProjectProfileKey: () => findProjectProfileKey,
|
|
979
|
+
foldLegacyRolesIntoNotes: () => foldLegacyRolesIntoNotes,
|
|
1018
980
|
followUpBehavior: () => followUpBehavior,
|
|
1019
981
|
formatAccountProfilePlaybookLine: () => formatAccountProfilePlaybookLine,
|
|
1020
982
|
formatProjectProfilePlaybookLines: () => formatProjectProfilePlaybookLines,
|
|
983
|
+
formatViewerContextDirective: () => formatViewerContextDirective,
|
|
984
|
+
formatViewerContextReminder: () => formatViewerContextReminder,
|
|
1021
985
|
formatWorkspaceProfileSuffix: () => formatWorkspaceProfileSuffix,
|
|
1022
986
|
getAbleTimeAccessToken: () => getAbleTimeAccessToken,
|
|
1023
987
|
getAbleTimeHost: () => getAbleTimeHost,
|
|
@@ -1038,7 +1002,7 @@ __export(app_settings_exports, {
|
|
|
1038
1002
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
1039
1003
|
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
1040
1004
|
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
1041
|
-
|
|
1005
|
+
readViewerContext: () => readViewerContext,
|
|
1042
1006
|
resolveAccountProfile: () => resolveAccountProfile,
|
|
1043
1007
|
resolveAccountProfileFromSettings: () => resolveAccountProfileFromSettings,
|
|
1044
1008
|
resolveAgentExecutable: () => resolveAgentExecutable,
|
|
@@ -1048,7 +1012,6 @@ __export(app_settings_exports, {
|
|
|
1048
1012
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
1049
1013
|
resolveViewerProfile: () => resolveViewerProfile,
|
|
1050
1014
|
resolveViewerProfileForRepo: () => resolveViewerProfileForRepo,
|
|
1051
|
-
reviewTeamHintsForRoles: () => reviewTeamHintsForRoles,
|
|
1052
1015
|
saveAbleTimeConnection: () => saveAbleTimeConnection,
|
|
1053
1016
|
saveAppSettings: () => saveAppSettings,
|
|
1054
1017
|
saveLinearOAuth: () => saveLinearOAuth,
|
|
@@ -1065,7 +1028,8 @@ __export(app_settings_exports, {
|
|
|
1065
1028
|
updateDefaultsSettings: () => updateDefaultsSettings,
|
|
1066
1029
|
updateIntegrationsSettings: () => updateIntegrationsSettings,
|
|
1067
1030
|
updateOpencodeSettings: () => updateOpencodeSettings,
|
|
1068
|
-
updateProjectProfileSettings: () => updateProjectProfileSettings
|
|
1031
|
+
updateProjectProfileSettings: () => updateProjectProfileSettings,
|
|
1032
|
+
writeViewerContext: () => writeViewerContext
|
|
1069
1033
|
});
|
|
1070
1034
|
function emptyPublicIntegrations() {
|
|
1071
1035
|
return {
|
|
@@ -1323,9 +1287,7 @@ function normalizeDefaults(raw) {
|
|
|
1323
1287
|
if (typeof source.fast === "boolean") {
|
|
1324
1288
|
out.fast = source.fast;
|
|
1325
1289
|
}
|
|
1326
|
-
const
|
|
1327
|
-
if (roles.length) out.roles = roles;
|
|
1328
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
1290
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
1329
1291
|
if (notes) out.notes = notes;
|
|
1330
1292
|
return out;
|
|
1331
1293
|
}
|
|
@@ -1336,11 +1298,9 @@ function normalizeProjectProfile(raw) {
|
|
|
1336
1298
|
if (!raw || typeof raw !== "object") return void 0;
|
|
1337
1299
|
const source = raw;
|
|
1338
1300
|
const out = {};
|
|
1339
|
-
const
|
|
1340
|
-
if (roles.length) out.roles = roles;
|
|
1341
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
1301
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
1342
1302
|
if (notes) out.notes = notes;
|
|
1343
|
-
return out.
|
|
1303
|
+
return out.notes ? out : void 0;
|
|
1344
1304
|
}
|
|
1345
1305
|
function normalizeProjects(raw) {
|
|
1346
1306
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -1899,17 +1859,13 @@ function updateDefaultsSettings(patch) {
|
|
|
1899
1859
|
defaults.fast = Boolean(patch.fast);
|
|
1900
1860
|
}
|
|
1901
1861
|
}
|
|
1902
|
-
if ("roles" in patch) {
|
|
1903
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
1904
|
-
if (!roles.length) delete defaults.roles;
|
|
1905
|
-
else defaults.roles = roles;
|
|
1906
|
-
delete defaults.role;
|
|
1907
|
-
}
|
|
1908
1862
|
if ("notes" in patch) {
|
|
1909
1863
|
const notes = normalizeProfileNotes(patch.notes);
|
|
1910
1864
|
if (!notes) delete defaults.notes;
|
|
1911
1865
|
else defaults.notes = notes;
|
|
1912
1866
|
}
|
|
1867
|
+
delete defaults.roles;
|
|
1868
|
+
delete defaults.role;
|
|
1913
1869
|
return saveAppSettings({ ...current, defaults });
|
|
1914
1870
|
}
|
|
1915
1871
|
function updateProjectProfileSettings(repoPath, patch) {
|
|
@@ -1919,17 +1875,14 @@ function updateProjectProfileSettings(repoPath, patch) {
|
|
|
1919
1875
|
const projects = { ...current.projects };
|
|
1920
1876
|
const existing = projects[key] ?? {};
|
|
1921
1877
|
const next = { ...existing };
|
|
1922
|
-
if ("roles" in patch) {
|
|
1923
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
1924
|
-
if (!roles.length) delete next.roles;
|
|
1925
|
-
else next.roles = roles;
|
|
1926
|
-
}
|
|
1927
1878
|
if ("notes" in patch) {
|
|
1928
1879
|
const notes = normalizeProfileNotes(patch.notes);
|
|
1929
1880
|
if (!notes) delete next.notes;
|
|
1930
1881
|
else next.notes = notes;
|
|
1931
1882
|
}
|
|
1932
|
-
|
|
1883
|
+
delete next.roles;
|
|
1884
|
+
delete next.role;
|
|
1885
|
+
if (!next.notes) delete projects[key];
|
|
1933
1886
|
else projects[key] = next;
|
|
1934
1887
|
return saveAppSettings({ ...current, projects });
|
|
1935
1888
|
}
|
|
@@ -1954,6 +1907,60 @@ function resolveViewerProfileForRepo(repoPath, settings = loadAppSettings()) {
|
|
|
1954
1907
|
const project = key ? settings.projects[key] : void 0;
|
|
1955
1908
|
return resolveViewerProfile(settings.defaults, project);
|
|
1956
1909
|
}
|
|
1910
|
+
function readViewerContext(repoPath) {
|
|
1911
|
+
const settings = loadAppSettings();
|
|
1912
|
+
const key = findProjectProfileKey(settings.projects, repoPath);
|
|
1913
|
+
const resolved = resolveViewerProfile(
|
|
1914
|
+
settings.defaults,
|
|
1915
|
+
key ? settings.projects[key] : void 0
|
|
1916
|
+
);
|
|
1917
|
+
return {
|
|
1918
|
+
account: resolved.accountNotes,
|
|
1919
|
+
project: resolved.projectNotes,
|
|
1920
|
+
combined: resolved.notes,
|
|
1921
|
+
repoPath: key ?? (repoPath?.trim() ? profileRepoKey(repoPath) : null)
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
function writeViewerContext(input) {
|
|
1925
|
+
const proposed = normalizeProfileNotes(input.context);
|
|
1926
|
+
if (input.scope === "project") {
|
|
1927
|
+
const settings = loadAppSettings();
|
|
1928
|
+
const key = findProjectProfileKey(settings.projects, input.repoPath) ?? profileRepoKey(input.repoPath ?? "");
|
|
1929
|
+
if (!key) {
|
|
1930
|
+
return {
|
|
1931
|
+
ok: false,
|
|
1932
|
+
error: "repoPath required",
|
|
1933
|
+
message: "Pass repoPath (or run from a registered worktree) when updating project context.",
|
|
1934
|
+
current: "",
|
|
1935
|
+
proposed
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
const current2 = resolveViewerProfileForRepo(key, settings).projectNotes;
|
|
1939
|
+
if (!input.confirmed) {
|
|
1940
|
+
return {
|
|
1941
|
+
ok: false,
|
|
1942
|
+
error: "Confirmation required",
|
|
1943
|
+
message: CONFIRM_PROJECT,
|
|
1944
|
+
current: current2,
|
|
1945
|
+
proposed
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
updateProjectProfileSettings(key, { notes: proposed || null });
|
|
1949
|
+
return { ok: true, scope: "project", context: proposed, repoPath: key };
|
|
1950
|
+
}
|
|
1951
|
+
const current = resolveAccountProfileFromSettings().accountNotes;
|
|
1952
|
+
if (!input.confirmed) {
|
|
1953
|
+
return {
|
|
1954
|
+
ok: false,
|
|
1955
|
+
error: "Confirmation required",
|
|
1956
|
+
message: CONFIRM_ACCOUNT,
|
|
1957
|
+
current,
|
|
1958
|
+
proposed
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
updateDefaultsSettings({ notes: proposed || null });
|
|
1962
|
+
return { ok: true, scope: "account", context: proposed };
|
|
1963
|
+
}
|
|
1957
1964
|
function resolveThreadDefaults(settings = loadAppSettings()) {
|
|
1958
1965
|
return {
|
|
1959
1966
|
agent: getDefaultAgent(settings),
|
|
@@ -2300,7 +2307,7 @@ function childEnvWithAppSettings(extra) {
|
|
|
2300
2307
|
function harnessEnvKey(harness) {
|
|
2301
2308
|
return HARNESS_ENV_KEYS[harness];
|
|
2302
2309
|
}
|
|
2303
|
-
var import_node_crypto2, import_node_fs7, import_node_os3, import_node_path8, 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;
|
|
2310
|
+
var import_node_crypto2, import_node_fs7, import_node_os3, import_node_path8, 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;
|
|
2304
2311
|
var init_app_settings = __esm({
|
|
2305
2312
|
"src/store/app-settings.ts"() {
|
|
2306
2313
|
"use strict";
|
|
@@ -2351,6 +2358,8 @@ var init_app_settings = __esm({
|
|
|
2351
2358
|
projects: {},
|
|
2352
2359
|
advanced: {}
|
|
2353
2360
|
};
|
|
2361
|
+
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.";
|
|
2362
|
+
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.";
|
|
2354
2363
|
DEFAULT_CLI_BIN = {
|
|
2355
2364
|
claude: "claude",
|
|
2356
2365
|
codex: "codex",
|
|
@@ -4373,12 +4382,12 @@ var init_worktree_labels = __esm({
|
|
|
4373
4382
|
});
|
|
4374
4383
|
|
|
4375
4384
|
// src/git/gh-errors.ts
|
|
4376
|
-
function isGhRateLimitError(
|
|
4377
|
-
return /API rate limit (already )?exceeded/i.test(
|
|
4385
|
+
function isGhRateLimitError(text7) {
|
|
4386
|
+
return /API rate limit (already )?exceeded/i.test(text7) || /rate limit exceeded/i.test(text7);
|
|
4378
4387
|
}
|
|
4379
|
-
function isGhPrBodyTooLongError(
|
|
4388
|
+
function isGhPrBodyTooLongError(text7) {
|
|
4380
4389
|
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(
|
|
4381
|
-
|
|
4390
|
+
text7
|
|
4382
4391
|
);
|
|
4383
4392
|
}
|
|
4384
4393
|
function clampGithubPrBody(body, max = GITHUB_PR_BODY_SAFE_CHARS) {
|
|
@@ -4398,21 +4407,21 @@ function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
|
|
|
4398
4407
|
const hours = Math.ceil(mins / 60);
|
|
4399
4408
|
return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
4400
4409
|
}
|
|
4401
|
-
function omitGhBodyArg(
|
|
4402
|
-
return
|
|
4410
|
+
function omitGhBodyArg(text7) {
|
|
4411
|
+
return text7.replace(
|
|
4403
4412
|
/--body(?:-file)?(\s+|=)(?:'[^']*'|"[^"]*"|\S+)/g,
|
|
4404
4413
|
"--body <omitted>"
|
|
4405
4414
|
);
|
|
4406
4415
|
}
|
|
4407
|
-
function capGhErrorText(
|
|
4408
|
-
const t =
|
|
4416
|
+
function capGhErrorText(text7, max = 400) {
|
|
4417
|
+
const t = text7.trim();
|
|
4409
4418
|
if (t.length <= max) return t;
|
|
4410
4419
|
return `${t.slice(0, max - 1).trimEnd()}\u2026`;
|
|
4411
4420
|
}
|
|
4412
|
-
function extractGhErrorDetail(
|
|
4413
|
-
const trimmed = omitGhBodyArg(
|
|
4421
|
+
function extractGhErrorDetail(text7) {
|
|
4422
|
+
const trimmed = omitGhBodyArg(text7.trim());
|
|
4414
4423
|
if (!trimmed) return "";
|
|
4415
|
-
if (isGhPrBodyTooLongError(
|
|
4424
|
+
if (isGhPrBodyTooLongError(text7) || isGhPrBodyTooLongError(trimmed)) {
|
|
4416
4425
|
return "GraphQL: body is too long (GitHub PR body / request size limit).";
|
|
4417
4426
|
}
|
|
4418
4427
|
const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
|
|
@@ -4452,9 +4461,9 @@ function formatGhLandError(raw, opts) {
|
|
|
4452
4461
|
}
|
|
4453
4462
|
return detail || "Failed to create or update pull request";
|
|
4454
4463
|
}
|
|
4455
|
-
function isPrNotMergeableError(
|
|
4464
|
+
function isPrNotMergeableError(text7) {
|
|
4456
4465
|
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(
|
|
4457
|
-
|
|
4466
|
+
text7
|
|
4458
4467
|
);
|
|
4459
4468
|
}
|
|
4460
4469
|
function formatMergePrError(raw) {
|
|
@@ -4666,8 +4675,8 @@ var init_github_agent_auth = __esm({
|
|
|
4666
4675
|
});
|
|
4667
4676
|
|
|
4668
4677
|
// src/git/stale-lock.ts
|
|
4669
|
-
function isIndexLockError(
|
|
4670
|
-
return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(
|
|
4678
|
+
function isIndexLockError(text7) {
|
|
4679
|
+
return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(text7);
|
|
4671
4680
|
}
|
|
4672
4681
|
function clearStaleIndexLock(gitDir, maxAgeMs = STALE_INDEX_LOCK_MS, now = Date.now()) {
|
|
4673
4682
|
const lockPath = (0, import_node_path17.join)(gitDir, "index.lock");
|
|
@@ -6090,14 +6099,7 @@ async function listPrs(repoPath, opts) {
|
|
|
6090
6099
|
const limit = resolved.limit ?? 200;
|
|
6091
6100
|
const reviewerKey = (resolved.reviewer ?? "").trim().toLowerCase();
|
|
6092
6101
|
const needsUnclaimed = reviewerKey === "unassigned" || reviewerKey === "none" || reviewerKey === "null";
|
|
6093
|
-
|
|
6094
|
-
try {
|
|
6095
|
-
profile = resolveViewerProfileForRepo(repoPath);
|
|
6096
|
-
} catch {
|
|
6097
|
-
profile = resolveViewerProfile();
|
|
6098
|
-
}
|
|
6099
|
-
const fetchedTeams = needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : [];
|
|
6100
|
-
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? preferTeamsForRole(fetchedTeams, profile.reviewTeamHints) : void 0);
|
|
6102
|
+
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : void 0);
|
|
6101
6103
|
const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
|
|
6102
6104
|
const args = buildGhPrListArgs({
|
|
6103
6105
|
slug,
|
|
@@ -7430,16 +7432,15 @@ function accountRolePlaybookLine() {
|
|
|
7430
7432
|
return "";
|
|
7431
7433
|
}
|
|
7432
7434
|
}
|
|
7433
|
-
function
|
|
7435
|
+
function accountContextReminderLine() {
|
|
7434
7436
|
let profile;
|
|
7435
7437
|
try {
|
|
7436
7438
|
profile = resolveAccountProfileFromSettings();
|
|
7437
7439
|
} catch {
|
|
7438
7440
|
return "";
|
|
7439
7441
|
}
|
|
7440
|
-
if (
|
|
7441
|
-
|
|
7442
|
-
return `- Viewer profile: ${roles}. Use Settings notes with list_issues / list_prs when they ask to find work.`;
|
|
7442
|
+
if (!profile.accountNotes) return "";
|
|
7443
|
+
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.";
|
|
7443
7444
|
}
|
|
7444
7445
|
function projectProfilePlaybookBlock() {
|
|
7445
7446
|
let settings;
|
|
@@ -7456,7 +7457,6 @@ function projectProfilePlaybookBlock() {
|
|
|
7456
7457
|
const key = path2.replace(/\/+$/, "");
|
|
7457
7458
|
return {
|
|
7458
7459
|
name: names.get(key) ?? key.split("/").filter(Boolean).pop() ?? path2,
|
|
7459
|
-
roleLabels: resolved.rolesFromProject ? resolved.roleLabels : [],
|
|
7460
7460
|
notes: resolved.projectNotes
|
|
7461
7461
|
};
|
|
7462
7462
|
});
|
|
@@ -7470,7 +7470,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
7470
7470
|
`- YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit it.`,
|
|
7471
7471
|
goal ? `- Goal / title: ${goal}` : null,
|
|
7472
7472
|
accountDefaultsPlaybookLine(),
|
|
7473
|
-
|
|
7473
|
+
accountContextReminderLine() || null,
|
|
7474
7474
|
"- 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."
|
|
7475
7475
|
].filter(Boolean).join("\n");
|
|
7476
7476
|
}
|
|
@@ -7524,7 +7524,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
7524
7524
|
"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.",
|
|
7525
7525
|
"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).",
|
|
7526
7526
|
"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.",
|
|
7527
|
-
"Typical flow (find work): list_workspaces \u2192 pick repo(s) matching
|
|
7527
|
+
"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.",
|
|
7528
7528
|
'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".',
|
|
7529
7529
|
"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.",
|
|
7530
7530
|
"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."
|
|
@@ -7578,10 +7578,10 @@ var init_coordinator_prompt = __esm({
|
|
|
7578
7578
|
"Role: you oversee Sideboard worktree agents across registered repos. You do not live inside one of those worktrees.",
|
|
7579
7579
|
"Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
|
|
7580
7580
|
"Discover:",
|
|
7581
|
-
"- list_workspaces \u2014 registered repos (path + github slug +
|
|
7581
|
+
"- 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.",
|
|
7582
7582
|
"- 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.",
|
|
7583
|
-
'- 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.
|
|
7584
|
-
"- 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)
|
|
7583
|
+
'- 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).',
|
|
7584
|
+
"- 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.",
|
|
7585
7585
|
"- 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.",
|
|
7586
7586
|
"- 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.",
|
|
7587
7587
|
"- 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.",
|
|
@@ -7591,7 +7591,8 @@ var init_coordinator_prompt = __esm({
|
|
|
7591
7591
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
7592
7592
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
7593
7593
|
"- 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.",
|
|
7594
|
-
"- 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.",
|
|
7594
|
+
"- 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.",
|
|
7595
|
+
"- 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.",
|
|
7595
7596
|
"- 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.",
|
|
7596
7597
|
"- 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.",
|
|
7597
7598
|
"Workspaces:",
|
|
@@ -8126,8 +8127,8 @@ ${input.permalink}` : "";
|
|
|
8126
8127
|
|
|
8127
8128
|
${body}${link}`;
|
|
8128
8129
|
}
|
|
8129
|
-
function isSlackExternalReplyPrompt(
|
|
8130
|
-
return
|
|
8130
|
+
function isSlackExternalReplyPrompt(text7) {
|
|
8131
|
+
return text7.startsWith("Slack reply from ") && text7.includes("not a command");
|
|
8131
8132
|
}
|
|
8132
8133
|
function pendingSlackExternalReplies(messages) {
|
|
8133
8134
|
let i = messages.length - 1;
|
|
@@ -8168,9 +8169,9 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
8168
8169
|
function listSlackOutboundWatches() {
|
|
8169
8170
|
return pruneWatches(readStore3());
|
|
8170
8171
|
}
|
|
8171
|
-
function formatOwnerSlackFyi(userName,
|
|
8172
|
+
function formatOwnerSlackFyi(userName, text7) {
|
|
8172
8173
|
const who = userName.trim() || "Someone";
|
|
8173
|
-
const body =
|
|
8174
|
+
const body = text7.trim() || "(no text)";
|
|
8174
8175
|
return `${who} replied in Slack:
|
|
8175
8176
|
${body}`;
|
|
8176
8177
|
}
|
|
@@ -8185,7 +8186,7 @@ async function relayExternalReply(opts) {
|
|
|
8185
8186
|
const thread = readThread(threadId);
|
|
8186
8187
|
if (!thread || thread.status === "archived") return "ignored";
|
|
8187
8188
|
try {
|
|
8188
|
-
const
|
|
8189
|
+
const text7 = formatSlackExternalReplyPrompt({
|
|
8189
8190
|
userName: opts.reply.userName,
|
|
8190
8191
|
kind: opts.watch.kind,
|
|
8191
8192
|
toLabel: opts.watch.toLabel,
|
|
@@ -8194,7 +8195,7 @@ async function relayExternalReply(opts) {
|
|
|
8194
8195
|
});
|
|
8195
8196
|
appendMessage(threadId, {
|
|
8196
8197
|
role: "agent",
|
|
8197
|
-
text:
|
|
8198
|
+
text: text7,
|
|
8198
8199
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
8199
8200
|
});
|
|
8200
8201
|
} catch {
|
|
@@ -8508,20 +8509,20 @@ function looksLikeMinifiedJsDump(line) {
|
|
|
8508
8509
|
function looksLikeNestedElectronCrash(line) {
|
|
8509
8510
|
return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
|
|
8510
8511
|
}
|
|
8511
|
-
function looksLikeNativeEventLoopCrash(
|
|
8512
|
-
return /uv_run|uv__io_poll|SpinEventLoopInternal/i.test(
|
|
8512
|
+
function looksLikeNativeEventLoopCrash(text7) {
|
|
8513
|
+
return /uv_run|uv__io_poll|SpinEventLoopInternal/i.test(text7);
|
|
8513
8514
|
}
|
|
8514
|
-
function looksLikeV8Oom(
|
|
8515
|
+
function looksLikeV8Oom(text7) {
|
|
8515
8516
|
return /javascript heap|reached heap limit|OOMErrorHandler|FatalProcessOutOfMemory|Allocation failed - JavaScript heap/i.test(
|
|
8516
|
-
|
|
8517
|
+
text7
|
|
8517
8518
|
);
|
|
8518
8519
|
}
|
|
8519
|
-
function looksLikeHomebrewLibuvCrash(
|
|
8520
|
-
if (!looksLikeNativeEventLoopCrash(
|
|
8521
|
-
return /Cellar\/(?:libuv|node)|libuv\.\d+\.dylib/i.test(
|
|
8520
|
+
function looksLikeHomebrewLibuvCrash(text7) {
|
|
8521
|
+
if (!looksLikeNativeEventLoopCrash(text7)) return false;
|
|
8522
|
+
return /Cellar\/(?:libuv|node)|libuv\.\d+\.dylib/i.test(text7);
|
|
8522
8523
|
}
|
|
8523
|
-
function clipStderr(
|
|
8524
|
-
const trimmed =
|
|
8524
|
+
function clipStderr(text7, maxChars) {
|
|
8525
|
+
const trimmed = text7.trim();
|
|
8525
8526
|
if (trimmed.length <= maxChars) return trimmed;
|
|
8526
8527
|
return trimmed.slice(0, maxChars);
|
|
8527
8528
|
}
|
|
@@ -8555,16 +8556,16 @@ function summarizeTurnStderr(tail, maxChars = 500) {
|
|
|
8555
8556
|
const joined = (useful.length ? useful : tail).slice(-6).join("\n").trim();
|
|
8556
8557
|
return clipStderr(joined, maxChars);
|
|
8557
8558
|
}
|
|
8558
|
-
function looksLikeInvalidAgentSession(
|
|
8559
|
-
const lower =
|
|
8559
|
+
function looksLikeInvalidAgentSession(text7) {
|
|
8560
|
+
const lower = text7.trim().toLowerCase();
|
|
8560
8561
|
if (!lower) return false;
|
|
8561
8562
|
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);
|
|
8562
8563
|
}
|
|
8563
|
-
function looksLikeRetryableRunnerCrash(
|
|
8564
|
-
if (looksLikeAgentFailureMessage(
|
|
8565
|
-
if (looksLikeInvalidAgentSession(
|
|
8566
|
-
if (looksLikeV8Oom(
|
|
8567
|
-
const lower =
|
|
8564
|
+
function looksLikeRetryableRunnerCrash(text7) {
|
|
8565
|
+
if (looksLikeAgentFailureMessage(text7)) return false;
|
|
8566
|
+
if (looksLikeInvalidAgentSession(text7)) return false;
|
|
8567
|
+
if (looksLikeV8Oom(text7)) return false;
|
|
8568
|
+
const lower = text7.trim().toLowerCase();
|
|
8568
8569
|
if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
|
|
8569
8570
|
if (!lower) return true;
|
|
8570
8571
|
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(
|
|
@@ -8576,12 +8577,12 @@ function shouldRetryFailedAgentTurn(detail, opts) {
|
|
|
8576
8577
|
if (looksLikeV8Oom(detail) && opts.hasSession) return true;
|
|
8577
8578
|
return looksLikeRetryableRunnerCrash(detail);
|
|
8578
8579
|
}
|
|
8579
|
-
function looksLikeAgentFailureMessage(
|
|
8580
|
-
const lower =
|
|
8580
|
+
function looksLikeAgentFailureMessage(text7) {
|
|
8581
|
+
const lower = text7.trim().toLowerCase();
|
|
8581
8582
|
if (!lower) return false;
|
|
8582
8583
|
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(
|
|
8583
8584
|
lower
|
|
8584
|
-
) || /\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(
|
|
8585
|
+
) || /\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);
|
|
8585
8586
|
}
|
|
8586
8587
|
function fallbackTurnFailDetail(assistantText) {
|
|
8587
8588
|
const t = assistantText.trim();
|
|
@@ -9640,10 +9641,10 @@ var init_brightsy = __esm({
|
|
|
9640
9641
|
});
|
|
9641
9642
|
|
|
9642
9643
|
// src/agents/claude-mcp.ts
|
|
9643
|
-
function parseMcpList(
|
|
9644
|
+
function parseMcpList(text7) {
|
|
9644
9645
|
const servers = [];
|
|
9645
9646
|
const seen = /* @__PURE__ */ new Set();
|
|
9646
|
-
for (const raw of
|
|
9647
|
+
for (const raw of text7.split("\n")) {
|
|
9647
9648
|
const line = raw.trim();
|
|
9648
9649
|
if (!line || /^Checking MCP/i.test(line)) continue;
|
|
9649
9650
|
const m = line.match(/^(.+?):\s+\S+/);
|
|
@@ -9834,8 +9835,8 @@ function isSubagentToolName(name) {
|
|
|
9834
9835
|
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
9835
9836
|
return /connectedAgentRequest/i.test(n);
|
|
9836
9837
|
}
|
|
9837
|
-
function isInternalAgentStatusText(
|
|
9838
|
-
const t =
|
|
9838
|
+
function isInternalAgentStatusText(text7) {
|
|
9839
|
+
const t = text7.trim();
|
|
9839
9840
|
if (!t) return false;
|
|
9840
9841
|
if (/waiting for gate to pass/i.test(t)) return true;
|
|
9841
9842
|
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
@@ -9849,9 +9850,9 @@ function lastTextPart(parts, type) {
|
|
|
9849
9850
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
9850
9851
|
const p = parts[i];
|
|
9851
9852
|
if (p.type !== type) continue;
|
|
9852
|
-
const
|
|
9853
|
-
if (!
|
|
9854
|
-
return
|
|
9853
|
+
const text7 = p.text.trim();
|
|
9854
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
9855
|
+
return text7;
|
|
9855
9856
|
}
|
|
9856
9857
|
return "";
|
|
9857
9858
|
}
|
|
@@ -9940,7 +9941,7 @@ function liveActivitySummary(parts, opts) {
|
|
|
9940
9941
|
);
|
|
9941
9942
|
const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
|
|
9942
9943
|
const thinking = lastTextPart(parts, "thinking");
|
|
9943
|
-
const
|
|
9944
|
+
const text7 = lastTextPart(parts, "text");
|
|
9944
9945
|
if (runningSubs.length > 0) {
|
|
9945
9946
|
const heads = runningSubs.map(toolLabel);
|
|
9946
9947
|
const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
|
|
@@ -9950,7 +9951,7 @@ function liveActivitySummary(parts, opts) {
|
|
|
9950
9951
|
if (runningTop) return toolLabel(runningTop);
|
|
9951
9952
|
if (runningPoll) return toolLabel(runningPoll);
|
|
9952
9953
|
if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
|
|
9953
|
-
if (
|
|
9954
|
+
if (text7) return "Writing reply\u2026";
|
|
9954
9955
|
const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
|
|
9955
9956
|
if (last) {
|
|
9956
9957
|
const label = toolLabel(last);
|
|
@@ -9982,9 +9983,9 @@ function toolFilePath(input) {
|
|
|
9982
9983
|
if (!input) return void 0;
|
|
9983
9984
|
return str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
|
|
9984
9985
|
}
|
|
9985
|
-
function countLines(
|
|
9986
|
-
if (!
|
|
9987
|
-
return
|
|
9986
|
+
function countLines(text7) {
|
|
9987
|
+
if (!text7) return 0;
|
|
9988
|
+
return text7.split("\n").length;
|
|
9988
9989
|
}
|
|
9989
9990
|
function diffFromInput(input) {
|
|
9990
9991
|
if (!input) return {};
|
|
@@ -10209,9 +10210,9 @@ function partsToAssistantText(parts) {
|
|
|
10209
10210
|
(p) => p.type === "text" && !messagePartParentId(p)
|
|
10210
10211
|
).map((p) => p.text).join("").trim();
|
|
10211
10212
|
}
|
|
10212
|
-
function stripBrightsyNdjsonNoise(
|
|
10213
|
-
if (!
|
|
10214
|
-
let out =
|
|
10213
|
+
function stripBrightsyNdjsonNoise(text7) {
|
|
10214
|
+
if (!text7 || !text7.includes('"type"')) return text7;
|
|
10215
|
+
let out = text7;
|
|
10215
10216
|
out = out.replace(
|
|
10216
10217
|
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
10217
10218
|
""
|
|
@@ -10254,7 +10255,9 @@ var init_profile = __esm({
|
|
|
10254
10255
|
"present_plan",
|
|
10255
10256
|
"present_schema",
|
|
10256
10257
|
"present_files",
|
|
10257
|
-
"wait_for_job"
|
|
10258
|
+
"wait_for_job",
|
|
10259
|
+
"get_viewer_context",
|
|
10260
|
+
"update_viewer_context"
|
|
10258
10261
|
];
|
|
10259
10262
|
WORKTREE_GITHUB_MCP_TOOLS = [
|
|
10260
10263
|
"github_get_issue",
|
|
@@ -10567,8 +10570,8 @@ function isBrightsyConnected() {
|
|
|
10567
10570
|
return false;
|
|
10568
10571
|
}
|
|
10569
10572
|
}
|
|
10570
|
-
function promptMentionsBrightsy(
|
|
10571
|
-
return BRIGHTSY_WORD.test(
|
|
10573
|
+
function promptMentionsBrightsy(text7) {
|
|
10574
|
+
return BRIGHTSY_WORD.test(text7 ?? "");
|
|
10572
10575
|
}
|
|
10573
10576
|
function isBrightsyMcpToolName(name) {
|
|
10574
10577
|
const n = name.toLowerCase();
|
|
@@ -10847,7 +10850,9 @@ var init_injected_mcp = __esm({
|
|
|
10847
10850
|
"mcp__sideboard__present_files",
|
|
10848
10851
|
"mcp__sideboard__ask_user",
|
|
10849
10852
|
"mcp__sideboard__present_plan",
|
|
10850
|
-
"mcp__sideboard__wait_for_job"
|
|
10853
|
+
"mcp__sideboard__wait_for_job",
|
|
10854
|
+
"mcp__sideboard__get_viewer_context",
|
|
10855
|
+
"mcp__sideboard__update_viewer_context"
|
|
10851
10856
|
];
|
|
10852
10857
|
SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
10853
10858
|
SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
@@ -11184,9 +11189,9 @@ function compactMetadataFromClaude(obj) {
|
|
|
11184
11189
|
return { trigger, postTokens };
|
|
11185
11190
|
}
|
|
11186
11191
|
function parseIssuesJson(raw) {
|
|
11187
|
-
const
|
|
11188
|
-
const candidates = [
|
|
11189
|
-
const match =
|
|
11192
|
+
const text7 = raw.trim();
|
|
11193
|
+
const candidates = [text7];
|
|
11194
|
+
const match = text7.match(/\[[\s\S]*\]/);
|
|
11190
11195
|
if (match) candidates.push(match[0]);
|
|
11191
11196
|
for (const c of candidates) {
|
|
11192
11197
|
try {
|
|
@@ -11454,8 +11459,8 @@ var init_claude = __esm({
|
|
|
11454
11459
|
if (errorDetail) {
|
|
11455
11460
|
events.push({ type: "stderr", data: errorDetail });
|
|
11456
11461
|
} else {
|
|
11457
|
-
const
|
|
11458
|
-
if (typeof
|
|
11462
|
+
const text7 = obj.result;
|
|
11463
|
+
if (typeof text7 === "string" && text7) events.push({ type: "stdout", data: text7 });
|
|
11459
11464
|
}
|
|
11460
11465
|
const usage = usageFromClaude(obj.usage);
|
|
11461
11466
|
if (usage) {
|
|
@@ -11596,9 +11601,9 @@ function asObject2(value) {
|
|
|
11596
11601
|
function listMcpNamesFromJsonMap(raw, key) {
|
|
11597
11602
|
return Object.keys(asObject2(asObject2(raw)[key])).filter((n) => n.trim());
|
|
11598
11603
|
}
|
|
11599
|
-
function listMcpNamesFromCodexToml(
|
|
11604
|
+
function listMcpNamesFromCodexToml(text7) {
|
|
11600
11605
|
try {
|
|
11601
|
-
const parsed = (0, import_smol_toml2.parse)(
|
|
11606
|
+
const parsed = (0, import_smol_toml2.parse)(text7);
|
|
11602
11607
|
return Object.keys(asObject2(parsed.mcp_servers)).filter((n) => n.trim());
|
|
11603
11608
|
} catch {
|
|
11604
11609
|
return [];
|
|
@@ -11729,8 +11734,8 @@ function codexConfigHasNetworkAccess() {
|
|
|
11729
11734
|
];
|
|
11730
11735
|
for (const path2 of candidates) {
|
|
11731
11736
|
if (!(0, import_node_fs32.existsSync)(path2)) continue;
|
|
11732
|
-
const
|
|
11733
|
-
if (/network_access\s*=\s*true/.test(
|
|
11737
|
+
const text7 = (0, import_node_fs32.readFileSync)(path2, "utf8");
|
|
11738
|
+
if (/network_access\s*=\s*true/.test(text7)) return true;
|
|
11734
11739
|
}
|
|
11735
11740
|
return false;
|
|
11736
11741
|
}
|
|
@@ -11743,8 +11748,8 @@ function unwrapCodexMcpResult(result) {
|
|
|
11743
11748
|
const texts = [];
|
|
11744
11749
|
for (const item of rec.content) {
|
|
11745
11750
|
if (!item || typeof item !== "object") continue;
|
|
11746
|
-
const
|
|
11747
|
-
if (typeof
|
|
11751
|
+
const text7 = item.text;
|
|
11752
|
+
if (typeof text7 === "string") texts.push(text7);
|
|
11748
11753
|
}
|
|
11749
11754
|
if (texts.length) return texts.join("\n");
|
|
11750
11755
|
}
|
|
@@ -11986,8 +11991,8 @@ var init_codex = __esm({
|
|
|
11986
11991
|
if (msg) nested.push(msg);
|
|
11987
11992
|
}
|
|
11988
11993
|
}
|
|
11989
|
-
for (const
|
|
11990
|
-
events.push({ type: "stdout", data:
|
|
11994
|
+
for (const text7 of nested) {
|
|
11995
|
+
events.push({ type: "stdout", data: text7, parentId: item.id });
|
|
11991
11996
|
}
|
|
11992
11997
|
events.push({
|
|
11993
11998
|
type: "tool_result",
|
|
@@ -12738,8 +12743,8 @@ var init_opencode = __esm({
|
|
|
12738
12743
|
return { type: "session_id", data: sid };
|
|
12739
12744
|
}
|
|
12740
12745
|
if (obj.type === "text") {
|
|
12741
|
-
const
|
|
12742
|
-
if (
|
|
12746
|
+
const text7 = obj.part?.text ?? obj.text;
|
|
12747
|
+
if (text7) return { type: "stdout", data: text7 };
|
|
12743
12748
|
}
|
|
12744
12749
|
if (obj.type === "tool_use") {
|
|
12745
12750
|
const part = obj.part;
|
|
@@ -13018,8 +13023,8 @@ var init_list_models = __esm({
|
|
|
13018
13023
|
});
|
|
13019
13024
|
|
|
13020
13025
|
// src/agents/session-quota.ts
|
|
13021
|
-
function isSessionQuotaLimit(
|
|
13022
|
-
const lower =
|
|
13026
|
+
function isSessionQuotaLimit(text7) {
|
|
13027
|
+
const lower = text7.trim().toLowerCase();
|
|
13023
13028
|
if (!lower) return false;
|
|
13024
13029
|
if (/credit balance is too low|out of credits|insufficient.?quota|billing/.test(lower)) {
|
|
13025
13030
|
return false;
|
|
@@ -13027,10 +13032,10 @@ function isSessionQuotaLimit(text6) {
|
|
|
13027
13032
|
if (/prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower)) {
|
|
13028
13033
|
return false;
|
|
13029
13034
|
}
|
|
13030
|
-
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(
|
|
13035
|
+
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);
|
|
13031
13036
|
}
|
|
13032
|
-
function parseSessionQuotaResetAt(
|
|
13033
|
-
const absolute =
|
|
13037
|
+
function parseSessionQuotaResetAt(text7, now = /* @__PURE__ */ new Date()) {
|
|
13038
|
+
const absolute = text7.match(
|
|
13034
13039
|
/resets\s+(?:at\s+)?(\d{1,2}):(\d{2})\s*(am|pm)(?:\s*\(([^)]+)\))?/i
|
|
13035
13040
|
);
|
|
13036
13041
|
if (absolute) {
|
|
@@ -13048,7 +13053,7 @@ function parseSessionQuotaResetAt(text6, now = /* @__PURE__ */ new Date()) {
|
|
|
13048
13053
|
}
|
|
13049
13054
|
return at;
|
|
13050
13055
|
}
|
|
13051
|
-
const relative =
|
|
13056
|
+
const relative = text7.match(
|
|
13052
13057
|
/resets\s+in\s+(\d+)\s*(minutes?|hours?|days?)/i
|
|
13053
13058
|
);
|
|
13054
13059
|
if (relative) {
|
|
@@ -13629,8 +13634,8 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
13629
13634
|
onEvent({ type: "exit", data: exitCode });
|
|
13630
13635
|
const finalized = finalizeParts(parts);
|
|
13631
13636
|
const rawText = assistantText.trim() || partsToAssistantText(finalized);
|
|
13632
|
-
const
|
|
13633
|
-
return { exitCode, sessionId, assistantText:
|
|
13637
|
+
const text7 = thread.agent === "brightsy" ? stripBrightsyNdjsonNoise(rawText) : rawText;
|
|
13638
|
+
return { exitCode, sessionId, assistantText: text7, parts: finalized, usage };
|
|
13634
13639
|
});
|
|
13635
13640
|
return {
|
|
13636
13641
|
pid: child.pid,
|
|
@@ -13909,9 +13914,9 @@ async function tryClaudeSummary(transcript, opts) {
|
|
|
13909
13914
|
{ cwd: opts?.cwd, reject: false }
|
|
13910
13915
|
);
|
|
13911
13916
|
if (exitCode !== 0) return null;
|
|
13912
|
-
const
|
|
13913
|
-
if (
|
|
13914
|
-
return
|
|
13917
|
+
const text7 = stdout.trim();
|
|
13918
|
+
if (text7.length < 40) return null;
|
|
13919
|
+
return text7;
|
|
13915
13920
|
} catch {
|
|
13916
13921
|
return null;
|
|
13917
13922
|
}
|
|
@@ -13960,9 +13965,9 @@ function extractiveSummary(transcript) {
|
|
|
13960
13965
|
);
|
|
13961
13966
|
return parts.join("\n");
|
|
13962
13967
|
}
|
|
13963
|
-
function clipSummary(
|
|
13964
|
-
if (
|
|
13965
|
-
return `${
|
|
13968
|
+
function clipSummary(text7) {
|
|
13969
|
+
if (text7.length <= MAX_SUMMARY_CHARS) return text7;
|
|
13970
|
+
return `${text7.slice(0, MAX_SUMMARY_CHARS)}
|
|
13966
13971
|
|
|
13967
13972
|
[\u2026summary truncated\u2026]`;
|
|
13968
13973
|
}
|
|
@@ -14116,8 +14121,8 @@ function findLastBrightsyContextSummary(messages) {
|
|
|
14116
14121
|
continue;
|
|
14117
14122
|
}
|
|
14118
14123
|
if (part.status === "error") continue;
|
|
14119
|
-
const
|
|
14120
|
-
if (
|
|
14124
|
+
const text7 = extractBrightsyContextSummary(part.result);
|
|
14125
|
+
if (text7) return { index: i, text: text7 };
|
|
14121
14126
|
}
|
|
14122
14127
|
}
|
|
14123
14128
|
return null;
|
|
@@ -15409,8 +15414,8 @@ function listRunningDetachedJobs(worktreePath) {
|
|
|
15409
15414
|
}
|
|
15410
15415
|
return running.sort();
|
|
15411
15416
|
}
|
|
15412
|
-
function looksLikeDeferredDonePromise(
|
|
15413
|
-
const t = (
|
|
15417
|
+
function looksLikeDeferredDonePromise(text7) {
|
|
15418
|
+
const t = (text7 ?? "").trim();
|
|
15414
15419
|
if (!t) return false;
|
|
15415
15420
|
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);
|
|
15416
15421
|
}
|
|
@@ -16156,11 +16161,11 @@ function unwrapToolResult(result) {
|
|
|
16156
16161
|
return typeof item.text === "string" ? item.text : "";
|
|
16157
16162
|
}).filter(Boolean);
|
|
16158
16163
|
if (texts.length === 1) {
|
|
16159
|
-
const
|
|
16164
|
+
const text7 = texts[0];
|
|
16160
16165
|
try {
|
|
16161
|
-
return JSON.parse(
|
|
16166
|
+
return JSON.parse(text7);
|
|
16162
16167
|
} catch {
|
|
16163
|
-
return
|
|
16168
|
+
return text7;
|
|
16164
16169
|
}
|
|
16165
16170
|
}
|
|
16166
16171
|
if (texts.length > 1) return texts.join("\n");
|
|
@@ -16922,8 +16927,8 @@ function summarizeTurnLive(parts) {
|
|
|
16922
16927
|
const interesting = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? lastTool;
|
|
16923
16928
|
const lastToolLabel = interesting ? interesting.description || toolDescription(interesting.name, interesting.input) || interesting.name : void 0;
|
|
16924
16929
|
const thinking = lastText(parts, "thinking");
|
|
16925
|
-
const
|
|
16926
|
-
const excerptRaw = thinking ||
|
|
16930
|
+
const text7 = lastText(parts, "text");
|
|
16931
|
+
const excerptRaw = thinking || text7;
|
|
16927
16932
|
const excerpt = excerptRaw.length > 280 ? `${excerptRaw.slice(-280)}` : excerptRaw || void 0;
|
|
16928
16933
|
const summary = liveActivitySummary(parts);
|
|
16929
16934
|
return {
|
|
@@ -17162,8 +17167,8 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
17162
17167
|
);
|
|
17163
17168
|
const recent = from.messages.slice(-8).map((m) => {
|
|
17164
17169
|
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
17165
|
-
const
|
|
17166
|
-
return
|
|
17170
|
+
const text7 = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
17171
|
+
return text7 ? `- ${role}: ${text7}` : null;
|
|
17167
17172
|
}).filter(Boolean);
|
|
17168
17173
|
const body = [
|
|
17169
17174
|
`# Orchestration handoff`,
|
|
@@ -17260,13 +17265,13 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
17260
17265
|
for (const hash of hashes) {
|
|
17261
17266
|
const agentsFile = (0, import_node_path41.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
17262
17267
|
if (!(0, import_node_fs43.existsSync)(agentsFile)) continue;
|
|
17263
|
-
let
|
|
17268
|
+
let text7;
|
|
17264
17269
|
try {
|
|
17265
|
-
|
|
17270
|
+
text7 = (0, import_node_fs43.readFileSync)(agentsFile, "utf8");
|
|
17266
17271
|
} catch {
|
|
17267
17272
|
continue;
|
|
17268
17273
|
}
|
|
17269
|
-
for (const line of
|
|
17274
|
+
for (const line of text7.split("\n")) {
|
|
17270
17275
|
const trimmed = line.trim();
|
|
17271
17276
|
if (!trimmed) continue;
|
|
17272
17277
|
try {
|
|
@@ -18075,13 +18080,13 @@ function formatStat(files) {
|
|
|
18075
18080
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
18076
18081
|
}
|
|
18077
18082
|
function emptyScopeStats() {
|
|
18078
|
-
const
|
|
18083
|
+
const z8 = emptyStat();
|
|
18079
18084
|
return {
|
|
18080
|
-
commits:
|
|
18081
|
-
uncommitted:
|
|
18082
|
-
staged:
|
|
18083
|
-
unstaged:
|
|
18084
|
-
last_turn:
|
|
18085
|
+
commits: z8,
|
|
18086
|
+
uncommitted: z8,
|
|
18087
|
+
staged: z8,
|
|
18088
|
+
unstaged: z8,
|
|
18089
|
+
last_turn: z8
|
|
18085
18090
|
};
|
|
18086
18091
|
}
|
|
18087
18092
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -19455,12 +19460,12 @@ function firstString2(record, keys) {
|
|
|
19455
19460
|
return "";
|
|
19456
19461
|
}
|
|
19457
19462
|
async function readJson(res) {
|
|
19458
|
-
const
|
|
19459
|
-
if (!
|
|
19463
|
+
const text7 = await res.text();
|
|
19464
|
+
if (!text7.trim()) return null;
|
|
19460
19465
|
try {
|
|
19461
|
-
return JSON.parse(
|
|
19466
|
+
return JSON.parse(text7);
|
|
19462
19467
|
} catch {
|
|
19463
|
-
return
|
|
19468
|
+
return text7;
|
|
19464
19469
|
}
|
|
19465
19470
|
}
|
|
19466
19471
|
async function authorizedGet(url, token) {
|
|
@@ -19710,11 +19715,11 @@ function resolvePlanMarkdown(opts) {
|
|
|
19710
19715
|
source: "exit_plan"
|
|
19711
19716
|
};
|
|
19712
19717
|
}
|
|
19713
|
-
const
|
|
19714
|
-
if (
|
|
19718
|
+
const text7 = opts.text?.trim();
|
|
19719
|
+
if (text7 && text7.length >= 80) {
|
|
19715
19720
|
return {
|
|
19716
19721
|
title: "Plan",
|
|
19717
|
-
content:
|
|
19722
|
+
content: text7,
|
|
19718
19723
|
path: PLAN_FILE_REL,
|
|
19719
19724
|
source: "text"
|
|
19720
19725
|
};
|
|
@@ -20593,11 +20598,11 @@ var init_orchestrator = __esm({
|
|
|
20593
20598
|
return results;
|
|
20594
20599
|
}
|
|
20595
20600
|
/** Edit the text of a not-yet-started queued message. */
|
|
20596
|
-
async editQueuedMessage(threadRef, index,
|
|
20601
|
+
async editQueuedMessage(threadRef, index, text7) {
|
|
20597
20602
|
const thread = this.requireThread(threadRef);
|
|
20598
20603
|
return withThreadLock(thread.id, async () => {
|
|
20599
20604
|
const current = this.requireThread(thread.id);
|
|
20600
|
-
const trimmed =
|
|
20605
|
+
const trimmed = text7.trim();
|
|
20601
20606
|
if (!trimmed || index < 0 || index >= current.queue.length) {
|
|
20602
20607
|
return current;
|
|
20603
20608
|
}
|
|
@@ -20783,6 +20788,7 @@ var init_orchestrator = __esm({
|
|
|
20783
20788
|
ticketId: issueTicket?.id,
|
|
20784
20789
|
ticketProvider: issueTicket?.provider
|
|
20785
20790
|
}) : null;
|
|
20791
|
+
const viewerContextReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatViewerContextReminder() : null;
|
|
20786
20792
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
20787
20793
|
pendingSlackExternalReplies(thread.messages)
|
|
20788
20794
|
);
|
|
@@ -20792,6 +20798,7 @@ var init_orchestrator = __esm({
|
|
|
20792
20798
|
worktreeReminder,
|
|
20793
20799
|
optionalServicesReminder,
|
|
20794
20800
|
issueToolsReminder,
|
|
20801
|
+
viewerContextReminder,
|
|
20795
20802
|
artifactReminder,
|
|
20796
20803
|
longRunningReminder,
|
|
20797
20804
|
slackReplyContext,
|
|
@@ -20832,6 +20839,18 @@ var init_orchestrator = __esm({
|
|
|
20832
20839
|
ticketId: freshIssueTicket?.id,
|
|
20833
20840
|
ticketProvider: freshIssueTicket?.provider
|
|
20834
20841
|
});
|
|
20842
|
+
let viewerContextDirective = null;
|
|
20843
|
+
if (!isBrightsy && !isOrchestration) {
|
|
20844
|
+
try {
|
|
20845
|
+
viewerContextDirective = formatViewerContextDirective(
|
|
20846
|
+
resolveViewerProfileForRepo(fresh.repoPath || fresh.worktreePath)
|
|
20847
|
+
);
|
|
20848
|
+
} catch {
|
|
20849
|
+
viewerContextDirective = formatViewerContextDirective(
|
|
20850
|
+
resolveViewerProfileForRepo()
|
|
20851
|
+
);
|
|
20852
|
+
}
|
|
20853
|
+
}
|
|
20835
20854
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
20836
20855
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
20837
20856
|
customPrompt: settings?.prompts?.renameBranch
|
|
@@ -20863,6 +20882,7 @@ var init_orchestrator = __esm({
|
|
|
20863
20882
|
worktreeDirective,
|
|
20864
20883
|
optionalServicesDirective,
|
|
20865
20884
|
issueToolsDirective,
|
|
20885
|
+
viewerContextDirective,
|
|
20866
20886
|
artifactDirective,
|
|
20867
20887
|
longRunningDirective,
|
|
20868
20888
|
renameBranchDirective,
|
|
@@ -20973,6 +20993,7 @@ var init_orchestrator = __esm({
|
|
|
20973
20993
|
worktreeDirective,
|
|
20974
20994
|
optionalServicesDirective,
|
|
20975
20995
|
issueToolsDirective,
|
|
20996
|
+
viewerContextDirective,
|
|
20976
20997
|
artifactDirective,
|
|
20977
20998
|
longRunningDirective,
|
|
20978
20999
|
renameBranchDirective,
|
|
@@ -21507,13 +21528,13 @@ var init_orchestrator = __esm({
|
|
|
21507
21528
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
21508
21529
|
const lastError = thread.lastError ?? null;
|
|
21509
21530
|
const rawText = (lastAgent?.text ?? "").trim();
|
|
21510
|
-
const
|
|
21531
|
+
const text7 = (rawText && !isInternalAgentStatusText(rawText) ? rawText : "") || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
|
|
21511
21532
|
const stillRunning = this.threadLooksLive(thread);
|
|
21512
21533
|
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
21513
21534
|
const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
|
|
21514
21535
|
const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
|
|
21515
21536
|
return {
|
|
21516
|
-
text:
|
|
21537
|
+
text: text7,
|
|
21517
21538
|
status: thread.status,
|
|
21518
21539
|
sessionId: thread.sessionId,
|
|
21519
21540
|
lastError,
|
|
@@ -22280,10 +22301,6 @@ var init_schedule_runner = __esm({
|
|
|
22280
22301
|
var index_exports = {};
|
|
22281
22302
|
__export(index_exports, {
|
|
22282
22303
|
ABLETIME_MCP_PATH: () => ABLETIME_MCP_PATH,
|
|
22283
|
-
ACCOUNT_ROLES: () => ACCOUNT_ROLES,
|
|
22284
|
-
ACCOUNT_ROLE_LABELS: () => ACCOUNT_ROLE_LABELS,
|
|
22285
|
-
ACCOUNT_ROLE_MAX: () => ACCOUNT_ROLE_MAX,
|
|
22286
|
-
ACCOUNT_ROLE_PRESETS: () => ACCOUNT_ROLE_PRESETS,
|
|
22287
22304
|
AGENT_GIT_ACTIONS: () => AGENT_GIT_ACTIONS,
|
|
22288
22305
|
AGENT_RUNNER_MAX_OLD_SPACE_MB: () => AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
22289
22306
|
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
@@ -22370,7 +22387,6 @@ __export(index_exports, {
|
|
|
22370
22387
|
WORKTREE_MCP_TOOLS: () => WORKTREE_MCP_TOOLS,
|
|
22371
22388
|
abletimeMcpRequest: () => abletimeMcpRequest,
|
|
22372
22389
|
abletimeMcpUrl: () => abletimeMcpUrl,
|
|
22373
|
-
accountRoleLabel: () => accountRoleLabel,
|
|
22374
22390
|
ackSlackInboundSeen: () => ackSlackInboundSeen,
|
|
22375
22391
|
addBoardPin: () => addBoardPin,
|
|
22376
22392
|
addPrStackLayer: () => addPrStackLayer,
|
|
@@ -22551,6 +22567,7 @@ __export(index_exports, {
|
|
|
22551
22567
|
fireSchedule: () => fireSchedule,
|
|
22552
22568
|
flattenTurnInput: () => flattenTurnInput,
|
|
22553
22569
|
flipLinearRelationType: () => flipLinearRelationType,
|
|
22570
|
+
foldLegacyRolesIntoNotes: () => foldLegacyRolesIntoNotes,
|
|
22554
22571
|
followUpBehavior: () => followUpBehavior,
|
|
22555
22572
|
forkChatTab: () => forkChatTab,
|
|
22556
22573
|
forkMessageSlice: () => forkMessageSlice,
|
|
@@ -22591,6 +22608,8 @@ __export(index_exports, {
|
|
|
22591
22608
|
formatSlackWorkingText: () => formatSlackWorkingText,
|
|
22592
22609
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
22593
22610
|
formatUiReminder: () => formatUiReminder,
|
|
22611
|
+
formatViewerContextDirective: () => formatViewerContextDirective,
|
|
22612
|
+
formatViewerContextReminder: () => formatViewerContextReminder,
|
|
22594
22613
|
formatWorkspaceInventory: () => formatWorkspaceInventory,
|
|
22595
22614
|
formatWorkspaceProfileSuffix: () => formatWorkspaceProfileSuffix,
|
|
22596
22615
|
formatWorktreeDirective: () => formatWorktreeDirective,
|
|
@@ -22828,7 +22847,6 @@ __export(index_exports, {
|
|
|
22828
22847
|
planQuestionsSignature: () => planQuestionsSignature,
|
|
22829
22848
|
pollSlackOutboundWatches: () => pollSlackOutboundWatches,
|
|
22830
22849
|
posixShellSingleQuote: () => posixShellSingleQuote,
|
|
22831
|
-
preferTeamsForRole: () => preferTeamsForRole,
|
|
22832
22850
|
preferredCursorCostCents: () => preferredCursorCostCents,
|
|
22833
22851
|
prepareTerminalCommand: () => prepareTerminalCommand,
|
|
22834
22852
|
previewLand: () => previewLand,
|
|
@@ -22840,6 +22858,7 @@ __export(index_exports, {
|
|
|
22840
22858
|
readSetupLog: () => readSetupLog,
|
|
22841
22859
|
readSkillBody: () => readSkillBody,
|
|
22842
22860
|
readThread: () => readThread,
|
|
22861
|
+
readViewerContext: () => readViewerContext,
|
|
22843
22862
|
readWorktreeFile: () => readWorktreeFile,
|
|
22844
22863
|
readWorktreeFileForUpload: () => readWorktreeFileForUpload,
|
|
22845
22864
|
readWorktreeInclude: () => readWorktreeInclude,
|
|
@@ -22896,7 +22915,6 @@ __export(index_exports, {
|
|
|
22896
22915
|
resolveViewerProfile: () => resolveViewerProfile,
|
|
22897
22916
|
resolveViewerProfileForRepo: () => resolveViewerProfileForRepo,
|
|
22898
22917
|
resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
|
|
22899
|
-
reviewTeamHintsForRoles: () => reviewTeamHintsForRoles,
|
|
22900
22918
|
rewriteAbleTimeError: () => rewriteAbleTimeError,
|
|
22901
22919
|
rewriteLinearError: () => rewriteLinearError,
|
|
22902
22920
|
run: () => run,
|
|
@@ -23030,6 +23048,7 @@ __export(index_exports, {
|
|
|
23030
23048
|
writeInjectedMcpConfig: () => writeInjectedMcpConfig,
|
|
23031
23049
|
writePlanFile: () => writePlanFile,
|
|
23032
23050
|
writeThread: () => writeThread,
|
|
23051
|
+
writeViewerContext: () => writeViewerContext,
|
|
23033
23052
|
writeWorktreeFile: () => writeWorktreeFile
|
|
23034
23053
|
});
|
|
23035
23054
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -23078,9 +23097,9 @@ async function getGitHubStatus() {
|
|
|
23078
23097
|
};
|
|
23079
23098
|
}
|
|
23080
23099
|
const status = await run("gh", ["auth", "status"], { reject: false });
|
|
23081
|
-
const
|
|
23100
|
+
const text7 = `${status.stdout}
|
|
23082
23101
|
${status.stderr}`;
|
|
23083
|
-
const loginMatch =
|
|
23102
|
+
const loginMatch = text7.match(/Logged in to ([^\s]+) account (\S+)/i) ?? text7.match(/Logged in to ([^\s]+) as (\S+)/i);
|
|
23084
23103
|
if (loginMatch) {
|
|
23085
23104
|
return {
|
|
23086
23105
|
connected: true,
|
|
@@ -24446,11 +24465,11 @@ function fenceLanguage(path2, language) {
|
|
|
24446
24465
|
}
|
|
24447
24466
|
function buildCodeRefAttachment(input) {
|
|
24448
24467
|
const path2 = input.path.trim();
|
|
24449
|
-
const
|
|
24468
|
+
const text7 = input.text.replace(/\n$/, "");
|
|
24450
24469
|
if (!path2) {
|
|
24451
24470
|
throw new Error("code reference requires a file path");
|
|
24452
24471
|
}
|
|
24453
|
-
if (!
|
|
24472
|
+
if (!text7.trim()) {
|
|
24454
24473
|
throw new Error("code reference requires selected text");
|
|
24455
24474
|
}
|
|
24456
24475
|
if (input.startLine < 1 || input.endLine < 1 || input.endLine < input.startLine) {
|
|
@@ -24463,7 +24482,7 @@ function buildCodeRefAttachment(input) {
|
|
|
24463
24482
|
`Referenced code from \`${path2}\` (${range}).`,
|
|
24464
24483
|
"",
|
|
24465
24484
|
fence,
|
|
24466
|
-
|
|
24485
|
+
text7,
|
|
24467
24486
|
"```"
|
|
24468
24487
|
].join("\n");
|
|
24469
24488
|
return {
|
|
@@ -24520,16 +24539,16 @@ var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
|
24520
24539
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
24521
24540
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
24522
24541
|
var PASTED_NAME_ALT_RE = /^pasted-(\d+)\.txt$/i;
|
|
24523
|
-
function pastedTextStats(
|
|
24524
|
-
const chars =
|
|
24542
|
+
function pastedTextStats(text7) {
|
|
24543
|
+
const chars = text7.length;
|
|
24525
24544
|
if (chars === 0) return { chars: 0, lines: 0 };
|
|
24526
|
-
const lines =
|
|
24545
|
+
const lines = text7.split(/\r\n|\r|\n/).length;
|
|
24527
24546
|
return { chars, lines };
|
|
24528
24547
|
}
|
|
24529
|
-
function shouldAttachPastedText(
|
|
24530
|
-
const trimmed =
|
|
24548
|
+
function shouldAttachPastedText(text7) {
|
|
24549
|
+
const trimmed = text7.trim();
|
|
24531
24550
|
if (!trimmed) return false;
|
|
24532
|
-
const { chars, lines } = pastedTextStats(
|
|
24551
|
+
const { chars, lines } = pastedTextStats(text7);
|
|
24533
24552
|
return chars >= PASTE_ATTACH_MIN_CHARS || lines >= PASTE_ATTACH_MIN_LINES;
|
|
24534
24553
|
}
|
|
24535
24554
|
function nextPastedTextName(existing) {
|
|
@@ -24540,13 +24559,13 @@ function nextPastedTextName(existing) {
|
|
|
24540
24559
|
}
|
|
24541
24560
|
return `Pasted text #${max + 1}.txt`;
|
|
24542
24561
|
}
|
|
24543
|
-
function buildPastedTextAttachment(
|
|
24562
|
+
function buildPastedTextAttachment(text7, opts) {
|
|
24544
24563
|
return {
|
|
24545
24564
|
id: opts?.id ?? (0, import_node_crypto11.randomUUID)(),
|
|
24546
24565
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
24547
24566
|
kind: "file",
|
|
24548
24567
|
path: opts?.path,
|
|
24549
|
-
content:
|
|
24568
|
+
content: text7
|
|
24550
24569
|
};
|
|
24551
24570
|
}
|
|
24552
24571
|
|
|
@@ -24646,8 +24665,8 @@ function latestPendingPlanQuestions(input) {
|
|
|
24646
24665
|
return null;
|
|
24647
24666
|
}
|
|
24648
24667
|
var PLAN_QUESTION_ANSWERS_PREFIX = "Answers to your questions:";
|
|
24649
|
-
function isPlanQuestionAnswersMessage(
|
|
24650
|
-
return
|
|
24668
|
+
function isPlanQuestionAnswersMessage(text7) {
|
|
24669
|
+
return text7.startsWith(PLAN_QUESTION_ANSWERS_PREFIX);
|
|
24651
24670
|
}
|
|
24652
24671
|
function formatPlanQuestionAnswers(questions, answers) {
|
|
24653
24672
|
const lines = [PLAN_QUESTION_ANSWERS_PREFIX, ""];
|
|
@@ -24694,7 +24713,7 @@ init_orphan_cleanup();
|
|
|
24694
24713
|
// src/mcp/server.ts
|
|
24695
24714
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
24696
24715
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
24697
|
-
var
|
|
24716
|
+
var import_zod7 = require("zod");
|
|
24698
24717
|
var import_node_crypto13 = require("crypto");
|
|
24699
24718
|
var import_node_path50 = require("path");
|
|
24700
24719
|
init_orchestrator();
|
|
@@ -24723,9 +24742,9 @@ init_message_parts();
|
|
|
24723
24742
|
function lastMessagePreview(messages, max = 160) {
|
|
24724
24743
|
if (!messages?.length) return null;
|
|
24725
24744
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
24726
|
-
const
|
|
24727
|
-
if (!
|
|
24728
|
-
const flat =
|
|
24745
|
+
const text7 = messages[i]?.text?.trim();
|
|
24746
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
24747
|
+
const flat = text7.replace(/\s+/g, " ");
|
|
24729
24748
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
24730
24749
|
}
|
|
24731
24750
|
return null;
|
|
@@ -24959,8 +24978,8 @@ function labelGithubUrl(url) {
|
|
|
24959
24978
|
}
|
|
24960
24979
|
return { kind: "other", label: "GitHub", url: raw };
|
|
24961
24980
|
}
|
|
24962
|
-
function appendGithubLink(
|
|
24963
|
-
const body =
|
|
24981
|
+
function appendGithubLink(text7, githubUrl) {
|
|
24982
|
+
const body = text7.trimEnd();
|
|
24964
24983
|
if (!githubUrl?.trim()) return body;
|
|
24965
24984
|
const labeled = labelGithubUrl(githubUrl);
|
|
24966
24985
|
if (!labeled) {
|
|
@@ -25656,6 +25675,104 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
25656
25675
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
25657
25676
|
}
|
|
25658
25677
|
|
|
25678
|
+
// src/mcp/viewer-context-tools.ts
|
|
25679
|
+
var import_zod5 = require("zod");
|
|
25680
|
+
init_worktree();
|
|
25681
|
+
init_app_settings();
|
|
25682
|
+
init_workspaces();
|
|
25683
|
+
function text5(payload, isError = false) {
|
|
25684
|
+
return mcpJson(payload, isError);
|
|
25685
|
+
}
|
|
25686
|
+
function matchRegisteredWorkspace(repoPath) {
|
|
25687
|
+
const hint = (repoPath ?? "").trim().replace(/\/+$/, "");
|
|
25688
|
+
if (!hint) return null;
|
|
25689
|
+
const match = listWorkspaces().find((w) => {
|
|
25690
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
25691
|
+
return stored === hint || hint.startsWith(`${stored}/`) || stored.startsWith(`${hint}/`);
|
|
25692
|
+
});
|
|
25693
|
+
return match?.path.replace(/\/+$/, "") ?? null;
|
|
25694
|
+
}
|
|
25695
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
25696
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
25697
|
+
let root = hint;
|
|
25698
|
+
try {
|
|
25699
|
+
root = await resolveRepoRoot(hint);
|
|
25700
|
+
} catch {
|
|
25701
|
+
root = hint.replace(/\/+$/, "");
|
|
25702
|
+
}
|
|
25703
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
25704
|
+
}
|
|
25705
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_workspaces. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
25706
|
+
function registerViewerContextTools(server) {
|
|
25707
|
+
server.tool(
|
|
25708
|
+
"get_viewer_context",
|
|
25709
|
+
"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).",
|
|
25710
|
+
{
|
|
25711
|
+
repoPath: import_zod5.z.string().optional().describe(
|
|
25712
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
25713
|
+
)
|
|
25714
|
+
},
|
|
25715
|
+
async ({ repoPath }) => {
|
|
25716
|
+
try {
|
|
25717
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
25718
|
+
return text5(readViewerContext(path2 ?? void 0));
|
|
25719
|
+
} catch (err) {
|
|
25720
|
+
return text5(
|
|
25721
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
25722
|
+
true
|
|
25723
|
+
);
|
|
25724
|
+
}
|
|
25725
|
+
}
|
|
25726
|
+
);
|
|
25727
|
+
server.tool(
|
|
25728
|
+
"update_viewer_context",
|
|
25729
|
+
"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.",
|
|
25730
|
+
{
|
|
25731
|
+
scope: import_zod5.z.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
25732
|
+
context: import_zod5.z.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
25733
|
+
confirmed: import_zod5.z.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
25734
|
+
repoPath: import_zod5.z.string().optional().describe(
|
|
25735
|
+
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
25736
|
+
)
|
|
25737
|
+
},
|
|
25738
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
25739
|
+
try {
|
|
25740
|
+
if (scope === "project") {
|
|
25741
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
25742
|
+
if (!path2) {
|
|
25743
|
+
return text5(
|
|
25744
|
+
{
|
|
25745
|
+
ok: false,
|
|
25746
|
+
error: "repoPath required",
|
|
25747
|
+
message: PROJECT_PATH_HINT
|
|
25748
|
+
},
|
|
25749
|
+
true
|
|
25750
|
+
);
|
|
25751
|
+
}
|
|
25752
|
+
const result2 = writeViewerContext({
|
|
25753
|
+
scope,
|
|
25754
|
+
context,
|
|
25755
|
+
repoPath: path2,
|
|
25756
|
+
confirmed: confirmed === true
|
|
25757
|
+
});
|
|
25758
|
+
return text5(result2, !result2.ok);
|
|
25759
|
+
}
|
|
25760
|
+
const result = writeViewerContext({
|
|
25761
|
+
scope,
|
|
25762
|
+
context,
|
|
25763
|
+
confirmed: confirmed === true
|
|
25764
|
+
});
|
|
25765
|
+
return text5(result, !result.ok);
|
|
25766
|
+
} catch (err) {
|
|
25767
|
+
return text5(
|
|
25768
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
25769
|
+
true
|
|
25770
|
+
);
|
|
25771
|
+
}
|
|
25772
|
+
}
|
|
25773
|
+
);
|
|
25774
|
+
}
|
|
25775
|
+
|
|
25659
25776
|
// src/mcp/pr-list.ts
|
|
25660
25777
|
function compactPrRow(pr) {
|
|
25661
25778
|
return {
|
|
@@ -25695,17 +25812,17 @@ init_list_prs();
|
|
|
25695
25812
|
init_app_settings();
|
|
25696
25813
|
|
|
25697
25814
|
// src/mcp/schedule-tools.ts
|
|
25698
|
-
var
|
|
25815
|
+
var import_zod6 = require("zod");
|
|
25699
25816
|
init_schedules();
|
|
25700
25817
|
init_schedule_runner();
|
|
25701
|
-
function
|
|
25818
|
+
function text6(payload, isError = false) {
|
|
25702
25819
|
return {
|
|
25703
25820
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
25704
25821
|
...isError ? { isError: true } : {}
|
|
25705
25822
|
};
|
|
25706
25823
|
}
|
|
25707
25824
|
function fail5(err) {
|
|
25708
|
-
return
|
|
25825
|
+
return text6(
|
|
25709
25826
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
25710
25827
|
true
|
|
25711
25828
|
);
|
|
@@ -25730,7 +25847,7 @@ function registerScheduleTools(server) {
|
|
|
25730
25847
|
{},
|
|
25731
25848
|
async () => {
|
|
25732
25849
|
try {
|
|
25733
|
-
return
|
|
25850
|
+
return text6({ schedules: listSchedules() });
|
|
25734
25851
|
} catch (err) {
|
|
25735
25852
|
return fail5(err);
|
|
25736
25853
|
}
|
|
@@ -25740,17 +25857,17 @@ function registerScheduleTools(server) {
|
|
|
25740
25857
|
"create_schedule",
|
|
25741
25858
|
"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.",
|
|
25742
25859
|
{
|
|
25743
|
-
prompt:
|
|
25744
|
-
name:
|
|
25745
|
-
at:
|
|
25746
|
-
every:
|
|
25747
|
-
cron:
|
|
25748
|
-
tz:
|
|
25749
|
-
threadId:
|
|
25860
|
+
prompt: import_zod6.z.string().describe("User message / goal queued when the schedule fires"),
|
|
25861
|
+
name: import_zod6.z.string().optional(),
|
|
25862
|
+
at: import_zod6.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
25863
|
+
every: import_zod6.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
25864
|
+
cron: import_zod6.z.string().optional().describe("5-field cron expression"),
|
|
25865
|
+
tz: import_zod6.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
25866
|
+
threadId: import_zod6.z.string().optional().describe(
|
|
25750
25867
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
25751
25868
|
),
|
|
25752
|
-
agent:
|
|
25753
|
-
model:
|
|
25869
|
+
agent: import_zod6.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
25870
|
+
model: import_zod6.z.string().optional()
|
|
25754
25871
|
},
|
|
25755
25872
|
async (args) => {
|
|
25756
25873
|
try {
|
|
@@ -25770,7 +25887,7 @@ function registerScheduleTools(server) {
|
|
|
25770
25887
|
model: args.model,
|
|
25771
25888
|
createdBy: "mcp"
|
|
25772
25889
|
});
|
|
25773
|
-
return
|
|
25890
|
+
return text6({
|
|
25774
25891
|
schedule,
|
|
25775
25892
|
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."
|
|
25776
25893
|
});
|
|
@@ -25783,17 +25900,17 @@ function registerScheduleTools(server) {
|
|
|
25783
25900
|
"update_schedule",
|
|
25784
25901
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
25785
25902
|
{
|
|
25786
|
-
id:
|
|
25787
|
-
prompt:
|
|
25788
|
-
name:
|
|
25789
|
-
at:
|
|
25790
|
-
every:
|
|
25791
|
-
cron:
|
|
25792
|
-
tz:
|
|
25793
|
-
threadId:
|
|
25794
|
-
agent:
|
|
25795
|
-
model:
|
|
25796
|
-
enabled:
|
|
25903
|
+
id: import_zod6.z.string(),
|
|
25904
|
+
prompt: import_zod6.z.string().optional(),
|
|
25905
|
+
name: import_zod6.z.string().optional(),
|
|
25906
|
+
at: import_zod6.z.string().optional(),
|
|
25907
|
+
every: import_zod6.z.string().optional(),
|
|
25908
|
+
cron: import_zod6.z.string().optional(),
|
|
25909
|
+
tz: import_zod6.z.string().optional(),
|
|
25910
|
+
threadId: import_zod6.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
25911
|
+
agent: import_zod6.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
25912
|
+
model: import_zod6.z.string().optional(),
|
|
25913
|
+
enabled: import_zod6.z.boolean().optional()
|
|
25797
25914
|
},
|
|
25798
25915
|
async (args) => {
|
|
25799
25916
|
try {
|
|
@@ -25822,7 +25939,7 @@ function registerScheduleTools(server) {
|
|
|
25822
25939
|
enabled: args.enabled,
|
|
25823
25940
|
model: args.model
|
|
25824
25941
|
});
|
|
25825
|
-
return
|
|
25942
|
+
return text6({ schedule });
|
|
25826
25943
|
} catch (err) {
|
|
25827
25944
|
return fail5(err);
|
|
25828
25945
|
}
|
|
@@ -25831,11 +25948,11 @@ function registerScheduleTools(server) {
|
|
|
25831
25948
|
server.tool(
|
|
25832
25949
|
"delete_schedule",
|
|
25833
25950
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
25834
|
-
{ id:
|
|
25951
|
+
{ id: import_zod6.z.string() },
|
|
25835
25952
|
async ({ id }) => {
|
|
25836
25953
|
try {
|
|
25837
25954
|
deleteSchedule(id);
|
|
25838
|
-
return
|
|
25955
|
+
return text6({ ok: true, id });
|
|
25839
25956
|
} catch (err) {
|
|
25840
25957
|
return fail5(err);
|
|
25841
25958
|
}
|
|
@@ -25844,14 +25961,14 @@ function registerScheduleTools(server) {
|
|
|
25844
25961
|
server.tool(
|
|
25845
25962
|
"run_schedule",
|
|
25846
25963
|
"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.",
|
|
25847
|
-
{ id:
|
|
25964
|
+
{ id: import_zod6.z.string() },
|
|
25848
25965
|
async ({ id }) => {
|
|
25849
25966
|
try {
|
|
25850
25967
|
if (!getSchedule(id)) {
|
|
25851
25968
|
return fail5(new Error(`Schedule not found: ${id}`));
|
|
25852
25969
|
}
|
|
25853
25970
|
const schedule = await fireSchedule(id);
|
|
25854
|
-
return
|
|
25971
|
+
return text6({ schedule });
|
|
25855
25972
|
} catch (err) {
|
|
25856
25973
|
return fail5(err);
|
|
25857
25974
|
}
|
|
@@ -26282,13 +26399,14 @@ async function startMcpServer() {
|
|
|
26282
26399
|
version: "0.1.0"
|
|
26283
26400
|
});
|
|
26284
26401
|
const worktreeProfile = sideboardMcpProfile() === "worktree";
|
|
26402
|
+
registerViewerContextTools(server);
|
|
26285
26403
|
if (worktreeProfile) {
|
|
26286
26404
|
registerConnectedIssueVendorTools(server);
|
|
26287
26405
|
}
|
|
26288
26406
|
if (!worktreeProfile) {
|
|
26289
26407
|
server.tool(
|
|
26290
26408
|
"list_workspaces",
|
|
26291
|
-
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and
|
|
26409
|
+
"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.",
|
|
26292
26410
|
{},
|
|
26293
26411
|
async () => {
|
|
26294
26412
|
const workspaces = orch.listWorkspaces();
|
|
@@ -26337,13 +26455,13 @@ async function startMcpServer() {
|
|
|
26337
26455
|
"list_board",
|
|
26338
26456
|
"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.",
|
|
26339
26457
|
{
|
|
26340
|
-
query:
|
|
26341
|
-
repoPath:
|
|
26342
|
-
kind:
|
|
26343
|
-
column:
|
|
26458
|
+
query: import_zod7.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
26459
|
+
repoPath: import_zod7.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
26460
|
+
kind: import_zod7.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
26461
|
+
column: import_zod7.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
26344
26462
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
26345
26463
|
),
|
|
26346
|
-
limit:
|
|
26464
|
+
limit: import_zod7.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
26347
26465
|
},
|
|
26348
26466
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
26349
26467
|
const workspaces = orch.listWorkspaces();
|
|
@@ -26381,7 +26499,7 @@ async function startMcpServer() {
|
|
|
26381
26499
|
server.tool(
|
|
26382
26500
|
"get_thread",
|
|
26383
26501
|
"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.",
|
|
26384
|
-
{ ref:
|
|
26502
|
+
{ ref: import_zod7.z.string() },
|
|
26385
26503
|
async ({ ref }) => {
|
|
26386
26504
|
const t = orch.getThread(ref);
|
|
26387
26505
|
if (!t) {
|
|
@@ -26423,17 +26541,17 @@ async function startMcpServer() {
|
|
|
26423
26541
|
"present_artifact",
|
|
26424
26542
|
"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.",
|
|
26425
26543
|
{
|
|
26426
|
-
title:
|
|
26427
|
-
type:
|
|
26544
|
+
title: import_zod7.z.string().describe("Short title shown in the artifact pane header"),
|
|
26545
|
+
type: import_zod7.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
26428
26546
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
26429
26547
|
),
|
|
26430
|
-
content:
|
|
26548
|
+
content: import_zod7.z.string().describe(
|
|
26431
26549
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
26432
26550
|
),
|
|
26433
|
-
artifact_id:
|
|
26434
|
-
status:
|
|
26435
|
-
phase:
|
|
26436
|
-
mode:
|
|
26551
|
+
artifact_id: import_zod7.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
26552
|
+
status: import_zod7.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
26553
|
+
phase: import_zod7.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
26554
|
+
mode: import_zod7.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
26437
26555
|
},
|
|
26438
26556
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
26439
26557
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26454,15 +26572,15 @@ async function startMcpServer() {
|
|
|
26454
26572
|
"ask_user",
|
|
26455
26573
|
"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.",
|
|
26456
26574
|
{
|
|
26457
|
-
questions:
|
|
26458
|
-
|
|
26459
|
-
question:
|
|
26460
|
-
header:
|
|
26461
|
-
multiSelect:
|
|
26462
|
-
options:
|
|
26463
|
-
|
|
26464
|
-
label:
|
|
26465
|
-
description:
|
|
26575
|
+
questions: import_zod7.z.array(
|
|
26576
|
+
import_zod7.z.object({
|
|
26577
|
+
question: import_zod7.z.string().describe("Full question text ending with ?"),
|
|
26578
|
+
header: import_zod7.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
26579
|
+
multiSelect: import_zod7.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
26580
|
+
options: import_zod7.z.array(
|
|
26581
|
+
import_zod7.z.object({
|
|
26582
|
+
label: import_zod7.z.string(),
|
|
26583
|
+
description: import_zod7.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
26466
26584
|
})
|
|
26467
26585
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
26468
26586
|
})
|
|
@@ -26481,9 +26599,9 @@ async function startMcpServer() {
|
|
|
26481
26599
|
"present_plan",
|
|
26482
26600
|
"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.",
|
|
26483
26601
|
{
|
|
26484
|
-
title:
|
|
26485
|
-
content:
|
|
26486
|
-
thread_id:
|
|
26602
|
+
title: import_zod7.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
26603
|
+
content: import_zod7.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
26604
|
+
thread_id: import_zod7.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
26487
26605
|
},
|
|
26488
26606
|
async ({ title, content, thread_id }) => {
|
|
26489
26607
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -26506,15 +26624,15 @@ async function startMcpServer() {
|
|
|
26506
26624
|
"present_schema",
|
|
26507
26625
|
"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.",
|
|
26508
26626
|
{
|
|
26509
|
-
title:
|
|
26510
|
-
mode:
|
|
26511
|
-
datasource:
|
|
26512
|
-
resource_id:
|
|
26513
|
-
record_id:
|
|
26514
|
-
resource:
|
|
26515
|
-
record:
|
|
26516
|
-
records:
|
|
26517
|
-
pane_id:
|
|
26627
|
+
title: import_zod7.z.string().describe("Pane title"),
|
|
26628
|
+
mode: import_zod7.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
26629
|
+
datasource: import_zod7.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
26630
|
+
resource_id: import_zod7.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
26631
|
+
record_id: import_zod7.z.string().optional().describe("Record id when opening form mode"),
|
|
26632
|
+
resource: import_zod7.z.record(import_zod7.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
26633
|
+
record: import_zod7.z.record(import_zod7.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
26634
|
+
records: import_zod7.z.array(import_zod7.z.record(import_zod7.z.unknown())).optional().describe("Inline records for table mode"),
|
|
26635
|
+
pane_id: import_zod7.z.string().optional().describe("Stable pane id across updates")
|
|
26518
26636
|
},
|
|
26519
26637
|
async (args) => {
|
|
26520
26638
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26535,10 +26653,10 @@ async function startMcpServer() {
|
|
|
26535
26653
|
"present_files",
|
|
26536
26654
|
"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.",
|
|
26537
26655
|
{
|
|
26538
|
-
title:
|
|
26539
|
-
datasource:
|
|
26540
|
-
path:
|
|
26541
|
-
pane_id:
|
|
26656
|
+
title: import_zod7.z.string().optional().describe("Pane title (default: Files)"),
|
|
26657
|
+
datasource: import_zod7.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
26658
|
+
path: import_zod7.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
26659
|
+
pane_id: import_zod7.z.string().optional().describe("Stable pane id across updates")
|
|
26542
26660
|
},
|
|
26543
26661
|
async (args) => {
|
|
26544
26662
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26557,8 +26675,8 @@ async function startMcpServer() {
|
|
|
26557
26675
|
"wait_for_job",
|
|
26558
26676
|
"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.",
|
|
26559
26677
|
{
|
|
26560
|
-
id:
|
|
26561
|
-
timeoutMs:
|
|
26678
|
+
id: import_zod7.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
26679
|
+
timeoutMs: import_zod7.z.number().optional()
|
|
26562
26680
|
},
|
|
26563
26681
|
async ({ id, timeoutMs }) => {
|
|
26564
26682
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
@@ -26579,7 +26697,7 @@ async function startMcpServer() {
|
|
|
26579
26697
|
"set_caffeinate",
|
|
26580
26698
|
"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.",
|
|
26581
26699
|
{
|
|
26582
|
-
enabled:
|
|
26700
|
+
enabled: import_zod7.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
26583
26701
|
},
|
|
26584
26702
|
async ({ enabled }) => {
|
|
26585
26703
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -26617,18 +26735,18 @@ async function startMcpServer() {
|
|
|
26617
26735
|
"create_thread",
|
|
26618
26736
|
`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.`,
|
|
26619
26737
|
{
|
|
26620
|
-
sourceType:
|
|
26621
|
-
sourceRef:
|
|
26622
|
-
agent:
|
|
26623
|
-
model:
|
|
26738
|
+
sourceType: import_zod7.z.enum(["branch", "pr", "ticket"]),
|
|
26739
|
+
sourceRef: import_zod7.z.string(),
|
|
26740
|
+
agent: import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
26741
|
+
model: import_zod7.z.string().nullable().optional().describe(
|
|
26624
26742
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
26625
26743
|
),
|
|
26626
|
-
repoPath:
|
|
26627
|
-
title:
|
|
26628
|
-
cowboy:
|
|
26744
|
+
repoPath: import_zod7.z.string(),
|
|
26745
|
+
title: import_zod7.z.string().optional(),
|
|
26746
|
+
cowboy: import_zod7.z.boolean().optional().describe(
|
|
26629
26747
|
"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."
|
|
26630
26748
|
),
|
|
26631
|
-
parentThreadId:
|
|
26749
|
+
parentThreadId: import_zod7.z.string().optional().describe(
|
|
26632
26750
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
26633
26751
|
)
|
|
26634
26752
|
},
|
|
@@ -26644,10 +26762,10 @@ async function startMcpServer() {
|
|
|
26644
26762
|
"start_board_card",
|
|
26645
26763
|
"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.",
|
|
26646
26764
|
{
|
|
26647
|
-
kind:
|
|
26648
|
-
ref:
|
|
26649
|
-
repoPath:
|
|
26650
|
-
title:
|
|
26765
|
+
kind: import_zod7.z.enum(["ticket", "pr", "branch"]),
|
|
26766
|
+
ref: import_zod7.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
26767
|
+
repoPath: import_zod7.z.string().describe("Workspace path from list_workspaces / list_board"),
|
|
26768
|
+
title: import_zod7.z.string().optional()
|
|
26651
26769
|
},
|
|
26652
26770
|
async ({ kind, ref, repoPath, title }) => {
|
|
26653
26771
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -26751,9 +26869,9 @@ async function startMcpServer() {
|
|
|
26751
26869
|
"send_to_thread",
|
|
26752
26870
|
'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.',
|
|
26753
26871
|
{
|
|
26754
|
-
ref:
|
|
26755
|
-
prompt:
|
|
26756
|
-
force_stop:
|
|
26872
|
+
ref: import_zod7.z.string(),
|
|
26873
|
+
prompt: import_zod7.z.string(),
|
|
26874
|
+
force_stop: import_zod7.z.boolean().optional()
|
|
26757
26875
|
},
|
|
26758
26876
|
async ({ ref, prompt, force_stop }) => {
|
|
26759
26877
|
if (force_stop) {
|
|
@@ -26782,8 +26900,8 @@ async function startMcpServer() {
|
|
|
26782
26900
|
"wait_for_turn",
|
|
26783
26901
|
"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).",
|
|
26784
26902
|
{
|
|
26785
|
-
ref:
|
|
26786
|
-
timeoutMs:
|
|
26903
|
+
ref: import_zod7.z.string(),
|
|
26904
|
+
timeoutMs: import_zod7.z.number().optional()
|
|
26787
26905
|
},
|
|
26788
26906
|
async ({ ref, timeoutMs }) => {
|
|
26789
26907
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -26813,7 +26931,7 @@ async function startMcpServer() {
|
|
|
26813
26931
|
server.tool(
|
|
26814
26932
|
"get_turn_result",
|
|
26815
26933
|
"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).",
|
|
26816
|
-
{ ref:
|
|
26934
|
+
{ ref: import_zod7.z.string() },
|
|
26817
26935
|
async ({ ref }) => {
|
|
26818
26936
|
const result = orch.getTurnResult(ref);
|
|
26819
26937
|
return {
|
|
@@ -26834,8 +26952,8 @@ async function startMcpServer() {
|
|
|
26834
26952
|
"stop_thread",
|
|
26835
26953
|
"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.",
|
|
26836
26954
|
{
|
|
26837
|
-
ref:
|
|
26838
|
-
force:
|
|
26955
|
+
ref: import_zod7.z.string(),
|
|
26956
|
+
force: import_zod7.z.boolean().optional()
|
|
26839
26957
|
},
|
|
26840
26958
|
async ({ ref, force }) => {
|
|
26841
26959
|
const t = orch.getThread(ref);
|
|
@@ -26865,7 +26983,7 @@ async function startMcpServer() {
|
|
|
26865
26983
|
server.tool(
|
|
26866
26984
|
"archive_thread",
|
|
26867
26985
|
"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.",
|
|
26868
|
-
{ ref:
|
|
26986
|
+
{ ref: import_zod7.z.string() },
|
|
26869
26987
|
async ({ ref }) => {
|
|
26870
26988
|
const t = orch.getThread(ref);
|
|
26871
26989
|
if (!t) {
|
|
@@ -26898,7 +27016,7 @@ async function startMcpServer() {
|
|
|
26898
27016
|
server.tool(
|
|
26899
27017
|
"restore_thread",
|
|
26900
27018
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
26901
|
-
{ ref:
|
|
27019
|
+
{ ref: import_zod7.z.string() },
|
|
26902
27020
|
async ({ ref }) => {
|
|
26903
27021
|
try {
|
|
26904
27022
|
const restored = await orch.restore(ref);
|
|
@@ -26924,8 +27042,8 @@ async function startMcpServer() {
|
|
|
26924
27042
|
"get_diff",
|
|
26925
27043
|
"Compact diff summary (capped hunks, paginated)",
|
|
26926
27044
|
{
|
|
26927
|
-
ref:
|
|
26928
|
-
maxFiles:
|
|
27045
|
+
ref: import_zod7.z.string(),
|
|
27046
|
+
maxFiles: import_zod7.z.number().optional()
|
|
26929
27047
|
},
|
|
26930
27048
|
async ({ ref, maxFiles }) => {
|
|
26931
27049
|
const summary = await orch.diffSummary(ref);
|
|
@@ -26945,7 +27063,7 @@ async function startMcpServer() {
|
|
|
26945
27063
|
server.tool(
|
|
26946
27064
|
"get_pr_checks",
|
|
26947
27065
|
"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.",
|
|
26948
|
-
{ ref:
|
|
27066
|
+
{ ref: import_zod7.z.string().describe("Worktree thread id/ref") },
|
|
26949
27067
|
async ({ ref }) => {
|
|
26950
27068
|
try {
|
|
26951
27069
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -26961,7 +27079,7 @@ async function startMcpServer() {
|
|
|
26961
27079
|
server.tool(
|
|
26962
27080
|
"request_review",
|
|
26963
27081
|
'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.',
|
|
26964
|
-
{ ref:
|
|
27082
|
+
{ ref: import_zod7.z.string().describe("Worktree thread id/ref to review") },
|
|
26965
27083
|
async ({ ref }) => {
|
|
26966
27084
|
try {
|
|
26967
27085
|
const tab = await orch.requestReview(ref);
|
|
@@ -26990,8 +27108,8 @@ async function startMcpServer() {
|
|
|
26990
27108
|
"ask_git",
|
|
26991
27109
|
"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.",
|
|
26992
27110
|
{
|
|
26993
|
-
ref:
|
|
26994
|
-
action:
|
|
27111
|
+
ref: import_zod7.z.string().describe("Worktree thread id/ref"),
|
|
27112
|
+
action: import_zod7.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
26995
27113
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
26996
27114
|
)
|
|
26997
27115
|
},
|
|
@@ -27036,7 +27154,7 @@ async function startMcpServer() {
|
|
|
27036
27154
|
}
|
|
27037
27155
|
}
|
|
27038
27156
|
);
|
|
27039
|
-
const agentEnum =
|
|
27157
|
+
const agentEnum = import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
27040
27158
|
server.tool(
|
|
27041
27159
|
"list_models",
|
|
27042
27160
|
"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.",
|
|
@@ -27059,11 +27177,11 @@ async function startMcpServer() {
|
|
|
27059
27177
|
"fork_worktree",
|
|
27060
27178
|
"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.",
|
|
27061
27179
|
{
|
|
27062
|
-
ref:
|
|
27063
|
-
through_index:
|
|
27180
|
+
ref: import_zod7.z.string().describe("Worktree thread id/ref to fork"),
|
|
27181
|
+
through_index: import_zod7.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27064
27182
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27065
|
-
model:
|
|
27066
|
-
title:
|
|
27183
|
+
model: import_zod7.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27184
|
+
title: import_zod7.z.string().optional()
|
|
27067
27185
|
},
|
|
27068
27186
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27069
27187
|
try {
|
|
@@ -27104,11 +27222,11 @@ async function startMcpServer() {
|
|
|
27104
27222
|
"fork_chat",
|
|
27105
27223
|
"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.",
|
|
27106
27224
|
{
|
|
27107
|
-
ref:
|
|
27108
|
-
through_index:
|
|
27225
|
+
ref: import_zod7.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
27226
|
+
through_index: import_zod7.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27109
27227
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27110
|
-
model:
|
|
27111
|
-
title:
|
|
27228
|
+
model: import_zod7.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27229
|
+
title: import_zod7.z.string().optional()
|
|
27112
27230
|
},
|
|
27113
27231
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27114
27232
|
try {
|
|
@@ -27154,8 +27272,8 @@ async function startMcpServer() {
|
|
|
27154
27272
|
"run_dev_script",
|
|
27155
27273
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
27156
27274
|
{
|
|
27157
|
-
ref:
|
|
27158
|
-
name:
|
|
27275
|
+
ref: import_zod7.z.string(),
|
|
27276
|
+
name: import_zod7.z.string().optional()
|
|
27159
27277
|
},
|
|
27160
27278
|
async ({ ref, name }) => {
|
|
27161
27279
|
const result = await orch.startDev(ref, name);
|
|
@@ -27177,7 +27295,7 @@ async function startMcpServer() {
|
|
|
27177
27295
|
server.tool(
|
|
27178
27296
|
"list_run_scripts",
|
|
27179
27297
|
"List named run scripts available for a thread",
|
|
27180
|
-
{ ref:
|
|
27298
|
+
{ ref: import_zod7.z.string() },
|
|
27181
27299
|
async ({ ref }) => {
|
|
27182
27300
|
const scripts = orch.listThreadRunScripts(ref);
|
|
27183
27301
|
const active = orch.getActiveRuns(ref);
|
|
@@ -27195,8 +27313,8 @@ async function startMcpServer() {
|
|
|
27195
27313
|
"stop_dev_script",
|
|
27196
27314
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
27197
27315
|
{
|
|
27198
|
-
ref:
|
|
27199
|
-
name:
|
|
27316
|
+
ref: import_zod7.z.string(),
|
|
27317
|
+
name: import_zod7.z.string().optional()
|
|
27200
27318
|
},
|
|
27201
27319
|
async ({ ref, name }) => {
|
|
27202
27320
|
orch.stopDev(ref, name);
|
|
@@ -27206,7 +27324,7 @@ async function startMcpServer() {
|
|
|
27206
27324
|
server.tool(
|
|
27207
27325
|
"run_setup",
|
|
27208
27326
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
27209
|
-
{ ref:
|
|
27327
|
+
{ ref: import_zod7.z.string() },
|
|
27210
27328
|
async ({ ref }) => {
|
|
27211
27329
|
const result = await orch.runSetup(ref);
|
|
27212
27330
|
return {
|
|
@@ -27217,7 +27335,7 @@ async function startMcpServer() {
|
|
|
27217
27335
|
server.tool(
|
|
27218
27336
|
"add_workspace",
|
|
27219
27337
|
"Register a git repo as a Sideboard workspace",
|
|
27220
|
-
{ repoPath:
|
|
27338
|
+
{ repoPath: import_zod7.z.string() },
|
|
27221
27339
|
async ({ repoPath }) => {
|
|
27222
27340
|
const ws = await orch.addWorkspace(repoPath);
|
|
27223
27341
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -27226,7 +27344,7 @@ async function startMcpServer() {
|
|
|
27226
27344
|
server.tool(
|
|
27227
27345
|
"remove_workspace",
|
|
27228
27346
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
27229
|
-
{ repoPath:
|
|
27347
|
+
{ repoPath: import_zod7.z.string() },
|
|
27230
27348
|
async ({ repoPath }) => {
|
|
27231
27349
|
orch.removeWorkspace(repoPath);
|
|
27232
27350
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -27236,12 +27354,12 @@ async function startMcpServer() {
|
|
|
27236
27354
|
"fanout",
|
|
27237
27355
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
27238
27356
|
{
|
|
27239
|
-
prompt:
|
|
27240
|
-
agents:
|
|
27241
|
-
repoPath:
|
|
27242
|
-
sourceType:
|
|
27243
|
-
sourceRef:
|
|
27244
|
-
title:
|
|
27357
|
+
prompt: import_zod7.z.string(),
|
|
27358
|
+
agents: import_zod7.z.array(import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
27359
|
+
repoPath: import_zod7.z.string(),
|
|
27360
|
+
sourceType: import_zod7.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
27361
|
+
sourceRef: import_zod7.z.string().optional(),
|
|
27362
|
+
title: import_zod7.z.string().optional()
|
|
27245
27363
|
},
|
|
27246
27364
|
async (args) => {
|
|
27247
27365
|
const threads = await orch.bestOfN(args);
|
|
@@ -27268,8 +27386,8 @@ async function startMcpServer() {
|
|
|
27268
27386
|
"list_branches",
|
|
27269
27387
|
"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).",
|
|
27270
27388
|
{
|
|
27271
|
-
repoPath:
|
|
27272
|
-
unmergedOnly:
|
|
27389
|
+
repoPath: import_zod7.z.string(),
|
|
27390
|
+
unmergedOnly: import_zod7.z.boolean().optional()
|
|
27273
27391
|
},
|
|
27274
27392
|
async ({ repoPath, unmergedOnly }) => {
|
|
27275
27393
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27290,19 +27408,19 @@ async function startMcpServer() {
|
|
|
27290
27408
|
"list_prs",
|
|
27291
27409
|
'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.',
|
|
27292
27410
|
{
|
|
27293
|
-
repoPath:
|
|
27294
|
-
query:
|
|
27295
|
-
queue:
|
|
27411
|
+
repoPath: import_zod7.z.string(),
|
|
27412
|
+
query: import_zod7.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
27413
|
+
queue: import_zod7.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
27296
27414
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
27297
27415
|
),
|
|
27298
|
-
state:
|
|
27299
|
-
label:
|
|
27416
|
+
state: import_zod7.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
27417
|
+
label: import_zod7.z.string().optional().describe(
|
|
27300
27418
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
27301
27419
|
),
|
|
27302
|
-
reviewer:
|
|
27420
|
+
reviewer: import_zod7.z.string().optional().describe(
|
|
27303
27421
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
27304
27422
|
),
|
|
27305
|
-
limit:
|
|
27423
|
+
limit: import_zod7.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27306
27424
|
},
|
|
27307
27425
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
27308
27426
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27334,7 +27452,7 @@ async function startMcpServer() {
|
|
|
27334
27452
|
server.tool(
|
|
27335
27453
|
"get_pr_stack",
|
|
27336
27454
|
"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).",
|
|
27337
|
-
{ ref:
|
|
27455
|
+
{ ref: import_zod7.z.string() },
|
|
27338
27456
|
async ({ ref }) => {
|
|
27339
27457
|
const stack = await orch.getPrStack(ref);
|
|
27340
27458
|
return {
|
|
@@ -27346,8 +27464,8 @@ async function startMcpServer() {
|
|
|
27346
27464
|
"open_pr_stack_layers",
|
|
27347
27465
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
27348
27466
|
{
|
|
27349
|
-
ref:
|
|
27350
|
-
layer:
|
|
27467
|
+
ref: import_zod7.z.string(),
|
|
27468
|
+
layer: import_zod7.z.number().int().positive().optional()
|
|
27351
27469
|
},
|
|
27352
27470
|
async ({ ref, layer }) => {
|
|
27353
27471
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -27381,9 +27499,9 @@ async function startMcpServer() {
|
|
|
27381
27499
|
"add_stack_layer",
|
|
27382
27500
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
27383
27501
|
{
|
|
27384
|
-
ref:
|
|
27385
|
-
branchName:
|
|
27386
|
-
title:
|
|
27502
|
+
ref: import_zod7.z.string(),
|
|
27503
|
+
branchName: import_zod7.z.string(),
|
|
27504
|
+
title: import_zod7.z.string().optional()
|
|
27387
27505
|
},
|
|
27388
27506
|
async ({ ref, branchName, title }) => {
|
|
27389
27507
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -27412,11 +27530,11 @@ async function startMcpServer() {
|
|
|
27412
27530
|
"create_pr_stack",
|
|
27413
27531
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
27414
27532
|
{
|
|
27415
|
-
repoPath:
|
|
27416
|
-
branches:
|
|
27417
|
-
agent:
|
|
27418
|
-
base:
|
|
27419
|
-
title:
|
|
27533
|
+
repoPath: import_zod7.z.string(),
|
|
27534
|
+
branches: import_zod7.z.array(import_zod7.z.string()).min(1),
|
|
27535
|
+
agent: import_zod7.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
27536
|
+
base: import_zod7.z.string().optional(),
|
|
27537
|
+
title: import_zod7.z.string().optional()
|
|
27420
27538
|
},
|
|
27421
27539
|
async (args) => {
|
|
27422
27540
|
const result = await orch.createPrStack({
|
|
@@ -27455,10 +27573,10 @@ async function startMcpServer() {
|
|
|
27455
27573
|
"list_issues",
|
|
27456
27574
|
"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.",
|
|
27457
27575
|
{
|
|
27458
|
-
repoPath:
|
|
27459
|
-
query:
|
|
27460
|
-
assignee:
|
|
27461
|
-
limit:
|
|
27576
|
+
repoPath: import_zod7.z.string(),
|
|
27577
|
+
query: import_zod7.z.string().optional().describe("Search title, identifier, or description"),
|
|
27578
|
+
assignee: import_zod7.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
27579
|
+
limit: import_zod7.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27462
27580
|
},
|
|
27463
27581
|
async ({ repoPath, query, assignee, limit }) => {
|
|
27464
27582
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27598,8 +27716,8 @@ var BrightsySideboardApi = class {
|
|
|
27598
27716
|
};
|
|
27599
27717
|
function taskMessageText(task) {
|
|
27600
27718
|
const parts = task.message?.parts ?? [];
|
|
27601
|
-
const
|
|
27602
|
-
return
|
|
27719
|
+
const text7 = parts.find((p) => p.kind === "text")?.text ?? "";
|
|
27720
|
+
return text7.trim();
|
|
27603
27721
|
}
|
|
27604
27722
|
|
|
27605
27723
|
// src/brightsy/cloud-connect.ts
|
|
@@ -28101,11 +28219,11 @@ var import_ws = require("ws");
|
|
|
28101
28219
|
init_api();
|
|
28102
28220
|
var BACKOFF_START_MS = 1e3;
|
|
28103
28221
|
var BACKOFF_MAX_MS = 3e4;
|
|
28104
|
-
function stripSlackMentions(
|
|
28105
|
-
return
|
|
28222
|
+
function stripSlackMentions(text7) {
|
|
28223
|
+
return text7.replace(/<@[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
28106
28224
|
}
|
|
28107
|
-
function isSlackStopCommand(
|
|
28108
|
-
const t = stripSlackMentions(
|
|
28225
|
+
function isSlackStopCommand(text7) {
|
|
28226
|
+
const t = stripSlackMentions(text7).toLowerCase();
|
|
28109
28227
|
return t === "stop" || t === "sideboard_force_stop";
|
|
28110
28228
|
}
|
|
28111
28229
|
function parseSlackSocketFrame(raw) {
|
|
@@ -28127,8 +28245,8 @@ function inboundFromSocketFrame(frame) {
|
|
|
28127
28245
|
const ts = event.ts?.trim();
|
|
28128
28246
|
if (!channelId || !ts) return null;
|
|
28129
28247
|
const rawText = event.text?.trim() ?? "";
|
|
28130
|
-
const
|
|
28131
|
-
if (!
|
|
28248
|
+
const text7 = stripSlackMentions(rawText);
|
|
28249
|
+
if (!text7) return null;
|
|
28132
28250
|
const teamId = (frame.payload?.team_id || event.team || "").trim();
|
|
28133
28251
|
if (!teamId) return null;
|
|
28134
28252
|
const isDm = event.type === "message" && (event.channel_type === "im" || event.channel_type === "mpim" || !event.channel_type && channelId.startsWith("D"));
|
|
@@ -28140,7 +28258,7 @@ function inboundFromSocketFrame(frame) {
|
|
|
28140
28258
|
ts,
|
|
28141
28259
|
threadTs: event.thread_ts?.trim() || void 0,
|
|
28142
28260
|
userId: event.user?.trim(),
|
|
28143
|
-
text:
|
|
28261
|
+
text: text7,
|
|
28144
28262
|
kind: isMention ? "mention" : "dm"
|
|
28145
28263
|
};
|
|
28146
28264
|
}
|
|
@@ -28684,12 +28802,12 @@ function formatSlackInboundPrompt(msg) {
|
|
|
28684
28802
|
|
|
28685
28803
|
${msg.text}`;
|
|
28686
28804
|
}
|
|
28687
|
-
function isSlackInboundUserPrompt(
|
|
28688
|
-
return
|
|
28805
|
+
function isSlackInboundUserPrompt(text7) {
|
|
28806
|
+
return text7.startsWith("Slack DM\n") || text7.startsWith("Slack @mention\n");
|
|
28689
28807
|
}
|
|
28690
|
-
function formatSlackSignedReply(deviceLabel,
|
|
28808
|
+
function formatSlackSignedReply(deviceLabel, text7) {
|
|
28691
28809
|
const label = deviceLabel.trim();
|
|
28692
|
-
const body =
|
|
28810
|
+
const body = text7.trim();
|
|
28693
28811
|
if (!body) return body;
|
|
28694
28812
|
if (!label) return body;
|
|
28695
28813
|
const head = `${label}:`;
|
|
@@ -28698,8 +28816,8 @@ function formatSlackSignedReply(deviceLabel, text6) {
|
|
|
28698
28816
|
}
|
|
28699
28817
|
return `${label}: ${body}`;
|
|
28700
28818
|
}
|
|
28701
|
-
function signForThisMac(
|
|
28702
|
-
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel,
|
|
28819
|
+
function signForThisMac(text7) {
|
|
28820
|
+
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel, text7);
|
|
28703
28821
|
}
|
|
28704
28822
|
function slackReplyThreadTs(msg) {
|
|
28705
28823
|
if (msg.threadTs && msg.threadTs !== msg.ts) return msg.threadTs;
|
|
@@ -28760,9 +28878,9 @@ function replyStub(target) {
|
|
|
28760
28878
|
kind: "dm"
|
|
28761
28879
|
};
|
|
28762
28880
|
}
|
|
28763
|
-
async function postSlackText(target,
|
|
28881
|
+
async function postSlackText(target, text7, opts) {
|
|
28764
28882
|
if (opts.postReply) {
|
|
28765
|
-
const result = await opts.postReply(replyStub(target),
|
|
28883
|
+
const result = await opts.postReply(replyStub(target), text7);
|
|
28766
28884
|
const ts2 = result && typeof result === "object" && typeof result.ts === "string" ? result.ts.trim() : "";
|
|
28767
28885
|
return ts2 ? { ts: ts2 } : null;
|
|
28768
28886
|
}
|
|
@@ -28772,7 +28890,7 @@ async function postSlackText(target, text6, opts) {
|
|
|
28772
28890
|
"chat.postMessage",
|
|
28773
28891
|
{
|
|
28774
28892
|
channel: target.channelId,
|
|
28775
|
-
text:
|
|
28893
|
+
text: text7,
|
|
28776
28894
|
thread_ts: target.threadTs
|
|
28777
28895
|
},
|
|
28778
28896
|
opts.fetchImpl
|
|
@@ -28780,9 +28898,9 @@ async function postSlackText(target, text6, opts) {
|
|
|
28780
28898
|
const ts = json.ts?.trim();
|
|
28781
28899
|
return ts ? { ts } : null;
|
|
28782
28900
|
}
|
|
28783
|
-
async function updateSlackText(target, ts,
|
|
28901
|
+
async function updateSlackText(target, ts, text7, opts) {
|
|
28784
28902
|
if (opts.updateReply) {
|
|
28785
|
-
await opts.updateReply(replyStub(target), ts,
|
|
28903
|
+
await opts.updateReply(replyStub(target), ts, text7);
|
|
28786
28904
|
return;
|
|
28787
28905
|
}
|
|
28788
28906
|
const token = writeTokenForTeam(target.teamId);
|
|
@@ -28792,7 +28910,7 @@ async function updateSlackText(target, ts, text6, opts) {
|
|
|
28792
28910
|
{
|
|
28793
28911
|
channel: target.channelId,
|
|
28794
28912
|
ts,
|
|
28795
|
-
text:
|
|
28913
|
+
text: text7
|
|
28796
28914
|
},
|
|
28797
28915
|
opts.fetchImpl
|
|
28798
28916
|
);
|
|
@@ -28820,8 +28938,8 @@ function replyTargetOf(msg) {
|
|
|
28820
28938
|
threadTs: slackReplyThreadTs(msg)
|
|
28821
28939
|
};
|
|
28822
28940
|
}
|
|
28823
|
-
async function postSlackReply(msg,
|
|
28824
|
-
await postSlackText(replyTargetOf(msg), signForThisMac(
|
|
28941
|
+
async function postSlackReply(msg, text7, opts) {
|
|
28942
|
+
await postSlackText(replyTargetOf(msg), signForThisMac(text7), opts);
|
|
28825
28943
|
}
|
|
28826
28944
|
function startSlackTurnProgress(msg, threadId, opts) {
|
|
28827
28945
|
const log = opts.onLog ?? (() => void 0);
|
|
@@ -28904,10 +29022,10 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
28904
29022
|
}
|
|
28905
29023
|
});
|
|
28906
29024
|
},
|
|
28907
|
-
finishWith: (
|
|
29025
|
+
finishWith: (text7) => {
|
|
28908
29026
|
stop();
|
|
28909
29027
|
return run2(async () => {
|
|
28910
|
-
const signed = signForThisMac(
|
|
29028
|
+
const signed = signForThisMac(text7.trim());
|
|
28911
29029
|
if (!signed) {
|
|
28912
29030
|
if (!postedTs) return;
|
|
28913
29031
|
const ts = postedTs;
|
|
@@ -28935,11 +29053,11 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
28935
29053
|
};
|
|
28936
29054
|
}
|
|
28937
29055
|
var lastRelayed = /* @__PURE__ */ new Map();
|
|
28938
|
-
function markRelayed(threadId,
|
|
28939
|
-
lastRelayed.set(threadId, `${threadId}:${
|
|
29056
|
+
function markRelayed(threadId, text7) {
|
|
29057
|
+
lastRelayed.set(threadId, `${threadId}:${text7}`);
|
|
28940
29058
|
}
|
|
28941
|
-
function alreadyRelayed(threadId,
|
|
28942
|
-
return lastRelayed.get(threadId) === `${threadId}:${
|
|
29059
|
+
function alreadyRelayed(threadId, text7) {
|
|
29060
|
+
return lastRelayed.get(threadId) === `${threadId}:${text7}`;
|
|
28943
29061
|
}
|
|
28944
29062
|
async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
28945
29063
|
const target = getSlackReplyTarget(threadId);
|
|
@@ -28948,14 +29066,14 @@ async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
|
28948
29066
|
const lastUser = thread ? [...thread.messages].reverse().find((m) => m.role === "user") : void 0;
|
|
28949
29067
|
if (lastUser && isSlackInboundUserPrompt(lastUser.text)) return;
|
|
28950
29068
|
const result = getOrchestrator().getTurnResult(threadId);
|
|
28951
|
-
const
|
|
28952
|
-
if (!
|
|
28953
|
-
if (alreadyRelayed(threadId,
|
|
28954
|
-
markRelayed(threadId,
|
|
29069
|
+
const text7 = result.text.trim();
|
|
29070
|
+
if (!text7) return;
|
|
29071
|
+
if (alreadyRelayed(threadId, text7)) return;
|
|
29072
|
+
markRelayed(threadId, text7);
|
|
28955
29073
|
const log = opts.onLog ?? (() => void 0);
|
|
28956
29074
|
try {
|
|
28957
|
-
await postSlackText(target, signForThisMac(
|
|
28958
|
-
log(`replied ${target.threadTs ?? target.channelId} (${
|
|
29075
|
+
await postSlackText(target, signForThisMac(text7), opts);
|
|
29076
|
+
log(`replied ${target.threadTs ?? target.channelId} (${text7.length} chars)`);
|
|
28959
29077
|
} catch (err) {
|
|
28960
29078
|
lastRelayed.delete(threadId);
|
|
28961
29079
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -29204,10 +29322,10 @@ function resolveSlackListenMode(opts) {
|
|
|
29204
29322
|
|
|
29205
29323
|
// src/slack/relay-hub.ts
|
|
29206
29324
|
init_api();
|
|
29207
|
-
function parseSlackDeviceDestination(
|
|
29208
|
-
const m =
|
|
29209
|
-
if (!m) return { label: null, rest:
|
|
29210
|
-
return { label: m[1], rest:
|
|
29325
|
+
function parseSlackDeviceDestination(text7) {
|
|
29326
|
+
const m = text7.match(/^\s*(?:to\s+)?(?:@|#)?([A-Za-z][\w-]{0,63})\s*[::]\s*/);
|
|
29327
|
+
if (!m) return { label: null, rest: text7 };
|
|
29328
|
+
return { label: m[1], rest: text7.slice(m[0].length) };
|
|
29211
29329
|
}
|
|
29212
29330
|
var SlackRelayHub = class {
|
|
29213
29331
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -29733,10 +29851,6 @@ init_outbound_watch();
|
|
|
29733
29851
|
// Annotate the CommonJS export names for ESM import in node:
|
|
29734
29852
|
0 && (module.exports = {
|
|
29735
29853
|
ABLETIME_MCP_PATH,
|
|
29736
|
-
ACCOUNT_ROLES,
|
|
29737
|
-
ACCOUNT_ROLE_LABELS,
|
|
29738
|
-
ACCOUNT_ROLE_MAX,
|
|
29739
|
-
ACCOUNT_ROLE_PRESETS,
|
|
29740
29854
|
AGENT_GIT_ACTIONS,
|
|
29741
29855
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
29742
29856
|
ATTACHMENTS_DIR,
|
|
@@ -29823,7 +29937,6 @@ init_outbound_watch();
|
|
|
29823
29937
|
WORKTREE_MCP_TOOLS,
|
|
29824
29938
|
abletimeMcpRequest,
|
|
29825
29939
|
abletimeMcpUrl,
|
|
29826
|
-
accountRoleLabel,
|
|
29827
29940
|
ackSlackInboundSeen,
|
|
29828
29941
|
addBoardPin,
|
|
29829
29942
|
addPrStackLayer,
|
|
@@ -30004,6 +30117,7 @@ init_outbound_watch();
|
|
|
30004
30117
|
fireSchedule,
|
|
30005
30118
|
flattenTurnInput,
|
|
30006
30119
|
flipLinearRelationType,
|
|
30120
|
+
foldLegacyRolesIntoNotes,
|
|
30007
30121
|
followUpBehavior,
|
|
30008
30122
|
forkChatTab,
|
|
30009
30123
|
forkMessageSlice,
|
|
@@ -30044,6 +30158,8 @@ init_outbound_watch();
|
|
|
30044
30158
|
formatSlackWorkingText,
|
|
30045
30159
|
formatTranscriptMarkdown,
|
|
30046
30160
|
formatUiReminder,
|
|
30161
|
+
formatViewerContextDirective,
|
|
30162
|
+
formatViewerContextReminder,
|
|
30047
30163
|
formatWorkspaceInventory,
|
|
30048
30164
|
formatWorkspaceProfileSuffix,
|
|
30049
30165
|
formatWorktreeDirective,
|
|
@@ -30281,7 +30397,6 @@ init_outbound_watch();
|
|
|
30281
30397
|
planQuestionsSignature,
|
|
30282
30398
|
pollSlackOutboundWatches,
|
|
30283
30399
|
posixShellSingleQuote,
|
|
30284
|
-
preferTeamsForRole,
|
|
30285
30400
|
preferredCursorCostCents,
|
|
30286
30401
|
prepareTerminalCommand,
|
|
30287
30402
|
previewLand,
|
|
@@ -30293,6 +30408,7 @@ init_outbound_watch();
|
|
|
30293
30408
|
readSetupLog,
|
|
30294
30409
|
readSkillBody,
|
|
30295
30410
|
readThread,
|
|
30411
|
+
readViewerContext,
|
|
30296
30412
|
readWorktreeFile,
|
|
30297
30413
|
readWorktreeFileForUpload,
|
|
30298
30414
|
readWorktreeInclude,
|
|
@@ -30349,7 +30465,6 @@ init_outbound_watch();
|
|
|
30349
30465
|
resolveViewerProfile,
|
|
30350
30466
|
resolveViewerProfileForRepo,
|
|
30351
30467
|
resolveWorktreeStartPoint,
|
|
30352
|
-
reviewTeamHintsForRoles,
|
|
30353
30468
|
rewriteAbleTimeError,
|
|
30354
30469
|
rewriteLinearError,
|
|
30355
30470
|
run,
|
|
@@ -30483,5 +30598,6 @@ init_outbound_watch();
|
|
|
30483
30598
|
writeInjectedMcpConfig,
|
|
30484
30599
|
writePlanFile,
|
|
30485
30600
|
writeThread,
|
|
30601
|
+
writeViewerContext,
|
|
30486
30602
|
writeWorktreeFile
|
|
30487
30603
|
});
|