@sideboard-ai/core 0.1.132 → 0.1.133
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-DDTV7WO3.js +36 -0
- package/dist/abletime-VTP4JR63.js +37 -0
- package/dist/{agents-LGJLZMM3.js → agents-NHS6P25H.js} +8 -7
- package/dist/{agents-CI6NGKRO.js → agents-SOU5GPWI.js} +8 -7
- package/dist/{app-settings-6RPALX4J.js → app-settings-U5RZKUXG.js} +11 -1
- package/dist/{app-settings-E7NEQY7D.js → app-settings-YQ7YX2CC.js} +11 -1
- package/dist/chunk-2XXAAIN3.js +463 -0
- package/dist/{chunk-VE22NWBD.js → chunk-3UVVWNHF.js} +2 -2
- package/dist/{chunk-MGYCQAZ6.js → chunk-4YAVXWFR.js} +6 -6
- package/dist/{chunk-RSFCB23A.js → chunk-6CRKPD4C.js} +4 -3
- package/dist/chunk-6GN5WPYZ.js +31 -0
- package/dist/{chunk-PK4PQUPU.js → chunk-AK7SWE2U.js} +6 -6
- package/dist/chunk-AT346U23.js +468 -0
- package/dist/{chunk-LLX3OMHU.js → chunk-ED4UPEJX.js} +1 -1
- package/dist/chunk-ELBYOGVA.js +32 -0
- package/dist/{chunk-HUYEU4GS.js → chunk-GJBHOJCZ.js} +83 -2
- package/dist/{chunk-ZND6XV6J.js → chunk-GKKHXZCT.js} +83 -2
- package/dist/{chunk-HZPO3SSZ.js → chunk-IM36S4HZ.js} +4 -3
- package/dist/{chunk-SIPDL62B.js → chunk-KBWND62T.js} +6 -6
- package/dist/{chunk-HULESWLI.js → chunk-LTP7GHIF.js} +3 -3
- package/dist/{chunk-AQESMTCZ.js → chunk-M267JPEA.js} +3 -29
- package/dist/{chunk-HRSBZIXS.js → chunk-Q6XQXCOM.js} +1 -1
- package/dist/{chunk-R7FGPBKL.js → chunk-S42XV45P.js} +3 -25
- package/dist/{chunk-VFXZTSAD.js → chunk-TG7YU2KG.js} +53 -29
- package/dist/{chunk-SMWJAE74.js → chunk-VSON7EJ6.js} +53 -29
- package/dist/{chunk-UDQI3N47.js → chunk-WEU5M7BW.js} +1 -1
- package/dist/{chunk-SYLTXDRF.js → chunk-Y645NFYN.js} +2 -2
- package/dist/{chunk-MNL4FSKY.js → chunk-YEIH7P7D.js} +3 -3
- package/dist/{connected-teams-S6E3D5C7.js → connected-teams-4A2AXDNN.js} +3 -2
- package/dist/{connected-teams-3BWNJIRY.js → connected-teams-IJUJQ7R6.js} +3 -2
- package/dist/{coordinator-prompt-SPJSGAQS.js → coordinator-prompt-DXHEDRRN.js} +3 -3
- package/dist/{coordinator-prompt-MEEUOEF2.js → coordinator-prompt-XF7LVOUN.js} +3 -3
- package/dist/{global-workspace-KNESLWKG.js → global-workspace-2LO5ATOH.js} +4 -4
- package/dist/{global-workspace-BMWP7YZC.js → global-workspace-MLQNULOU.js} +4 -4
- package/dist/index.cjs +1105 -340
- package/dist/index.d.cts +171 -13
- package/dist/index.d.ts +171 -13
- package/dist/index.js +432 -239
- package/dist/mcp/run-stdio.cjs +998 -287
- package/dist/mcp/run-stdio.js +338 -186
- package/dist/{oauth-FT7EL2PY.js → oauth-A7XXI2TM.js} +2 -1
- package/dist/{oauth-43NNH6C6.js → oauth-YJBAURIJ.js} +2 -1
- package/dist/{orchestrator-JA65XKB6.js → orchestrator-KR5RA5B7.js} +10 -9
- package/dist/{orchestrator-5TN2DJWO.js → orchestrator-P2SYHVLS.js} +10 -9
- package/dist/{workspaces-O4QZOASB.js → workspaces-ARWPZG6F.js} +5 -5
- package/dist/{workspaces-HSYRYJYV.js → workspaces-MINB764G.js} +5 -5
- package/dist/{worktree-ERHC7Q4F.js → worktree-EBZMUUAL.js} +2 -2
- package/dist/{worktree-JVGSLSLJ.js → worktree-S7MKMYAT.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
fireSchedule,
|
|
18
18
|
getOrchestrator,
|
|
19
19
|
getSchedule,
|
|
20
|
+
isAccountWideIssueSource,
|
|
20
21
|
issueNeedsWorkspacePick,
|
|
21
22
|
listLinearIssues,
|
|
22
23
|
listSchedules,
|
|
@@ -31,23 +32,35 @@ import {
|
|
|
31
32
|
slackTokenFor,
|
|
32
33
|
syncBoardPins,
|
|
33
34
|
updateSchedule
|
|
34
|
-
} from "../chunk-
|
|
35
|
+
} from "../chunk-VSON7EJ6.js";
|
|
35
36
|
import {
|
|
36
37
|
listModelsForAgent,
|
|
37
38
|
sideboardMcpProfile
|
|
38
|
-
} from "../chunk-
|
|
39
|
-
import "../chunk-
|
|
39
|
+
} from "../chunk-AK7SWE2U.js";
|
|
40
|
+
import "../chunk-ED4UPEJX.js";
|
|
41
|
+
import "../chunk-S42XV45P.js";
|
|
42
|
+
import "../chunk-Y645NFYN.js";
|
|
43
|
+
import {
|
|
44
|
+
createAbleTimeTask,
|
|
45
|
+
ensureAbleTimeTask,
|
|
46
|
+
getAbleTimeOrientation,
|
|
47
|
+
getAbleTimeTask,
|
|
48
|
+
listAbleTimeAssignedIssues,
|
|
49
|
+
listAbleTimeProjects,
|
|
50
|
+
listAbleTimeTasks,
|
|
51
|
+
searchAbleTimeTasks,
|
|
52
|
+
toAbleTimeIssueInfo
|
|
53
|
+
} from "../chunk-2XXAAIN3.js";
|
|
40
54
|
import {
|
|
41
55
|
httpFetch
|
|
42
|
-
} from "../chunk-
|
|
43
|
-
import "../chunk-SYLTXDRF.js";
|
|
56
|
+
} from "../chunk-6GN5WPYZ.js";
|
|
44
57
|
import "../chunk-N5PM7HGQ.js";
|
|
45
58
|
import "../chunk-KLBOWJ74.js";
|
|
46
59
|
import {
|
|
47
60
|
GLOBAL_WORKSPACE_ID,
|
|
48
61
|
isCloudCoordinatorThread
|
|
49
|
-
} from "../chunk-
|
|
50
|
-
import "../chunk-
|
|
62
|
+
} from "../chunk-YEIH7P7D.js";
|
|
63
|
+
import "../chunk-6CRKPD4C.js";
|
|
51
64
|
import {
|
|
52
65
|
canonicalizeRepoPath,
|
|
53
66
|
listBranches,
|
|
@@ -55,7 +68,7 @@ import {
|
|
|
55
68
|
resolveGithubRepoSlug,
|
|
56
69
|
resolveRepoRoot,
|
|
57
70
|
warmGithubAgentAuth
|
|
58
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-Q6XQXCOM.js";
|
|
59
72
|
import "../chunk-B3SJXYIJ.js";
|
|
60
73
|
import "../chunk-G6X6UFJO.js";
|
|
61
74
|
import {
|
|
@@ -64,11 +77,12 @@ import {
|
|
|
64
77
|
import {
|
|
65
78
|
disconnectLinearConnection,
|
|
66
79
|
dropNestedElectronEnvFromProcess,
|
|
80
|
+
isAbleTimeConnected,
|
|
67
81
|
isLinearConnected,
|
|
68
82
|
loadAppSettings,
|
|
69
83
|
resolveEffectiveIssueSource,
|
|
70
84
|
saveLinearOAuth
|
|
71
|
-
} from "../chunk-
|
|
85
|
+
} from "../chunk-GKKHXZCT.js";
|
|
72
86
|
import "../chunk-NSTQ6QKD.js";
|
|
73
87
|
import "../chunk-KGZBYWZ3.js";
|
|
74
88
|
import {
|
|
@@ -78,7 +92,7 @@ import {
|
|
|
78
92
|
// src/mcp/server.ts
|
|
79
93
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
80
94
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
81
|
-
import { z as
|
|
95
|
+
import { z as z5 } from "zod";
|
|
82
96
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
83
97
|
import { basename } from "path";
|
|
84
98
|
|
|
@@ -576,6 +590,7 @@ async function listIssues(repoPath) {
|
|
|
576
590
|
const settings = loadAppSettings();
|
|
577
591
|
const preferredSource = settings.integrations.issueSource ?? "github";
|
|
578
592
|
const linearConnected = isLinearConnected(settings);
|
|
593
|
+
const abletimeConnected = isAbleTimeConnected(settings);
|
|
579
594
|
const source = resolveEffectiveIssueSource(settings);
|
|
580
595
|
if (source === "linear") {
|
|
581
596
|
const listed = await listLinearAssignedIssues();
|
|
@@ -583,6 +598,21 @@ async function listIssues(repoPath) {
|
|
|
583
598
|
source,
|
|
584
599
|
preferredSource,
|
|
585
600
|
linearConnected,
|
|
601
|
+
abletimeConnected,
|
|
602
|
+
issues: listed.issues,
|
|
603
|
+
viewer: {
|
|
604
|
+
login: listed.viewer.name,
|
|
605
|
+
name: listed.viewer.name
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
if (source === "abletime") {
|
|
610
|
+
const listed = await listAbleTimeAssignedIssues();
|
|
611
|
+
return {
|
|
612
|
+
source,
|
|
613
|
+
preferredSource,
|
|
614
|
+
linearConnected,
|
|
615
|
+
abletimeConnected,
|
|
586
616
|
issues: listed.issues,
|
|
587
617
|
viewer: {
|
|
588
618
|
login: listed.viewer.name,
|
|
@@ -598,6 +628,7 @@ async function listIssues(repoPath) {
|
|
|
598
628
|
source,
|
|
599
629
|
preferredSource,
|
|
600
630
|
linearConnected,
|
|
631
|
+
abletimeConnected,
|
|
601
632
|
issues,
|
|
602
633
|
viewer: login ? { login } : void 0
|
|
603
634
|
};
|
|
@@ -841,8 +872,8 @@ function labelGithubUrl(url) {
|
|
|
841
872
|
}
|
|
842
873
|
return { kind: "other", label: "GitHub", url: raw };
|
|
843
874
|
}
|
|
844
|
-
function appendGithubLink(
|
|
845
|
-
const body =
|
|
875
|
+
function appendGithubLink(text5, githubUrl) {
|
|
876
|
+
const body = text5.trimEnd();
|
|
846
877
|
if (!githubUrl?.trim()) return body;
|
|
847
878
|
const labeled = labelGithubUrl(githubUrl);
|
|
848
879
|
if (!labeled) {
|
|
@@ -1125,7 +1156,7 @@ function registerSlackTools(server) {
|
|
|
1125
1156
|
);
|
|
1126
1157
|
}
|
|
1127
1158
|
|
|
1128
|
-
// src/mcp/
|
|
1159
|
+
// src/mcp/abletime-tools.ts
|
|
1129
1160
|
import { z as z2 } from "zod";
|
|
1130
1161
|
function text2(payload, isError = false) {
|
|
1131
1162
|
return {
|
|
@@ -1139,7 +1170,127 @@ function fail2(err) {
|
|
|
1139
1170
|
true
|
|
1140
1171
|
);
|
|
1141
1172
|
}
|
|
1142
|
-
|
|
1173
|
+
function registerAbleTimeTools(server) {
|
|
1174
|
+
server.tool(
|
|
1175
|
+
"abletime_orientation",
|
|
1176
|
+
"Call first when using AbleTime. Returns who the Account token is, open drafts, active tasks, and current projects. Requires Agent access (MCP) enabled in AbleTime.",
|
|
1177
|
+
{},
|
|
1178
|
+
async () => {
|
|
1179
|
+
try {
|
|
1180
|
+
const orientation = await getAbleTimeOrientation();
|
|
1181
|
+
return text2({
|
|
1182
|
+
viewer: orientation.viewer,
|
|
1183
|
+
projects: orientation.projects,
|
|
1184
|
+
tasks: orientation.tasks.map(toAbleTimeIssueInfo)
|
|
1185
|
+
});
|
|
1186
|
+
} catch (err) {
|
|
1187
|
+
return fail2(err);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
server.tool(
|
|
1192
|
+
"abletime_list_projects",
|
|
1193
|
+
"List AbleTime projects (and categories) the Account token can see. Use a project id on abletime_create_task / abletime_ensure_task.",
|
|
1194
|
+
{},
|
|
1195
|
+
async () => {
|
|
1196
|
+
try {
|
|
1197
|
+
return text2({ projects: await listAbleTimeProjects() });
|
|
1198
|
+
} catch (err) {
|
|
1199
|
+
return fail2(err);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
);
|
|
1203
|
+
server.tool(
|
|
1204
|
+
"abletime_list_tasks",
|
|
1205
|
+
"List open AbleTime tasks (excludes done/archived). Optional projectId filter.",
|
|
1206
|
+
{ projectId: z2.string().optional() },
|
|
1207
|
+
async ({ projectId }) => {
|
|
1208
|
+
try {
|
|
1209
|
+
const tasks = await listAbleTimeTasks({ projectId });
|
|
1210
|
+
return text2({ issues: tasks.map(toAbleTimeIssueInfo) });
|
|
1211
|
+
} catch (err) {
|
|
1212
|
+
return fail2(err);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
server.tool(
|
|
1217
|
+
"abletime_search_tasks",
|
|
1218
|
+
"Search AbleTime tasks by text, reference, or tag.",
|
|
1219
|
+
{ query: z2.string() },
|
|
1220
|
+
async ({ query }) => {
|
|
1221
|
+
try {
|
|
1222
|
+
const tasks = await searchAbleTimeTasks(query);
|
|
1223
|
+
return text2({ issues: tasks.map(toAbleTimeIssueInfo) });
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
return fail2(err);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
);
|
|
1229
|
+
server.tool(
|
|
1230
|
+
"abletime_get_task",
|
|
1231
|
+
"Get one AbleTime task by id or reference (e.g. CRM-232).",
|
|
1232
|
+
{ id: z2.string() },
|
|
1233
|
+
async ({ id }) => {
|
|
1234
|
+
try {
|
|
1235
|
+
return text2(toAbleTimeIssueInfo(await getAbleTimeTask(id)));
|
|
1236
|
+
} catch (err) {
|
|
1237
|
+
return fail2(err);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
);
|
|
1241
|
+
server.tool(
|
|
1242
|
+
"abletime_create_task",
|
|
1243
|
+
"Create an AbleTime task in a project. Call abletime_list_projects if you do not have a project id. New tasks start in todo (or backlog).",
|
|
1244
|
+
{
|
|
1245
|
+
title: z2.string(),
|
|
1246
|
+
description: z2.string().optional(),
|
|
1247
|
+
projectId: z2.string().optional(),
|
|
1248
|
+
categoryId: z2.string().optional(),
|
|
1249
|
+
state: z2.enum(["backlog", "todo"]).optional()
|
|
1250
|
+
},
|
|
1251
|
+
async (args) => {
|
|
1252
|
+
try {
|
|
1253
|
+
return text2(toAbleTimeIssueInfo(await createAbleTimeTask(args)));
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
return fail2(err);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
);
|
|
1259
|
+
server.tool(
|
|
1260
|
+
"abletime_ensure_task",
|
|
1261
|
+
"Find an open AbleTime task for this work, or create one to track against. Matches identifier or exact title before creating. Prefer this when starting work that has no ticket yet, then create_thread with sourceType=ticket and the returned identifier.",
|
|
1262
|
+
{
|
|
1263
|
+
title: z2.string(),
|
|
1264
|
+
description: z2.string().optional(),
|
|
1265
|
+
projectId: z2.string().optional(),
|
|
1266
|
+
categoryId: z2.string().optional()
|
|
1267
|
+
},
|
|
1268
|
+
async (args) => {
|
|
1269
|
+
try {
|
|
1270
|
+
const task = await ensureAbleTimeTask(args);
|
|
1271
|
+
return text2({ ...toAbleTimeIssueInfo(task), created: task.created });
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
return fail2(err);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/mcp/linear-tools.ts
|
|
1280
|
+
import { z as z3 } from "zod";
|
|
1281
|
+
function text3(payload, isError = false) {
|
|
1282
|
+
return {
|
|
1283
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
1284
|
+
...isError ? { isError: true } : {}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function fail3(err) {
|
|
1288
|
+
return text3(
|
|
1289
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
1290
|
+
true
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
var prioritySchema = z3.number().int().min(0).max(4).optional().describe("0 none, 1 urgent, 2 high, 3 medium, 4 low");
|
|
1143
1294
|
function registerLinearTools(server) {
|
|
1144
1295
|
server.tool(
|
|
1145
1296
|
"linear_list_teams",
|
|
@@ -1147,21 +1298,21 @@ function registerLinearTools(server) {
|
|
|
1147
1298
|
{},
|
|
1148
1299
|
async () => {
|
|
1149
1300
|
try {
|
|
1150
|
-
return
|
|
1301
|
+
return text3(await listLinearTeams());
|
|
1151
1302
|
} catch (err) {
|
|
1152
|
-
return
|
|
1303
|
+
return fail3(err);
|
|
1153
1304
|
}
|
|
1154
1305
|
}
|
|
1155
1306
|
);
|
|
1156
1307
|
server.tool(
|
|
1157
1308
|
"linear_get_issue",
|
|
1158
1309
|
"Get a Linear issue by uuid or identifier (ENG-123).",
|
|
1159
|
-
{ id:
|
|
1310
|
+
{ id: z3.string() },
|
|
1160
1311
|
async ({ id }) => {
|
|
1161
1312
|
try {
|
|
1162
|
-
return
|
|
1313
|
+
return text3(await getLinearIssue(id));
|
|
1163
1314
|
} catch (err) {
|
|
1164
|
-
return
|
|
1315
|
+
return fail3(err);
|
|
1165
1316
|
}
|
|
1166
1317
|
}
|
|
1167
1318
|
);
|
|
@@ -1169,18 +1320,18 @@ function registerLinearTools(server) {
|
|
|
1169
1320
|
"linear_create_issue",
|
|
1170
1321
|
'Create a Linear issue. Call linear_list_teams first. team is id, key, or name. state is name, type (unstarted/started/completed/canceled/backlog), or id. assignee is "me" or a user id.',
|
|
1171
1322
|
{
|
|
1172
|
-
team:
|
|
1173
|
-
title:
|
|
1174
|
-
description:
|
|
1175
|
-
state:
|
|
1176
|
-
assignee:
|
|
1323
|
+
team: z3.string(),
|
|
1324
|
+
title: z3.string(),
|
|
1325
|
+
description: z3.string().optional(),
|
|
1326
|
+
state: z3.string().optional(),
|
|
1327
|
+
assignee: z3.string().nullable().optional(),
|
|
1177
1328
|
priority: prioritySchema
|
|
1178
1329
|
},
|
|
1179
1330
|
async (args) => {
|
|
1180
1331
|
try {
|
|
1181
|
-
return
|
|
1332
|
+
return text3(await createLinearIssue(args));
|
|
1182
1333
|
} catch (err) {
|
|
1183
|
-
return
|
|
1334
|
+
return fail3(err);
|
|
1184
1335
|
}
|
|
1185
1336
|
}
|
|
1186
1337
|
);
|
|
@@ -1188,18 +1339,18 @@ function registerLinearTools(server) {
|
|
|
1188
1339
|
"linear_update_issue",
|
|
1189
1340
|
'Update a Linear issue (uuid or ENG-123). Pass at least one of title, description, state, assignee, priority. state is name, type, or id. assignee is "me", a user id, or null to unassign.',
|
|
1190
1341
|
{
|
|
1191
|
-
id:
|
|
1192
|
-
title:
|
|
1193
|
-
description:
|
|
1194
|
-
state:
|
|
1195
|
-
assignee:
|
|
1342
|
+
id: z3.string(),
|
|
1343
|
+
title: z3.string().optional(),
|
|
1344
|
+
description: z3.string().optional(),
|
|
1345
|
+
state: z3.string().optional(),
|
|
1346
|
+
assignee: z3.string().nullable().optional(),
|
|
1196
1347
|
priority: prioritySchema
|
|
1197
1348
|
},
|
|
1198
1349
|
async (args) => {
|
|
1199
1350
|
try {
|
|
1200
|
-
return
|
|
1351
|
+
return text3(await updateLinearIssue(args));
|
|
1201
1352
|
} catch (err) {
|
|
1202
|
-
return
|
|
1353
|
+
return fail3(err);
|
|
1203
1354
|
}
|
|
1204
1355
|
}
|
|
1205
1356
|
);
|
|
@@ -1207,29 +1358,29 @@ function registerLinearTools(server) {
|
|
|
1207
1358
|
"linear_comment",
|
|
1208
1359
|
"Add a markdown comment on a Linear issue (uuid or ENG-123).",
|
|
1209
1360
|
{
|
|
1210
|
-
id:
|
|
1211
|
-
body:
|
|
1361
|
+
id: z3.string(),
|
|
1362
|
+
body: z3.string()
|
|
1212
1363
|
},
|
|
1213
1364
|
async (args) => {
|
|
1214
1365
|
try {
|
|
1215
|
-
return
|
|
1366
|
+
return text3(await commentLinearIssue(args));
|
|
1216
1367
|
} catch (err) {
|
|
1217
|
-
return
|
|
1368
|
+
return fail3(err);
|
|
1218
1369
|
}
|
|
1219
1370
|
}
|
|
1220
1371
|
);
|
|
1221
1372
|
}
|
|
1222
1373
|
|
|
1223
1374
|
// src/mcp/schedule-tools.ts
|
|
1224
|
-
import { z as
|
|
1225
|
-
function
|
|
1375
|
+
import { z as z4 } from "zod";
|
|
1376
|
+
function text4(payload, isError = false) {
|
|
1226
1377
|
return {
|
|
1227
1378
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
1228
1379
|
...isError ? { isError: true } : {}
|
|
1229
1380
|
};
|
|
1230
1381
|
}
|
|
1231
|
-
function
|
|
1232
|
-
return
|
|
1382
|
+
function fail4(err) {
|
|
1383
|
+
return text4(
|
|
1233
1384
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
1234
1385
|
true
|
|
1235
1386
|
);
|
|
@@ -1254,9 +1405,9 @@ function registerScheduleTools(server) {
|
|
|
1254
1405
|
{},
|
|
1255
1406
|
async () => {
|
|
1256
1407
|
try {
|
|
1257
|
-
return
|
|
1408
|
+
return text4({ schedules: listSchedules() });
|
|
1258
1409
|
} catch (err) {
|
|
1259
|
-
return
|
|
1410
|
+
return fail4(err);
|
|
1260
1411
|
}
|
|
1261
1412
|
}
|
|
1262
1413
|
);
|
|
@@ -1264,24 +1415,24 @@ function registerScheduleTools(server) {
|
|
|
1264
1415
|
"create_schedule",
|
|
1265
1416
|
"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.",
|
|
1266
1417
|
{
|
|
1267
|
-
prompt:
|
|
1268
|
-
name:
|
|
1269
|
-
at:
|
|
1270
|
-
every:
|
|
1271
|
-
cron:
|
|
1272
|
-
tz:
|
|
1273
|
-
threadId:
|
|
1418
|
+
prompt: z4.string().describe("User message / goal queued when the schedule fires"),
|
|
1419
|
+
name: z4.string().optional(),
|
|
1420
|
+
at: z4.string().optional().describe("ISO datetime for a one-shot"),
|
|
1421
|
+
every: z4.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
1422
|
+
cron: z4.string().optional().describe("5-field cron expression"),
|
|
1423
|
+
tz: z4.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
1424
|
+
threadId: z4.string().optional().describe(
|
|
1274
1425
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
1275
1426
|
),
|
|
1276
|
-
agent:
|
|
1277
|
-
model:
|
|
1427
|
+
agent: z4.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
1428
|
+
model: z4.string().optional()
|
|
1278
1429
|
},
|
|
1279
1430
|
async (args) => {
|
|
1280
1431
|
try {
|
|
1281
1432
|
const when = parseWhen(args);
|
|
1282
1433
|
const threadId = resolveScheduleThreadId(args.threadId);
|
|
1283
1434
|
if (args.threadId?.trim().toLowerCase() === "self" && !threadId) {
|
|
1284
|
-
return
|
|
1435
|
+
return fail4(
|
|
1285
1436
|
new Error("threadId=self requires this turn to be an orchestration chat")
|
|
1286
1437
|
);
|
|
1287
1438
|
}
|
|
@@ -1294,12 +1445,12 @@ function registerScheduleTools(server) {
|
|
|
1294
1445
|
model: args.model,
|
|
1295
1446
|
createdBy: "mcp"
|
|
1296
1447
|
});
|
|
1297
|
-
return
|
|
1448
|
+
return text4({
|
|
1298
1449
|
schedule,
|
|
1299
1450
|
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."
|
|
1300
1451
|
});
|
|
1301
1452
|
} catch (err) {
|
|
1302
|
-
return
|
|
1453
|
+
return fail4(err);
|
|
1303
1454
|
}
|
|
1304
1455
|
}
|
|
1305
1456
|
);
|
|
@@ -1307,17 +1458,17 @@ function registerScheduleTools(server) {
|
|
|
1307
1458
|
"update_schedule",
|
|
1308
1459
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
1309
1460
|
{
|
|
1310
|
-
id:
|
|
1311
|
-
prompt:
|
|
1312
|
-
name:
|
|
1313
|
-
at:
|
|
1314
|
-
every:
|
|
1315
|
-
cron:
|
|
1316
|
-
tz:
|
|
1317
|
-
threadId:
|
|
1318
|
-
agent:
|
|
1319
|
-
model:
|
|
1320
|
-
enabled:
|
|
1461
|
+
id: z4.string(),
|
|
1462
|
+
prompt: z4.string().optional(),
|
|
1463
|
+
name: z4.string().optional(),
|
|
1464
|
+
at: z4.string().optional(),
|
|
1465
|
+
every: z4.string().optional(),
|
|
1466
|
+
cron: z4.string().optional(),
|
|
1467
|
+
tz: z4.string().optional(),
|
|
1468
|
+
threadId: z4.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
1469
|
+
agent: z4.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
1470
|
+
model: z4.string().optional(),
|
|
1471
|
+
enabled: z4.boolean().optional()
|
|
1321
1472
|
},
|
|
1322
1473
|
async (args) => {
|
|
1323
1474
|
try {
|
|
@@ -1332,7 +1483,7 @@ function registerScheduleTools(server) {
|
|
|
1332
1483
|
if (args.threadId !== void 0) {
|
|
1333
1484
|
threadId = resolveScheduleThreadId(args.threadId);
|
|
1334
1485
|
if (args.threadId.trim().toLowerCase() === "self" && !threadId) {
|
|
1335
|
-
return
|
|
1486
|
+
return fail4(
|
|
1336
1487
|
new Error("threadId=self requires this turn to be an orchestration chat")
|
|
1337
1488
|
);
|
|
1338
1489
|
}
|
|
@@ -1346,38 +1497,38 @@ function registerScheduleTools(server) {
|
|
|
1346
1497
|
enabled: args.enabled,
|
|
1347
1498
|
model: args.model
|
|
1348
1499
|
});
|
|
1349
|
-
return
|
|
1500
|
+
return text4({ schedule });
|
|
1350
1501
|
} catch (err) {
|
|
1351
|
-
return
|
|
1502
|
+
return fail4(err);
|
|
1352
1503
|
}
|
|
1353
1504
|
}
|
|
1354
1505
|
);
|
|
1355
1506
|
server.tool(
|
|
1356
1507
|
"delete_schedule",
|
|
1357
1508
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
1358
|
-
{ id:
|
|
1509
|
+
{ id: z4.string() },
|
|
1359
1510
|
async ({ id }) => {
|
|
1360
1511
|
try {
|
|
1361
1512
|
deleteSchedule(id);
|
|
1362
|
-
return
|
|
1513
|
+
return text4({ ok: true, id });
|
|
1363
1514
|
} catch (err) {
|
|
1364
|
-
return
|
|
1515
|
+
return fail4(err);
|
|
1365
1516
|
}
|
|
1366
1517
|
}
|
|
1367
1518
|
);
|
|
1368
1519
|
server.tool(
|
|
1369
1520
|
"run_schedule",
|
|
1370
1521
|
"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.",
|
|
1371
|
-
{ id:
|
|
1522
|
+
{ id: z4.string() },
|
|
1372
1523
|
async ({ id }) => {
|
|
1373
1524
|
try {
|
|
1374
1525
|
if (!getSchedule(id)) {
|
|
1375
|
-
return
|
|
1526
|
+
return fail4(new Error(`Schedule not found: ${id}`));
|
|
1376
1527
|
}
|
|
1377
1528
|
const schedule = await fireSchedule(id);
|
|
1378
|
-
return
|
|
1529
|
+
return text4({ schedule });
|
|
1379
1530
|
} catch (err) {
|
|
1380
|
-
return
|
|
1531
|
+
return fail4(err);
|
|
1381
1532
|
}
|
|
1382
1533
|
}
|
|
1383
1534
|
);
|
|
@@ -1488,7 +1639,7 @@ async function loadHomeBoardInputs(workspaces) {
|
|
|
1488
1639
|
const first = await listIssues(paths[0]);
|
|
1489
1640
|
issueSource = first.source;
|
|
1490
1641
|
viewerLogin = first.viewer?.login || first.viewer?.name || void 0;
|
|
1491
|
-
if (first.source
|
|
1642
|
+
if (isAccountWideIssueSource(first.source)) {
|
|
1492
1643
|
const repoPath = paths[0] ?? "";
|
|
1493
1644
|
issues = first.issues.map((issue) => ({
|
|
1494
1645
|
...issue,
|
|
@@ -1716,7 +1867,7 @@ async function startMcpServer() {
|
|
|
1716
1867
|
);
|
|
1717
1868
|
}
|
|
1718
1869
|
try {
|
|
1719
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
1870
|
+
const { maxConcurrentAgents } = await import("../app-settings-YQ7YX2CC.js");
|
|
1720
1871
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
1721
1872
|
} catch {
|
|
1722
1873
|
}
|
|
@@ -1774,13 +1925,13 @@ async function startMcpServer() {
|
|
|
1774
1925
|
"list_board",
|
|
1775
1926
|
"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.",
|
|
1776
1927
|
{
|
|
1777
|
-
query:
|
|
1778
|
-
repoPath:
|
|
1779
|
-
kind:
|
|
1780
|
-
column:
|
|
1928
|
+
query: z5.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
1929
|
+
repoPath: z5.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
1930
|
+
kind: z5.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
1931
|
+
column: z5.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
1781
1932
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
1782
1933
|
),
|
|
1783
|
-
limit:
|
|
1934
|
+
limit: z5.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
1784
1935
|
},
|
|
1785
1936
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
1786
1937
|
const workspaces = orch.listWorkspaces();
|
|
@@ -1818,7 +1969,7 @@ async function startMcpServer() {
|
|
|
1818
1969
|
server.tool(
|
|
1819
1970
|
"get_thread",
|
|
1820
1971
|
"Get a compact thread summary by id/ref. While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (thread billed token + costUsd totals when providers reported cost) and lastTurnUsage.",
|
|
1821
|
-
{ ref:
|
|
1972
|
+
{ ref: z5.string() },
|
|
1822
1973
|
async ({ ref }) => {
|
|
1823
1974
|
const t = orch.getThread(ref);
|
|
1824
1975
|
if (!t) {
|
|
@@ -1855,17 +2006,17 @@ async function startMcpServer() {
|
|
|
1855
2006
|
"present_artifact",
|
|
1856
2007
|
"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.",
|
|
1857
2008
|
{
|
|
1858
|
-
title:
|
|
1859
|
-
type:
|
|
2009
|
+
title: z5.string().describe("Short title shown in the artifact pane header"),
|
|
2010
|
+
type: z5.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
1860
2011
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
1861
2012
|
),
|
|
1862
|
-
content:
|
|
2013
|
+
content: z5.string().describe(
|
|
1863
2014
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
1864
2015
|
),
|
|
1865
|
-
artifact_id:
|
|
1866
|
-
status:
|
|
1867
|
-
phase:
|
|
1868
|
-
mode:
|
|
2016
|
+
artifact_id: z5.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
2017
|
+
status: z5.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
2018
|
+
phase: z5.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
2019
|
+
mode: z5.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
1869
2020
|
},
|
|
1870
2021
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
1871
2022
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -1886,15 +2037,15 @@ async function startMcpServer() {
|
|
|
1886
2037
|
"ask_user",
|
|
1887
2038
|
"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.",
|
|
1888
2039
|
{
|
|
1889
|
-
questions:
|
|
1890
|
-
|
|
1891
|
-
question:
|
|
1892
|
-
header:
|
|
1893
|
-
multiSelect:
|
|
1894
|
-
options:
|
|
1895
|
-
|
|
1896
|
-
label:
|
|
1897
|
-
description:
|
|
2040
|
+
questions: z5.array(
|
|
2041
|
+
z5.object({
|
|
2042
|
+
question: z5.string().describe("Full question text ending with ?"),
|
|
2043
|
+
header: z5.string().max(24).optional().describe("Short label shown above the question"),
|
|
2044
|
+
multiSelect: z5.boolean().optional().describe("Allow selecting multiple options"),
|
|
2045
|
+
options: z5.array(
|
|
2046
|
+
z5.object({
|
|
2047
|
+
label: z5.string(),
|
|
2048
|
+
description: z5.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
1898
2049
|
})
|
|
1899
2050
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
1900
2051
|
})
|
|
@@ -1913,9 +2064,9 @@ async function startMcpServer() {
|
|
|
1913
2064
|
"present_plan",
|
|
1914
2065
|
"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.",
|
|
1915
2066
|
{
|
|
1916
|
-
title:
|
|
1917
|
-
content:
|
|
1918
|
-
thread_id:
|
|
2067
|
+
title: z5.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
2068
|
+
content: z5.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
2069
|
+
thread_id: z5.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
1919
2070
|
},
|
|
1920
2071
|
async ({ title, content, thread_id }) => {
|
|
1921
2072
|
const { writePlanFile } = await import("../plan-file-PHVKUAEE.js");
|
|
@@ -1938,15 +2089,15 @@ async function startMcpServer() {
|
|
|
1938
2089
|
"present_schema",
|
|
1939
2090
|
"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.",
|
|
1940
2091
|
{
|
|
1941
|
-
title:
|
|
1942
|
-
mode:
|
|
1943
|
-
datasource:
|
|
1944
|
-
resource_id:
|
|
1945
|
-
record_id:
|
|
1946
|
-
resource:
|
|
1947
|
-
record:
|
|
1948
|
-
records:
|
|
1949
|
-
pane_id:
|
|
2092
|
+
title: z5.string().describe("Pane title"),
|
|
2093
|
+
mode: z5.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
2094
|
+
datasource: z5.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
2095
|
+
resource_id: z5.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
2096
|
+
record_id: z5.string().optional().describe("Record id when opening form mode"),
|
|
2097
|
+
resource: z5.record(z5.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
2098
|
+
record: z5.record(z5.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
2099
|
+
records: z5.array(z5.record(z5.unknown())).optional().describe("Inline records for table mode"),
|
|
2100
|
+
pane_id: z5.string().optional().describe("Stable pane id across updates")
|
|
1950
2101
|
},
|
|
1951
2102
|
async (args) => {
|
|
1952
2103
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -1967,10 +2118,10 @@ async function startMcpServer() {
|
|
|
1967
2118
|
"present_files",
|
|
1968
2119
|
"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.",
|
|
1969
2120
|
{
|
|
1970
|
-
title:
|
|
1971
|
-
datasource:
|
|
1972
|
-
path:
|
|
1973
|
-
pane_id:
|
|
2121
|
+
title: z5.string().optional().describe("Pane title (default: Files)"),
|
|
2122
|
+
datasource: z5.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
2123
|
+
path: z5.string().optional().describe("Initial folder path (e.g. public)"),
|
|
2124
|
+
pane_id: z5.string().optional().describe("Stable pane id across updates")
|
|
1974
2125
|
},
|
|
1975
2126
|
async (args) => {
|
|
1976
2127
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -1988,16 +2139,17 @@ async function startMcpServer() {
|
|
|
1988
2139
|
if (!worktreeProfile) {
|
|
1989
2140
|
registerSlackTools(server);
|
|
1990
2141
|
registerLinearTools(server);
|
|
2142
|
+
registerAbleTimeTools(server);
|
|
1991
2143
|
registerScheduleTools(server);
|
|
1992
2144
|
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-NBRUWYXO.js");
|
|
1993
|
-
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-
|
|
2145
|
+
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-YQ7YX2CC.js");
|
|
1994
2146
|
const accountDefaults = resolveThreadDefaults();
|
|
1995
2147
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
1996
2148
|
server.tool(
|
|
1997
2149
|
"set_caffeinate",
|
|
1998
2150
|
"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.",
|
|
1999
2151
|
{
|
|
2000
|
-
enabled:
|
|
2152
|
+
enabled: z5.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
2001
2153
|
},
|
|
2002
2154
|
async ({ enabled }) => {
|
|
2003
2155
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -2035,18 +2187,18 @@ async function startMcpServer() {
|
|
|
2035
2187
|
"create_thread",
|
|
2036
2188
|
`Create a worktree thread (chat) from branch, pr, or ticket. If a live worktree already matches that ticket, PR, or named branch, 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.`,
|
|
2037
2189
|
{
|
|
2038
|
-
sourceType:
|
|
2039
|
-
sourceRef:
|
|
2040
|
-
agent:
|
|
2041
|
-
model:
|
|
2190
|
+
sourceType: z5.enum(["branch", "pr", "ticket"]),
|
|
2191
|
+
sourceRef: z5.string(),
|
|
2192
|
+
agent: z5.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
2193
|
+
model: z5.string().nullable().optional().describe(
|
|
2042
2194
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
2043
2195
|
),
|
|
2044
|
-
repoPath:
|
|
2045
|
-
title:
|
|
2046
|
-
cowboy:
|
|
2196
|
+
repoPath: z5.string(),
|
|
2197
|
+
title: z5.string().optional(),
|
|
2198
|
+
cowboy: z5.boolean().optional().describe(
|
|
2047
2199
|
"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."
|
|
2048
2200
|
),
|
|
2049
|
-
parentThreadId:
|
|
2201
|
+
parentThreadId: z5.string().optional().describe(
|
|
2050
2202
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
2051
2203
|
)
|
|
2052
2204
|
},
|
|
@@ -2062,10 +2214,10 @@ async function startMcpServer() {
|
|
|
2062
2214
|
"start_board_card",
|
|
2063
2215
|
"Same as create_thread for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Reuses the live worktree if one already matches. Then send_to_thread.",
|
|
2064
2216
|
{
|
|
2065
|
-
kind:
|
|
2066
|
-
ref:
|
|
2067
|
-
repoPath:
|
|
2068
|
-
title:
|
|
2217
|
+
kind: z5.enum(["ticket", "pr", "branch"]),
|
|
2218
|
+
ref: z5.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
2219
|
+
repoPath: z5.string().describe("Workspace path from list_workspaces / list_board"),
|
|
2220
|
+
title: z5.string().optional()
|
|
2069
2221
|
},
|
|
2070
2222
|
async ({ kind, ref, repoPath, title }) => {
|
|
2071
2223
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -2169,9 +2321,9 @@ async function startMcpServer() {
|
|
|
2169
2321
|
"send_to_thread",
|
|
2170
2322
|
'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. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.',
|
|
2171
2323
|
{
|
|
2172
|
-
ref:
|
|
2173
|
-
prompt:
|
|
2174
|
-
force_stop:
|
|
2324
|
+
ref: z5.string(),
|
|
2325
|
+
prompt: z5.string(),
|
|
2326
|
+
force_stop: z5.boolean().optional()
|
|
2175
2327
|
},
|
|
2176
2328
|
async ({ ref, prompt, force_stop }) => {
|
|
2177
2329
|
if (force_stop) {
|
|
@@ -2200,8 +2352,8 @@ async function startMcpServer() {
|
|
|
2200
2352
|
"wait_for_turn",
|
|
2201
2353
|
"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. 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. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
|
|
2202
2354
|
{
|
|
2203
|
-
ref:
|
|
2204
|
-
timeoutMs:
|
|
2355
|
+
ref: z5.string(),
|
|
2356
|
+
timeoutMs: z5.number().optional()
|
|
2205
2357
|
},
|
|
2206
2358
|
async ({ ref, timeoutMs }) => {
|
|
2207
2359
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -2230,7 +2382,7 @@ async function startMcpServer() {
|
|
|
2230
2382
|
server.tool(
|
|
2231
2383
|
"get_turn_result",
|
|
2232
2384
|
"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).",
|
|
2233
|
-
{ ref:
|
|
2385
|
+
{ ref: z5.string() },
|
|
2234
2386
|
async ({ ref }) => {
|
|
2235
2387
|
const result = orch.getTurnResult(ref);
|
|
2236
2388
|
return {
|
|
@@ -2250,8 +2402,8 @@ async function startMcpServer() {
|
|
|
2250
2402
|
"stop_thread",
|
|
2251
2403
|
"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.",
|
|
2252
2404
|
{
|
|
2253
|
-
ref:
|
|
2254
|
-
force:
|
|
2405
|
+
ref: z5.string(),
|
|
2406
|
+
force: z5.boolean().optional()
|
|
2255
2407
|
},
|
|
2256
2408
|
async ({ ref, force }) => {
|
|
2257
2409
|
const t = orch.getThread(ref);
|
|
@@ -2281,7 +2433,7 @@ async function startMcpServer() {
|
|
|
2281
2433
|
server.tool(
|
|
2282
2434
|
"archive_thread",
|
|
2283
2435
|
"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.",
|
|
2284
|
-
{ ref:
|
|
2436
|
+
{ ref: z5.string() },
|
|
2285
2437
|
async ({ ref }) => {
|
|
2286
2438
|
const t = orch.getThread(ref);
|
|
2287
2439
|
if (!t) {
|
|
@@ -2314,7 +2466,7 @@ async function startMcpServer() {
|
|
|
2314
2466
|
server.tool(
|
|
2315
2467
|
"restore_thread",
|
|
2316
2468
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
2317
|
-
{ ref:
|
|
2469
|
+
{ ref: z5.string() },
|
|
2318
2470
|
async ({ ref }) => {
|
|
2319
2471
|
try {
|
|
2320
2472
|
const restored = await orch.restore(ref);
|
|
@@ -2340,8 +2492,8 @@ async function startMcpServer() {
|
|
|
2340
2492
|
"get_diff",
|
|
2341
2493
|
"Compact diff summary (capped hunks, paginated)",
|
|
2342
2494
|
{
|
|
2343
|
-
ref:
|
|
2344
|
-
maxFiles:
|
|
2495
|
+
ref: z5.string(),
|
|
2496
|
+
maxFiles: z5.number().optional()
|
|
2345
2497
|
},
|
|
2346
2498
|
async ({ ref, maxFiles }) => {
|
|
2347
2499
|
const summary = await orch.diffSummary(ref);
|
|
@@ -2361,7 +2513,7 @@ async function startMcpServer() {
|
|
|
2361
2513
|
server.tool(
|
|
2362
2514
|
"request_review",
|
|
2363
2515
|
'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 seeds that skill from .sideboard/review.md or 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.',
|
|
2364
|
-
{ ref:
|
|
2516
|
+
{ ref: z5.string().describe("Worktree thread id/ref to review") },
|
|
2365
2517
|
async ({ ref }) => {
|
|
2366
2518
|
try {
|
|
2367
2519
|
const tab = await orch.requestReview(ref);
|
|
@@ -2390,8 +2542,8 @@ async function startMcpServer() {
|
|
|
2390
2542
|
"ask_git",
|
|
2391
2543
|
"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` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). 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.",
|
|
2392
2544
|
{
|
|
2393
|
-
ref:
|
|
2394
|
-
action:
|
|
2545
|
+
ref: z5.string().describe("Worktree thread id/ref"),
|
|
2546
|
+
action: z5.enum(AGENT_GIT_ACTIONS).describe(
|
|
2395
2547
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
2396
2548
|
)
|
|
2397
2549
|
},
|
|
@@ -2418,7 +2570,7 @@ async function startMcpServer() {
|
|
|
2418
2570
|
}
|
|
2419
2571
|
}
|
|
2420
2572
|
);
|
|
2421
|
-
const agentEnum =
|
|
2573
|
+
const agentEnum = z5.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
2422
2574
|
server.tool(
|
|
2423
2575
|
"list_models",
|
|
2424
2576
|
"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.",
|
|
@@ -2441,11 +2593,11 @@ async function startMcpServer() {
|
|
|
2441
2593
|
"fork_worktree",
|
|
2442
2594
|
"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.",
|
|
2443
2595
|
{
|
|
2444
|
-
ref:
|
|
2445
|
-
through_index:
|
|
2596
|
+
ref: z5.string().describe("Worktree thread id/ref to fork"),
|
|
2597
|
+
through_index: z5.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
2446
2598
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
2447
|
-
model:
|
|
2448
|
-
title:
|
|
2599
|
+
model: z5.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
2600
|
+
title: z5.string().optional()
|
|
2449
2601
|
},
|
|
2450
2602
|
async ({ ref, through_index, agent, model, title }) => {
|
|
2451
2603
|
try {
|
|
@@ -2486,11 +2638,11 @@ async function startMcpServer() {
|
|
|
2486
2638
|
"fork_chat",
|
|
2487
2639
|
"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.",
|
|
2488
2640
|
{
|
|
2489
|
-
ref:
|
|
2490
|
-
through_index:
|
|
2641
|
+
ref: z5.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
2642
|
+
through_index: z5.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
2491
2643
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
2492
|
-
model:
|
|
2493
|
-
title:
|
|
2644
|
+
model: z5.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
2645
|
+
title: z5.string().optional()
|
|
2494
2646
|
},
|
|
2495
2647
|
async ({ ref, through_index, agent, model, title }) => {
|
|
2496
2648
|
try {
|
|
@@ -2536,8 +2688,8 @@ async function startMcpServer() {
|
|
|
2536
2688
|
"run_dev_script",
|
|
2537
2689
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
2538
2690
|
{
|
|
2539
|
-
ref:
|
|
2540
|
-
name:
|
|
2691
|
+
ref: z5.string(),
|
|
2692
|
+
name: z5.string().optional()
|
|
2541
2693
|
},
|
|
2542
2694
|
async ({ ref, name }) => {
|
|
2543
2695
|
const result = await orch.startDev(ref, name);
|
|
@@ -2559,7 +2711,7 @@ async function startMcpServer() {
|
|
|
2559
2711
|
server.tool(
|
|
2560
2712
|
"list_run_scripts",
|
|
2561
2713
|
"List named run scripts available for a thread",
|
|
2562
|
-
{ ref:
|
|
2714
|
+
{ ref: z5.string() },
|
|
2563
2715
|
async ({ ref }) => {
|
|
2564
2716
|
const scripts = orch.listThreadRunScripts(ref);
|
|
2565
2717
|
const active = orch.getActiveRuns(ref);
|
|
@@ -2577,8 +2729,8 @@ async function startMcpServer() {
|
|
|
2577
2729
|
"stop_dev_script",
|
|
2578
2730
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
2579
2731
|
{
|
|
2580
|
-
ref:
|
|
2581
|
-
name:
|
|
2732
|
+
ref: z5.string(),
|
|
2733
|
+
name: z5.string().optional()
|
|
2582
2734
|
},
|
|
2583
2735
|
async ({ ref, name }) => {
|
|
2584
2736
|
orch.stopDev(ref, name);
|
|
@@ -2588,7 +2740,7 @@ async function startMcpServer() {
|
|
|
2588
2740
|
server.tool(
|
|
2589
2741
|
"run_setup",
|
|
2590
2742
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
2591
|
-
{ ref:
|
|
2743
|
+
{ ref: z5.string() },
|
|
2592
2744
|
async ({ ref }) => {
|
|
2593
2745
|
const result = await orch.runSetup(ref);
|
|
2594
2746
|
return {
|
|
@@ -2599,7 +2751,7 @@ async function startMcpServer() {
|
|
|
2599
2751
|
server.tool(
|
|
2600
2752
|
"add_workspace",
|
|
2601
2753
|
"Register a git repo as a Sideboard workspace",
|
|
2602
|
-
{ repoPath:
|
|
2754
|
+
{ repoPath: z5.string() },
|
|
2603
2755
|
async ({ repoPath }) => {
|
|
2604
2756
|
const ws = await orch.addWorkspace(repoPath);
|
|
2605
2757
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -2608,7 +2760,7 @@ async function startMcpServer() {
|
|
|
2608
2760
|
server.tool(
|
|
2609
2761
|
"remove_workspace",
|
|
2610
2762
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
2611
|
-
{ repoPath:
|
|
2763
|
+
{ repoPath: z5.string() },
|
|
2612
2764
|
async ({ repoPath }) => {
|
|
2613
2765
|
orch.removeWorkspace(repoPath);
|
|
2614
2766
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -2618,12 +2770,12 @@ async function startMcpServer() {
|
|
|
2618
2770
|
"fanout",
|
|
2619
2771
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
2620
2772
|
{
|
|
2621
|
-
prompt:
|
|
2622
|
-
agents:
|
|
2623
|
-
repoPath:
|
|
2624
|
-
sourceType:
|
|
2625
|
-
sourceRef:
|
|
2626
|
-
title:
|
|
2773
|
+
prompt: z5.string(),
|
|
2774
|
+
agents: z5.array(z5.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
2775
|
+
repoPath: z5.string(),
|
|
2776
|
+
sourceType: z5.enum(["branch", "pr", "ticket"]).optional(),
|
|
2777
|
+
sourceRef: z5.string().optional(),
|
|
2778
|
+
title: z5.string().optional()
|
|
2627
2779
|
},
|
|
2628
2780
|
async (args) => {
|
|
2629
2781
|
const threads = await orch.bestOfN(args);
|
|
@@ -2650,8 +2802,8 @@ async function startMcpServer() {
|
|
|
2650
2802
|
"list_branches",
|
|
2651
2803
|
"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).",
|
|
2652
2804
|
{
|
|
2653
|
-
repoPath:
|
|
2654
|
-
unmergedOnly:
|
|
2805
|
+
repoPath: z5.string(),
|
|
2806
|
+
unmergedOnly: z5.boolean().optional()
|
|
2655
2807
|
},
|
|
2656
2808
|
async ({ repoPath, unmergedOnly }) => {
|
|
2657
2809
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -2671,7 +2823,7 @@ async function startMcpServer() {
|
|
|
2671
2823
|
server.tool(
|
|
2672
2824
|
"list_prs",
|
|
2673
2825
|
"List open GitHub PRs for a registered workspace. Pass repoPath from list_workspaces (uses gh against that repo remote). Then create_thread with sourceType=pr.",
|
|
2674
|
-
{ repoPath:
|
|
2826
|
+
{ repoPath: z5.string() },
|
|
2675
2827
|
async ({ repoPath }) => {
|
|
2676
2828
|
const root = await resolveRepoRoot(repoPath);
|
|
2677
2829
|
const prs = await listPrs(root);
|
|
@@ -2690,7 +2842,7 @@ async function startMcpServer() {
|
|
|
2690
2842
|
server.tool(
|
|
2691
2843
|
"get_pr_stack",
|
|
2692
2844
|
"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).",
|
|
2693
|
-
{ ref:
|
|
2845
|
+
{ ref: z5.string() },
|
|
2694
2846
|
async ({ ref }) => {
|
|
2695
2847
|
const stack = await orch.getPrStack(ref);
|
|
2696
2848
|
return {
|
|
@@ -2702,8 +2854,8 @@ async function startMcpServer() {
|
|
|
2702
2854
|
"open_pr_stack_layers",
|
|
2703
2855
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
2704
2856
|
{
|
|
2705
|
-
ref:
|
|
2706
|
-
layer:
|
|
2857
|
+
ref: z5.string(),
|
|
2858
|
+
layer: z5.number().int().positive().optional()
|
|
2707
2859
|
},
|
|
2708
2860
|
async ({ ref, layer }) => {
|
|
2709
2861
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -2737,9 +2889,9 @@ async function startMcpServer() {
|
|
|
2737
2889
|
"add_stack_layer",
|
|
2738
2890
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
2739
2891
|
{
|
|
2740
|
-
ref:
|
|
2741
|
-
branchName:
|
|
2742
|
-
title:
|
|
2892
|
+
ref: z5.string(),
|
|
2893
|
+
branchName: z5.string(),
|
|
2894
|
+
title: z5.string().optional()
|
|
2743
2895
|
},
|
|
2744
2896
|
async ({ ref, branchName, title }) => {
|
|
2745
2897
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -2768,11 +2920,11 @@ async function startMcpServer() {
|
|
|
2768
2920
|
"create_pr_stack",
|
|
2769
2921
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
2770
2922
|
{
|
|
2771
|
-
repoPath:
|
|
2772
|
-
branches:
|
|
2773
|
-
agent:
|
|
2774
|
-
base:
|
|
2775
|
-
title:
|
|
2923
|
+
repoPath: z5.string(),
|
|
2924
|
+
branches: z5.array(z5.string()).min(1),
|
|
2925
|
+
agent: z5.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
2926
|
+
base: z5.string().optional(),
|
|
2927
|
+
title: z5.string().optional()
|
|
2776
2928
|
},
|
|
2777
2929
|
async (args) => {
|
|
2778
2930
|
const result = await orch.createPrStack({
|
|
@@ -2809,8 +2961,8 @@ async function startMcpServer() {
|
|
|
2809
2961
|
);
|
|
2810
2962
|
server.tool(
|
|
2811
2963
|
"list_issues",
|
|
2812
|
-
"List issues from Sideboard Account connections (Linear
|
|
2813
|
-
{ repoPath:
|
|
2964
|
+
"List issues from Sideboard Account connections (Linear, AbleTime MCP, or GitHub Issues; falls back to GitHub when the preferred tracker is not connected). Linear/AbleTime are account-wide; GitHub Issues are scoped to repoPath from list_workspaces. When AbleTime is the issue source and work has no ticket, call abletime_ensure_task (or create_thread from the default branch \u2014 Sideboard auto-creates a task to track against). Prefer list_board for Home columns. Then create_thread with sourceType=ticket.",
|
|
2965
|
+
{ repoPath: z5.string() },
|
|
2814
2966
|
async ({ repoPath }) => {
|
|
2815
2967
|
const root = await resolveRepoRoot(repoPath);
|
|
2816
2968
|
const result = await listIssues(root);
|
|
@@ -2821,8 +2973,8 @@ async function startMcpServer() {
|
|
|
2821
2973
|
"list_linear_issues",
|
|
2822
2974
|
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
2823
2975
|
{
|
|
2824
|
-
agent:
|
|
2825
|
-
repoPath:
|
|
2976
|
+
agent: z5.enum(["claude", "codex", "opencode"]),
|
|
2977
|
+
repoPath: z5.string()
|
|
2826
2978
|
},
|
|
2827
2979
|
async ({ agent, repoPath }) => {
|
|
2828
2980
|
const issues = await listLinearIssues(agent, repoPath);
|