@sideboard-ai/core 0.1.158 → 0.1.160
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abletime-GWZE6OYM.js → abletime-AGF54TNA.js} +2 -2
- package/dist/{abletime-BEIDJG7W.js → abletime-BMS53RIZ.js} +2 -2
- package/dist/{agents-RGE7PSUY.js → agents-6UOXBORU.js} +4 -4
- package/dist/{agents-E3ZPWZRS.js → agents-RD774UEV.js} +4 -4
- package/dist/{app-settings-VRQI3FIE.js → app-settings-BRLZQA7N.js} +13 -17
- package/dist/{app-settings-H3INSR67.js → app-settings-JJQDWPEK.js} +13 -17
- package/dist/{chunk-EPQIOAAY.js → chunk-7OWRFJK6.js} +7 -5
- package/dist/{chunk-KYKNFJK5.js → chunk-BQ4PEXIF.js} +39 -20
- package/dist/{chunk-3BWVOUQG.js → chunk-CVZ4KXVP.js} +127 -113
- package/dist/{chunk-3SUZMPGL.js → chunk-HZROPNYL.js} +12 -13
- package/dist/{chunk-DVJBO3M7.js → chunk-ILXKJ4ZD.js} +1 -1
- package/dist/{chunk-A7QXLYBN.js → chunk-IO5WGZRR.js} +3 -13
- package/dist/{chunk-N27GVFZY.js → chunk-KJFFQAMA.js} +1 -1
- package/dist/{chunk-D7EGJ3EI.js → chunk-LTXS26K5.js} +127 -113
- package/dist/{chunk-CVR4RJ6G.js → chunk-QANLIT2E.js} +12 -13
- package/dist/{chunk-FBFIOVDH.js → chunk-QYKADGPP.js} +38 -19
- package/dist/{chunk-BLRHO7XQ.js → chunk-RQNXLIXO.js} +10 -6
- package/dist/{chunk-LQMFCS54.js → chunk-UUEBNUEG.js} +3 -13
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-X3LXE5Q4.js} +3 -3
- package/dist/{coordinator-prompt-GZESIQNH.js → coordinator-prompt-YYUXC7EM.js} +3 -3
- package/dist/{global-workspace-REI55FZ6.js → global-workspace-E6S5XQZF.js} +3 -3
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-SPR2Y4YQ.js} +3 -3
- package/dist/index.cjs +599 -475
- package/dist/index.d.cts +62 -57
- package/dist/index.d.ts +62 -57
- package/dist/index.js +325 -230
- package/dist/mcp/run-stdio.cjs +528 -402
- package/dist/mcp/run-stdio.js +265 -163
- package/dist/{orchestrator-ZVW2AC6B.js → orchestrator-BSVSOTBK.js} +5 -5
- package/dist/{orchestrator-J727WIJY.js → orchestrator-KVKPIUVU.js} +5 -5
- package/dist/{workspaces-NYSM2EPX.js → workspaces-MZELP4BI.js} +3 -3
- package/dist/{workspaces-OCEPZXFD.js → workspaces-OK2SNNQF.js} +3 -3
- package/dist/{worktree-VTMBZKY6.js → worktree-BX53JZHO.js} +2 -2
- package/dist/{worktree-T4SWFSTT.js → worktree-IY7YCEGH.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -234,7 +234,7 @@ import {
|
|
|
234
234
|
worktreeCleanupSettings,
|
|
235
235
|
wrapReviewSkillMarkdown,
|
|
236
236
|
writeWorktreeFile
|
|
237
|
-
} from "./chunk-
|
|
237
|
+
} from "./chunk-BQ4PEXIF.js";
|
|
238
238
|
import {
|
|
239
239
|
LEGACY_PLAN_FILE_REL,
|
|
240
240
|
PLAN_FILE_NAME,
|
|
@@ -337,7 +337,7 @@ import {
|
|
|
337
337
|
withEventParentId,
|
|
338
338
|
withEventsParentId,
|
|
339
339
|
writeInjectedMcpConfig
|
|
340
|
-
} from "./chunk-
|
|
340
|
+
} from "./chunk-RQNXLIXO.js";
|
|
341
341
|
import {
|
|
342
342
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
343
343
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
@@ -384,7 +384,7 @@ import {
|
|
|
384
384
|
stageBuffersAsAttachments,
|
|
385
385
|
syncWorkspacesFromThreads,
|
|
386
386
|
takenTeamSlugsForOrchestration
|
|
387
|
-
} from "./chunk-
|
|
387
|
+
} from "./chunk-QANLIT2E.js";
|
|
388
388
|
import {
|
|
389
389
|
FAMOUS_SOCCER_TEAMS,
|
|
390
390
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
@@ -477,7 +477,7 @@ import {
|
|
|
477
477
|
worktreeDisplayLabel,
|
|
478
478
|
worktreeDisplayLabelForGroup,
|
|
479
479
|
worktreeNameFromPath
|
|
480
|
-
} from "./chunk-
|
|
480
|
+
} from "./chunk-UUEBNUEG.js";
|
|
481
481
|
import {
|
|
482
482
|
ATTACHMENTS_DIR,
|
|
483
483
|
DETACHED_JOBS_DIR,
|
|
@@ -527,7 +527,7 @@ import {
|
|
|
527
527
|
toAbleTimeIssueInfo,
|
|
528
528
|
updateAbleTimeTask,
|
|
529
529
|
verifyAbleTimeConnection
|
|
530
|
-
} from "./chunk-
|
|
530
|
+
} from "./chunk-ILXKJ4ZD.js";
|
|
531
531
|
import {
|
|
532
532
|
formatFetchError,
|
|
533
533
|
httpFetch,
|
|
@@ -544,16 +544,11 @@ import {
|
|
|
544
544
|
withMaxOldSpaceSize
|
|
545
545
|
} from "./chunk-HEII7W3Q.js";
|
|
546
546
|
import {
|
|
547
|
-
ACCOUNT_ROLES,
|
|
548
|
-
ACCOUNT_ROLE_LABELS,
|
|
549
|
-
ACCOUNT_ROLE_MAX,
|
|
550
|
-
ACCOUNT_ROLE_PRESETS,
|
|
551
547
|
FOLLOW_UP_BEHAVIORS,
|
|
552
548
|
GITHUB_GIT_AUTH_MODES,
|
|
553
549
|
HARNESS_ENV_KEYS,
|
|
554
550
|
ISSUE_SOURCE_LABELS,
|
|
555
551
|
PROFILE_NOTES_MAX,
|
|
556
|
-
accountRoleLabel,
|
|
557
552
|
appSettingsPath,
|
|
558
553
|
applyAppEnvironment,
|
|
559
554
|
autoArchiveOnMergeEnabled,
|
|
@@ -577,9 +572,12 @@ import {
|
|
|
577
572
|
emptyPublicIntegrations,
|
|
578
573
|
ensureSlackDeviceIdentity,
|
|
579
574
|
findProjectProfileKey,
|
|
575
|
+
foldLegacyRolesIntoNotes,
|
|
580
576
|
followUpBehavior,
|
|
581
577
|
formatAccountProfilePlaybookLine,
|
|
582
578
|
formatProjectProfilePlaybookLines,
|
|
579
|
+
formatViewerContextDirective,
|
|
580
|
+
formatViewerContextReminder,
|
|
583
581
|
formatWorkspaceProfileSuffix,
|
|
584
582
|
getAbleTimeAccessToken,
|
|
585
583
|
getAbleTimeHost,
|
|
@@ -601,8 +599,8 @@ import {
|
|
|
601
599
|
orchestrationQuotaFallbackAgent,
|
|
602
600
|
orchestrationQuotaOnLimit,
|
|
603
601
|
persistVaultKeyInKeychain,
|
|
604
|
-
preferTeamsForRole,
|
|
605
602
|
readKeychainVaultKey,
|
|
603
|
+
readViewerContext,
|
|
606
604
|
resolveAccountProfile,
|
|
607
605
|
resolveAccountProfileFromSettings,
|
|
608
606
|
resolveAgentExecutable,
|
|
@@ -613,7 +611,6 @@ import {
|
|
|
613
611
|
resolveVaultKey,
|
|
614
612
|
resolveViewerProfile,
|
|
615
613
|
resolveViewerProfileForRepo,
|
|
616
|
-
reviewTeamHintsForRoles,
|
|
617
614
|
saveAbleTimeConnection,
|
|
618
615
|
saveAppSettings,
|
|
619
616
|
saveLinearOAuth,
|
|
@@ -632,8 +629,9 @@ import {
|
|
|
632
629
|
updateDefaultsSettings,
|
|
633
630
|
updateIntegrationsSettings,
|
|
634
631
|
updateOpencodeSettings,
|
|
635
|
-
updateProjectProfileSettings
|
|
636
|
-
|
|
632
|
+
updateProjectProfileSettings,
|
|
633
|
+
writeViewerContext
|
|
634
|
+
} from "./chunk-CVZ4KXVP.js";
|
|
637
635
|
import {
|
|
638
636
|
stripNestedElectronEnv
|
|
639
637
|
} from "./chunk-4TR3HZFT.js";
|
|
@@ -738,9 +736,9 @@ async function getGitHubStatus() {
|
|
|
738
736
|
};
|
|
739
737
|
}
|
|
740
738
|
const status = await run("gh", ["auth", "status"], { reject: false });
|
|
741
|
-
const
|
|
739
|
+
const text7 = `${status.stdout}
|
|
742
740
|
${status.stderr}`;
|
|
743
|
-
const loginMatch =
|
|
741
|
+
const loginMatch = text7.match(/Logged in to ([^\s]+) account (\S+)/i) ?? text7.match(/Logged in to ([^\s]+) as (\S+)/i);
|
|
744
742
|
if (loginMatch) {
|
|
745
743
|
return {
|
|
746
744
|
connected: true,
|
|
@@ -2065,11 +2063,11 @@ function fenceLanguage(path2, language) {
|
|
|
2065
2063
|
}
|
|
2066
2064
|
function buildCodeRefAttachment(input) {
|
|
2067
2065
|
const path2 = input.path.trim();
|
|
2068
|
-
const
|
|
2066
|
+
const text7 = input.text.replace(/\n$/, "");
|
|
2069
2067
|
if (!path2) {
|
|
2070
2068
|
throw new Error("code reference requires a file path");
|
|
2071
2069
|
}
|
|
2072
|
-
if (!
|
|
2070
|
+
if (!text7.trim()) {
|
|
2073
2071
|
throw new Error("code reference requires selected text");
|
|
2074
2072
|
}
|
|
2075
2073
|
if (input.startLine < 1 || input.endLine < 1 || input.endLine < input.startLine) {
|
|
@@ -2082,7 +2080,7 @@ function buildCodeRefAttachment(input) {
|
|
|
2082
2080
|
`Referenced code from \`${path2}\` (${range}).`,
|
|
2083
2081
|
"",
|
|
2084
2082
|
fence,
|
|
2085
|
-
|
|
2083
|
+
text7,
|
|
2086
2084
|
"```"
|
|
2087
2085
|
].join("\n");
|
|
2088
2086
|
return {
|
|
@@ -2136,16 +2134,16 @@ var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
|
2136
2134
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
2137
2135
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
2138
2136
|
var PASTED_NAME_ALT_RE = /^pasted-(\d+)\.txt$/i;
|
|
2139
|
-
function pastedTextStats(
|
|
2140
|
-
const chars =
|
|
2137
|
+
function pastedTextStats(text7) {
|
|
2138
|
+
const chars = text7.length;
|
|
2141
2139
|
if (chars === 0) return { chars: 0, lines: 0 };
|
|
2142
|
-
const lines =
|
|
2140
|
+
const lines = text7.split(/\r\n|\r|\n/).length;
|
|
2143
2141
|
return { chars, lines };
|
|
2144
2142
|
}
|
|
2145
|
-
function shouldAttachPastedText(
|
|
2146
|
-
const trimmed =
|
|
2143
|
+
function shouldAttachPastedText(text7) {
|
|
2144
|
+
const trimmed = text7.trim();
|
|
2147
2145
|
if (!trimmed) return false;
|
|
2148
|
-
const { chars, lines } = pastedTextStats(
|
|
2146
|
+
const { chars, lines } = pastedTextStats(text7);
|
|
2149
2147
|
return chars >= PASTE_ATTACH_MIN_CHARS || lines >= PASTE_ATTACH_MIN_LINES;
|
|
2150
2148
|
}
|
|
2151
2149
|
function nextPastedTextName(existing) {
|
|
@@ -2156,13 +2154,13 @@ function nextPastedTextName(existing) {
|
|
|
2156
2154
|
}
|
|
2157
2155
|
return `Pasted text #${max + 1}.txt`;
|
|
2158
2156
|
}
|
|
2159
|
-
function buildPastedTextAttachment(
|
|
2157
|
+
function buildPastedTextAttachment(text7, opts) {
|
|
2160
2158
|
return {
|
|
2161
2159
|
id: opts?.id ?? randomUUID(),
|
|
2162
2160
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
2163
2161
|
kind: "file",
|
|
2164
2162
|
path: opts?.path,
|
|
2165
|
-
content:
|
|
2163
|
+
content: text7
|
|
2166
2164
|
};
|
|
2167
2165
|
}
|
|
2168
2166
|
|
|
@@ -2247,8 +2245,8 @@ function latestPendingPlanQuestions(input) {
|
|
|
2247
2245
|
return null;
|
|
2248
2246
|
}
|
|
2249
2247
|
var PLAN_QUESTION_ANSWERS_PREFIX = "Answers to your questions:";
|
|
2250
|
-
function isPlanQuestionAnswersMessage(
|
|
2251
|
-
return
|
|
2248
|
+
function isPlanQuestionAnswersMessage(text7) {
|
|
2249
|
+
return text7.startsWith(PLAN_QUESTION_ANSWERS_PREFIX);
|
|
2252
2250
|
}
|
|
2253
2251
|
function formatPlanQuestionAnswers(questions, answers) {
|
|
2254
2252
|
const lines = [PLAN_QUESTION_ANSWERS_PREFIX, ""];
|
|
@@ -2287,7 +2285,7 @@ function formatPlanQuestionsForChat(questions) {
|
|
|
2287
2285
|
// src/mcp/server.ts
|
|
2288
2286
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2289
2287
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2290
|
-
import { z as
|
|
2288
|
+
import { z as z7 } from "zod";
|
|
2291
2289
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2292
2290
|
import { basename } from "path";
|
|
2293
2291
|
|
|
@@ -2303,9 +2301,9 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
2303
2301
|
function lastMessagePreview(messages, max = 160) {
|
|
2304
2302
|
if (!messages?.length) return null;
|
|
2305
2303
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2306
|
-
const
|
|
2307
|
-
if (!
|
|
2308
|
-
const flat =
|
|
2304
|
+
const text7 = messages[i]?.text?.trim();
|
|
2305
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
2306
|
+
const flat = text7.replace(/\s+/g, " ");
|
|
2309
2307
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
2310
2308
|
}
|
|
2311
2309
|
return null;
|
|
@@ -2537,8 +2535,8 @@ function labelGithubUrl(url) {
|
|
|
2537
2535
|
}
|
|
2538
2536
|
return { kind: "other", label: "GitHub", url: raw };
|
|
2539
2537
|
}
|
|
2540
|
-
function appendGithubLink(
|
|
2541
|
-
const body =
|
|
2538
|
+
function appendGithubLink(text7, githubUrl) {
|
|
2539
|
+
const body = text7.trimEnd();
|
|
2542
2540
|
if (!githubUrl?.trim()) return body;
|
|
2543
2541
|
const labeled = labelGithubUrl(githubUrl);
|
|
2544
2542
|
if (!labeled) {
|
|
@@ -3228,6 +3226,104 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
3228
3226
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
3229
3227
|
}
|
|
3230
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
|
+
let best = null;
|
|
3238
|
+
for (const w of listWorkspaces()) {
|
|
3239
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
3240
|
+
if (stored === hint || hint.startsWith(`${stored}/`)) {
|
|
3241
|
+
if (!best || stored.length > best.length) best = stored;
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
return best;
|
|
3245
|
+
}
|
|
3246
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
3247
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
3248
|
+
let root = hint;
|
|
3249
|
+
try {
|
|
3250
|
+
root = await resolveRepoRoot(hint);
|
|
3251
|
+
} catch {
|
|
3252
|
+
root = hint.replace(/\/+$/, "");
|
|
3253
|
+
}
|
|
3254
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
3255
|
+
}
|
|
3256
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_workspaces. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
3257
|
+
function registerViewerContextTools(server) {
|
|
3258
|
+
server.tool(
|
|
3259
|
+
"get_viewer_context",
|
|
3260
|
+
"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).",
|
|
3261
|
+
{
|
|
3262
|
+
repoPath: z5.string().optional().describe(
|
|
3263
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
3264
|
+
)
|
|
3265
|
+
},
|
|
3266
|
+
async ({ repoPath }) => {
|
|
3267
|
+
try {
|
|
3268
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
3269
|
+
return text5(readViewerContext(path2 ?? void 0));
|
|
3270
|
+
} catch (err) {
|
|
3271
|
+
return text5(
|
|
3272
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
3273
|
+
true
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
);
|
|
3278
|
+
server.tool(
|
|
3279
|
+
"update_viewer_context",
|
|
3280
|
+
"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.",
|
|
3281
|
+
{
|
|
3282
|
+
scope: z5.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
3283
|
+
context: z5.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
3284
|
+
confirmed: z5.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
3285
|
+
repoPath: z5.string().optional().describe(
|
|
3286
|
+
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
3287
|
+
)
|
|
3288
|
+
},
|
|
3289
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
3290
|
+
try {
|
|
3291
|
+
if (scope === "project") {
|
|
3292
|
+
const path2 = await resolveProjectRepoPath(repoPath);
|
|
3293
|
+
if (!path2) {
|
|
3294
|
+
return text5(
|
|
3295
|
+
{
|
|
3296
|
+
ok: false,
|
|
3297
|
+
error: "repoPath required",
|
|
3298
|
+
message: PROJECT_PATH_HINT
|
|
3299
|
+
},
|
|
3300
|
+
true
|
|
3301
|
+
);
|
|
3302
|
+
}
|
|
3303
|
+
const result2 = writeViewerContext({
|
|
3304
|
+
scope,
|
|
3305
|
+
context,
|
|
3306
|
+
repoPath: path2,
|
|
3307
|
+
confirmed: confirmed === true
|
|
3308
|
+
});
|
|
3309
|
+
return text5(result2, !result2.ok);
|
|
3310
|
+
}
|
|
3311
|
+
const result = writeViewerContext({
|
|
3312
|
+
scope,
|
|
3313
|
+
context,
|
|
3314
|
+
confirmed: confirmed === true
|
|
3315
|
+
});
|
|
3316
|
+
return text5(result, !result.ok);
|
|
3317
|
+
} catch (err) {
|
|
3318
|
+
return text5(
|
|
3319
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
3320
|
+
true
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3231
3327
|
// src/mcp/pr-list.ts
|
|
3232
3328
|
function compactPrRow(pr) {
|
|
3233
3329
|
return {
|
|
@@ -3263,15 +3359,15 @@ function formatMcpPrList(input) {
|
|
|
3263
3359
|
}
|
|
3264
3360
|
|
|
3265
3361
|
// src/mcp/schedule-tools.ts
|
|
3266
|
-
import { z as
|
|
3267
|
-
function
|
|
3362
|
+
import { z as z6 } from "zod";
|
|
3363
|
+
function text6(payload, isError = false) {
|
|
3268
3364
|
return {
|
|
3269
3365
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
3270
3366
|
...isError ? { isError: true } : {}
|
|
3271
3367
|
};
|
|
3272
3368
|
}
|
|
3273
3369
|
function fail5(err) {
|
|
3274
|
-
return
|
|
3370
|
+
return text6(
|
|
3275
3371
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
3276
3372
|
true
|
|
3277
3373
|
);
|
|
@@ -3296,7 +3392,7 @@ function registerScheduleTools(server) {
|
|
|
3296
3392
|
{},
|
|
3297
3393
|
async () => {
|
|
3298
3394
|
try {
|
|
3299
|
-
return
|
|
3395
|
+
return text6({ schedules: listSchedules() });
|
|
3300
3396
|
} catch (err) {
|
|
3301
3397
|
return fail5(err);
|
|
3302
3398
|
}
|
|
@@ -3306,17 +3402,17 @@ function registerScheduleTools(server) {
|
|
|
3306
3402
|
"create_schedule",
|
|
3307
3403
|
"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.",
|
|
3308
3404
|
{
|
|
3309
|
-
prompt:
|
|
3310
|
-
name:
|
|
3311
|
-
at:
|
|
3312
|
-
every:
|
|
3313
|
-
cron:
|
|
3314
|
-
tz:
|
|
3315
|
-
threadId:
|
|
3405
|
+
prompt: z6.string().describe("User message / goal queued when the schedule fires"),
|
|
3406
|
+
name: z6.string().optional(),
|
|
3407
|
+
at: z6.string().optional().describe("ISO datetime for a one-shot"),
|
|
3408
|
+
every: z6.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
3409
|
+
cron: z6.string().optional().describe("5-field cron expression"),
|
|
3410
|
+
tz: z6.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
3411
|
+
threadId: z6.string().optional().describe(
|
|
3316
3412
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
3317
3413
|
),
|
|
3318
|
-
agent:
|
|
3319
|
-
model:
|
|
3414
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
3415
|
+
model: z6.string().optional()
|
|
3320
3416
|
},
|
|
3321
3417
|
async (args) => {
|
|
3322
3418
|
try {
|
|
@@ -3336,7 +3432,7 @@ function registerScheduleTools(server) {
|
|
|
3336
3432
|
model: args.model,
|
|
3337
3433
|
createdBy: "mcp"
|
|
3338
3434
|
});
|
|
3339
|
-
return
|
|
3435
|
+
return text6({
|
|
3340
3436
|
schedule,
|
|
3341
3437
|
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."
|
|
3342
3438
|
});
|
|
@@ -3349,17 +3445,17 @@ function registerScheduleTools(server) {
|
|
|
3349
3445
|
"update_schedule",
|
|
3350
3446
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
3351
3447
|
{
|
|
3352
|
-
id:
|
|
3353
|
-
prompt:
|
|
3354
|
-
name:
|
|
3355
|
-
at:
|
|
3356
|
-
every:
|
|
3357
|
-
cron:
|
|
3358
|
-
tz:
|
|
3359
|
-
threadId:
|
|
3360
|
-
agent:
|
|
3361
|
-
model:
|
|
3362
|
-
enabled:
|
|
3448
|
+
id: z6.string(),
|
|
3449
|
+
prompt: z6.string().optional(),
|
|
3450
|
+
name: z6.string().optional(),
|
|
3451
|
+
at: z6.string().optional(),
|
|
3452
|
+
every: z6.string().optional(),
|
|
3453
|
+
cron: z6.string().optional(),
|
|
3454
|
+
tz: z6.string().optional(),
|
|
3455
|
+
threadId: z6.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
3456
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
3457
|
+
model: z6.string().optional(),
|
|
3458
|
+
enabled: z6.boolean().optional()
|
|
3363
3459
|
},
|
|
3364
3460
|
async (args) => {
|
|
3365
3461
|
try {
|
|
@@ -3388,7 +3484,7 @@ function registerScheduleTools(server) {
|
|
|
3388
3484
|
enabled: args.enabled,
|
|
3389
3485
|
model: args.model
|
|
3390
3486
|
});
|
|
3391
|
-
return
|
|
3487
|
+
return text6({ schedule });
|
|
3392
3488
|
} catch (err) {
|
|
3393
3489
|
return fail5(err);
|
|
3394
3490
|
}
|
|
@@ -3397,11 +3493,11 @@ function registerScheduleTools(server) {
|
|
|
3397
3493
|
server.tool(
|
|
3398
3494
|
"delete_schedule",
|
|
3399
3495
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
3400
|
-
{ id:
|
|
3496
|
+
{ id: z6.string() },
|
|
3401
3497
|
async ({ id }) => {
|
|
3402
3498
|
try {
|
|
3403
3499
|
deleteSchedule(id);
|
|
3404
|
-
return
|
|
3500
|
+
return text6({ ok: true, id });
|
|
3405
3501
|
} catch (err) {
|
|
3406
3502
|
return fail5(err);
|
|
3407
3503
|
}
|
|
@@ -3410,14 +3506,14 @@ function registerScheduleTools(server) {
|
|
|
3410
3506
|
server.tool(
|
|
3411
3507
|
"run_schedule",
|
|
3412
3508
|
"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.",
|
|
3413
|
-
{ id:
|
|
3509
|
+
{ id: z6.string() },
|
|
3414
3510
|
async ({ id }) => {
|
|
3415
3511
|
try {
|
|
3416
3512
|
if (!getSchedule(id)) {
|
|
3417
3513
|
return fail5(new Error(`Schedule not found: ${id}`));
|
|
3418
3514
|
}
|
|
3419
3515
|
const schedule = await fireSchedule(id);
|
|
3420
|
-
return
|
|
3516
|
+
return text6({ schedule });
|
|
3421
3517
|
} catch (err) {
|
|
3422
3518
|
return fail5(err);
|
|
3423
3519
|
}
|
|
@@ -3820,7 +3916,7 @@ async function startMcpServer() {
|
|
|
3820
3916
|
);
|
|
3821
3917
|
}
|
|
3822
3918
|
try {
|
|
3823
|
-
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-
|
|
3919
|
+
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-JJQDWPEK.js");
|
|
3824
3920
|
orch.setMaxConcurrent(maxConcurrentAgents2());
|
|
3825
3921
|
} catch {
|
|
3826
3922
|
}
|
|
@@ -3837,13 +3933,14 @@ async function startMcpServer() {
|
|
|
3837
3933
|
version: "0.1.0"
|
|
3838
3934
|
});
|
|
3839
3935
|
const worktreeProfile = sideboardMcpProfile() === "worktree";
|
|
3936
|
+
registerViewerContextTools(server);
|
|
3840
3937
|
if (worktreeProfile) {
|
|
3841
3938
|
registerConnectedIssueVendorTools(server);
|
|
3842
3939
|
}
|
|
3843
3940
|
if (!worktreeProfile) {
|
|
3844
3941
|
server.tool(
|
|
3845
3942
|
"list_workspaces",
|
|
3846
|
-
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and
|
|
3943
|
+
"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.",
|
|
3847
3944
|
{},
|
|
3848
3945
|
async () => {
|
|
3849
3946
|
const workspaces = orch.listWorkspaces();
|
|
@@ -3892,13 +3989,13 @@ async function startMcpServer() {
|
|
|
3892
3989
|
"list_board",
|
|
3893
3990
|
"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.",
|
|
3894
3991
|
{
|
|
3895
|
-
query:
|
|
3896
|
-
repoPath:
|
|
3897
|
-
kind:
|
|
3898
|
-
column:
|
|
3992
|
+
query: z7.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
3993
|
+
repoPath: z7.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
3994
|
+
kind: z7.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
3995
|
+
column: z7.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
3899
3996
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
3900
3997
|
),
|
|
3901
|
-
limit:
|
|
3998
|
+
limit: z7.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
3902
3999
|
},
|
|
3903
4000
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
3904
4001
|
const workspaces = orch.listWorkspaces();
|
|
@@ -3936,7 +4033,7 @@ async function startMcpServer() {
|
|
|
3936
4033
|
server.tool(
|
|
3937
4034
|
"get_thread",
|
|
3938
4035
|
"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.",
|
|
3939
|
-
{ ref:
|
|
4036
|
+
{ ref: z7.string() },
|
|
3940
4037
|
async ({ ref }) => {
|
|
3941
4038
|
const t = orch.getThread(ref);
|
|
3942
4039
|
if (!t) {
|
|
@@ -3978,17 +4075,17 @@ async function startMcpServer() {
|
|
|
3978
4075
|
"present_artifact",
|
|
3979
4076
|
"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.",
|
|
3980
4077
|
{
|
|
3981
|
-
title:
|
|
3982
|
-
type:
|
|
4078
|
+
title: z7.string().describe("Short title shown in the artifact pane header"),
|
|
4079
|
+
type: z7.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
3983
4080
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
3984
4081
|
),
|
|
3985
|
-
content:
|
|
4082
|
+
content: z7.string().describe(
|
|
3986
4083
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
3987
4084
|
),
|
|
3988
|
-
artifact_id:
|
|
3989
|
-
status:
|
|
3990
|
-
phase:
|
|
3991
|
-
mode:
|
|
4085
|
+
artifact_id: z7.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
4086
|
+
status: z7.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
4087
|
+
phase: z7.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
4088
|
+
mode: z7.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
3992
4089
|
},
|
|
3993
4090
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
3994
4091
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4009,15 +4106,15 @@ async function startMcpServer() {
|
|
|
4009
4106
|
"ask_user",
|
|
4010
4107
|
"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.",
|
|
4011
4108
|
{
|
|
4012
|
-
questions:
|
|
4013
|
-
|
|
4014
|
-
question:
|
|
4015
|
-
header:
|
|
4016
|
-
multiSelect:
|
|
4017
|
-
options:
|
|
4018
|
-
|
|
4019
|
-
label:
|
|
4020
|
-
description:
|
|
4109
|
+
questions: z7.array(
|
|
4110
|
+
z7.object({
|
|
4111
|
+
question: z7.string().describe("Full question text ending with ?"),
|
|
4112
|
+
header: z7.string().max(24).optional().describe("Short label shown above the question"),
|
|
4113
|
+
multiSelect: z7.boolean().optional().describe("Allow selecting multiple options"),
|
|
4114
|
+
options: z7.array(
|
|
4115
|
+
z7.object({
|
|
4116
|
+
label: z7.string(),
|
|
4117
|
+
description: z7.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
4021
4118
|
})
|
|
4022
4119
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
4023
4120
|
})
|
|
@@ -4036,9 +4133,9 @@ async function startMcpServer() {
|
|
|
4036
4133
|
"present_plan",
|
|
4037
4134
|
"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.",
|
|
4038
4135
|
{
|
|
4039
|
-
title:
|
|
4040
|
-
content:
|
|
4041
|
-
thread_id:
|
|
4136
|
+
title: z7.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
4137
|
+
content: z7.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
4138
|
+
thread_id: z7.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
4042
4139
|
},
|
|
4043
4140
|
async ({ title, content, thread_id }) => {
|
|
4044
4141
|
const { writePlanFile: writePlanFile2 } = await import("./plan-file-LSSSQGTQ.js");
|
|
@@ -4061,15 +4158,15 @@ async function startMcpServer() {
|
|
|
4061
4158
|
"present_schema",
|
|
4062
4159
|
"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.",
|
|
4063
4160
|
{
|
|
4064
|
-
title:
|
|
4065
|
-
mode:
|
|
4066
|
-
datasource:
|
|
4067
|
-
resource_id:
|
|
4068
|
-
record_id:
|
|
4069
|
-
resource:
|
|
4070
|
-
record:
|
|
4071
|
-
records:
|
|
4072
|
-
pane_id:
|
|
4161
|
+
title: z7.string().describe("Pane title"),
|
|
4162
|
+
mode: z7.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
4163
|
+
datasource: z7.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
4164
|
+
resource_id: z7.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
4165
|
+
record_id: z7.string().optional().describe("Record id when opening form mode"),
|
|
4166
|
+
resource: z7.record(z7.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
4167
|
+
record: z7.record(z7.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
4168
|
+
records: z7.array(z7.record(z7.unknown())).optional().describe("Inline records for table mode"),
|
|
4169
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
4073
4170
|
},
|
|
4074
4171
|
async (args) => {
|
|
4075
4172
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4090,10 +4187,10 @@ async function startMcpServer() {
|
|
|
4090
4187
|
"present_files",
|
|
4091
4188
|
"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.",
|
|
4092
4189
|
{
|
|
4093
|
-
title:
|
|
4094
|
-
datasource:
|
|
4095
|
-
path:
|
|
4096
|
-
pane_id:
|
|
4190
|
+
title: z7.string().optional().describe("Pane title (default: Files)"),
|
|
4191
|
+
datasource: z7.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
4192
|
+
path: z7.string().optional().describe("Initial folder path (e.g. public)"),
|
|
4193
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
4097
4194
|
},
|
|
4098
4195
|
async (args) => {
|
|
4099
4196
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4112,8 +4209,8 @@ async function startMcpServer() {
|
|
|
4112
4209
|
"wait_for_job",
|
|
4113
4210
|
"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.",
|
|
4114
4211
|
{
|
|
4115
|
-
id:
|
|
4116
|
-
timeoutMs:
|
|
4212
|
+
id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
4213
|
+
timeoutMs: z7.number().optional()
|
|
4117
4214
|
},
|
|
4118
4215
|
async ({ id, timeoutMs }) => {
|
|
4119
4216
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
@@ -4127,14 +4224,14 @@ async function startMcpServer() {
|
|
|
4127
4224
|
registerConnectedIssueVendorTools(server);
|
|
4128
4225
|
registerScheduleTools(server);
|
|
4129
4226
|
const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-SK2QISPE.js");
|
|
4130
|
-
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-
|
|
4227
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-JJQDWPEK.js");
|
|
4131
4228
|
const accountDefaults = resolveThreadDefaults2();
|
|
4132
4229
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
4133
4230
|
server.tool(
|
|
4134
4231
|
"set_caffeinate",
|
|
4135
4232
|
"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.",
|
|
4136
4233
|
{
|
|
4137
|
-
enabled:
|
|
4234
|
+
enabled: z7.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
4138
4235
|
},
|
|
4139
4236
|
async ({ enabled }) => {
|
|
4140
4237
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -4172,18 +4269,18 @@ async function startMcpServer() {
|
|
|
4172
4269
|
"create_thread",
|
|
4173
4270
|
`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.`,
|
|
4174
4271
|
{
|
|
4175
|
-
sourceType:
|
|
4176
|
-
sourceRef:
|
|
4177
|
-
agent:
|
|
4178
|
-
model:
|
|
4272
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]),
|
|
4273
|
+
sourceRef: z7.string(),
|
|
4274
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
4275
|
+
model: z7.string().nullable().optional().describe(
|
|
4179
4276
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
4180
4277
|
),
|
|
4181
|
-
repoPath:
|
|
4182
|
-
title:
|
|
4183
|
-
cowboy:
|
|
4278
|
+
repoPath: z7.string(),
|
|
4279
|
+
title: z7.string().optional(),
|
|
4280
|
+
cowboy: z7.boolean().optional().describe(
|
|
4184
4281
|
"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."
|
|
4185
4282
|
),
|
|
4186
|
-
parentThreadId:
|
|
4283
|
+
parentThreadId: z7.string().optional().describe(
|
|
4187
4284
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
4188
4285
|
)
|
|
4189
4286
|
},
|
|
@@ -4199,10 +4296,10 @@ async function startMcpServer() {
|
|
|
4199
4296
|
"start_board_card",
|
|
4200
4297
|
"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.",
|
|
4201
4298
|
{
|
|
4202
|
-
kind:
|
|
4203
|
-
ref:
|
|
4204
|
-
repoPath:
|
|
4205
|
-
title:
|
|
4299
|
+
kind: z7.enum(["ticket", "pr", "branch"]),
|
|
4300
|
+
ref: z7.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
4301
|
+
repoPath: z7.string().describe("Workspace path from list_workspaces / list_board"),
|
|
4302
|
+
title: z7.string().optional()
|
|
4206
4303
|
},
|
|
4207
4304
|
async ({ kind, ref, repoPath, title }) => {
|
|
4208
4305
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4306,9 +4403,9 @@ async function startMcpServer() {
|
|
|
4306
4403
|
"send_to_thread",
|
|
4307
4404
|
'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.',
|
|
4308
4405
|
{
|
|
4309
|
-
ref:
|
|
4310
|
-
prompt:
|
|
4311
|
-
force_stop:
|
|
4406
|
+
ref: z7.string(),
|
|
4407
|
+
prompt: z7.string(),
|
|
4408
|
+
force_stop: z7.boolean().optional()
|
|
4312
4409
|
},
|
|
4313
4410
|
async ({ ref, prompt, force_stop }) => {
|
|
4314
4411
|
if (force_stop) {
|
|
@@ -4337,8 +4434,8 @@ async function startMcpServer() {
|
|
|
4337
4434
|
"wait_for_turn",
|
|
4338
4435
|
"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).",
|
|
4339
4436
|
{
|
|
4340
|
-
ref:
|
|
4341
|
-
timeoutMs:
|
|
4437
|
+
ref: z7.string(),
|
|
4438
|
+
timeoutMs: z7.number().optional()
|
|
4342
4439
|
},
|
|
4343
4440
|
async ({ ref, timeoutMs }) => {
|
|
4344
4441
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -4368,7 +4465,7 @@ async function startMcpServer() {
|
|
|
4368
4465
|
server.tool(
|
|
4369
4466
|
"get_turn_result",
|
|
4370
4467
|
"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).",
|
|
4371
|
-
{ ref:
|
|
4468
|
+
{ ref: z7.string() },
|
|
4372
4469
|
async ({ ref }) => {
|
|
4373
4470
|
const result = orch.getTurnResult(ref);
|
|
4374
4471
|
return {
|
|
@@ -4389,8 +4486,8 @@ async function startMcpServer() {
|
|
|
4389
4486
|
"stop_thread",
|
|
4390
4487
|
"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.",
|
|
4391
4488
|
{
|
|
4392
|
-
ref:
|
|
4393
|
-
force:
|
|
4489
|
+
ref: z7.string(),
|
|
4490
|
+
force: z7.boolean().optional()
|
|
4394
4491
|
},
|
|
4395
4492
|
async ({ ref, force }) => {
|
|
4396
4493
|
const t = orch.getThread(ref);
|
|
@@ -4420,7 +4517,7 @@ async function startMcpServer() {
|
|
|
4420
4517
|
server.tool(
|
|
4421
4518
|
"archive_thread",
|
|
4422
4519
|
"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.",
|
|
4423
|
-
{ ref:
|
|
4520
|
+
{ ref: z7.string() },
|
|
4424
4521
|
async ({ ref }) => {
|
|
4425
4522
|
const t = orch.getThread(ref);
|
|
4426
4523
|
if (!t) {
|
|
@@ -4453,7 +4550,7 @@ async function startMcpServer() {
|
|
|
4453
4550
|
server.tool(
|
|
4454
4551
|
"restore_thread",
|
|
4455
4552
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
4456
|
-
{ ref:
|
|
4553
|
+
{ ref: z7.string() },
|
|
4457
4554
|
async ({ ref }) => {
|
|
4458
4555
|
try {
|
|
4459
4556
|
const restored = await orch.restore(ref);
|
|
@@ -4479,8 +4576,8 @@ async function startMcpServer() {
|
|
|
4479
4576
|
"get_diff",
|
|
4480
4577
|
"Compact diff summary (capped hunks, paginated)",
|
|
4481
4578
|
{
|
|
4482
|
-
ref:
|
|
4483
|
-
maxFiles:
|
|
4579
|
+
ref: z7.string(),
|
|
4580
|
+
maxFiles: z7.number().optional()
|
|
4484
4581
|
},
|
|
4485
4582
|
async ({ ref, maxFiles }) => {
|
|
4486
4583
|
const summary = await orch.diffSummary(ref);
|
|
@@ -4500,7 +4597,7 @@ async function startMcpServer() {
|
|
|
4500
4597
|
server.tool(
|
|
4501
4598
|
"get_pr_checks",
|
|
4502
4599
|
"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.",
|
|
4503
|
-
{ ref:
|
|
4600
|
+
{ ref: z7.string().describe("Worktree thread id/ref") },
|
|
4504
4601
|
async ({ ref }) => {
|
|
4505
4602
|
try {
|
|
4506
4603
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -4516,7 +4613,7 @@ async function startMcpServer() {
|
|
|
4516
4613
|
server.tool(
|
|
4517
4614
|
"request_review",
|
|
4518
4615
|
'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.',
|
|
4519
|
-
{ ref:
|
|
4616
|
+
{ ref: z7.string().describe("Worktree thread id/ref to review") },
|
|
4520
4617
|
async ({ ref }) => {
|
|
4521
4618
|
try {
|
|
4522
4619
|
const tab = await orch.requestReview(ref);
|
|
@@ -4545,8 +4642,8 @@ async function startMcpServer() {
|
|
|
4545
4642
|
"ask_git",
|
|
4546
4643
|
"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.",
|
|
4547
4644
|
{
|
|
4548
|
-
ref:
|
|
4549
|
-
action:
|
|
4645
|
+
ref: z7.string().describe("Worktree thread id/ref"),
|
|
4646
|
+
action: z7.enum(AGENT_GIT_ACTIONS).describe(
|
|
4550
4647
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
4551
4648
|
)
|
|
4552
4649
|
},
|
|
@@ -4591,7 +4688,7 @@ async function startMcpServer() {
|
|
|
4591
4688
|
}
|
|
4592
4689
|
}
|
|
4593
4690
|
);
|
|
4594
|
-
const agentEnum =
|
|
4691
|
+
const agentEnum = z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
4595
4692
|
server.tool(
|
|
4596
4693
|
"list_models",
|
|
4597
4694
|
"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.",
|
|
@@ -4614,11 +4711,11 @@ async function startMcpServer() {
|
|
|
4614
4711
|
"fork_worktree",
|
|
4615
4712
|
"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.",
|
|
4616
4713
|
{
|
|
4617
|
-
ref:
|
|
4618
|
-
through_index:
|
|
4714
|
+
ref: z7.string().describe("Worktree thread id/ref to fork"),
|
|
4715
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4619
4716
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4620
|
-
model:
|
|
4621
|
-
title:
|
|
4717
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4718
|
+
title: z7.string().optional()
|
|
4622
4719
|
},
|
|
4623
4720
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4624
4721
|
try {
|
|
@@ -4659,11 +4756,11 @@ async function startMcpServer() {
|
|
|
4659
4756
|
"fork_chat",
|
|
4660
4757
|
"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.",
|
|
4661
4758
|
{
|
|
4662
|
-
ref:
|
|
4663
|
-
through_index:
|
|
4759
|
+
ref: z7.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
4760
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4664
4761
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4665
|
-
model:
|
|
4666
|
-
title:
|
|
4762
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4763
|
+
title: z7.string().optional()
|
|
4667
4764
|
},
|
|
4668
4765
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4669
4766
|
try {
|
|
@@ -4709,8 +4806,8 @@ async function startMcpServer() {
|
|
|
4709
4806
|
"run_dev_script",
|
|
4710
4807
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
4711
4808
|
{
|
|
4712
|
-
ref:
|
|
4713
|
-
name:
|
|
4809
|
+
ref: z7.string(),
|
|
4810
|
+
name: z7.string().optional()
|
|
4714
4811
|
},
|
|
4715
4812
|
async ({ ref, name }) => {
|
|
4716
4813
|
const result = await orch.startDev(ref, name);
|
|
@@ -4732,7 +4829,7 @@ async function startMcpServer() {
|
|
|
4732
4829
|
server.tool(
|
|
4733
4830
|
"list_run_scripts",
|
|
4734
4831
|
"List named run scripts available for a thread",
|
|
4735
|
-
{ ref:
|
|
4832
|
+
{ ref: z7.string() },
|
|
4736
4833
|
async ({ ref }) => {
|
|
4737
4834
|
const scripts = orch.listThreadRunScripts(ref);
|
|
4738
4835
|
const active = orch.getActiveRuns(ref);
|
|
@@ -4750,8 +4847,8 @@ async function startMcpServer() {
|
|
|
4750
4847
|
"stop_dev_script",
|
|
4751
4848
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
4752
4849
|
{
|
|
4753
|
-
ref:
|
|
4754
|
-
name:
|
|
4850
|
+
ref: z7.string(),
|
|
4851
|
+
name: z7.string().optional()
|
|
4755
4852
|
},
|
|
4756
4853
|
async ({ ref, name }) => {
|
|
4757
4854
|
orch.stopDev(ref, name);
|
|
@@ -4761,7 +4858,7 @@ async function startMcpServer() {
|
|
|
4761
4858
|
server.tool(
|
|
4762
4859
|
"run_setup",
|
|
4763
4860
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
4764
|
-
{ ref:
|
|
4861
|
+
{ ref: z7.string() },
|
|
4765
4862
|
async ({ ref }) => {
|
|
4766
4863
|
const result = await orch.runSetup(ref);
|
|
4767
4864
|
return {
|
|
@@ -4772,7 +4869,7 @@ async function startMcpServer() {
|
|
|
4772
4869
|
server.tool(
|
|
4773
4870
|
"add_workspace",
|
|
4774
4871
|
"Register a git repo as a Sideboard workspace",
|
|
4775
|
-
{ repoPath:
|
|
4872
|
+
{ repoPath: z7.string() },
|
|
4776
4873
|
async ({ repoPath }) => {
|
|
4777
4874
|
const ws = await orch.addWorkspace(repoPath);
|
|
4778
4875
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -4781,7 +4878,7 @@ async function startMcpServer() {
|
|
|
4781
4878
|
server.tool(
|
|
4782
4879
|
"remove_workspace",
|
|
4783
4880
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
4784
|
-
{ repoPath:
|
|
4881
|
+
{ repoPath: z7.string() },
|
|
4785
4882
|
async ({ repoPath }) => {
|
|
4786
4883
|
orch.removeWorkspace(repoPath);
|
|
4787
4884
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -4791,12 +4888,12 @@ async function startMcpServer() {
|
|
|
4791
4888
|
"fanout",
|
|
4792
4889
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
4793
4890
|
{
|
|
4794
|
-
prompt:
|
|
4795
|
-
agents:
|
|
4796
|
-
repoPath:
|
|
4797
|
-
sourceType:
|
|
4798
|
-
sourceRef:
|
|
4799
|
-
title:
|
|
4891
|
+
prompt: z7.string(),
|
|
4892
|
+
agents: z7.array(z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
4893
|
+
repoPath: z7.string(),
|
|
4894
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]).optional(),
|
|
4895
|
+
sourceRef: z7.string().optional(),
|
|
4896
|
+
title: z7.string().optional()
|
|
4800
4897
|
},
|
|
4801
4898
|
async (args) => {
|
|
4802
4899
|
const threads = await orch.bestOfN(args);
|
|
@@ -4823,8 +4920,8 @@ async function startMcpServer() {
|
|
|
4823
4920
|
"list_branches",
|
|
4824
4921
|
"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).",
|
|
4825
4922
|
{
|
|
4826
|
-
repoPath:
|
|
4827
|
-
unmergedOnly:
|
|
4923
|
+
repoPath: z7.string(),
|
|
4924
|
+
unmergedOnly: z7.boolean().optional()
|
|
4828
4925
|
},
|
|
4829
4926
|
async ({ repoPath, unmergedOnly }) => {
|
|
4830
4927
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4845,19 +4942,19 @@ async function startMcpServer() {
|
|
|
4845
4942
|
"list_prs",
|
|
4846
4943
|
'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.',
|
|
4847
4944
|
{
|
|
4848
|
-
repoPath:
|
|
4849
|
-
query:
|
|
4850
|
-
queue:
|
|
4945
|
+
repoPath: z7.string(),
|
|
4946
|
+
query: z7.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
4947
|
+
queue: z7.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
4851
4948
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
4852
4949
|
),
|
|
4853
|
-
state:
|
|
4854
|
-
label:
|
|
4950
|
+
state: z7.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
4951
|
+
label: z7.string().optional().describe(
|
|
4855
4952
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
4856
4953
|
),
|
|
4857
|
-
reviewer:
|
|
4954
|
+
reviewer: z7.string().optional().describe(
|
|
4858
4955
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
4859
4956
|
),
|
|
4860
|
-
limit:
|
|
4957
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
4861
4958
|
},
|
|
4862
4959
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
4863
4960
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4889,7 +4986,7 @@ async function startMcpServer() {
|
|
|
4889
4986
|
server.tool(
|
|
4890
4987
|
"get_pr_stack",
|
|
4891
4988
|
"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).",
|
|
4892
|
-
{ ref:
|
|
4989
|
+
{ ref: z7.string() },
|
|
4893
4990
|
async ({ ref }) => {
|
|
4894
4991
|
const stack = await orch.getPrStack(ref);
|
|
4895
4992
|
return {
|
|
@@ -4901,8 +4998,8 @@ async function startMcpServer() {
|
|
|
4901
4998
|
"open_pr_stack_layers",
|
|
4902
4999
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
4903
5000
|
{
|
|
4904
|
-
ref:
|
|
4905
|
-
layer:
|
|
5001
|
+
ref: z7.string(),
|
|
5002
|
+
layer: z7.number().int().positive().optional()
|
|
4906
5003
|
},
|
|
4907
5004
|
async ({ ref, layer }) => {
|
|
4908
5005
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -4936,9 +5033,9 @@ async function startMcpServer() {
|
|
|
4936
5033
|
"add_stack_layer",
|
|
4937
5034
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
4938
5035
|
{
|
|
4939
|
-
ref:
|
|
4940
|
-
branchName:
|
|
4941
|
-
title:
|
|
5036
|
+
ref: z7.string(),
|
|
5037
|
+
branchName: z7.string(),
|
|
5038
|
+
title: z7.string().optional()
|
|
4942
5039
|
},
|
|
4943
5040
|
async ({ ref, branchName, title }) => {
|
|
4944
5041
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -4967,11 +5064,11 @@ async function startMcpServer() {
|
|
|
4967
5064
|
"create_pr_stack",
|
|
4968
5065
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
4969
5066
|
{
|
|
4970
|
-
repoPath:
|
|
4971
|
-
branches:
|
|
4972
|
-
agent:
|
|
4973
|
-
base:
|
|
4974
|
-
title:
|
|
5067
|
+
repoPath: z7.string(),
|
|
5068
|
+
branches: z7.array(z7.string()).min(1),
|
|
5069
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
5070
|
+
base: z7.string().optional(),
|
|
5071
|
+
title: z7.string().optional()
|
|
4975
5072
|
},
|
|
4976
5073
|
async (args) => {
|
|
4977
5074
|
const result = await orch.createPrStack({
|
|
@@ -5010,10 +5107,10 @@ async function startMcpServer() {
|
|
|
5010
5107
|
"list_issues",
|
|
5011
5108
|
"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.",
|
|
5012
5109
|
{
|
|
5013
|
-
repoPath:
|
|
5014
|
-
query:
|
|
5015
|
-
assignee:
|
|
5016
|
-
limit:
|
|
5110
|
+
repoPath: z7.string(),
|
|
5111
|
+
query: z7.string().optional().describe("Search title, identifier, or description"),
|
|
5112
|
+
assignee: z7.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
5113
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
5017
5114
|
},
|
|
5018
5115
|
async ({ repoPath, query, assignee, limit }) => {
|
|
5019
5116
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -5145,8 +5242,8 @@ var BrightsySideboardApi = class {
|
|
|
5145
5242
|
};
|
|
5146
5243
|
function taskMessageText(task) {
|
|
5147
5244
|
const parts = task.message?.parts ?? [];
|
|
5148
|
-
const
|
|
5149
|
-
return
|
|
5245
|
+
const text7 = parts.find((p) => p.kind === "text")?.text ?? "";
|
|
5246
|
+
return text7.trim();
|
|
5150
5247
|
}
|
|
5151
5248
|
|
|
5152
5249
|
// src/brightsy/cloud-connect.ts
|
|
@@ -5619,11 +5716,11 @@ async function startSlackOAuth(opts) {
|
|
|
5619
5716
|
import { WebSocket as WsWebSocket } from "ws";
|
|
5620
5717
|
var BACKOFF_START_MS = 1e3;
|
|
5621
5718
|
var BACKOFF_MAX_MS = 3e4;
|
|
5622
|
-
function stripSlackMentions(
|
|
5623
|
-
return
|
|
5719
|
+
function stripSlackMentions(text7) {
|
|
5720
|
+
return text7.replace(/<@[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
5624
5721
|
}
|
|
5625
|
-
function isSlackStopCommand(
|
|
5626
|
-
const t = stripSlackMentions(
|
|
5722
|
+
function isSlackStopCommand(text7) {
|
|
5723
|
+
const t = stripSlackMentions(text7).toLowerCase();
|
|
5627
5724
|
return t === "stop" || t === "sideboard_force_stop";
|
|
5628
5725
|
}
|
|
5629
5726
|
function parseSlackSocketFrame(raw) {
|
|
@@ -5645,8 +5742,8 @@ function inboundFromSocketFrame(frame) {
|
|
|
5645
5742
|
const ts = event.ts?.trim();
|
|
5646
5743
|
if (!channelId || !ts) return null;
|
|
5647
5744
|
const rawText = event.text?.trim() ?? "";
|
|
5648
|
-
const
|
|
5649
|
-
if (!
|
|
5745
|
+
const text7 = stripSlackMentions(rawText);
|
|
5746
|
+
if (!text7) return null;
|
|
5650
5747
|
const teamId = (frame.payload?.team_id || event.team || "").trim();
|
|
5651
5748
|
if (!teamId) return null;
|
|
5652
5749
|
const isDm = event.type === "message" && (event.channel_type === "im" || event.channel_type === "mpim" || !event.channel_type && channelId.startsWith("D"));
|
|
@@ -5658,7 +5755,7 @@ function inboundFromSocketFrame(frame) {
|
|
|
5658
5755
|
ts,
|
|
5659
5756
|
threadTs: event.thread_ts?.trim() || void 0,
|
|
5660
5757
|
userId: event.user?.trim(),
|
|
5661
|
-
text:
|
|
5758
|
+
text: text7,
|
|
5662
5759
|
kind: isMention ? "mention" : "dm"
|
|
5663
5760
|
};
|
|
5664
5761
|
}
|
|
@@ -6198,12 +6295,12 @@ function formatSlackInboundPrompt(msg) {
|
|
|
6198
6295
|
|
|
6199
6296
|
${msg.text}`;
|
|
6200
6297
|
}
|
|
6201
|
-
function isSlackInboundUserPrompt(
|
|
6202
|
-
return
|
|
6298
|
+
function isSlackInboundUserPrompt(text7) {
|
|
6299
|
+
return text7.startsWith("Slack DM\n") || text7.startsWith("Slack @mention\n");
|
|
6203
6300
|
}
|
|
6204
|
-
function formatSlackSignedReply(deviceLabel,
|
|
6301
|
+
function formatSlackSignedReply(deviceLabel, text7) {
|
|
6205
6302
|
const label = deviceLabel.trim();
|
|
6206
|
-
const body =
|
|
6303
|
+
const body = text7.trim();
|
|
6207
6304
|
if (!body) return body;
|
|
6208
6305
|
if (!label) return body;
|
|
6209
6306
|
const head = `${label}:`;
|
|
@@ -6212,8 +6309,8 @@ function formatSlackSignedReply(deviceLabel, text6) {
|
|
|
6212
6309
|
}
|
|
6213
6310
|
return `${label}: ${body}`;
|
|
6214
6311
|
}
|
|
6215
|
-
function signForThisMac(
|
|
6216
|
-
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel,
|
|
6312
|
+
function signForThisMac(text7) {
|
|
6313
|
+
return formatSlackSignedReply(ensureSlackDeviceIdentity().deviceLabel, text7);
|
|
6217
6314
|
}
|
|
6218
6315
|
function slackReplyThreadTs(msg) {
|
|
6219
6316
|
if (msg.threadTs && msg.threadTs !== msg.ts) return msg.threadTs;
|
|
@@ -6274,9 +6371,9 @@ function replyStub(target) {
|
|
|
6274
6371
|
kind: "dm"
|
|
6275
6372
|
};
|
|
6276
6373
|
}
|
|
6277
|
-
async function postSlackText(target,
|
|
6374
|
+
async function postSlackText(target, text7, opts) {
|
|
6278
6375
|
if (opts.postReply) {
|
|
6279
|
-
const result = await opts.postReply(replyStub(target),
|
|
6376
|
+
const result = await opts.postReply(replyStub(target), text7);
|
|
6280
6377
|
const ts2 = result && typeof result === "object" && typeof result.ts === "string" ? result.ts.trim() : "";
|
|
6281
6378
|
return ts2 ? { ts: ts2 } : null;
|
|
6282
6379
|
}
|
|
@@ -6286,7 +6383,7 @@ async function postSlackText(target, text6, opts) {
|
|
|
6286
6383
|
"chat.postMessage",
|
|
6287
6384
|
{
|
|
6288
6385
|
channel: target.channelId,
|
|
6289
|
-
text:
|
|
6386
|
+
text: text7,
|
|
6290
6387
|
thread_ts: target.threadTs
|
|
6291
6388
|
},
|
|
6292
6389
|
opts.fetchImpl
|
|
@@ -6294,9 +6391,9 @@ async function postSlackText(target, text6, opts) {
|
|
|
6294
6391
|
const ts = json.ts?.trim();
|
|
6295
6392
|
return ts ? { ts } : null;
|
|
6296
6393
|
}
|
|
6297
|
-
async function updateSlackText(target, ts,
|
|
6394
|
+
async function updateSlackText(target, ts, text7, opts) {
|
|
6298
6395
|
if (opts.updateReply) {
|
|
6299
|
-
await opts.updateReply(replyStub(target), ts,
|
|
6396
|
+
await opts.updateReply(replyStub(target), ts, text7);
|
|
6300
6397
|
return;
|
|
6301
6398
|
}
|
|
6302
6399
|
const token = writeTokenForTeam(target.teamId);
|
|
@@ -6306,7 +6403,7 @@ async function updateSlackText(target, ts, text6, opts) {
|
|
|
6306
6403
|
{
|
|
6307
6404
|
channel: target.channelId,
|
|
6308
6405
|
ts,
|
|
6309
|
-
text:
|
|
6406
|
+
text: text7
|
|
6310
6407
|
},
|
|
6311
6408
|
opts.fetchImpl
|
|
6312
6409
|
);
|
|
@@ -6334,8 +6431,8 @@ function replyTargetOf(msg) {
|
|
|
6334
6431
|
threadTs: slackReplyThreadTs(msg)
|
|
6335
6432
|
};
|
|
6336
6433
|
}
|
|
6337
|
-
async function postSlackReply(msg,
|
|
6338
|
-
await postSlackText(replyTargetOf(msg), signForThisMac(
|
|
6434
|
+
async function postSlackReply(msg, text7, opts) {
|
|
6435
|
+
await postSlackText(replyTargetOf(msg), signForThisMac(text7), opts);
|
|
6339
6436
|
}
|
|
6340
6437
|
function startSlackTurnProgress(msg, threadId, opts) {
|
|
6341
6438
|
const log = opts.onLog ?? (() => void 0);
|
|
@@ -6418,10 +6515,10 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
6418
6515
|
}
|
|
6419
6516
|
});
|
|
6420
6517
|
},
|
|
6421
|
-
finishWith: (
|
|
6518
|
+
finishWith: (text7) => {
|
|
6422
6519
|
stop();
|
|
6423
6520
|
return run2(async () => {
|
|
6424
|
-
const signed = signForThisMac(
|
|
6521
|
+
const signed = signForThisMac(text7.trim());
|
|
6425
6522
|
if (!signed) {
|
|
6426
6523
|
if (!postedTs) return;
|
|
6427
6524
|
const ts = postedTs;
|
|
@@ -6449,11 +6546,11 @@ function startSlackTurnProgress(msg, threadId, opts) {
|
|
|
6449
6546
|
};
|
|
6450
6547
|
}
|
|
6451
6548
|
var lastRelayed = /* @__PURE__ */ new Map();
|
|
6452
|
-
function markRelayed(threadId,
|
|
6453
|
-
lastRelayed.set(threadId, `${threadId}:${
|
|
6549
|
+
function markRelayed(threadId, text7) {
|
|
6550
|
+
lastRelayed.set(threadId, `${threadId}:${text7}`);
|
|
6454
6551
|
}
|
|
6455
|
-
function alreadyRelayed(threadId,
|
|
6456
|
-
return lastRelayed.get(threadId) === `${threadId}:${
|
|
6552
|
+
function alreadyRelayed(threadId, text7) {
|
|
6553
|
+
return lastRelayed.get(threadId) === `${threadId}:${text7}`;
|
|
6457
6554
|
}
|
|
6458
6555
|
async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
6459
6556
|
const target = getSlackReplyTarget(threadId);
|
|
@@ -6462,14 +6559,14 @@ async function relayCoordinatorReplyToSlack(threadId, opts) {
|
|
|
6462
6559
|
const lastUser = thread ? [...thread.messages].reverse().find((m) => m.role === "user") : void 0;
|
|
6463
6560
|
if (lastUser && isSlackInboundUserPrompt(lastUser.text)) return;
|
|
6464
6561
|
const result = getOrchestrator().getTurnResult(threadId);
|
|
6465
|
-
const
|
|
6466
|
-
if (!
|
|
6467
|
-
if (alreadyRelayed(threadId,
|
|
6468
|
-
markRelayed(threadId,
|
|
6562
|
+
const text7 = result.text.trim();
|
|
6563
|
+
if (!text7) return;
|
|
6564
|
+
if (alreadyRelayed(threadId, text7)) return;
|
|
6565
|
+
markRelayed(threadId, text7);
|
|
6469
6566
|
const log = opts.onLog ?? (() => void 0);
|
|
6470
6567
|
try {
|
|
6471
|
-
await postSlackText(target, signForThisMac(
|
|
6472
|
-
log(`replied ${target.threadTs ?? target.channelId} (${
|
|
6568
|
+
await postSlackText(target, signForThisMac(text7), opts);
|
|
6569
|
+
log(`replied ${target.threadTs ?? target.channelId} (${text7.length} chars)`);
|
|
6473
6570
|
} catch (err) {
|
|
6474
6571
|
lastRelayed.delete(threadId);
|
|
6475
6572
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -6717,10 +6814,10 @@ function resolveSlackListenMode(opts) {
|
|
|
6717
6814
|
}
|
|
6718
6815
|
|
|
6719
6816
|
// src/slack/relay-hub.ts
|
|
6720
|
-
function parseSlackDeviceDestination(
|
|
6721
|
-
const m =
|
|
6722
|
-
if (!m) return { label: null, rest:
|
|
6723
|
-
return { label: m[1], rest:
|
|
6817
|
+
function parseSlackDeviceDestination(text7) {
|
|
6818
|
+
const m = text7.match(/^\s*(?:to\s+)?(?:@|#)?([A-Za-z][\w-]{0,63})\s*[::]\s*/);
|
|
6819
|
+
if (!m) return { label: null, rest: text7 };
|
|
6820
|
+
return { label: m[1], rest: text7.slice(m[0].length) };
|
|
6724
6821
|
}
|
|
6725
6822
|
var SlackRelayHub = class {
|
|
6726
6823
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -7242,10 +7339,6 @@ async function startSlackRelayServer(opts) {
|
|
|
7242
7339
|
}
|
|
7243
7340
|
export {
|
|
7244
7341
|
ABLETIME_MCP_PATH,
|
|
7245
|
-
ACCOUNT_ROLES,
|
|
7246
|
-
ACCOUNT_ROLE_LABELS,
|
|
7247
|
-
ACCOUNT_ROLE_MAX,
|
|
7248
|
-
ACCOUNT_ROLE_PRESETS,
|
|
7249
7342
|
AGENT_GIT_ACTIONS,
|
|
7250
7343
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
7251
7344
|
ATTACHMENTS_DIR,
|
|
@@ -7332,7 +7425,6 @@ export {
|
|
|
7332
7425
|
WORKTREE_MCP_TOOLS,
|
|
7333
7426
|
abletimeMcpRequest,
|
|
7334
7427
|
abletimeMcpUrl,
|
|
7335
|
-
accountRoleLabel,
|
|
7336
7428
|
ackSlackInboundSeen,
|
|
7337
7429
|
addBoardPin,
|
|
7338
7430
|
addPrStackLayer,
|
|
@@ -7513,6 +7605,7 @@ export {
|
|
|
7513
7605
|
fireSchedule,
|
|
7514
7606
|
flattenTurnInput,
|
|
7515
7607
|
flipLinearRelationType,
|
|
7608
|
+
foldLegacyRolesIntoNotes,
|
|
7516
7609
|
followUpBehavior,
|
|
7517
7610
|
forkChatTab,
|
|
7518
7611
|
forkMessageSlice,
|
|
@@ -7553,6 +7646,8 @@ export {
|
|
|
7553
7646
|
formatSlackWorkingText,
|
|
7554
7647
|
formatTranscriptMarkdown,
|
|
7555
7648
|
formatUiReminder,
|
|
7649
|
+
formatViewerContextDirective,
|
|
7650
|
+
formatViewerContextReminder,
|
|
7556
7651
|
formatWorkspaceInventory,
|
|
7557
7652
|
formatWorkspaceProfileSuffix,
|
|
7558
7653
|
formatWorktreeDirective,
|
|
@@ -7790,7 +7885,6 @@ export {
|
|
|
7790
7885
|
planQuestionsSignature,
|
|
7791
7886
|
pollSlackOutboundWatches,
|
|
7792
7887
|
posixShellSingleQuote,
|
|
7793
|
-
preferTeamsForRole,
|
|
7794
7888
|
preferredCursorCostCents,
|
|
7795
7889
|
prepareTerminalCommand,
|
|
7796
7890
|
previewLand,
|
|
@@ -7802,6 +7896,7 @@ export {
|
|
|
7802
7896
|
readSetupLog,
|
|
7803
7897
|
readSkillBody,
|
|
7804
7898
|
readThread,
|
|
7899
|
+
readViewerContext,
|
|
7805
7900
|
readWorktreeFile,
|
|
7806
7901
|
readWorktreeFileForUpload,
|
|
7807
7902
|
readWorktreeInclude,
|
|
@@ -7858,7 +7953,6 @@ export {
|
|
|
7858
7953
|
resolveViewerProfile,
|
|
7859
7954
|
resolveViewerProfileForRepo,
|
|
7860
7955
|
resolveWorktreeStartPoint,
|
|
7861
|
-
reviewTeamHintsForRoles,
|
|
7862
7956
|
rewriteAbleTimeError,
|
|
7863
7957
|
rewriteLinearError,
|
|
7864
7958
|
run,
|
|
@@ -7992,5 +8086,6 @@ export {
|
|
|
7992
8086
|
writeInjectedMcpConfig,
|
|
7993
8087
|
writePlanFile,
|
|
7994
8088
|
writeThread,
|
|
8089
|
+
writeViewerContext,
|
|
7995
8090
|
writeWorktreeFile
|
|
7996
8091
|
};
|