@sideboard-ai/core 0.1.157 → 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-LCDXN2B2.js → agents-5HY76BCR.js} +4 -4
- package/dist/{agents-C55LAEUR.js → agents-67ZBH5OE.js} +5 -5
- 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-NVXVEGGM.js → chunk-5U4NZLBH.js} +4 -0
- package/dist/{chunk-DVJBO3M7.js → chunk-AGLPYXDT.js} +1 -1
- package/dist/{chunk-KQ4JOGUH.js → chunk-DWUOKF3W.js} +11 -5
- package/dist/{chunk-D7EGJ3EI.js → chunk-EQIJ5MHR.js} +119 -110
- package/dist/{chunk-3BWVOUQG.js → chunk-FLG4A5FJ.js} +119 -110
- package/dist/{chunk-2SDMENAL.js → chunk-JV4VNFMS.js} +1 -1
- package/dist/{chunk-LQMFCS54.js → chunk-KEDHHXKW.js} +3 -13
- package/dist/{chunk-CJQLPPEJ.js → chunk-MEZXWELT.js} +4 -14
- package/dist/{chunk-CVR4RJ6G.js → chunk-O7RWWPMR.js} +12 -13
- package/dist/{chunk-GWEEGE6L.js → chunk-OYOXLY6B.js} +404 -115
- package/dist/{chunk-4I4VKAPZ.js → chunk-SIMFKJIB.js} +13 -14
- package/dist/{chunk-SUCJAWV4.js → chunk-TGU3LSDG.js} +402 -113
- package/dist/{chunk-N27GVFZY.js → chunk-UBMCEZRE.js} +1 -1
- package/dist/{chunk-TIJ6QVX5.js → chunk-V4R6L2ZL.js} +15 -6
- package/dist/{coordinator-prompt-AM47SATF.js → coordinator-prompt-7NKY7QRK.js} +4 -4
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-CNI6AK65.js} +3 -3
- package/dist/{global-workspace-QV7CC7SK.js → global-workspace-FKAJZHO5.js} +4 -4
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-IJYCUO4M.js} +3 -3
- package/dist/index.cjs +1037 -648
- package/dist/index.d.cts +71 -57
- package/dist/index.d.ts +71 -57
- package/dist/index.js +339 -250
- package/dist/mcp/run-stdio.cjs +967 -575
- package/dist/mcp/run-stdio.js +283 -187
- package/dist/{orchestrator-BUH3LJLL.js → orchestrator-KFLS5BTP.js} +5 -5
- package/dist/{orchestrator-KQO4MXBI.js → orchestrator-NSVP7DJH.js} +7 -7
- package/dist/{plan-file-OZEBFSGQ.js → plan-file-I24VJVP2.js} +2 -2
- package/dist/{workspaces-NYSM2EPX.js → workspaces-D2M3AM2M.js} +3 -3
- package/dist/{workspaces-Q2VCHIAS.js → workspaces-OFSDJ7PZ.js} +4 -4
- package/dist/{worktree-PEWDAX5O.js → worktree-BQE5KLRY.js} +3 -3
- package/dist/{worktree-T4SWFSTT.js → worktree-EA6AD6D6.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -162,6 +162,10 @@ import {
|
|
|
162
162
|
listWorktreeFiles,
|
|
163
163
|
loadAgentInstructions,
|
|
164
164
|
maybeCompactContext,
|
|
165
|
+
mcpWaitFinishedHint,
|
|
166
|
+
mcpWaitForJobTimeoutMs,
|
|
167
|
+
mcpWaitForTurnTimeoutMs,
|
|
168
|
+
mcpWaitStillRunningHint,
|
|
165
169
|
mergeSetupOutput,
|
|
166
170
|
messagesSinceLastBrightsyContextSummary,
|
|
167
171
|
normalizeServiceOrigin,
|
|
@@ -223,13 +227,14 @@ import {
|
|
|
223
227
|
updateSchedule,
|
|
224
228
|
upsertSlackWorkspace,
|
|
225
229
|
verifyOptionalService,
|
|
230
|
+
waitForDetachedJob,
|
|
226
231
|
waitForPidExit,
|
|
227
232
|
withAgentInstructions,
|
|
228
233
|
worktreeBoardStatus,
|
|
229
234
|
worktreeCleanupSettings,
|
|
230
235
|
wrapReviewSkillMarkdown,
|
|
231
236
|
writeWorktreeFile
|
|
232
|
-
} from "./chunk-
|
|
237
|
+
} from "./chunk-OYOXLY6B.js";
|
|
233
238
|
import {
|
|
234
239
|
LEGACY_PLAN_FILE_REL,
|
|
235
240
|
PLAN_FILE_NAME,
|
|
@@ -332,7 +337,7 @@ import {
|
|
|
332
337
|
withEventParentId,
|
|
333
338
|
withEventsParentId,
|
|
334
339
|
writeInjectedMcpConfig
|
|
335
|
-
} from "./chunk-
|
|
340
|
+
} from "./chunk-V4R6L2ZL.js";
|
|
336
341
|
import {
|
|
337
342
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
338
343
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
@@ -379,7 +384,7 @@ import {
|
|
|
379
384
|
stageBuffersAsAttachments,
|
|
380
385
|
syncWorkspacesFromThreads,
|
|
381
386
|
takenTeamSlugsForOrchestration
|
|
382
|
-
} from "./chunk-
|
|
387
|
+
} from "./chunk-O7RWWPMR.js";
|
|
383
388
|
import {
|
|
384
389
|
FAMOUS_SOCCER_TEAMS,
|
|
385
390
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
@@ -472,7 +477,7 @@ import {
|
|
|
472
477
|
worktreeDisplayLabel,
|
|
473
478
|
worktreeDisplayLabelForGroup,
|
|
474
479
|
worktreeNameFromPath
|
|
475
|
-
} from "./chunk-
|
|
480
|
+
} from "./chunk-KEDHHXKW.js";
|
|
476
481
|
import {
|
|
477
482
|
ATTACHMENTS_DIR,
|
|
478
483
|
DETACHED_JOBS_DIR,
|
|
@@ -522,7 +527,7 @@ import {
|
|
|
522
527
|
toAbleTimeIssueInfo,
|
|
523
528
|
updateAbleTimeTask,
|
|
524
529
|
verifyAbleTimeConnection
|
|
525
|
-
} from "./chunk-
|
|
530
|
+
} from "./chunk-AGLPYXDT.js";
|
|
526
531
|
import {
|
|
527
532
|
formatFetchError,
|
|
528
533
|
httpFetch,
|
|
@@ -539,16 +544,11 @@ import {
|
|
|
539
544
|
withMaxOldSpaceSize
|
|
540
545
|
} from "./chunk-HEII7W3Q.js";
|
|
541
546
|
import {
|
|
542
|
-
ACCOUNT_ROLES,
|
|
543
|
-
ACCOUNT_ROLE_LABELS,
|
|
544
|
-
ACCOUNT_ROLE_MAX,
|
|
545
|
-
ACCOUNT_ROLE_PRESETS,
|
|
546
547
|
FOLLOW_UP_BEHAVIORS,
|
|
547
548
|
GITHUB_GIT_AUTH_MODES,
|
|
548
549
|
HARNESS_ENV_KEYS,
|
|
549
550
|
ISSUE_SOURCE_LABELS,
|
|
550
551
|
PROFILE_NOTES_MAX,
|
|
551
|
-
accountRoleLabel,
|
|
552
552
|
appSettingsPath,
|
|
553
553
|
applyAppEnvironment,
|
|
554
554
|
autoArchiveOnMergeEnabled,
|
|
@@ -572,9 +572,12 @@ import {
|
|
|
572
572
|
emptyPublicIntegrations,
|
|
573
573
|
ensureSlackDeviceIdentity,
|
|
574
574
|
findProjectProfileKey,
|
|
575
|
+
foldLegacyRolesIntoNotes,
|
|
575
576
|
followUpBehavior,
|
|
576
577
|
formatAccountProfilePlaybookLine,
|
|
577
578
|
formatProjectProfilePlaybookLines,
|
|
579
|
+
formatViewerContextDirective,
|
|
580
|
+
formatViewerContextReminder,
|
|
578
581
|
formatWorkspaceProfileSuffix,
|
|
579
582
|
getAbleTimeAccessToken,
|
|
580
583
|
getAbleTimeHost,
|
|
@@ -596,8 +599,8 @@ import {
|
|
|
596
599
|
orchestrationQuotaFallbackAgent,
|
|
597
600
|
orchestrationQuotaOnLimit,
|
|
598
601
|
persistVaultKeyInKeychain,
|
|
599
|
-
preferTeamsForRole,
|
|
600
602
|
readKeychainVaultKey,
|
|
603
|
+
readViewerContext,
|
|
601
604
|
resolveAccountProfile,
|
|
602
605
|
resolveAccountProfileFromSettings,
|
|
603
606
|
resolveAgentExecutable,
|
|
@@ -608,7 +611,6 @@ import {
|
|
|
608
611
|
resolveVaultKey,
|
|
609
612
|
resolveViewerProfile,
|
|
610
613
|
resolveViewerProfileForRepo,
|
|
611
|
-
reviewTeamHintsForRoles,
|
|
612
614
|
saveAbleTimeConnection,
|
|
613
615
|
saveAppSettings,
|
|
614
616
|
saveLinearOAuth,
|
|
@@ -627,8 +629,9 @@ import {
|
|
|
627
629
|
updateDefaultsSettings,
|
|
628
630
|
updateIntegrationsSettings,
|
|
629
631
|
updateOpencodeSettings,
|
|
630
|
-
updateProjectProfileSettings
|
|
631
|
-
|
|
632
|
+
updateProjectProfileSettings,
|
|
633
|
+
writeViewerContext
|
|
634
|
+
} from "./chunk-FLG4A5FJ.js";
|
|
632
635
|
import {
|
|
633
636
|
stripNestedElectronEnv
|
|
634
637
|
} from "./chunk-4TR3HZFT.js";
|
|
@@ -733,9 +736,9 @@ async function getGitHubStatus() {
|
|
|
733
736
|
};
|
|
734
737
|
}
|
|
735
738
|
const status = await run("gh", ["auth", "status"], { reject: false });
|
|
736
|
-
const
|
|
739
|
+
const text7 = `${status.stdout}
|
|
737
740
|
${status.stderr}`;
|
|
738
|
-
const loginMatch =
|
|
741
|
+
const loginMatch = text7.match(/Logged in to ([^\s]+) account (\S+)/i) ?? text7.match(/Logged in to ([^\s]+) as (\S+)/i);
|
|
739
742
|
if (loginMatch) {
|
|
740
743
|
return {
|
|
741
744
|
connected: true,
|
|
@@ -2060,11 +2063,11 @@ function fenceLanguage(path2, language) {
|
|
|
2060
2063
|
}
|
|
2061
2064
|
function buildCodeRefAttachment(input) {
|
|
2062
2065
|
const path2 = input.path.trim();
|
|
2063
|
-
const
|
|
2066
|
+
const text7 = input.text.replace(/\n$/, "");
|
|
2064
2067
|
if (!path2) {
|
|
2065
2068
|
throw new Error("code reference requires a file path");
|
|
2066
2069
|
}
|
|
2067
|
-
if (!
|
|
2070
|
+
if (!text7.trim()) {
|
|
2068
2071
|
throw new Error("code reference requires selected text");
|
|
2069
2072
|
}
|
|
2070
2073
|
if (input.startLine < 1 || input.endLine < 1 || input.endLine < input.startLine) {
|
|
@@ -2077,7 +2080,7 @@ function buildCodeRefAttachment(input) {
|
|
|
2077
2080
|
`Referenced code from \`${path2}\` (${range}).`,
|
|
2078
2081
|
"",
|
|
2079
2082
|
fence,
|
|
2080
|
-
|
|
2083
|
+
text7,
|
|
2081
2084
|
"```"
|
|
2082
2085
|
].join("\n");
|
|
2083
2086
|
return {
|
|
@@ -2131,16 +2134,16 @@ var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
|
2131
2134
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
2132
2135
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
2133
2136
|
var PASTED_NAME_ALT_RE = /^pasted-(\d+)\.txt$/i;
|
|
2134
|
-
function pastedTextStats(
|
|
2135
|
-
const chars =
|
|
2137
|
+
function pastedTextStats(text7) {
|
|
2138
|
+
const chars = text7.length;
|
|
2136
2139
|
if (chars === 0) return { chars: 0, lines: 0 };
|
|
2137
|
-
const lines =
|
|
2140
|
+
const lines = text7.split(/\r\n|\r|\n/).length;
|
|
2138
2141
|
return { chars, lines };
|
|
2139
2142
|
}
|
|
2140
|
-
function shouldAttachPastedText(
|
|
2141
|
-
const trimmed =
|
|
2143
|
+
function shouldAttachPastedText(text7) {
|
|
2144
|
+
const trimmed = text7.trim();
|
|
2142
2145
|
if (!trimmed) return false;
|
|
2143
|
-
const { chars, lines } = pastedTextStats(
|
|
2146
|
+
const { chars, lines } = pastedTextStats(text7);
|
|
2144
2147
|
return chars >= PASTE_ATTACH_MIN_CHARS || lines >= PASTE_ATTACH_MIN_LINES;
|
|
2145
2148
|
}
|
|
2146
2149
|
function nextPastedTextName(existing) {
|
|
@@ -2151,13 +2154,13 @@ function nextPastedTextName(existing) {
|
|
|
2151
2154
|
}
|
|
2152
2155
|
return `Pasted text #${max + 1}.txt`;
|
|
2153
2156
|
}
|
|
2154
|
-
function buildPastedTextAttachment(
|
|
2157
|
+
function buildPastedTextAttachment(text7, opts) {
|
|
2155
2158
|
return {
|
|
2156
2159
|
id: opts?.id ?? randomUUID(),
|
|
2157
2160
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
2158
2161
|
kind: "file",
|
|
2159
2162
|
path: opts?.path,
|
|
2160
|
-
content:
|
|
2163
|
+
content: text7
|
|
2161
2164
|
};
|
|
2162
2165
|
}
|
|
2163
2166
|
|
|
@@ -2242,8 +2245,8 @@ function latestPendingPlanQuestions(input) {
|
|
|
2242
2245
|
return null;
|
|
2243
2246
|
}
|
|
2244
2247
|
var PLAN_QUESTION_ANSWERS_PREFIX = "Answers to your questions:";
|
|
2245
|
-
function isPlanQuestionAnswersMessage(
|
|
2246
|
-
return
|
|
2248
|
+
function isPlanQuestionAnswersMessage(text7) {
|
|
2249
|
+
return text7.startsWith(PLAN_QUESTION_ANSWERS_PREFIX);
|
|
2247
2250
|
}
|
|
2248
2251
|
function formatPlanQuestionAnswers(questions, answers) {
|
|
2249
2252
|
const lines = [PLAN_QUESTION_ANSWERS_PREFIX, ""];
|
|
@@ -2282,7 +2285,7 @@ function formatPlanQuestionsForChat(questions) {
|
|
|
2282
2285
|
// src/mcp/server.ts
|
|
2283
2286
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2284
2287
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2285
|
-
import { z as
|
|
2288
|
+
import { z as z7 } from "zod";
|
|
2286
2289
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2287
2290
|
import { basename } from "path";
|
|
2288
2291
|
|
|
@@ -2294,35 +2297,13 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
2294
2297
|
return null;
|
|
2295
2298
|
}
|
|
2296
2299
|
|
|
2297
|
-
// src/mcp/wait-for-turn.ts
|
|
2298
|
-
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
2299
|
-
function mcpWaitForTurnTimeoutMs(requested) {
|
|
2300
|
-
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
2301
|
-
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
2302
|
-
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
2303
|
-
}
|
|
2304
|
-
var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
|
|
2305
|
-
var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
|
|
2306
|
-
function mcpWaitStillRunningHint(status) {
|
|
2307
|
-
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
2308
|
-
}
|
|
2309
|
-
var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
|
|
2310
|
-
var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
2311
|
-
var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
|
|
2312
|
-
function mcpWaitFinishedHint(status) {
|
|
2313
|
-
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
2314
|
-
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
2315
|
-
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
2316
|
-
return void 0;
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
2300
|
// src/mcp/thread-visibility.ts
|
|
2320
2301
|
function lastMessagePreview(messages, max = 160) {
|
|
2321
2302
|
if (!messages?.length) return null;
|
|
2322
2303
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2323
|
-
const
|
|
2324
|
-
if (!
|
|
2325
|
-
const flat =
|
|
2304
|
+
const text7 = messages[i]?.text?.trim();
|
|
2305
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
2306
|
+
const flat = text7.replace(/\s+/g, " ");
|
|
2326
2307
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
2327
2308
|
}
|
|
2328
2309
|
return null;
|
|
@@ -2554,8 +2535,8 @@ function labelGithubUrl(url) {
|
|
|
2554
2535
|
}
|
|
2555
2536
|
return { kind: "other", label: "GitHub", url: raw };
|
|
2556
2537
|
}
|
|
2557
|
-
function appendGithubLink(
|
|
2558
|
-
const body =
|
|
2538
|
+
function appendGithubLink(text7, githubUrl) {
|
|
2539
|
+
const body = text7.trimEnd();
|
|
2559
2540
|
if (!githubUrl?.trim()) return body;
|
|
2560
2541
|
const labeled = labelGithubUrl(githubUrl);
|
|
2561
2542
|
if (!labeled) {
|
|
@@ -3245,6 +3226,101 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
3245
3226
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
3246
3227
|
}
|
|
3247
3228
|
|
|
3229
|
+
// src/mcp/viewer-context-tools.ts
|
|
3230
|
+
import { z as z5 } from "zod";
|
|
3231
|
+
function text5(payload, isError = false) {
|
|
3232
|
+
return mcpJson(payload, isError);
|
|
3233
|
+
}
|
|
3234
|
+
function matchRegisteredWorkspace(repoPath) {
|
|
3235
|
+
const hint = (repoPath ?? "").trim().replace(/\/+$/, "");
|
|
3236
|
+
if (!hint) return null;
|
|
3237
|
+
const match = listWorkspaces().find((w) => {
|
|
3238
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
3239
|
+
return stored === hint || hint.startsWith(`${stored}/`) || stored.startsWith(`${hint}/`);
|
|
3240
|
+
});
|
|
3241
|
+
return match?.path.replace(/\/+$/, "") ?? null;
|
|
3242
|
+
}
|
|
3243
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
3244
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
3245
|
+
let root = hint;
|
|
3246
|
+
try {
|
|
3247
|
+
root = await resolveRepoRoot(hint);
|
|
3248
|
+
} catch {
|
|
3249
|
+
root = hint.replace(/\/+$/, "");
|
|
3250
|
+
}
|
|
3251
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
3252
|
+
}
|
|
3253
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_workspaces. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
3254
|
+
function registerViewerContextTools(server) {
|
|
3255
|
+
server.tool(
|
|
3256
|
+
"get_viewer_context",
|
|
3257
|
+
"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).",
|
|
3258
|
+
{
|
|
3259
|
+
repoPath: z5.string().optional().describe(
|
|
3260
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
3261
|
+
)
|
|
3262
|
+
},
|
|
3263
|
+
async ({ repoPath }) => {
|
|
3264
|
+
try {
|
|
3265
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
3266
|
+
return text5(readViewerContext(path2 ?? void 0));
|
|
3267
|
+
} catch (err) {
|
|
3268
|
+
return text5(
|
|
3269
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
3270
|
+
true
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
);
|
|
3275
|
+
server.tool(
|
|
3276
|
+
"update_viewer_context",
|
|
3277
|
+
"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.",
|
|
3278
|
+
{
|
|
3279
|
+
scope: z5.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
3280
|
+
context: z5.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
3281
|
+
confirmed: z5.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
3282
|
+
repoPath: z5.string().optional().describe(
|
|
3283
|
+
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
3284
|
+
)
|
|
3285
|
+
},
|
|
3286
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
3287
|
+
try {
|
|
3288
|
+
if (scope === "project") {
|
|
3289
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
3290
|
+
if (!path2) {
|
|
3291
|
+
return text5(
|
|
3292
|
+
{
|
|
3293
|
+
ok: false,
|
|
3294
|
+
error: "repoPath required",
|
|
3295
|
+
message: PROJECT_PATH_HINT
|
|
3296
|
+
},
|
|
3297
|
+
true
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
const result2 = writeViewerContext({
|
|
3301
|
+
scope,
|
|
3302
|
+
context,
|
|
3303
|
+
repoPath: path2,
|
|
3304
|
+
confirmed: confirmed === true
|
|
3305
|
+
});
|
|
3306
|
+
return text5(result2, !result2.ok);
|
|
3307
|
+
}
|
|
3308
|
+
const result = writeViewerContext({
|
|
3309
|
+
scope,
|
|
3310
|
+
context,
|
|
3311
|
+
confirmed: confirmed === true
|
|
3312
|
+
});
|
|
3313
|
+
return text5(result, !result.ok);
|
|
3314
|
+
} catch (err) {
|
|
3315
|
+
return text5(
|
|
3316
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
3317
|
+
true
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3248
3324
|
// src/mcp/pr-list.ts
|
|
3249
3325
|
function compactPrRow(pr) {
|
|
3250
3326
|
return {
|
|
@@ -3280,15 +3356,15 @@ function formatMcpPrList(input) {
|
|
|
3280
3356
|
}
|
|
3281
3357
|
|
|
3282
3358
|
// src/mcp/schedule-tools.ts
|
|
3283
|
-
import { z as
|
|
3284
|
-
function
|
|
3359
|
+
import { z as z6 } from "zod";
|
|
3360
|
+
function text6(payload, isError = false) {
|
|
3285
3361
|
return {
|
|
3286
3362
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
3287
3363
|
...isError ? { isError: true } : {}
|
|
3288
3364
|
};
|
|
3289
3365
|
}
|
|
3290
3366
|
function fail5(err) {
|
|
3291
|
-
return
|
|
3367
|
+
return text6(
|
|
3292
3368
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
3293
3369
|
true
|
|
3294
3370
|
);
|
|
@@ -3313,7 +3389,7 @@ function registerScheduleTools(server) {
|
|
|
3313
3389
|
{},
|
|
3314
3390
|
async () => {
|
|
3315
3391
|
try {
|
|
3316
|
-
return
|
|
3392
|
+
return text6({ schedules: listSchedules() });
|
|
3317
3393
|
} catch (err) {
|
|
3318
3394
|
return fail5(err);
|
|
3319
3395
|
}
|
|
@@ -3323,17 +3399,17 @@ function registerScheduleTools(server) {
|
|
|
3323
3399
|
"create_schedule",
|
|
3324
3400
|
"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.",
|
|
3325
3401
|
{
|
|
3326
|
-
prompt:
|
|
3327
|
-
name:
|
|
3328
|
-
at:
|
|
3329
|
-
every:
|
|
3330
|
-
cron:
|
|
3331
|
-
tz:
|
|
3332
|
-
threadId:
|
|
3402
|
+
prompt: z6.string().describe("User message / goal queued when the schedule fires"),
|
|
3403
|
+
name: z6.string().optional(),
|
|
3404
|
+
at: z6.string().optional().describe("ISO datetime for a one-shot"),
|
|
3405
|
+
every: z6.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
3406
|
+
cron: z6.string().optional().describe("5-field cron expression"),
|
|
3407
|
+
tz: z6.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
3408
|
+
threadId: z6.string().optional().describe(
|
|
3333
3409
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
3334
3410
|
),
|
|
3335
|
-
agent:
|
|
3336
|
-
model:
|
|
3411
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
3412
|
+
model: z6.string().optional()
|
|
3337
3413
|
},
|
|
3338
3414
|
async (args) => {
|
|
3339
3415
|
try {
|
|
@@ -3353,7 +3429,7 @@ function registerScheduleTools(server) {
|
|
|
3353
3429
|
model: args.model,
|
|
3354
3430
|
createdBy: "mcp"
|
|
3355
3431
|
});
|
|
3356
|
-
return
|
|
3432
|
+
return text6({
|
|
3357
3433
|
schedule,
|
|
3358
3434
|
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."
|
|
3359
3435
|
});
|
|
@@ -3366,17 +3442,17 @@ function registerScheduleTools(server) {
|
|
|
3366
3442
|
"update_schedule",
|
|
3367
3443
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
3368
3444
|
{
|
|
3369
|
-
id:
|
|
3370
|
-
prompt:
|
|
3371
|
-
name:
|
|
3372
|
-
at:
|
|
3373
|
-
every:
|
|
3374
|
-
cron:
|
|
3375
|
-
tz:
|
|
3376
|
-
threadId:
|
|
3377
|
-
agent:
|
|
3378
|
-
model:
|
|
3379
|
-
enabled:
|
|
3445
|
+
id: z6.string(),
|
|
3446
|
+
prompt: z6.string().optional(),
|
|
3447
|
+
name: z6.string().optional(),
|
|
3448
|
+
at: z6.string().optional(),
|
|
3449
|
+
every: z6.string().optional(),
|
|
3450
|
+
cron: z6.string().optional(),
|
|
3451
|
+
tz: z6.string().optional(),
|
|
3452
|
+
threadId: z6.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
3453
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
3454
|
+
model: z6.string().optional(),
|
|
3455
|
+
enabled: z6.boolean().optional()
|
|
3380
3456
|
},
|
|
3381
3457
|
async (args) => {
|
|
3382
3458
|
try {
|
|
@@ -3405,7 +3481,7 @@ function registerScheduleTools(server) {
|
|
|
3405
3481
|
enabled: args.enabled,
|
|
3406
3482
|
model: args.model
|
|
3407
3483
|
});
|
|
3408
|
-
return
|
|
3484
|
+
return text6({ schedule });
|
|
3409
3485
|
} catch (err) {
|
|
3410
3486
|
return fail5(err);
|
|
3411
3487
|
}
|
|
@@ -3414,11 +3490,11 @@ function registerScheduleTools(server) {
|
|
|
3414
3490
|
server.tool(
|
|
3415
3491
|
"delete_schedule",
|
|
3416
3492
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
3417
|
-
{ id:
|
|
3493
|
+
{ id: z6.string() },
|
|
3418
3494
|
async ({ id }) => {
|
|
3419
3495
|
try {
|
|
3420
3496
|
deleteSchedule(id);
|
|
3421
|
-
return
|
|
3497
|
+
return text6({ ok: true, id });
|
|
3422
3498
|
} catch (err) {
|
|
3423
3499
|
return fail5(err);
|
|
3424
3500
|
}
|
|
@@ -3427,14 +3503,14 @@ function registerScheduleTools(server) {
|
|
|
3427
3503
|
server.tool(
|
|
3428
3504
|
"run_schedule",
|
|
3429
3505
|
"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.",
|
|
3430
|
-
{ id:
|
|
3506
|
+
{ id: z6.string() },
|
|
3431
3507
|
async ({ id }) => {
|
|
3432
3508
|
try {
|
|
3433
3509
|
if (!getSchedule(id)) {
|
|
3434
3510
|
return fail5(new Error(`Schedule not found: ${id}`));
|
|
3435
3511
|
}
|
|
3436
3512
|
const schedule = await fireSchedule(id);
|
|
3437
|
-
return
|
|
3513
|
+
return text6({ schedule });
|
|
3438
3514
|
} catch (err) {
|
|
3439
3515
|
return fail5(err);
|
|
3440
3516
|
}
|
|
@@ -3837,7 +3913,7 @@ async function startMcpServer() {
|
|
|
3837
3913
|
);
|
|
3838
3914
|
}
|
|
3839
3915
|
try {
|
|
3840
|
-
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-
|
|
3916
|
+
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-YWJ4MVYM.js");
|
|
3841
3917
|
orch.setMaxConcurrent(maxConcurrentAgents2());
|
|
3842
3918
|
} catch {
|
|
3843
3919
|
}
|
|
@@ -3854,13 +3930,14 @@ async function startMcpServer() {
|
|
|
3854
3930
|
version: "0.1.0"
|
|
3855
3931
|
});
|
|
3856
3932
|
const worktreeProfile = sideboardMcpProfile() === "worktree";
|
|
3933
|
+
registerViewerContextTools(server);
|
|
3857
3934
|
if (worktreeProfile) {
|
|
3858
3935
|
registerConnectedIssueVendorTools(server);
|
|
3859
3936
|
}
|
|
3860
3937
|
if (!worktreeProfile) {
|
|
3861
3938
|
server.tool(
|
|
3862
3939
|
"list_workspaces",
|
|
3863
|
-
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and
|
|
3940
|
+
"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.",
|
|
3864
3941
|
{},
|
|
3865
3942
|
async () => {
|
|
3866
3943
|
const workspaces = orch.listWorkspaces();
|
|
@@ -3909,13 +3986,13 @@ async function startMcpServer() {
|
|
|
3909
3986
|
"list_board",
|
|
3910
3987
|
"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.",
|
|
3911
3988
|
{
|
|
3912
|
-
query:
|
|
3913
|
-
repoPath:
|
|
3914
|
-
kind:
|
|
3915
|
-
column:
|
|
3989
|
+
query: z7.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
3990
|
+
repoPath: z7.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
3991
|
+
kind: z7.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
3992
|
+
column: z7.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
3916
3993
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
3917
3994
|
),
|
|
3918
|
-
limit:
|
|
3995
|
+
limit: z7.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
3919
3996
|
},
|
|
3920
3997
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
3921
3998
|
const workspaces = orch.listWorkspaces();
|
|
@@ -3953,7 +4030,7 @@ async function startMcpServer() {
|
|
|
3953
4030
|
server.tool(
|
|
3954
4031
|
"get_thread",
|
|
3955
4032
|
"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.",
|
|
3956
|
-
{ ref:
|
|
4033
|
+
{ ref: z7.string() },
|
|
3957
4034
|
async ({ ref }) => {
|
|
3958
4035
|
const t = orch.getThread(ref);
|
|
3959
4036
|
if (!t) {
|
|
@@ -3995,17 +4072,17 @@ async function startMcpServer() {
|
|
|
3995
4072
|
"present_artifact",
|
|
3996
4073
|
"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.",
|
|
3997
4074
|
{
|
|
3998
|
-
title:
|
|
3999
|
-
type:
|
|
4075
|
+
title: z7.string().describe("Short title shown in the artifact pane header"),
|
|
4076
|
+
type: z7.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
4000
4077
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
4001
4078
|
),
|
|
4002
|
-
content:
|
|
4079
|
+
content: z7.string().describe(
|
|
4003
4080
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
4004
4081
|
),
|
|
4005
|
-
artifact_id:
|
|
4006
|
-
status:
|
|
4007
|
-
phase:
|
|
4008
|
-
mode:
|
|
4082
|
+
artifact_id: z7.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
4083
|
+
status: z7.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
4084
|
+
phase: z7.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
4085
|
+
mode: z7.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
4009
4086
|
},
|
|
4010
4087
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
4011
4088
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4026,15 +4103,15 @@ async function startMcpServer() {
|
|
|
4026
4103
|
"ask_user",
|
|
4027
4104
|
"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.",
|
|
4028
4105
|
{
|
|
4029
|
-
questions:
|
|
4030
|
-
|
|
4031
|
-
question:
|
|
4032
|
-
header:
|
|
4033
|
-
multiSelect:
|
|
4034
|
-
options:
|
|
4035
|
-
|
|
4036
|
-
label:
|
|
4037
|
-
description:
|
|
4106
|
+
questions: z7.array(
|
|
4107
|
+
z7.object({
|
|
4108
|
+
question: z7.string().describe("Full question text ending with ?"),
|
|
4109
|
+
header: z7.string().max(24).optional().describe("Short label shown above the question"),
|
|
4110
|
+
multiSelect: z7.boolean().optional().describe("Allow selecting multiple options"),
|
|
4111
|
+
options: z7.array(
|
|
4112
|
+
z7.object({
|
|
4113
|
+
label: z7.string(),
|
|
4114
|
+
description: z7.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
4038
4115
|
})
|
|
4039
4116
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
4040
4117
|
})
|
|
@@ -4053,9 +4130,9 @@ async function startMcpServer() {
|
|
|
4053
4130
|
"present_plan",
|
|
4054
4131
|
"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.",
|
|
4055
4132
|
{
|
|
4056
|
-
title:
|
|
4057
|
-
content:
|
|
4058
|
-
thread_id:
|
|
4133
|
+
title: z7.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
4134
|
+
content: z7.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
4135
|
+
thread_id: z7.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
4059
4136
|
},
|
|
4060
4137
|
async ({ title, content, thread_id }) => {
|
|
4061
4138
|
const { writePlanFile: writePlanFile2 } = await import("./plan-file-LSSSQGTQ.js");
|
|
@@ -4078,15 +4155,15 @@ async function startMcpServer() {
|
|
|
4078
4155
|
"present_schema",
|
|
4079
4156
|
"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.",
|
|
4080
4157
|
{
|
|
4081
|
-
title:
|
|
4082
|
-
mode:
|
|
4083
|
-
datasource:
|
|
4084
|
-
resource_id:
|
|
4085
|
-
record_id:
|
|
4086
|
-
resource:
|
|
4087
|
-
record:
|
|
4088
|
-
records:
|
|
4089
|
-
pane_id:
|
|
4158
|
+
title: z7.string().describe("Pane title"),
|
|
4159
|
+
mode: z7.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
4160
|
+
datasource: z7.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
4161
|
+
resource_id: z7.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
4162
|
+
record_id: z7.string().optional().describe("Record id when opening form mode"),
|
|
4163
|
+
resource: z7.record(z7.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
4164
|
+
record: z7.record(z7.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
4165
|
+
records: z7.array(z7.record(z7.unknown())).optional().describe("Inline records for table mode"),
|
|
4166
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
4090
4167
|
},
|
|
4091
4168
|
async (args) => {
|
|
4092
4169
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4107,10 +4184,10 @@ async function startMcpServer() {
|
|
|
4107
4184
|
"present_files",
|
|
4108
4185
|
"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.",
|
|
4109
4186
|
{
|
|
4110
|
-
title:
|
|
4111
|
-
datasource:
|
|
4112
|
-
path:
|
|
4113
|
-
pane_id:
|
|
4187
|
+
title: z7.string().optional().describe("Pane title (default: Files)"),
|
|
4188
|
+
datasource: z7.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
4189
|
+
path: z7.string().optional().describe("Initial folder path (e.g. public)"),
|
|
4190
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
4114
4191
|
},
|
|
4115
4192
|
async (args) => {
|
|
4116
4193
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4125,19 +4202,33 @@ async function startMcpServer() {
|
|
|
4125
4202
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
4126
4203
|
}
|
|
4127
4204
|
);
|
|
4205
|
+
server.tool(
|
|
4206
|
+
"wait_for_job",
|
|
4207
|
+
"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.",
|
|
4208
|
+
{
|
|
4209
|
+
id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
4210
|
+
timeoutMs: z7.number().optional()
|
|
4211
|
+
},
|
|
4212
|
+
async ({ id, timeoutMs }) => {
|
|
4213
|
+
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
4214
|
+
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
4215
|
+
});
|
|
4216
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
4217
|
+
}
|
|
4218
|
+
);
|
|
4128
4219
|
if (!worktreeProfile) {
|
|
4129
4220
|
registerSlackTools(server);
|
|
4130
4221
|
registerConnectedIssueVendorTools(server);
|
|
4131
4222
|
registerScheduleTools(server);
|
|
4132
4223
|
const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-SK2QISPE.js");
|
|
4133
|
-
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-
|
|
4224
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-YWJ4MVYM.js");
|
|
4134
4225
|
const accountDefaults = resolveThreadDefaults2();
|
|
4135
4226
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
4136
4227
|
server.tool(
|
|
4137
4228
|
"set_caffeinate",
|
|
4138
4229
|
"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.",
|
|
4139
4230
|
{
|
|
4140
|
-
enabled:
|
|
4231
|
+
enabled: z7.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
4141
4232
|
},
|
|
4142
4233
|
async ({ enabled }) => {
|
|
4143
4234
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -4175,18 +4266,18 @@ async function startMcpServer() {
|
|
|
4175
4266
|
"create_thread",
|
|
4176
4267
|
`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.`,
|
|
4177
4268
|
{
|
|
4178
|
-
sourceType:
|
|
4179
|
-
sourceRef:
|
|
4180
|
-
agent:
|
|
4181
|
-
model:
|
|
4269
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]),
|
|
4270
|
+
sourceRef: z7.string(),
|
|
4271
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
4272
|
+
model: z7.string().nullable().optional().describe(
|
|
4182
4273
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
4183
4274
|
),
|
|
4184
|
-
repoPath:
|
|
4185
|
-
title:
|
|
4186
|
-
cowboy:
|
|
4275
|
+
repoPath: z7.string(),
|
|
4276
|
+
title: z7.string().optional(),
|
|
4277
|
+
cowboy: z7.boolean().optional().describe(
|
|
4187
4278
|
"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."
|
|
4188
4279
|
),
|
|
4189
|
-
parentThreadId:
|
|
4280
|
+
parentThreadId: z7.string().optional().describe(
|
|
4190
4281
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
4191
4282
|
)
|
|
4192
4283
|
},
|
|
@@ -4202,10 +4293,10 @@ async function startMcpServer() {
|
|
|
4202
4293
|
"start_board_card",
|
|
4203
4294
|
"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.",
|
|
4204
4295
|
{
|
|
4205
|
-
kind:
|
|
4206
|
-
ref:
|
|
4207
|
-
repoPath:
|
|
4208
|
-
title:
|
|
4296
|
+
kind: z7.enum(["ticket", "pr", "branch"]),
|
|
4297
|
+
ref: z7.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
4298
|
+
repoPath: z7.string().describe("Workspace path from list_workspaces / list_board"),
|
|
4299
|
+
title: z7.string().optional()
|
|
4209
4300
|
},
|
|
4210
4301
|
async ({ kind, ref, repoPath, title }) => {
|
|
4211
4302
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4309,9 +4400,9 @@ async function startMcpServer() {
|
|
|
4309
4400
|
"send_to_thread",
|
|
4310
4401
|
'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.',
|
|
4311
4402
|
{
|
|
4312
|
-
ref:
|
|
4313
|
-
prompt:
|
|
4314
|
-
force_stop:
|
|
4403
|
+
ref: z7.string(),
|
|
4404
|
+
prompt: z7.string(),
|
|
4405
|
+
force_stop: z7.boolean().optional()
|
|
4315
4406
|
},
|
|
4316
4407
|
async ({ ref, prompt, force_stop }) => {
|
|
4317
4408
|
if (force_stop) {
|
|
@@ -4340,8 +4431,8 @@ async function startMcpServer() {
|
|
|
4340
4431
|
"wait_for_turn",
|
|
4341
4432
|
"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).",
|
|
4342
4433
|
{
|
|
4343
|
-
ref:
|
|
4344
|
-
timeoutMs:
|
|
4434
|
+
ref: z7.string(),
|
|
4435
|
+
timeoutMs: z7.number().optional()
|
|
4345
4436
|
},
|
|
4346
4437
|
async ({ ref, timeoutMs }) => {
|
|
4347
4438
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -4371,7 +4462,7 @@ async function startMcpServer() {
|
|
|
4371
4462
|
server.tool(
|
|
4372
4463
|
"get_turn_result",
|
|
4373
4464
|
"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).",
|
|
4374
|
-
{ ref:
|
|
4465
|
+
{ ref: z7.string() },
|
|
4375
4466
|
async ({ ref }) => {
|
|
4376
4467
|
const result = orch.getTurnResult(ref);
|
|
4377
4468
|
return {
|
|
@@ -4392,8 +4483,8 @@ async function startMcpServer() {
|
|
|
4392
4483
|
"stop_thread",
|
|
4393
4484
|
"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.",
|
|
4394
4485
|
{
|
|
4395
|
-
ref:
|
|
4396
|
-
force:
|
|
4486
|
+
ref: z7.string(),
|
|
4487
|
+
force: z7.boolean().optional()
|
|
4397
4488
|
},
|
|
4398
4489
|
async ({ ref, force }) => {
|
|
4399
4490
|
const t = orch.getThread(ref);
|
|
@@ -4423,7 +4514,7 @@ async function startMcpServer() {
|
|
|
4423
4514
|
server.tool(
|
|
4424
4515
|
"archive_thread",
|
|
4425
4516
|
"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.",
|
|
4426
|
-
{ ref:
|
|
4517
|
+
{ ref: z7.string() },
|
|
4427
4518
|
async ({ ref }) => {
|
|
4428
4519
|
const t = orch.getThread(ref);
|
|
4429
4520
|
if (!t) {
|
|
@@ -4456,7 +4547,7 @@ async function startMcpServer() {
|
|
|
4456
4547
|
server.tool(
|
|
4457
4548
|
"restore_thread",
|
|
4458
4549
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
4459
|
-
{ ref:
|
|
4550
|
+
{ ref: z7.string() },
|
|
4460
4551
|
async ({ ref }) => {
|
|
4461
4552
|
try {
|
|
4462
4553
|
const restored = await orch.restore(ref);
|
|
@@ -4482,8 +4573,8 @@ async function startMcpServer() {
|
|
|
4482
4573
|
"get_diff",
|
|
4483
4574
|
"Compact diff summary (capped hunks, paginated)",
|
|
4484
4575
|
{
|
|
4485
|
-
ref:
|
|
4486
|
-
maxFiles:
|
|
4576
|
+
ref: z7.string(),
|
|
4577
|
+
maxFiles: z7.number().optional()
|
|
4487
4578
|
},
|
|
4488
4579
|
async ({ ref, maxFiles }) => {
|
|
4489
4580
|
const summary = await orch.diffSummary(ref);
|
|
@@ -4503,7 +4594,7 @@ async function startMcpServer() {
|
|
|
4503
4594
|
server.tool(
|
|
4504
4595
|
"get_pr_checks",
|
|
4505
4596
|
"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.",
|
|
4506
|
-
{ ref:
|
|
4597
|
+
{ ref: z7.string().describe("Worktree thread id/ref") },
|
|
4507
4598
|
async ({ ref }) => {
|
|
4508
4599
|
try {
|
|
4509
4600
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -4519,7 +4610,7 @@ async function startMcpServer() {
|
|
|
4519
4610
|
server.tool(
|
|
4520
4611
|
"request_review",
|
|
4521
4612
|
'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.',
|
|
4522
|
-
{ ref:
|
|
4613
|
+
{ ref: z7.string().describe("Worktree thread id/ref to review") },
|
|
4523
4614
|
async ({ ref }) => {
|
|
4524
4615
|
try {
|
|
4525
4616
|
const tab = await orch.requestReview(ref);
|
|
@@ -4548,8 +4639,8 @@ async function startMcpServer() {
|
|
|
4548
4639
|
"ask_git",
|
|
4549
4640
|
"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.",
|
|
4550
4641
|
{
|
|
4551
|
-
ref:
|
|
4552
|
-
action:
|
|
4642
|
+
ref: z7.string().describe("Worktree thread id/ref"),
|
|
4643
|
+
action: z7.enum(AGENT_GIT_ACTIONS).describe(
|
|
4553
4644
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
4554
4645
|
)
|
|
4555
4646
|
},
|
|
@@ -4594,7 +4685,7 @@ async function startMcpServer() {
|
|
|
4594
4685
|
}
|
|
4595
4686
|
}
|
|
4596
4687
|
);
|
|
4597
|
-
const agentEnum =
|
|
4688
|
+
const agentEnum = z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
4598
4689
|
server.tool(
|
|
4599
4690
|
"list_models",
|
|
4600
4691
|
"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.",
|
|
@@ -4617,11 +4708,11 @@ async function startMcpServer() {
|
|
|
4617
4708
|
"fork_worktree",
|
|
4618
4709
|
"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.",
|
|
4619
4710
|
{
|
|
4620
|
-
ref:
|
|
4621
|
-
through_index:
|
|
4711
|
+
ref: z7.string().describe("Worktree thread id/ref to fork"),
|
|
4712
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4622
4713
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4623
|
-
model:
|
|
4624
|
-
title:
|
|
4714
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4715
|
+
title: z7.string().optional()
|
|
4625
4716
|
},
|
|
4626
4717
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4627
4718
|
try {
|
|
@@ -4662,11 +4753,11 @@ async function startMcpServer() {
|
|
|
4662
4753
|
"fork_chat",
|
|
4663
4754
|
"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.",
|
|
4664
4755
|
{
|
|
4665
|
-
ref:
|
|
4666
|
-
through_index:
|
|
4756
|
+
ref: z7.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
4757
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4667
4758
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4668
|
-
model:
|
|
4669
|
-
title:
|
|
4759
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4760
|
+
title: z7.string().optional()
|
|
4670
4761
|
},
|
|
4671
4762
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4672
4763
|
try {
|
|
@@ -4712,8 +4803,8 @@ async function startMcpServer() {
|
|
|
4712
4803
|
"run_dev_script",
|
|
4713
4804
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
4714
4805
|
{
|
|
4715
|
-
ref:
|
|
4716
|
-
name:
|
|
4806
|
+
ref: z7.string(),
|
|
4807
|
+
name: z7.string().optional()
|
|
4717
4808
|
},
|
|
4718
4809
|
async ({ ref, name }) => {
|
|
4719
4810
|
const result = await orch.startDev(ref, name);
|
|
@@ -4735,7 +4826,7 @@ async function startMcpServer() {
|
|
|
4735
4826
|
server.tool(
|
|
4736
4827
|
"list_run_scripts",
|
|
4737
4828
|
"List named run scripts available for a thread",
|
|
4738
|
-
{ ref:
|
|
4829
|
+
{ ref: z7.string() },
|
|
4739
4830
|
async ({ ref }) => {
|
|
4740
4831
|
const scripts = orch.listThreadRunScripts(ref);
|
|
4741
4832
|
const active = orch.getActiveRuns(ref);
|
|
@@ -4753,8 +4844,8 @@ async function startMcpServer() {
|
|
|
4753
4844
|
"stop_dev_script",
|
|
4754
4845
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
4755
4846
|
{
|
|
4756
|
-
ref:
|
|
4757
|
-
name:
|
|
4847
|
+
ref: z7.string(),
|
|
4848
|
+
name: z7.string().optional()
|
|
4758
4849
|
},
|
|
4759
4850
|
async ({ ref, name }) => {
|
|
4760
4851
|
orch.stopDev(ref, name);
|
|
@@ -4764,7 +4855,7 @@ async function startMcpServer() {
|
|
|
4764
4855
|
server.tool(
|
|
4765
4856
|
"run_setup",
|
|
4766
4857
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
4767
|
-
{ ref:
|
|
4858
|
+
{ ref: z7.string() },
|
|
4768
4859
|
async ({ ref }) => {
|
|
4769
4860
|
const result = await orch.runSetup(ref);
|
|
4770
4861
|
return {
|
|
@@ -4775,7 +4866,7 @@ async function startMcpServer() {
|
|
|
4775
4866
|
server.tool(
|
|
4776
4867
|
"add_workspace",
|
|
4777
4868
|
"Register a git repo as a Sideboard workspace",
|
|
4778
|
-
{ repoPath:
|
|
4869
|
+
{ repoPath: z7.string() },
|
|
4779
4870
|
async ({ repoPath }) => {
|
|
4780
4871
|
const ws = await orch.addWorkspace(repoPath);
|
|
4781
4872
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -4784,7 +4875,7 @@ async function startMcpServer() {
|
|
|
4784
4875
|
server.tool(
|
|
4785
4876
|
"remove_workspace",
|
|
4786
4877
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
4787
|
-
{ repoPath:
|
|
4878
|
+
{ repoPath: z7.string() },
|
|
4788
4879
|
async ({ repoPath }) => {
|
|
4789
4880
|
orch.removeWorkspace(repoPath);
|
|
4790
4881
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -4794,12 +4885,12 @@ async function startMcpServer() {
|
|
|
4794
4885
|
"fanout",
|
|
4795
4886
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
4796
4887
|
{
|
|
4797
|
-
prompt:
|
|
4798
|
-
agents:
|
|
4799
|
-
repoPath:
|
|
4800
|
-
sourceType:
|
|
4801
|
-
sourceRef:
|
|
4802
|
-
title:
|
|
4888
|
+
prompt: z7.string(),
|
|
4889
|
+
agents: z7.array(z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
4890
|
+
repoPath: z7.string(),
|
|
4891
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]).optional(),
|
|
4892
|
+
sourceRef: z7.string().optional(),
|
|
4893
|
+
title: z7.string().optional()
|
|
4803
4894
|
},
|
|
4804
4895
|
async (args) => {
|
|
4805
4896
|
const threads = await orch.bestOfN(args);
|
|
@@ -4826,8 +4917,8 @@ async function startMcpServer() {
|
|
|
4826
4917
|
"list_branches",
|
|
4827
4918
|
"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).",
|
|
4828
4919
|
{
|
|
4829
|
-
repoPath:
|
|
4830
|
-
unmergedOnly:
|
|
4920
|
+
repoPath: z7.string(),
|
|
4921
|
+
unmergedOnly: z7.boolean().optional()
|
|
4831
4922
|
},
|
|
4832
4923
|
async ({ repoPath, unmergedOnly }) => {
|
|
4833
4924
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4848,19 +4939,19 @@ async function startMcpServer() {
|
|
|
4848
4939
|
"list_prs",
|
|
4849
4940
|
'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.',
|
|
4850
4941
|
{
|
|
4851
|
-
repoPath:
|
|
4852
|
-
query:
|
|
4853
|
-
queue:
|
|
4942
|
+
repoPath: z7.string(),
|
|
4943
|
+
query: z7.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
4944
|
+
queue: z7.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
4854
4945
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
4855
4946
|
),
|
|
4856
|
-
state:
|
|
4857
|
-
label:
|
|
4947
|
+
state: z7.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
4948
|
+
label: z7.string().optional().describe(
|
|
4858
4949
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
4859
4950
|
),
|
|
4860
|
-
reviewer:
|
|
4951
|
+
reviewer: z7.string().optional().describe(
|
|
4861
4952
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
4862
4953
|
),
|
|
4863
|
-
limit:
|
|
4954
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
4864
4955
|
},
|
|
4865
4956
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
4866
4957
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4892,7 +4983,7 @@ async function startMcpServer() {
|
|
|
4892
4983
|
server.tool(
|
|
4893
4984
|
"get_pr_stack",
|
|
4894
4985
|
"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).",
|
|
4895
|
-
{ ref:
|
|
4986
|
+
{ ref: z7.string() },
|
|
4896
4987
|
async ({ ref }) => {
|
|
4897
4988
|
const stack = await orch.getPrStack(ref);
|
|
4898
4989
|
return {
|
|
@@ -4904,8 +4995,8 @@ async function startMcpServer() {
|
|
|
4904
4995
|
"open_pr_stack_layers",
|
|
4905
4996
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
4906
4997
|
{
|
|
4907
|
-
ref:
|
|
4908
|
-
layer:
|
|
4998
|
+
ref: z7.string(),
|
|
4999
|
+
layer: z7.number().int().positive().optional()
|
|
4909
5000
|
},
|
|
4910
5001
|
async ({ ref, layer }) => {
|
|
4911
5002
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -4939,9 +5030,9 @@ async function startMcpServer() {
|
|
|
4939
5030
|
"add_stack_layer",
|
|
4940
5031
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
4941
5032
|
{
|
|
4942
|
-
ref:
|
|
4943
|
-
branchName:
|
|
4944
|
-
title:
|
|
5033
|
+
ref: z7.string(),
|
|
5034
|
+
branchName: z7.string(),
|
|
5035
|
+
title: z7.string().optional()
|
|
4945
5036
|
},
|
|
4946
5037
|
async ({ ref, branchName, title }) => {
|
|
4947
5038
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -4970,11 +5061,11 @@ async function startMcpServer() {
|
|
|
4970
5061
|
"create_pr_stack",
|
|
4971
5062
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
4972
5063
|
{
|
|
4973
|
-
repoPath:
|
|
4974
|
-
branches:
|
|
4975
|
-
agent:
|
|
4976
|
-
base:
|
|
4977
|
-
title:
|
|
5064
|
+
repoPath: z7.string(),
|
|
5065
|
+
branches: z7.array(z7.string()).min(1),
|
|
5066
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
5067
|
+
base: z7.string().optional(),
|
|
5068
|
+
title: z7.string().optional()
|
|
4978
5069
|
},
|
|
4979
5070
|
async (args) => {
|
|
4980
5071
|
const result = await orch.createPrStack({
|
|
@@ -5013,10 +5104,10 @@ async function startMcpServer() {
|
|
|
5013
5104
|
"list_issues",
|
|
5014
5105
|
"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.",
|
|
5015
5106
|
{
|
|
5016
|
-
repoPath:
|
|
5017
|
-
query:
|
|
5018
|
-
assignee:
|
|
5019
|
-
limit:
|
|
5107
|
+
repoPath: z7.string(),
|
|
5108
|
+
query: z7.string().optional().describe("Search title, identifier, or description"),
|
|
5109
|
+
assignee: z7.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
5110
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
5020
5111
|
},
|
|
5021
5112
|
async ({ repoPath, query, assignee, limit }) => {
|
|
5022
5113
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -5148,8 +5239,8 @@ var BrightsySideboardApi = class {
|
|
|
5148
5239
|
};
|
|
5149
5240
|
function taskMessageText(task) {
|
|
5150
5241
|
const parts = task.message?.parts ?? [];
|
|
5151
|
-
const
|
|
5152
|
-
return
|
|
5242
|
+
const text7 = parts.find((p) => p.kind === "text")?.text ?? "";
|
|
5243
|
+
return text7.trim();
|
|
5153
5244
|
}
|
|
5154
5245
|
|
|
5155
5246
|
// src/brightsy/cloud-connect.ts
|
|
@@ -5622,11 +5713,11 @@ async function startSlackOAuth(opts) {
|
|
|
5622
5713
|
import { WebSocket as WsWebSocket } from "ws";
|
|
5623
5714
|
var BACKOFF_START_MS = 1e3;
|
|
5624
5715
|
var BACKOFF_MAX_MS = 3e4;
|
|
5625
|
-
function stripSlackMentions(
|
|
5626
|
-
return
|
|
5716
|
+
function stripSlackMentions(text7) {
|
|
5717
|
+
return text7.replace(/<@[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
5627
5718
|
}
|
|
5628
|
-
function isSlackStopCommand(
|
|
5629
|
-
const t = stripSlackMentions(
|
|
5719
|
+
function isSlackStopCommand(text7) {
|
|
5720
|
+
const t = stripSlackMentions(text7).toLowerCase();
|
|
5630
5721
|
return t === "stop" || t === "sideboard_force_stop";
|
|
5631
5722
|
}
|
|
5632
5723
|
function parseSlackSocketFrame(raw) {
|
|
@@ -5648,8 +5739,8 @@ function inboundFromSocketFrame(frame) {
|
|
|
5648
5739
|
const ts = event.ts?.trim();
|
|
5649
5740
|
if (!channelId || !ts) return null;
|
|
5650
5741
|
const rawText = event.text?.trim() ?? "";
|
|
5651
|
-
const
|
|
5652
|
-
if (!
|
|
5742
|
+
const text7 = stripSlackMentions(rawText);
|
|
5743
|
+
if (!text7) return null;
|
|
5653
5744
|
const teamId = (frame.payload?.team_id || event.team || "").trim();
|
|
5654
5745
|
if (!teamId) return null;
|
|
5655
5746
|
const isDm = event.type === "message" && (event.channel_type === "im" || event.channel_type === "mpim" || !event.channel_type && channelId.startsWith("D"));
|
|
@@ -5661,7 +5752,7 @@ function inboundFromSocketFrame(frame) {
|
|
|
5661
5752
|
ts,
|
|
5662
5753
|
threadTs: event.thread_ts?.trim() || void 0,
|
|
5663
5754
|
userId: event.user?.trim(),
|
|
5664
|
-
text:
|
|
5755
|
+
text: text7,
|
|
5665
5756
|
kind: isMention ? "mention" : "dm"
|
|
5666
5757
|
};
|
|
5667
5758
|
}
|
|
@@ -6201,12 +6292,12 @@ function formatSlackInboundPrompt(msg) {
|
|
|
6201
6292
|
|
|
6202
6293
|
${msg.text}`;
|
|
6203
6294
|
}
|
|
6204
|
-
function isSlackInboundUserPrompt(
|
|
6205
|
-
return
|
|
6295
|
+
function isSlackInboundUserPrompt(text7) {
|
|
6296
|
+
return text7.startsWith("Slack DM\n") || text7.startsWith("Slack @mention\n");
|
|
6206
6297
|
}
|
|
6207
|
-
function formatSlackSignedReply(deviceLabel,
|
|
6298
|
+
function formatSlackSignedReply(deviceLabel, text7) {
|
|
6208
6299
|
const label = deviceLabel.trim();
|
|
6209
|
-
const body =
|
|
6300
|
+
const body = text7.trim();
|
|
6210
6301
|
if (!body) return body;
|
|
6211
6302
|
if (!label) return body;
|
|
6212
6303
|
const head = `${label}:`;
|
|
@@ -6215,8 +6306,8 @@ function formatSlackSignedReply(deviceLabel, text6) {
|
|
|
6215
6306
|
}
|
|
6216
6307
|
return `${label}: ${body}`;
|
|
6217
6308
|
}
|
|
6218
|
-
function signForThisMac(
|
|
6219
|
-
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel,
|
|
6309
|
+
function signForThisMac(text7) {
|
|
6310
|
+
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel, text7);
|
|
6220
6311
|
}
|
|
6221
6312
|
function slackReplyThreadTs(msg) {
|
|
6222
6313
|
if (msg.threadTs && msg.threadTs !== msg.ts) return msg.threadTs;
|
|
@@ -6277,9 +6368,9 @@ function replyStub(target) {
|
|
|
6277
6368
|
kind: "dm"
|
|
6278
6369
|
};
|
|
6279
6370
|
}
|
|
6280
|
-
async function postSlackText(target,
|
|
6371
|
+
async function postSlackText(target, text7, opts) {
|
|
6281
6372
|
if (opts.postReply) {
|
|
6282
|
-
const result = await opts.postReply(replyStub(target),
|
|
6373
|
+
const result = await opts.postReply(replyStub(target), text7);
|
|
6283
6374
|
const ts2 = result && typeof result === "object" && typeof result.ts === "string" ? result.ts.trim() : "";
|
|
6284
6375
|
return ts2 ? { ts: ts2 } : null;
|
|
6285
6376
|
}
|
|
@@ -6289,7 +6380,7 @@ async function postSlackText(target, text6, opts) {
|
|
|
6289
6380
|
"chat.postMessage",
|
|
6290
6381
|
{
|
|
6291
6382
|
channel: target.channelId,
|
|
6292
|
-
text:
|
|
6383
|
+
text: text7,
|
|
6293
6384
|
thread_ts: target.threadTs
|
|
6294
6385
|
},
|
|
6295
6386
|
opts.fetchImpl
|
|
@@ -6297,9 +6388,9 @@ async function postSlackText(target, text6, opts) {
|
|
|
6297
6388
|
const ts = json.ts?.trim();
|
|
6298
6389
|
return ts ? { ts } : null;
|
|
6299
6390
|
}
|
|
6300
|
-
async function updateSlackText(target, ts,
|
|
6391
|
+
async function updateSlackText(target, ts, text7, opts) {
|
|
6301
6392
|
if (opts.updateReply) {
|
|
6302
|
-
await opts.updateReply(replyStub(target), ts,
|
|
6393
|
+
await opts.updateReply(replyStub(target), ts, text7);
|
|
6303
6394
|
return;
|
|
6304
6395
|
}
|
|
6305
6396
|
const token = writeTokenForTeam(target.teamId);
|
|
@@ -6309,7 +6400,7 @@ async function updateSlackText(target, ts, text6, opts) {
|
|
|
6309
6400
|
{
|
|
6310
6401
|
channel: target.channelId,
|
|
6311
6402
|
ts,
|
|
6312
|
-
text:
|
|
6403
|
+
text: text7
|
|
6313
6404
|
},
|
|
6314
6405
|
opts.fetchImpl
|
|
6315
6406
|
);
|
|
@@ -6337,8 +6428,8 @@ function replyTargetOf(msg) {
|
|
|
6337
6428
|
threadTs: slackReplyThreadTs(msg)
|
|
6338
6429
|
};
|
|
6339
6430
|
}
|
|
6340
|
-
async function postSlackReply(msg,
|
|
6341
|
-
await postSlackText(replyTargetOf(msg), signForThisMac(
|
|
6431
|
+
async function postSlackReply(msg, text7, opts) {
|
|
6432
|
+
await postSlackText(replyTargetOf(msg), signForThisMac(text7), opts);
|
|
6342
6433
|
}
|
|
6343
6434
|
function startSlackTurnProgress(msg, threadId, opts) {
|
|
6344
6435
|
const log = opts.onLog ?? (() => void 0);
|
|
@@ -6421,10 +6512,10 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
6421
6512
|
}
|
|
6422
6513
|
});
|
|
6423
6514
|
},
|
|
6424
|
-
finishWith: (
|
|
6515
|
+
finishWith: (text7) => {
|
|
6425
6516
|
stop();
|
|
6426
6517
|
return run2(async () => {
|
|
6427
|
-
const signed = signForThisMac(
|
|
6518
|
+
const signed = signForThisMac(text7.trim());
|
|
6428
6519
|
if (!signed) {
|
|
6429
6520
|
if (!postedTs) return;
|
|
6430
6521
|
const ts = postedTs;
|
|
@@ -6452,11 +6543,11 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
6452
6543
|
};
|
|
6453
6544
|
}
|
|
6454
6545
|
var lastRelayed = /* @__PURE__ */ new Map();
|
|
6455
|
-
function markRelayed(threadId,
|
|
6456
|
-
lastRelayed.set(threadId, `${threadId}:${
|
|
6546
|
+
function markRelayed(threadId, text7) {
|
|
6547
|
+
lastRelayed.set(threadId, `${threadId}:${text7}`);
|
|
6457
6548
|
}
|
|
6458
|
-
function alreadyRelayed(threadId,
|
|
6459
|
-
return lastRelayed.get(threadId) === `${threadId}:${
|
|
6549
|
+
function alreadyRelayed(threadId, text7) {
|
|
6550
|
+
return lastRelayed.get(threadId) === `${threadId}:${text7}`;
|
|
6460
6551
|
}
|
|
6461
6552
|
async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
6462
6553
|
const target = getSlackReplyTarget(threadId);
|
|
@@ -6465,14 +6556,14 @@ async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
|
6465
6556
|
const lastUser = thread ? [...thread.messages].reverse().find((m) => m.role === "user") : void 0;
|
|
6466
6557
|
if (lastUser && isSlackInboundUserPrompt(lastUser.text)) return;
|
|
6467
6558
|
const result = getOrchestrator().getTurnResult(threadId);
|
|
6468
|
-
const
|
|
6469
|
-
if (!
|
|
6470
|
-
if (alreadyRelayed(threadId,
|
|
6471
|
-
markRelayed(threadId,
|
|
6559
|
+
const text7 = result.text.trim();
|
|
6560
|
+
if (!text7) return;
|
|
6561
|
+
if (alreadyRelayed(threadId, text7)) return;
|
|
6562
|
+
markRelayed(threadId, text7);
|
|
6472
6563
|
const log = opts.onLog ?? (() => void 0);
|
|
6473
6564
|
try {
|
|
6474
|
-
await postSlackText(target, signForThisMac(
|
|
6475
|
-
log(`replied ${target.threadTs ?? target.channelId} (${
|
|
6565
|
+
await postSlackText(target, signForThisMac(text7), opts);
|
|
6566
|
+
log(`replied ${target.threadTs ?? target.channelId} (${text7.length} chars)`);
|
|
6476
6567
|
} catch (err) {
|
|
6477
6568
|
lastRelayed.delete(threadId);
|
|
6478
6569
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -6720,10 +6811,10 @@ function resolveSlackListenMode(opts) {
|
|
|
6720
6811
|
}
|
|
6721
6812
|
|
|
6722
6813
|
// src/slack/relay-hub.ts
|
|
6723
|
-
function parseSlackDeviceDestination(
|
|
6724
|
-
const m =
|
|
6725
|
-
if (!m) return { label: null, rest:
|
|
6726
|
-
return { label: m[1], rest:
|
|
6814
|
+
function parseSlackDeviceDestination(text7) {
|
|
6815
|
+
const m = text7.match(/^\s*(?:to\s+)?(?:@|#)?([A-Za-z][\w-]{0,63})\s*[::]\s*/);
|
|
6816
|
+
if (!m) return { label: null, rest: text7 };
|
|
6817
|
+
return { label: m[1], rest: text7.slice(m[0].length) };
|
|
6727
6818
|
}
|
|
6728
6819
|
var SlackRelayHub = class {
|
|
6729
6820
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -7245,10 +7336,6 @@ async function startSlackRelayServer(opts) {
|
|
|
7245
7336
|
}
|
|
7246
7337
|
export {
|
|
7247
7338
|
ABLETIME_MCP_PATH,
|
|
7248
|
-
ACCOUNT_ROLES,
|
|
7249
|
-
ACCOUNT_ROLE_LABELS,
|
|
7250
|
-
ACCOUNT_ROLE_MAX,
|
|
7251
|
-
ACCOUNT_ROLE_PRESETS,
|
|
7252
7339
|
AGENT_GIT_ACTIONS,
|
|
7253
7340
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
7254
7341
|
ATTACHMENTS_DIR,
|
|
@@ -7335,7 +7422,6 @@ export {
|
|
|
7335
7422
|
WORKTREE_MCP_TOOLS,
|
|
7336
7423
|
abletimeMcpRequest,
|
|
7337
7424
|
abletimeMcpUrl,
|
|
7338
|
-
accountRoleLabel,
|
|
7339
7425
|
ackSlackInboundSeen,
|
|
7340
7426
|
addBoardPin,
|
|
7341
7427
|
addPrStackLayer,
|
|
@@ -7516,6 +7602,7 @@ export {
|
|
|
7516
7602
|
fireSchedule,
|
|
7517
7603
|
flattenTurnInput,
|
|
7518
7604
|
flipLinearRelationType,
|
|
7605
|
+
foldLegacyRolesIntoNotes,
|
|
7519
7606
|
followUpBehavior,
|
|
7520
7607
|
forkChatTab,
|
|
7521
7608
|
forkMessageSlice,
|
|
@@ -7556,6 +7643,8 @@ export {
|
|
|
7556
7643
|
formatSlackWorkingText,
|
|
7557
7644
|
formatTranscriptMarkdown,
|
|
7558
7645
|
formatUiReminder,
|
|
7646
|
+
formatViewerContextDirective,
|
|
7647
|
+
formatViewerContextReminder,
|
|
7559
7648
|
formatWorkspaceInventory,
|
|
7560
7649
|
formatWorkspaceProfileSuffix,
|
|
7561
7650
|
formatWorktreeDirective,
|
|
@@ -7793,7 +7882,6 @@ export {
|
|
|
7793
7882
|
planQuestionsSignature,
|
|
7794
7883
|
pollSlackOutboundWatches,
|
|
7795
7884
|
posixShellSingleQuote,
|
|
7796
|
-
preferTeamsForRole,
|
|
7797
7885
|
preferredCursorCostCents,
|
|
7798
7886
|
prepareTerminalCommand,
|
|
7799
7887
|
previewLand,
|
|
@@ -7805,6 +7893,7 @@ export {
|
|
|
7805
7893
|
readSetupLog,
|
|
7806
7894
|
readSkillBody,
|
|
7807
7895
|
readThread,
|
|
7896
|
+
readViewerContext,
|
|
7808
7897
|
readWorktreeFile,
|
|
7809
7898
|
readWorktreeFileForUpload,
|
|
7810
7899
|
readWorktreeInclude,
|
|
@@ -7861,7 +7950,6 @@ export {
|
|
|
7861
7950
|
resolveViewerProfile,
|
|
7862
7951
|
resolveViewerProfileForRepo,
|
|
7863
7952
|
resolveWorktreeStartPoint,
|
|
7864
|
-
reviewTeamHintsForRoles,
|
|
7865
7953
|
rewriteAbleTimeError,
|
|
7866
7954
|
rewriteLinearError,
|
|
7867
7955
|
run,
|
|
@@ -7995,5 +8083,6 @@ export {
|
|
|
7995
8083
|
writeInjectedMcpConfig,
|
|
7996
8084
|
writePlanFile,
|
|
7997
8085
|
writeThread,
|
|
8086
|
+
writeViewerContext,
|
|
7998
8087
|
writeWorktreeFile
|
|
7999
8088
|
};
|