@sideboard-ai/core 0.1.297 → 0.1.298
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-ICHYDSCD.js → agents-GHZFY65E.js} +5 -5
- package/dist/{agents-DZTPUKVF.js → agents-XL67HSZH.js} +5 -5
- package/dist/{chunk-QT36F4TD.js → chunk-2POLTCSC.js} +326 -25
- package/dist/{chunk-VXNZG4J6.js → chunk-3IXGI2FP.js} +1 -1
- package/dist/{chunk-67BWWLEB.js → chunk-4FWMUMXG.js} +5 -5
- package/dist/{chunk-WQREAX35.js → chunk-5FMQU2RU.js} +9 -5
- package/dist/{chunk-ED4ZXQVO.js → chunk-5XQ43YZU.js} +2 -1
- package/dist/{chunk-JWXJXEA3.js → chunk-BIMHIHX3.js} +1 -1
- package/dist/{chunk-Q3DPZ4DE.js → chunk-F36QRDM4.js} +2 -1
- package/dist/{chunk-6GTABVNG.js → chunk-IL77XODJ.js} +6 -4
- package/dist/{chunk-ZXWRNA4G.js → chunk-JM5P36PM.js} +5 -5
- package/dist/{chunk-WZLV4WNS.js → chunk-K5EACGG2.js} +11 -0
- package/dist/{chunk-BGEECLKH.js → chunk-KMYCNSSI.js} +9 -5
- package/dist/{chunk-W2A63ZOK.js → chunk-KRGQWQME.js} +329 -26
- package/dist/{chunk-2O3ICR77.js → chunk-RQMLJZ2V.js} +6 -4
- package/dist/{conductor-ZXDIA6OF.js → conductor-HUITLA2M.js} +5 -5
- package/dist/{conductor-HJFDYIZD.js → conductor-NGIEDMAB.js} +5 -5
- package/dist/{coordinator-prompt-L3V2CMWX.js → coordinator-prompt-52T5A4SU.js} +3 -3
- package/dist/{coordinator-prompt-ILHSVPOG.js → coordinator-prompt-JUN3IITF.js} +3 -3
- package/dist/{global-workspace-47G453ZU.js → global-workspace-VVUIFQ72.js} +3 -3
- package/dist/{global-workspace-NMRUGGFF.js → global-workspace-Z5YRL67Y.js} +3 -3
- package/dist/index.cjs +793 -337
- package/dist/index.d.cts +16 -9
- package/dist/index.d.ts +16 -9
- package/dist/index.js +343 -228
- package/dist/mcp/run-stdio.cjs +739 -302
- package/dist/mcp/run-stdio.js +323 -217
- package/dist/{orchestrator-MR7TCIPP.js → orchestrator-JY4ZETFX.js} +7 -7
- package/dist/{orchestrator-ZQU62CES.js → orchestrator-S7TKI6DR.js} +7 -7
- package/dist/{thread-store-H6D6MDUM.js → thread-store-5XXHSWMN.js} +1 -1
- package/dist/{thread-store-SADLLKRE.js → thread-store-WXLXKDVN.js} +1 -1
- package/dist/{workspaces-PGY4UWZ3.js → workspaces-7FVNGUBV.js} +3 -3
- package/dist/{workspaces-WGZPRQJX.js → workspaces-OSF56FHS.js} +3 -3
- package/dist/{worktree-JZXDAKQF.js → worktree-6G5X4ABY.js} +2 -2
- package/dist/{worktree-3MF5J5XZ.js → worktree-LFSBIUCN.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-TVN4TDFB.js → chunk-HTL4FBTP.js} +0 -0
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
chatBlockedSuffix,
|
|
15
15
|
clampBlockReason,
|
|
16
16
|
clampMcpIssueLimit,
|
|
17
|
+
connectedOrchestrator,
|
|
17
18
|
createSchedule,
|
|
18
19
|
dedupeBoardIssues,
|
|
19
20
|
dedupeBoardPrs,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
findBoardPr,
|
|
24
25
|
findLiveThreadForCreate,
|
|
25
26
|
fireSchedule,
|
|
27
|
+
formatFleetArchive,
|
|
26
28
|
formatGitHubSearchUpdatedSince,
|
|
27
29
|
formatListedIssuesForMcp,
|
|
28
30
|
getOrchestrator,
|
|
@@ -31,6 +33,7 @@ import {
|
|
|
31
33
|
isInjectedNoticeText,
|
|
32
34
|
issueMatchesUpdatedSince,
|
|
33
35
|
issueNeedsWorkspacePick,
|
|
36
|
+
listFleetArchive,
|
|
34
37
|
listSchedules,
|
|
35
38
|
localhostPreviewUrl,
|
|
36
39
|
mcpIssueUpdatedSinceSchema,
|
|
@@ -53,7 +56,7 @@ import {
|
|
|
53
56
|
updateSchedule,
|
|
54
57
|
workspaceTagTargetRef,
|
|
55
58
|
writeAgentBlock
|
|
56
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-2POLTCSC.js";
|
|
57
60
|
import "../chunk-LX5R5JOR.js";
|
|
58
61
|
import {
|
|
59
62
|
commentAbleTimeTask,
|
|
@@ -96,7 +99,7 @@ import {
|
|
|
96
99
|
sideboardMcpProfile,
|
|
97
100
|
trustSystemCertificates,
|
|
98
101
|
worktreeMcpToolNames
|
|
99
|
-
} from "../chunk-
|
|
102
|
+
} from "../chunk-RQMLJZ2V.js";
|
|
100
103
|
import "../chunk-WZCPZVUC.js";
|
|
101
104
|
import "../chunk-7IAMHN2N.js";
|
|
102
105
|
import "../chunk-PXMLMBMI.js";
|
|
@@ -113,18 +116,18 @@ import {
|
|
|
113
116
|
waitForDetachedJob,
|
|
114
117
|
waitForTurnToolResult,
|
|
115
118
|
workspaceAgentChatSummaries
|
|
116
|
-
} from "../chunk-
|
|
119
|
+
} from "../chunk-JM5P36PM.js";
|
|
117
120
|
import {
|
|
118
121
|
HUGE_TOOL_RESULT_SUMMARY,
|
|
119
122
|
clipToolResultForStore,
|
|
120
123
|
formatUnknownDetail,
|
|
121
124
|
isInternalAgentStatusText,
|
|
122
125
|
looksLikeHugeToolResultDump
|
|
123
|
-
} from "../chunk-
|
|
126
|
+
} from "../chunk-5XQ43YZU.js";
|
|
124
127
|
import {
|
|
125
128
|
GLOBAL_WORKSPACE_ID,
|
|
126
129
|
isCloudCoordinatorThread
|
|
127
|
-
} from "../chunk-
|
|
130
|
+
} from "../chunk-5FMQU2RU.js";
|
|
128
131
|
import {
|
|
129
132
|
canonicalizeRepoPath,
|
|
130
133
|
formatGhLandError,
|
|
@@ -135,7 +138,7 @@ import {
|
|
|
135
138
|
resolveListPrsOptions,
|
|
136
139
|
resolveRepoRoot,
|
|
137
140
|
warmGithubAgentAuth
|
|
138
|
-
} from "../chunk-
|
|
141
|
+
} from "../chunk-3IXGI2FP.js";
|
|
139
142
|
import {
|
|
140
143
|
CLI_DIR
|
|
141
144
|
} from "../chunk-N2WW62LF.js";
|
|
@@ -168,7 +171,7 @@ import {
|
|
|
168
171
|
normalizeWorkspaceTags,
|
|
169
172
|
readThread,
|
|
170
173
|
updateThread
|
|
171
|
-
} from "../chunk-
|
|
174
|
+
} from "../chunk-HTL4FBTP.js";
|
|
172
175
|
import "../chunk-4RZRHTSZ.js";
|
|
173
176
|
import "../chunk-KGZBYWZ3.js";
|
|
174
177
|
import {
|
|
@@ -282,7 +285,7 @@ function createClippedMcpServer() {
|
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
// src/mcp/server.ts
|
|
285
|
-
import { z as
|
|
288
|
+
import { z as z8 } from "zod";
|
|
286
289
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
287
290
|
import { basename } from "path";
|
|
288
291
|
|
|
@@ -2161,14 +2164,6 @@ function commentsFromAbleTimeIssues(tasks, issues, sinceIso) {
|
|
|
2161
2164
|
return out;
|
|
2162
2165
|
}
|
|
2163
2166
|
|
|
2164
|
-
// src/mcp/archive-guard.ts
|
|
2165
|
-
function mcpArchiveBlockedReason(thread) {
|
|
2166
|
-
if (isCloudCoordinatorThread(thread)) {
|
|
2167
|
-
return "Refusing to archive the cloud coordinator via MCP. Archive it from the Sideboard UI if needed.";
|
|
2168
|
-
}
|
|
2169
|
-
return null;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
2167
|
// src/mcp/run-script-ref.ts
|
|
2173
2168
|
function resolveRunScriptThreadRef(ref) {
|
|
2174
2169
|
const explicit = ref?.trim();
|
|
@@ -3395,8 +3390,137 @@ function formatMcpPrList(input) {
|
|
|
3395
3390
|
};
|
|
3396
3391
|
}
|
|
3397
3392
|
|
|
3398
|
-
// src/mcp/
|
|
3393
|
+
// src/mcp/archive-tools.ts
|
|
3399
3394
|
import { z as z6 } from "zod";
|
|
3395
|
+
|
|
3396
|
+
// src/mcp/archive-guard.ts
|
|
3397
|
+
function mcpArchiveBlockedReason(thread) {
|
|
3398
|
+
if (isCloudCoordinatorThread(thread)) {
|
|
3399
|
+
return "Refusing to archive the cloud coordinator via MCP. Archive it from the Sideboard UI if needed.";
|
|
3400
|
+
}
|
|
3401
|
+
return null;
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// src/mcp/archive-tools.ts
|
|
3405
|
+
function registerArchiveTools(server, orch) {
|
|
3406
|
+
server.tool(
|
|
3407
|
+
"list_archive",
|
|
3408
|
+
"Browse Settings \u2192 History (archived workspaces and agents), newest first. A workspace row is a checkout with no live agents \u2014 it is off Home and list_board. Nested lines are that workspace's agents; any of those ids restores the whole checkout. An agent row is an orchestration chat, or one agent archived while its workspace stayed live (workspace-live). kind=agent lists every archived agent, including agents on an archived workspace (workspace-archived). query matches title, branch, repo, agent, ticket, or PR. limit default 40, max 250. Then restore_workspace (whole checkout) or restore_chat (one agent).",
|
|
3409
|
+
{
|
|
3410
|
+
query: z6.string().optional().describe("Title, branch, repo, agent, ticket, or PR"),
|
|
3411
|
+
kind: z6.enum(["all", "workspace", "agent"]).optional().describe("all (default), workspace, or agent"),
|
|
3412
|
+
limit: z6.number().int().positive().max(250).optional().describe("Page size (default 40, max 250)")
|
|
3413
|
+
},
|
|
3414
|
+
async ({ query, kind, limit }) => {
|
|
3415
|
+
const view = listFleetArchive(orch.getThreads(true), { query, kind, limit });
|
|
3416
|
+
return { content: [{ type: "text", text: formatFleetArchive(view) }] };
|
|
3417
|
+
}
|
|
3418
|
+
);
|
|
3419
|
+
server.tool(
|
|
3420
|
+
"archive_workspace",
|
|
3421
|
+
"Archive a workspace: every live agent on that checkout, then remove the worktree when the last agent archives (cowboy keeps the project folder). The Home card moves to Settings \u2192 History. Pass any chat id on the workspace. Orchestration chats are agents \u2014 use archive_chat. To archive one agent and leave the checkout up, use archive_chat.",
|
|
3422
|
+
{ ref: z6.string().describe("Any chat id on the workspace") },
|
|
3423
|
+
async ({ ref }) => {
|
|
3424
|
+
const thread = orch.getThread(ref);
|
|
3425
|
+
if (!thread) {
|
|
3426
|
+
return { content: [{ type: "text", text: `Chat not found: ${ref}` }], isError: true };
|
|
3427
|
+
}
|
|
3428
|
+
for (const chat of threadsSharingWorktree(thread.worktreePath)) {
|
|
3429
|
+
const blocked = mcpArchiveBlockedReason(chat);
|
|
3430
|
+
if (blocked) {
|
|
3431
|
+
return { content: [{ type: "text", text: blocked }], isError: true };
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
try {
|
|
3435
|
+
const archived = await orch.archiveWorkspace(ref);
|
|
3436
|
+
return mcpJson({
|
|
3437
|
+
worktreePath: thread.worktreePath,
|
|
3438
|
+
archived: archived.map((chat) => ({
|
|
3439
|
+
id: chat.id,
|
|
3440
|
+
title: chat.title,
|
|
3441
|
+
agent: chat.agent,
|
|
3442
|
+
status: chat.status,
|
|
3443
|
+
link: `sideboard://chat/${chat.id}`
|
|
3444
|
+
}))
|
|
3445
|
+
});
|
|
3446
|
+
} catch (err) {
|
|
3447
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3448
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
);
|
|
3452
|
+
server.tool(
|
|
3453
|
+
"archive_chat",
|
|
3454
|
+
"Archive one agent chat: stop the turn and move that chat to Settings \u2192 History. Other agents on the workspace stay, and the worktree is removed only when this is the last agent. Orchestration chats archive without removing a project checkout. Refuses the cloud coordinator. To archive every agent on a checkout, use archive_workspace. Commit, push, and open PRs via ask_git before archiving when the work should be kept. Merge only when the user explicitly asked.",
|
|
3455
|
+
{ ref: z6.string().describe("Chat id to archive") },
|
|
3456
|
+
async ({ ref }) => {
|
|
3457
|
+
const t = orch.getThread(ref);
|
|
3458
|
+
if (!t) {
|
|
3459
|
+
return {
|
|
3460
|
+
content: [{ type: "text", text: `Chat not found: ${ref}` }],
|
|
3461
|
+
isError: true
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
const blocked = mcpArchiveBlockedReason(t);
|
|
3465
|
+
if (blocked) {
|
|
3466
|
+
return {
|
|
3467
|
+
content: [{ type: "text", text: blocked }],
|
|
3468
|
+
isError: true
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
const archived = await orch.archive(ref);
|
|
3472
|
+
return mcpJson({
|
|
3473
|
+
id: archived.id,
|
|
3474
|
+
status: archived.status
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
);
|
|
3478
|
+
server.tool(
|
|
3479
|
+
"restore_workspace",
|
|
3480
|
+
"Restore a workspace from Settings \u2192 History: recreate the worktree if it was removed and bring every archived agent on that checkout back. Pass any chat id from list_archive. If some agents are still live, only the archived ones return to that checkout. One agent only: restore_chat. Orchestration agents: restore_chat.",
|
|
3481
|
+
{ ref: z6.string().describe("Any chat id from list_archive on that workspace") },
|
|
3482
|
+
async ({ ref }) => {
|
|
3483
|
+
try {
|
|
3484
|
+
const restored = await orch.restoreWorkspace(ref);
|
|
3485
|
+
return mcpJson({
|
|
3486
|
+
worktreePath: restored[0]?.worktreePath ?? null,
|
|
3487
|
+
restored: restored.map((chat) => ({
|
|
3488
|
+
id: chat.id,
|
|
3489
|
+
title: chat.title,
|
|
3490
|
+
agent: chat.agent,
|
|
3491
|
+
status: chat.status,
|
|
3492
|
+
worktreePath: chat.worktreePath,
|
|
3493
|
+
link: `sideboard://chat/${chat.id}`
|
|
3494
|
+
}))
|
|
3495
|
+
});
|
|
3496
|
+
} catch (err) {
|
|
3497
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3498
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
);
|
|
3502
|
+
server.tool(
|
|
3503
|
+
"restore_chat",
|
|
3504
|
+
"Restore one archived agent from list_archive. Recreates the worktree when that checkout was fully archived and the folder is gone. Other archived agents on the checkout stay in History until restore_workspace or another restore_chat. Orchestration agents come back as Global chats.",
|
|
3505
|
+
{ ref: z6.string().describe("Archived chat id from list_archive") },
|
|
3506
|
+
async ({ ref }) => {
|
|
3507
|
+
try {
|
|
3508
|
+
const restored = await orch.restore(ref);
|
|
3509
|
+
return mcpJson({
|
|
3510
|
+
id: restored.id,
|
|
3511
|
+
status: restored.status,
|
|
3512
|
+
worktreePath: restored.worktreePath
|
|
3513
|
+
});
|
|
3514
|
+
} catch (err) {
|
|
3515
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3516
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
);
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
// src/mcp/schedule-tools.ts
|
|
3523
|
+
import { z as z7 } from "zod";
|
|
3400
3524
|
function text4(payload, isError = false) {
|
|
3401
3525
|
return mcpJson(payload, isError);
|
|
3402
3526
|
}
|
|
@@ -3436,18 +3560,18 @@ function registerScheduleTools(server) {
|
|
|
3436
3560
|
"create_schedule",
|
|
3437
3561
|
"Create a local schedule that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
|
|
3438
3562
|
{
|
|
3439
|
-
prompt:
|
|
3440
|
-
name:
|
|
3441
|
-
at:
|
|
3442
|
-
every:
|
|
3443
|
-
cron:
|
|
3444
|
-
tz:
|
|
3445
|
-
chatId:
|
|
3563
|
+
prompt: z7.string().describe("User message / goal queued when the schedule fires"),
|
|
3564
|
+
name: z7.string().optional(),
|
|
3565
|
+
at: z7.string().optional().describe("ISO datetime for a one-shot"),
|
|
3566
|
+
every: z7.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
3567
|
+
cron: z7.string().optional().describe("5-field cron expression"),
|
|
3568
|
+
tz: z7.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
3569
|
+
chatId: z7.string().optional().describe(
|
|
3446
3570
|
'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
|
|
3447
3571
|
),
|
|
3448
|
-
threadId:
|
|
3449
|
-
agent:
|
|
3450
|
-
model:
|
|
3572
|
+
threadId: z7.string().optional().describe("Legacy alias for chatId."),
|
|
3573
|
+
agent: z7.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
3574
|
+
model: z7.string().optional()
|
|
3451
3575
|
},
|
|
3452
3576
|
async (args) => {
|
|
3453
3577
|
try {
|
|
@@ -3483,20 +3607,20 @@ function registerScheduleTools(server) {
|
|
|
3483
3607
|
"update_schedule",
|
|
3484
3608
|
"Update a local schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
|
|
3485
3609
|
{
|
|
3486
|
-
id:
|
|
3487
|
-
prompt:
|
|
3488
|
-
name:
|
|
3489
|
-
at:
|
|
3490
|
-
every:
|
|
3491
|
-
cron:
|
|
3492
|
-
tz:
|
|
3493
|
-
chatId:
|
|
3610
|
+
id: z7.string(),
|
|
3611
|
+
prompt: z7.string().optional(),
|
|
3612
|
+
name: z7.string().optional(),
|
|
3613
|
+
at: z7.string().optional(),
|
|
3614
|
+
every: z7.string().optional(),
|
|
3615
|
+
cron: z7.string().optional(),
|
|
3616
|
+
tz: z7.string().optional(),
|
|
3617
|
+
chatId: z7.string().optional().describe(
|
|
3494
3618
|
'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
|
|
3495
3619
|
),
|
|
3496
|
-
threadId:
|
|
3497
|
-
agent:
|
|
3498
|
-
model:
|
|
3499
|
-
enabled:
|
|
3620
|
+
threadId: z7.string().optional().describe("Legacy alias for chatId."),
|
|
3621
|
+
agent: z7.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
3622
|
+
model: z7.string().optional(),
|
|
3623
|
+
enabled: z7.boolean().optional()
|
|
3500
3624
|
},
|
|
3501
3625
|
async (args) => {
|
|
3502
3626
|
try {
|
|
@@ -3537,7 +3661,7 @@ function registerScheduleTools(server) {
|
|
|
3537
3661
|
server.tool(
|
|
3538
3662
|
"delete_schedule",
|
|
3539
3663
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
3540
|
-
{ id:
|
|
3664
|
+
{ id: z7.string() },
|
|
3541
3665
|
async ({ id }) => {
|
|
3542
3666
|
try {
|
|
3543
3667
|
deleteSchedule(id);
|
|
@@ -3550,7 +3674,7 @@ function registerScheduleTools(server) {
|
|
|
3550
3674
|
server.tool(
|
|
3551
3675
|
"run_schedule",
|
|
3552
3676
|
"Fire a schedule now (does not wait for nextRunAt). Queues the prompt on the target chat or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
|
|
3553
|
-
{ id:
|
|
3677
|
+
{ id: z7.string() },
|
|
3554
3678
|
async ({ id }) => {
|
|
3555
3679
|
try {
|
|
3556
3680
|
if (!getSchedule(id)) {
|
|
@@ -3950,10 +4074,10 @@ async function startMcpServer() {
|
|
|
3950
4074
|
);
|
|
3951
4075
|
server.tool(
|
|
3952
4076
|
"list_chats",
|
|
3953
|
-
"List chats across all projects (one summary line each \u2014 token-frugal). Includes parent id, last message preview, blocked: when an agent is waiting on a person, and live progress so you can see workspace children. Each line ends with sideboard://chat/<id> \u2014 use that URL in markdown links so the UI can open the chat.",
|
|
4077
|
+
"List live chats across all projects (one summary line each \u2014 token-frugal). Includes parent id, last message preview, blocked: when an agent is waiting on a person, and live progress so you can see workspace children. Each line ends with sideboard://chat/<id> \u2014 use that URL in markdown links so the UI can open the chat. Archived workspaces and agents are list_archive, not this list.",
|
|
3954
4078
|
{},
|
|
3955
4079
|
async () => {
|
|
3956
|
-
const threads = orch.getThreads(
|
|
4080
|
+
const threads = orch.getThreads();
|
|
3957
4081
|
const lines = threads.map((t) => {
|
|
3958
4082
|
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : basename(t.repoPath) || t.repoPath;
|
|
3959
4083
|
const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
|
|
@@ -3971,18 +4095,18 @@ async function startMcpServer() {
|
|
|
3971
4095
|
);
|
|
3972
4096
|
server.tool(
|
|
3973
4097
|
"list_board",
|
|
3974
|
-
"Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. 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), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
|
|
4098
|
+
"Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. 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 (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
|
|
3975
4099
|
{
|
|
3976
|
-
query:
|
|
3977
|
-
repoPath:
|
|
3978
|
-
kind:
|
|
3979
|
-
ownership:
|
|
4100
|
+
query: z8.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
|
|
4101
|
+
repoPath: z8.string().optional().describe("Limit to one project path from list_projects"),
|
|
4102
|
+
kind: z8.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
4103
|
+
ownership: z8.enum(["all", "mine", "reviewing"]).optional().describe(
|
|
3980
4104
|
"Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
|
|
3981
4105
|
),
|
|
3982
|
-
column:
|
|
4106
|
+
column: z8.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
3983
4107
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
3984
4108
|
),
|
|
3985
|
-
limit:
|
|
4109
|
+
limit: z8.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
3986
4110
|
},
|
|
3987
4111
|
async ({ query, repoPath, kind, ownership, column, limit }) => {
|
|
3988
4112
|
const workspaces = orch.listWorkspaces();
|
|
@@ -4012,7 +4136,7 @@ async function startMcpServer() {
|
|
|
4012
4136
|
server.tool(
|
|
4013
4137
|
"get_chat",
|
|
4014
4138
|
"Get a compact chat summary by id/ref. Includes last message preview, parentChatId, and child workspace chats (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (billed token + costUsd totals when providers reported cost) and lastTurnUsage. blockedReason is why that agent is waiting on a person (question, plan approval, or a reported block).",
|
|
4015
|
-
{ ref:
|
|
4139
|
+
{ ref: z8.string() },
|
|
4016
4140
|
async ({ ref }) => {
|
|
4017
4141
|
const t = orch.getThread(ref);
|
|
4018
4142
|
if (!t) {
|
|
@@ -4059,17 +4183,17 @@ async function startMcpServer() {
|
|
|
4059
4183
|
"present_artifact",
|
|
4060
4184
|
"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.",
|
|
4061
4185
|
{
|
|
4062
|
-
title:
|
|
4063
|
-
type:
|
|
4186
|
+
title: z8.string().describe("Short title shown in the artifact pane header"),
|
|
4187
|
+
type: z8.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
4064
4188
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
4065
4189
|
),
|
|
4066
|
-
content:
|
|
4190
|
+
content: z8.string().describe(
|
|
4067
4191
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
4068
4192
|
),
|
|
4069
|
-
artifact_id:
|
|
4070
|
-
status:
|
|
4071
|
-
phase:
|
|
4072
|
-
mode:
|
|
4193
|
+
artifact_id: z8.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
4194
|
+
status: z8.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
4195
|
+
phase: z8.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
4196
|
+
mode: z8.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
4073
4197
|
},
|
|
4074
4198
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
4075
4199
|
return mcpJson(
|
|
@@ -4082,15 +4206,15 @@ async function startMcpServer() {
|
|
|
4082
4206
|
"ask_user",
|
|
4083
4207
|
"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.",
|
|
4084
4208
|
{
|
|
4085
|
-
questions:
|
|
4086
|
-
|
|
4087
|
-
question:
|
|
4088
|
-
header:
|
|
4089
|
-
multiSelect:
|
|
4090
|
-
options:
|
|
4091
|
-
|
|
4092
|
-
label:
|
|
4093
|
-
description:
|
|
4209
|
+
questions: z8.array(
|
|
4210
|
+
z8.object({
|
|
4211
|
+
question: z8.string().describe("Full question text ending with ?"),
|
|
4212
|
+
header: z8.string().max(24).optional().describe("Short label shown above the question"),
|
|
4213
|
+
multiSelect: z8.boolean().optional().describe("Allow selecting multiple options"),
|
|
4214
|
+
options: z8.array(
|
|
4215
|
+
z8.object({
|
|
4216
|
+
label: z8.string(),
|
|
4217
|
+
description: z8.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
4094
4218
|
})
|
|
4095
4219
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
4096
4220
|
})
|
|
@@ -4116,9 +4240,9 @@ async function startMcpServer() {
|
|
|
4116
4240
|
"present_plan",
|
|
4117
4241
|
PRESENT_PLAN_TOOL_DESCRIPTION,
|
|
4118
4242
|
{
|
|
4119
|
-
title:
|
|
4120
|
-
content:
|
|
4121
|
-
thread_id:
|
|
4243
|
+
title: z8.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
4244
|
+
content: z8.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
4245
|
+
thread_id: z8.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
4122
4246
|
},
|
|
4123
4247
|
async ({ title, content, thread_id }) => {
|
|
4124
4248
|
const { writePlanFile } = await import("../plan-file-VAHV5WBB.js");
|
|
@@ -4147,15 +4271,15 @@ async function startMcpServer() {
|
|
|
4147
4271
|
"present_schema",
|
|
4148
4272
|
"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.",
|
|
4149
4273
|
{
|
|
4150
|
-
title:
|
|
4151
|
-
mode:
|
|
4152
|
-
datasource:
|
|
4153
|
-
resource_id:
|
|
4154
|
-
record_id:
|
|
4155
|
-
resource:
|
|
4156
|
-
record:
|
|
4157
|
-
records:
|
|
4158
|
-
pane_id:
|
|
4274
|
+
title: z8.string().describe("Pane title"),
|
|
4275
|
+
mode: z8.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
4276
|
+
datasource: z8.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
4277
|
+
resource_id: z8.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
4278
|
+
record_id: z8.string().optional().describe("Record id when opening form mode"),
|
|
4279
|
+
resource: z8.record(z8.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
4280
|
+
record: z8.record(z8.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
4281
|
+
records: z8.array(z8.record(z8.unknown())).optional().describe("Inline records for table mode"),
|
|
4282
|
+
pane_id: z8.string().optional().describe("Stable pane id across updates")
|
|
4159
4283
|
},
|
|
4160
4284
|
async (args) => {
|
|
4161
4285
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4176,10 +4300,10 @@ async function startMcpServer() {
|
|
|
4176
4300
|
"present_files",
|
|
4177
4301
|
"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.",
|
|
4178
4302
|
{
|
|
4179
|
-
title:
|
|
4180
|
-
datasource:
|
|
4181
|
-
path:
|
|
4182
|
-
pane_id:
|
|
4303
|
+
title: z8.string().optional().describe("Pane title (default: Files)"),
|
|
4304
|
+
datasource: z8.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
4305
|
+
path: z8.string().optional().describe("Initial folder path (e.g. public)"),
|
|
4306
|
+
pane_id: z8.string().optional().describe("Stable pane id across updates")
|
|
4183
4307
|
},
|
|
4184
4308
|
async (args) => {
|
|
4185
4309
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -4199,8 +4323,8 @@ async function startMcpServer() {
|
|
|
4199
4323
|
"wait_for_job",
|
|
4200
4324
|
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. MCP clients kill tools around 60s, so this returns within 45s. Sideboard opens a type=log pane from this result (artifact_id = job id, content = delta). stillRunning is the source of truth \u2014 if true, call wait_for_job again. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
4201
4325
|
{
|
|
4202
|
-
id:
|
|
4203
|
-
timeoutMs:
|
|
4326
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
4327
|
+
timeoutMs: z8.number().optional()
|
|
4204
4328
|
},
|
|
4205
4329
|
async ({ id, timeoutMs }) => {
|
|
4206
4330
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
@@ -4213,8 +4337,8 @@ async function startMcpServer() {
|
|
|
4213
4337
|
"stop_job",
|
|
4214
4338
|
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
|
|
4215
4339
|
{
|
|
4216
|
-
id:
|
|
4217
|
-
reason:
|
|
4340
|
+
id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
4341
|
+
reason: z8.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
|
|
4218
4342
|
},
|
|
4219
4343
|
async ({ id, reason }) => {
|
|
4220
4344
|
const result = await stopDetachedJob(process.cwd(), id, { reason });
|
|
@@ -4226,7 +4350,7 @@ async function startMcpServer() {
|
|
|
4226
4350
|
"list_run_scripts",
|
|
4227
4351
|
"List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Each script and active run includes preview: url (open http://localhost:<SIDEBOARD_PORT>) or window (Electron: native window is the app; url is renderer/HMR only). Never guess :3000. Worktree turns may omit ref (uses cwd).",
|
|
4228
4352
|
{
|
|
4229
|
-
ref:
|
|
4353
|
+
ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
|
|
4230
4354
|
},
|
|
4231
4355
|
async ({ ref }) => {
|
|
4232
4356
|
try {
|
|
@@ -4256,8 +4380,8 @@ async function startMcpServer() {
|
|
|
4256
4380
|
"run_dev_script",
|
|
4257
4381
|
"Start a .sideboard/.conductor run script (same as the desktop Dev / play button). Several named scripts can run at once; each gets its own port, and starting one does not stop the others. Logs and Stop appear in the thread UI \u2014 do not detached-job or shell-spawn the same command. Returns {port, ports, url, preview}. preview=url \u2192 open url (browser, curl, Playwright). preview=window \u2192 Electron: the native window is the app; url is renderer/HMR only (no preload/IPC in a browser). The port is allocated SIDEBOARD_PORT, not 3000. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
|
|
4258
4382
|
{
|
|
4259
|
-
ref:
|
|
4260
|
-
name:
|
|
4383
|
+
ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
4384
|
+
name: z8.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
|
|
4261
4385
|
},
|
|
4262
4386
|
async ({ ref, name }) => {
|
|
4263
4387
|
try {
|
|
@@ -4285,8 +4409,8 @@ async function startMcpServer() {
|
|
|
4285
4409
|
"stop_dev_script",
|
|
4286
4410
|
"Stop a run script started with run_dev_script or the desktop Dev button (same as UI Stop). Omit name to stop all scripts on this worktree. Worktree turns may omit ref (uses cwd).",
|
|
4287
4411
|
{
|
|
4288
|
-
ref:
|
|
4289
|
-
name:
|
|
4412
|
+
ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
4413
|
+
name: z8.string().optional().describe("Script name to stop; omit to stop all active scripts.")
|
|
4290
4414
|
},
|
|
4291
4415
|
async ({ ref, name }) => {
|
|
4292
4416
|
try {
|
|
@@ -4303,9 +4427,9 @@ async function startMcpServer() {
|
|
|
4303
4427
|
"get_run_log",
|
|
4304
4428
|
"Read the Run-tab terminal for a worktree script (same stdout/stderr as the desktop Dev pane). Default is a tail \u2014 raise `tail` only if the error is missing. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
|
|
4305
4429
|
{
|
|
4306
|
-
ref:
|
|
4307
|
-
name:
|
|
4308
|
-
tail:
|
|
4430
|
+
ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
4431
|
+
name: z8.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
|
|
4432
|
+
tail: z8.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
|
|
4309
4433
|
`Max characters to return from the end (default ${DEFAULT_RUN_LOG_TAIL_CHARS}). Do not request the full buffer unless the tail is truncated and you still need more.`
|
|
4310
4434
|
)
|
|
4311
4435
|
},
|
|
@@ -4339,12 +4463,37 @@ async function startMcpServer() {
|
|
|
4339
4463
|
}
|
|
4340
4464
|
);
|
|
4341
4465
|
if (worktreeProfile) {
|
|
4466
|
+
server.tool(
|
|
4467
|
+
"get_orchestrator",
|
|
4468
|
+
"The orchestration chat this agent is connected to \u2014 the parent that lists this workspace among its agents. Returns id, title, status, agent, and a sideboard://chat link. connected is false when this chat was not opened by an orchestrator. notify_orchestrator wakes that chat. You cannot list or steer its other agents from here.",
|
|
4469
|
+
{},
|
|
4470
|
+
async () => {
|
|
4471
|
+
try {
|
|
4472
|
+
const child = resolveNotifyCallerThread();
|
|
4473
|
+
const parent = connectedOrchestrator(child, (id) => orch.getThread(id));
|
|
4474
|
+
if (!parent) return mcpJson({ connected: false, orchestrator: null });
|
|
4475
|
+
return mcpJson({
|
|
4476
|
+
connected: true,
|
|
4477
|
+
orchestrator: {
|
|
4478
|
+
id: parent.id,
|
|
4479
|
+
title: parent.title,
|
|
4480
|
+
status: parent.status,
|
|
4481
|
+
agent: parent.agent,
|
|
4482
|
+
link: `sideboard://chat/${parent.id}`
|
|
4483
|
+
}
|
|
4484
|
+
});
|
|
4485
|
+
} catch (err) {
|
|
4486
|
+
const text5 = err instanceof Error ? err.message : String(err);
|
|
4487
|
+
return { content: [{ type: "text", text: text5 }], isError: true };
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
);
|
|
4342
4491
|
server.tool(
|
|
4343
4492
|
"notify_orchestrator",
|
|
4344
4493
|
"Wake the parent Global orchestrator with a short status so it does not have to poll wait_for_turn. Use when you are blocked and the parent may have moved on (missing access, waiting on something the coordinator must handle). The message is shown on the sidebar and board until the next user message in this chat. ask_user already notifies for input-required \u2014 do not call this for the same question. Information only: never a git command. Do not use this to steer other worktrees.",
|
|
4345
4494
|
{
|
|
4346
|
-
message:
|
|
4347
|
-
reason:
|
|
4495
|
+
message: z8.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
|
|
4496
|
+
reason: z8.enum(["input-required", "blocked"]).optional().describe(
|
|
4348
4497
|
"input-required = waiting on the user in this chat; blocked = need the coordinator (default)."
|
|
4349
4498
|
)
|
|
4350
4499
|
},
|
|
@@ -4381,11 +4530,11 @@ async function startMcpServer() {
|
|
|
4381
4530
|
"set_workspace_tags",
|
|
4382
4531
|
worktreeProfile ? "Update tags on this workspace (the checkout you are in). Omit ref to use this chat. mode=replace (default), add, or remove. Short labels \u2014 the same ones the orchestrator used for this task. You cannot retag a different checkout." : "Label a project workspace so related checkouts stay recognizable in the sidebar and on the phone. Pass any chat id on that workspace. Use the same tags on every workspace from one task. mode=replace is the default; add and remove change the set. create_workspace tags= sets them at create time. Not for orchestration chats.",
|
|
4383
4532
|
{
|
|
4384
|
-
ref:
|
|
4533
|
+
ref: z8.string().optional().describe(
|
|
4385
4534
|
worktreeProfile ? "Omit for this chat. Another chat id only if it is on this same checkout." : "Chat id on the workspace to label."
|
|
4386
4535
|
),
|
|
4387
|
-
tags:
|
|
4388
|
-
mode:
|
|
4536
|
+
tags: z8.union([z8.string(), z8.array(z8.string())]).describe("Labels to set. A string may be comma-separated."),
|
|
4537
|
+
mode: z8.enum(["replace", "add", "remove"]).optional()
|
|
4389
4538
|
},
|
|
4390
4539
|
async ({ ref, tags, mode }) => {
|
|
4391
4540
|
try {
|
|
@@ -4419,7 +4568,7 @@ async function startMcpServer() {
|
|
|
4419
4568
|
"set_caffeinate",
|
|
4420
4569
|
"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.",
|
|
4421
4570
|
{
|
|
4422
|
-
enabled:
|
|
4571
|
+
enabled: z8.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
4423
4572
|
},
|
|
4424
4573
|
async ({ enabled }) => {
|
|
4425
4574
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -4445,24 +4594,24 @@ async function startMcpServer() {
|
|
|
4445
4594
|
"create_workspace",
|
|
4446
4595
|
`Create a workspace (isolated git checkout + first chat) from branch, pr, or ticket. A ticket, PR, or named branch has one checkout \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. That is not one agent: chats lists every agent on that checkout. Add another with fork_chat; send_to_chat only the chat that should do this job. Creating from the default branch still opens a new isolated checkout. Pass repoPath from list_projects. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentChatId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Omit agent/model \u2014 Sideboard applies ${accountDefaultsHint}. Do not pass your own agent or agent=cursor; those are ignored. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Pass the same tags on every workspace you open for one task (short labels such as "phone-sync") so they still match in the sidebar the next day. Reusing a checkout adds those tags.`,
|
|
4447
4596
|
{
|
|
4448
|
-
sourceType:
|
|
4449
|
-
sourceRef:
|
|
4450
|
-
agent:
|
|
4597
|
+
sourceType: z8.enum(["branch", "pr", "ticket"]),
|
|
4598
|
+
sourceRef: z8.string(),
|
|
4599
|
+
agent: z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
|
|
4451
4600
|
`Usually omit. Sideboard uses Account default agent (${accountDefaults.agent}). Passing your own agent or cursor is ignored.`
|
|
4452
4601
|
),
|
|
4453
|
-
model:
|
|
4602
|
+
model: z8.string().nullable().optional().describe(
|
|
4454
4603
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
4455
4604
|
),
|
|
4456
|
-
repoPath:
|
|
4457
|
-
title:
|
|
4458
|
-
cowboy:
|
|
4605
|
+
repoPath: z8.string(),
|
|
4606
|
+
title: z8.string().optional(),
|
|
4607
|
+
cowboy: z8.boolean().optional().describe(
|
|
4459
4608
|
"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."
|
|
4460
4609
|
),
|
|
4461
|
-
parentChatId:
|
|
4610
|
+
parentChatId: z8.string().optional().describe(
|
|
4462
4611
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
4463
4612
|
),
|
|
4464
|
-
parentThreadId:
|
|
4465
|
-
tags:
|
|
4613
|
+
parentThreadId: z8.string().optional().describe("Legacy alias for parentChatId."),
|
|
4614
|
+
tags: z8.union([z8.string(), z8.array(z8.string())]).optional().describe(
|
|
4466
4615
|
"Short labels for this workspace (or a comma-separated string). Use the same tags on every checkout for one task. Added when the checkout already exists."
|
|
4467
4616
|
)
|
|
4468
4617
|
},
|
|
@@ -4478,11 +4627,11 @@ async function startMcpServer() {
|
|
|
4478
4627
|
"start_board_card",
|
|
4479
4628
|
"Same as create_workspace for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second checkout when one already matches \u2014 returns that workspace (alreadyStarted) and its chats. A workspace can have many agents. fork_chat to add one; send_to_chat only the chat that should do this job.",
|
|
4480
4629
|
{
|
|
4481
|
-
kind:
|
|
4482
|
-
ref:
|
|
4483
|
-
repoPath:
|
|
4484
|
-
title:
|
|
4485
|
-
tags:
|
|
4630
|
+
kind: z8.enum(["ticket", "pr", "branch"]),
|
|
4631
|
+
ref: z8.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
4632
|
+
repoPath: z8.string().describe("Project path from list_projects / list_board"),
|
|
4633
|
+
title: z8.string().optional(),
|
|
4634
|
+
tags: z8.union([z8.string(), z8.array(z8.string())]).optional().describe(
|
|
4486
4635
|
"Short labels for this workspace. Same tags on every checkout for one task. Added when the checkout already exists."
|
|
4487
4636
|
)
|
|
4488
4637
|
},
|
|
@@ -4587,9 +4736,9 @@ async function startMcpServer() {
|
|
|
4587
4736
|
"send_to_chat",
|
|
4588
4737
|
'Steer a prompt on a workspace chat (Settings \u2192 Follow-up, default steer: interrupt the in-flight turn and start now, same as the workspace composer). Use after create_workspace 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 send_to_chat to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
|
|
4589
4738
|
{
|
|
4590
|
-
ref:
|
|
4591
|
-
prompt:
|
|
4592
|
-
force_stop:
|
|
4739
|
+
ref: z8.string(),
|
|
4740
|
+
prompt: z8.string(),
|
|
4741
|
+
force_stop: z8.boolean().optional()
|
|
4593
4742
|
},
|
|
4594
4743
|
async ({ ref, prompt, force_stop }) => {
|
|
4595
4744
|
if (force_stop) {
|
|
@@ -4614,8 +4763,8 @@ async function startMcpServer() {
|
|
|
4614
4763
|
"wait_for_turn",
|
|
4615
4764
|
"Wait until the chat 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. taskState is the A2A-style lifecycle: submitted (queued, not started), working, input-required (ask_user), completed, failed, canceled. stillRunning is true only for submitted/working. blockedReason is why the child is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. If stillRunning, text and usage are empty (they would be the previous turn) \u2014 read progress and call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts). On failed, lastError/text is the failure. On canceled, the child did not finish \u2014 resume with send_to_chat or tell the user. On input-required, wait for the user in that chat. When finished, text is this turn\u2019s assistant reply and usage is that turn\u2019s tokens + costUsd (this turn, not the Claude session total; sessionCostUsd is the provider session total when present).",
|
|
4616
4765
|
{
|
|
4617
|
-
ref:
|
|
4618
|
-
timeoutMs:
|
|
4766
|
+
ref: z8.string(),
|
|
4767
|
+
timeoutMs: z8.number().optional()
|
|
4619
4768
|
},
|
|
4620
4769
|
async ({ ref, timeoutMs }) => {
|
|
4621
4770
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -4643,7 +4792,7 @@ async function startMcpServer() {
|
|
|
4643
4792
|
server.tool(
|
|
4644
4793
|
"get_turn_result",
|
|
4645
4794
|
"Assistant message and last-turn usage when the turn finished. While stillRunning (submitted/working), text and usage are empty so a previous reply cannot be mistaken for this turn \u2014 use progress for tools/thinking. Not the full transcript. Includes taskState (A2A lifecycle) and blockedReason (why the agent is waiting on a person).",
|
|
4646
|
-
{ ref:
|
|
4795
|
+
{ ref: z8.string() },
|
|
4647
4796
|
async ({ ref }) => {
|
|
4648
4797
|
const result = orch.getTurnResult(ref);
|
|
4649
4798
|
const hint = mcpWaitTaskHint(result.taskState, result.status, result.blockedReason);
|
|
@@ -4659,9 +4808,9 @@ async function startMcpServer() {
|
|
|
4659
4808
|
"stop_chat",
|
|
4660
4809
|
"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. Pass reason \u2014 a short note on why this chat is stopping. It is stored on the thread and kept when the turn unwinds. If you omit it, Sideboard still records that the queue was cleared.",
|
|
4661
4810
|
{
|
|
4662
|
-
ref:
|
|
4663
|
-
force:
|
|
4664
|
-
reason:
|
|
4811
|
+
ref: z8.string(),
|
|
4812
|
+
force: z8.boolean().optional(),
|
|
4813
|
+
reason: z8.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
|
|
4665
4814
|
},
|
|
4666
4815
|
async ({ ref, force, reason }) => {
|
|
4667
4816
|
const t = orch.getThread(ref);
|
|
@@ -4682,56 +4831,13 @@ async function startMcpServer() {
|
|
|
4682
4831
|
});
|
|
4683
4832
|
}
|
|
4684
4833
|
);
|
|
4685
|
-
server
|
|
4686
|
-
"archive_chat",
|
|
4687
|
-
"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.",
|
|
4688
|
-
{ ref: z7.string() },
|
|
4689
|
-
async ({ ref }) => {
|
|
4690
|
-
const t = orch.getThread(ref);
|
|
4691
|
-
if (!t) {
|
|
4692
|
-
return {
|
|
4693
|
-
content: [{ type: "text", text: `Chat not found: ${ref}` }],
|
|
4694
|
-
isError: true
|
|
4695
|
-
};
|
|
4696
|
-
}
|
|
4697
|
-
const blocked = mcpArchiveBlockedReason(t);
|
|
4698
|
-
if (blocked) {
|
|
4699
|
-
return {
|
|
4700
|
-
content: [{ type: "text", text: blocked }],
|
|
4701
|
-
isError: true
|
|
4702
|
-
};
|
|
4703
|
-
}
|
|
4704
|
-
const archived = await orch.archive(ref);
|
|
4705
|
-
return mcpJson({
|
|
4706
|
-
id: archived.id,
|
|
4707
|
-
status: archived.status
|
|
4708
|
-
});
|
|
4709
|
-
}
|
|
4710
|
-
);
|
|
4711
|
-
server.tool(
|
|
4712
|
-
"restore_chat",
|
|
4713
|
-
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
4714
|
-
{ ref: z7.string() },
|
|
4715
|
-
async ({ ref }) => {
|
|
4716
|
-
try {
|
|
4717
|
-
const restored = await orch.restore(ref);
|
|
4718
|
-
return mcpJson({
|
|
4719
|
-
id: restored.id,
|
|
4720
|
-
status: restored.status,
|
|
4721
|
-
worktreePath: restored.worktreePath
|
|
4722
|
-
});
|
|
4723
|
-
} catch (err) {
|
|
4724
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4725
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
4726
|
-
}
|
|
4727
|
-
}
|
|
4728
|
-
);
|
|
4834
|
+
registerArchiveTools(server, orch);
|
|
4729
4835
|
server.tool(
|
|
4730
4836
|
"get_diff",
|
|
4731
4837
|
"Compact diff summary (capped hunks, paginated)",
|
|
4732
4838
|
{
|
|
4733
|
-
ref:
|
|
4734
|
-
maxFiles:
|
|
4839
|
+
ref: z8.string(),
|
|
4840
|
+
maxFiles: z8.number().optional()
|
|
4735
4841
|
},
|
|
4736
4842
|
async ({ ref, maxFiles }) => {
|
|
4737
4843
|
const summary = await orch.diffSummary(ref);
|
|
@@ -4744,7 +4850,7 @@ async function startMcpServer() {
|
|
|
4744
4850
|
server.tool(
|
|
4745
4851
|
"get_pr_checks",
|
|
4746
4852
|
"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.",
|
|
4747
|
-
{ ref:
|
|
4853
|
+
{ ref: z8.string().describe("Worktree thread id/ref") },
|
|
4748
4854
|
async ({ ref }) => {
|
|
4749
4855
|
try {
|
|
4750
4856
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -4758,7 +4864,7 @@ async function startMcpServer() {
|
|
|
4758
4864
|
server.tool(
|
|
4759
4865
|
"request_review",
|
|
4760
4866
|
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). If that worktree has a single unused idle agent (no messages), runs the review there; otherwise 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 in that chat. The review stays there so the user can read it and type next steps \u2014 do not comment on or update the PR or ticket, and do not ask_user after the review, until they ask. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review thread id.',
|
|
4761
|
-
{ ref:
|
|
4867
|
+
{ ref: z8.string().describe("Worktree thread id/ref to review") },
|
|
4762
4868
|
async ({ ref }) => {
|
|
4763
4869
|
try {
|
|
4764
4870
|
const tab = await orch.requestReview(ref);
|
|
@@ -4780,8 +4886,8 @@ async function startMcpServer() {
|
|
|
4780
4886
|
"ask_git",
|
|
4781
4887
|
"Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (including Resolve). Always steers the worktree agent; does not push or open the PR locally. 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=ready-for-review and action=merge only when the user explicitly asked (or clicked the desktop button). Do not run git or gh from the orchestration cwd.",
|
|
4782
4888
|
{
|
|
4783
|
-
ref:
|
|
4784
|
-
action:
|
|
4889
|
+
ref: z8.string().describe("Worktree thread id/ref"),
|
|
4890
|
+
action: z8.enum(AGENT_GIT_ACTIONS).describe(
|
|
4785
4891
|
"commit-push | create-draft | create-web | resolve-conflicts | ready-for-review | merge"
|
|
4786
4892
|
)
|
|
4787
4893
|
},
|
|
@@ -4810,7 +4916,7 @@ async function startMcpServer() {
|
|
|
4810
4916
|
}
|
|
4811
4917
|
}
|
|
4812
4918
|
);
|
|
4813
|
-
const agentEnum =
|
|
4919
|
+
const agentEnum = z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
4814
4920
|
server.tool(
|
|
4815
4921
|
"list_models",
|
|
4816
4922
|
"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.",
|
|
@@ -4831,11 +4937,11 @@ async function startMcpServer() {
|
|
|
4831
4937
|
"fork_workspace",
|
|
4832
4938
|
"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_chat / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
4833
4939
|
{
|
|
4834
|
-
ref:
|
|
4835
|
-
through_index:
|
|
4940
|
+
ref: z8.string().describe("Worktree thread id/ref to fork"),
|
|
4941
|
+
through_index: z8.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4836
4942
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4837
|
-
model:
|
|
4838
|
-
title:
|
|
4943
|
+
model: z8.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4944
|
+
title: z8.string().optional()
|
|
4839
4945
|
},
|
|
4840
4946
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4841
4947
|
try {
|
|
@@ -4869,11 +4975,11 @@ async function startMcpServer() {
|
|
|
4869
4975
|
"fork_chat",
|
|
4870
4976
|
"Add another agent chat on the SAME workspace (same files, Run, and git). Also forks a Global orchestration chat into a new orchestration tab. Pass agent when the new chat should use a different harness. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Use this for a second agent on a checkout (review, another harness, parallel work on the same branch). Do not create_workspace again for that \u2014 it returns the existing checkout. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id. Use fork_workspace only when the work needs its own git checkout.",
|
|
4871
4977
|
{
|
|
4872
|
-
ref:
|
|
4873
|
-
through_index:
|
|
4978
|
+
ref: z8.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
4979
|
+
through_index: z8.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
4874
4980
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
4875
|
-
model:
|
|
4876
|
-
title:
|
|
4981
|
+
model: z8.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
4982
|
+
title: z8.string().optional()
|
|
4877
4983
|
},
|
|
4878
4984
|
async ({ ref, through_index, agent, model, title }) => {
|
|
4879
4985
|
try {
|
|
@@ -4911,7 +5017,7 @@ async function startMcpServer() {
|
|
|
4911
5017
|
server.tool(
|
|
4912
5018
|
"run_setup",
|
|
4913
5019
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
4914
|
-
{ ref:
|
|
5020
|
+
{ ref: z8.string() },
|
|
4915
5021
|
async ({ ref }) => {
|
|
4916
5022
|
const result = await orch.runSetup(ref);
|
|
4917
5023
|
return mcpJson(result);
|
|
@@ -4920,7 +5026,7 @@ async function startMcpServer() {
|
|
|
4920
5026
|
server.tool(
|
|
4921
5027
|
"add_project",
|
|
4922
5028
|
"Register a git repo as a Sideboard workspace",
|
|
4923
|
-
{ repoPath:
|
|
5029
|
+
{ repoPath: z8.string() },
|
|
4924
5030
|
async ({ repoPath }) => {
|
|
4925
5031
|
const ws = await orch.addWorkspace(repoPath);
|
|
4926
5032
|
return mcpJson(ws);
|
|
@@ -4929,7 +5035,7 @@ async function startMcpServer() {
|
|
|
4929
5035
|
server.tool(
|
|
4930
5036
|
"remove_project",
|
|
4931
5037
|
"Unregister a Sideboard workspace and archive its chats",
|
|
4932
|
-
{ repoPath:
|
|
5038
|
+
{ repoPath: z8.string() },
|
|
4933
5039
|
async ({ repoPath }) => {
|
|
4934
5040
|
await orch.removeWorkspace(repoPath);
|
|
4935
5041
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -4939,12 +5045,12 @@ async function startMcpServer() {
|
|
|
4939
5045
|
"fanout",
|
|
4940
5046
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
4941
5047
|
{
|
|
4942
|
-
prompt:
|
|
4943
|
-
agents:
|
|
4944
|
-
repoPath:
|
|
4945
|
-
sourceType:
|
|
4946
|
-
sourceRef:
|
|
4947
|
-
title:
|
|
5048
|
+
prompt: z8.string(),
|
|
5049
|
+
agents: z8.array(z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
5050
|
+
repoPath: z8.string(),
|
|
5051
|
+
sourceType: z8.enum(["branch", "pr", "ticket"]).optional(),
|
|
5052
|
+
sourceRef: z8.string().optional(),
|
|
5053
|
+
title: z8.string().optional()
|
|
4948
5054
|
},
|
|
4949
5055
|
async (args) => {
|
|
4950
5056
|
const threads = await orch.bestOfN(args);
|
|
@@ -4962,8 +5068,8 @@ async function startMcpServer() {
|
|
|
4962
5068
|
"list_branches",
|
|
4963
5069
|
"List git branches in a registered workspace. Pass repoPath from list_projects (unmerged into the default branch by default \u2014 for create_workspace sourceType=branch).",
|
|
4964
5070
|
{
|
|
4965
|
-
repoPath:
|
|
4966
|
-
unmergedOnly:
|
|
5071
|
+
repoPath: z8.string(),
|
|
5072
|
+
unmergedOnly: z8.boolean().optional()
|
|
4967
5073
|
},
|
|
4968
5074
|
async ({ repoPath, unmergedOnly }) => {
|
|
4969
5075
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -4984,19 +5090,19 @@ async function startMcpServer() {
|
|
|
4984
5090
|
"list_prs",
|
|
4985
5091
|
`List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_projects. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default 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_workspace with sourceType=pr.`,
|
|
4986
5092
|
{
|
|
4987
|
-
repoPath:
|
|
4988
|
-
query:
|
|
4989
|
-
queue:
|
|
5093
|
+
repoPath: z8.string(),
|
|
5094
|
+
query: z8.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
5095
|
+
queue: z8.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
4990
5096
|
"review = unclaimed ready-for-review inbox (Settings \u2192 Projects label, default eng-review). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes."
|
|
4991
5097
|
),
|
|
4992
|
-
state:
|
|
4993
|
-
label:
|
|
5098
|
+
state: z8.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
5099
|
+
label: z8.string().optional().describe(
|
|
4994
5100
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
4995
5101
|
),
|
|
4996
|
-
reviewer:
|
|
5102
|
+
reviewer: z8.string().optional().describe(
|
|
4997
5103
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
4998
5104
|
),
|
|
4999
|
-
limit:
|
|
5105
|
+
limit: z8.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
5000
5106
|
},
|
|
5001
5107
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
5002
5108
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -5029,7 +5135,7 @@ async function startMcpServer() {
|
|
|
5029
5135
|
server.tool(
|
|
5030
5136
|
"get_pr_stack",
|
|
5031
5137
|
"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).",
|
|
5032
|
-
{ ref:
|
|
5138
|
+
{ ref: z8.string() },
|
|
5033
5139
|
async ({ ref }) => {
|
|
5034
5140
|
const stack = await orch.getPrStack(ref);
|
|
5035
5141
|
return mcpJson(stack);
|
|
@@ -5039,8 +5145,8 @@ async function startMcpServer() {
|
|
|
5039
5145
|
"open_pr_stack_layers",
|
|
5040
5146
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
5041
5147
|
{
|
|
5042
|
-
ref:
|
|
5043
|
-
layer:
|
|
5148
|
+
ref: z8.string(),
|
|
5149
|
+
layer: z8.number().int().positive().optional()
|
|
5044
5150
|
},
|
|
5045
5151
|
async ({ ref, layer }) => {
|
|
5046
5152
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -5063,9 +5169,9 @@ async function startMcpServer() {
|
|
|
5063
5169
|
"add_stack_layer",
|
|
5064
5170
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
5065
5171
|
{
|
|
5066
|
-
ref:
|
|
5067
|
-
branchName:
|
|
5068
|
-
title:
|
|
5172
|
+
ref: z8.string(),
|
|
5173
|
+
branchName: z8.string(),
|
|
5174
|
+
title: z8.string().optional()
|
|
5069
5175
|
},
|
|
5070
5176
|
async ({ ref, branchName, title }) => {
|
|
5071
5177
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -5083,11 +5189,11 @@ async function startMcpServer() {
|
|
|
5083
5189
|
"create_pr_stack",
|
|
5084
5190
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
5085
5191
|
{
|
|
5086
|
-
repoPath:
|
|
5087
|
-
branches:
|
|
5088
|
-
agent:
|
|
5089
|
-
base:
|
|
5090
|
-
title:
|
|
5192
|
+
repoPath: z8.string(),
|
|
5193
|
+
branches: z8.array(z8.string()).min(1),
|
|
5194
|
+
agent: z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
5195
|
+
base: z8.string().optional(),
|
|
5196
|
+
title: z8.string().optional()
|
|
5091
5197
|
},
|
|
5092
5198
|
async (args) => {
|
|
5093
5199
|
const result = await orch.createPrStack({
|
|
@@ -5115,10 +5221,10 @@ async function startMcpServer() {
|
|
|
5115
5221
|
"list_issues",
|
|
5116
5222
|
"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. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
|
|
5117
5223
|
{
|
|
5118
|
-
repoPath:
|
|
5119
|
-
query:
|
|
5120
|
-
assignee:
|
|
5121
|
-
limit:
|
|
5224
|
+
repoPath: z8.string(),
|
|
5225
|
+
query: z8.string().optional().describe("Search title, identifier, or description"),
|
|
5226
|
+
assignee: z8.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
5227
|
+
limit: z8.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
|
|
5122
5228
|
updatedSince: mcpIssueUpdatedSinceSchema
|
|
5123
5229
|
},
|
|
5124
5230
|
async ({ repoPath, query, assignee, limit, updatedSince }) => {
|