@sideboard-ai/core 0.1.130 → 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/cursor-runner.js +1 -1
- package/dist/{agents-7BUQY2WX.js → agents-NHS6P25H.js} +8 -6
- package/dist/{agents-X2I3RNCY.js → agents-SOU5GPWI.js} +9 -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-NV73AO7Q.js → chunk-4YAVXWFR.js} +21 -9
- package/dist/{chunk-RSFCB23A.js → chunk-6CRKPD4C.js} +4 -3
- package/dist/chunk-6GN5WPYZ.js +31 -0
- package/dist/{chunk-ZMUOPTKZ.js → chunk-AK7SWE2U.js} +55 -12
- package/dist/chunk-AT346U23.js +468 -0
- package/dist/{chunk-YMRT2DU6.js → chunk-ED4UPEJX.js} +25 -50
- package/dist/chunk-ELBYOGVA.js +32 -0
- package/dist/{chunk-IUJOI7KK.js → chunk-FSCQXMGI.js} +35 -4
- 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-EKIDHL2T.js → chunk-KBWND62T.js} +32 -55
- package/dist/{chunk-HULESWLI.js → chunk-LTP7GHIF.js} +3 -3
- package/dist/chunk-M267JPEA.js +103 -0
- package/dist/{chunk-HRSBZIXS.js → chunk-Q6XQXCOM.js} +1 -1
- package/dist/chunk-S42XV45P.js +104 -0
- package/dist/{chunk-YALVX3UE.js → chunk-TG7YU2KG.js} +157 -40
- package/dist/{chunk-FMBLOOAO.js → chunk-VSON7EJ6.js} +155 -39
- 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-ZCOU6KCD.js → connected-teams-4A2AXDNN.js} +3 -1
- package/dist/{connected-teams-O6D4PM7A.js → connected-teams-IJUJQ7R6.js} +3 -1
- 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 +1410 -425
- package/dist/index.d.cts +224 -16
- package/dist/index.d.ts +224 -16
- package/dist/index.js +461 -292
- package/dist/mcp/run-stdio.cjs +1261 -342
- package/dist/mcp/run-stdio.js +340 -209
- package/dist/oauth-A7XXI2TM.js +11 -0
- package/dist/oauth-YJBAURIJ.js +13 -0
- package/dist/{orchestrator-3ZSFTG72.js → orchestrator-KR5RA5B7.js} +10 -8
- package/dist/{orchestrator-G6DU3AF3.js → orchestrator-P2SYHVLS.js} +11 -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,20 +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-
|
|
40
|
-
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";
|
|
54
|
+
import {
|
|
55
|
+
httpFetch
|
|
56
|
+
} from "../chunk-6GN5WPYZ.js";
|
|
41
57
|
import "../chunk-N5PM7HGQ.js";
|
|
42
58
|
import "../chunk-KLBOWJ74.js";
|
|
43
59
|
import {
|
|
44
60
|
GLOBAL_WORKSPACE_ID,
|
|
45
61
|
isCloudCoordinatorThread
|
|
46
|
-
} from "../chunk-
|
|
47
|
-
import "../chunk-
|
|
62
|
+
} from "../chunk-YEIH7P7D.js";
|
|
63
|
+
import "../chunk-6CRKPD4C.js";
|
|
48
64
|
import {
|
|
49
65
|
canonicalizeRepoPath,
|
|
50
66
|
listBranches,
|
|
@@ -52,7 +68,7 @@ import {
|
|
|
52
68
|
resolveGithubRepoSlug,
|
|
53
69
|
resolveRepoRoot,
|
|
54
70
|
warmGithubAgentAuth
|
|
55
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-Q6XQXCOM.js";
|
|
56
72
|
import "../chunk-B3SJXYIJ.js";
|
|
57
73
|
import "../chunk-G6X6UFJO.js";
|
|
58
74
|
import {
|
|
@@ -61,11 +77,12 @@ import {
|
|
|
61
77
|
import {
|
|
62
78
|
disconnectLinearConnection,
|
|
63
79
|
dropNestedElectronEnvFromProcess,
|
|
80
|
+
isAbleTimeConnected,
|
|
64
81
|
isLinearConnected,
|
|
65
82
|
loadAppSettings,
|
|
66
83
|
resolveEffectiveIssueSource,
|
|
67
84
|
saveLinearOAuth
|
|
68
|
-
} from "../chunk-
|
|
85
|
+
} from "../chunk-GKKHXZCT.js";
|
|
69
86
|
import "../chunk-NSTQ6QKD.js";
|
|
70
87
|
import "../chunk-KGZBYWZ3.js";
|
|
71
88
|
import {
|
|
@@ -75,34 +92,10 @@ import {
|
|
|
75
92
|
// src/mcp/server.ts
|
|
76
93
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
77
94
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
78
|
-
import { z as
|
|
95
|
+
import { z as z5 } from "zod";
|
|
79
96
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
80
97
|
import { basename } from "path";
|
|
81
98
|
|
|
82
|
-
// src/http/fetch.ts
|
|
83
|
-
var injected = null;
|
|
84
|
-
function formatFetchError(err, url) {
|
|
85
|
-
if (!(err instanceof Error)) return `${String(err)} (${url})`;
|
|
86
|
-
const cause = err.cause;
|
|
87
|
-
let detail = "";
|
|
88
|
-
if (cause instanceof Error) {
|
|
89
|
-
const code = typeof cause.code === "string" ? cause.code : void 0;
|
|
90
|
-
detail = code ? ` [${code}: ${cause.message}]` : ` [${cause.message}]`;
|
|
91
|
-
} else if (cause != null) {
|
|
92
|
-
detail = ` [${String(cause)}]`;
|
|
93
|
-
}
|
|
94
|
-
return `${err.message}${detail} (${url})`;
|
|
95
|
-
}
|
|
96
|
-
async function httpFetch(input, init) {
|
|
97
|
-
const url = typeof input === "string" ? input : input.href;
|
|
98
|
-
const fn = injected ?? globalThis.fetch.bind(globalThis);
|
|
99
|
-
try {
|
|
100
|
-
return await fn(url, init);
|
|
101
|
-
} catch (err) {
|
|
102
|
-
throw new Error(formatFetchError(err, url));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
99
|
// src/integrations/linear-oauth.ts
|
|
107
100
|
import { createHash, randomBytes } from "crypto";
|
|
108
101
|
import { createServer } from "http";
|
|
@@ -597,6 +590,7 @@ async function listIssues(repoPath) {
|
|
|
597
590
|
const settings = loadAppSettings();
|
|
598
591
|
const preferredSource = settings.integrations.issueSource ?? "github";
|
|
599
592
|
const linearConnected = isLinearConnected(settings);
|
|
593
|
+
const abletimeConnected = isAbleTimeConnected(settings);
|
|
600
594
|
const source = resolveEffectiveIssueSource(settings);
|
|
601
595
|
if (source === "linear") {
|
|
602
596
|
const listed = await listLinearAssignedIssues();
|
|
@@ -604,6 +598,21 @@ async function listIssues(repoPath) {
|
|
|
604
598
|
source,
|
|
605
599
|
preferredSource,
|
|
606
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,
|
|
607
616
|
issues: listed.issues,
|
|
608
617
|
viewer: {
|
|
609
618
|
login: listed.viewer.name,
|
|
@@ -619,6 +628,7 @@ async function listIssues(repoPath) {
|
|
|
619
628
|
source,
|
|
620
629
|
preferredSource,
|
|
621
630
|
linearConnected,
|
|
631
|
+
abletimeConnected,
|
|
622
632
|
issues,
|
|
623
633
|
viewer: login ? { login } : void 0
|
|
624
634
|
};
|
|
@@ -862,8 +872,8 @@ function labelGithubUrl(url) {
|
|
|
862
872
|
}
|
|
863
873
|
return { kind: "other", label: "GitHub", url: raw };
|
|
864
874
|
}
|
|
865
|
-
function appendGithubLink(
|
|
866
|
-
const body =
|
|
875
|
+
function appendGithubLink(text5, githubUrl) {
|
|
876
|
+
const body = text5.trimEnd();
|
|
867
877
|
if (!githubUrl?.trim()) return body;
|
|
868
878
|
const labeled = labelGithubUrl(githubUrl);
|
|
869
879
|
if (!labeled) {
|
|
@@ -1146,7 +1156,7 @@ function registerSlackTools(server) {
|
|
|
1146
1156
|
);
|
|
1147
1157
|
}
|
|
1148
1158
|
|
|
1149
|
-
// src/mcp/
|
|
1159
|
+
// src/mcp/abletime-tools.ts
|
|
1150
1160
|
import { z as z2 } from "zod";
|
|
1151
1161
|
function text2(payload, isError = false) {
|
|
1152
1162
|
return {
|
|
@@ -1160,7 +1170,127 @@ function fail2(err) {
|
|
|
1160
1170
|
true
|
|
1161
1171
|
);
|
|
1162
1172
|
}
|
|
1163
|
-
|
|
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");
|
|
1164
1294
|
function registerLinearTools(server) {
|
|
1165
1295
|
server.tool(
|
|
1166
1296
|
"linear_list_teams",
|
|
@@ -1168,21 +1298,21 @@ function registerLinearTools(server) {
|
|
|
1168
1298
|
{},
|
|
1169
1299
|
async () => {
|
|
1170
1300
|
try {
|
|
1171
|
-
return
|
|
1301
|
+
return text3(await listLinearTeams());
|
|
1172
1302
|
} catch (err) {
|
|
1173
|
-
return
|
|
1303
|
+
return fail3(err);
|
|
1174
1304
|
}
|
|
1175
1305
|
}
|
|
1176
1306
|
);
|
|
1177
1307
|
server.tool(
|
|
1178
1308
|
"linear_get_issue",
|
|
1179
1309
|
"Get a Linear issue by uuid or identifier (ENG-123).",
|
|
1180
|
-
{ id:
|
|
1310
|
+
{ id: z3.string() },
|
|
1181
1311
|
async ({ id }) => {
|
|
1182
1312
|
try {
|
|
1183
|
-
return
|
|
1313
|
+
return text3(await getLinearIssue(id));
|
|
1184
1314
|
} catch (err) {
|
|
1185
|
-
return
|
|
1315
|
+
return fail3(err);
|
|
1186
1316
|
}
|
|
1187
1317
|
}
|
|
1188
1318
|
);
|
|
@@ -1190,18 +1320,18 @@ function registerLinearTools(server) {
|
|
|
1190
1320
|
"linear_create_issue",
|
|
1191
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.',
|
|
1192
1322
|
{
|
|
1193
|
-
team:
|
|
1194
|
-
title:
|
|
1195
|
-
description:
|
|
1196
|
-
state:
|
|
1197
|
-
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(),
|
|
1198
1328
|
priority: prioritySchema
|
|
1199
1329
|
},
|
|
1200
1330
|
async (args) => {
|
|
1201
1331
|
try {
|
|
1202
|
-
return
|
|
1332
|
+
return text3(await createLinearIssue(args));
|
|
1203
1333
|
} catch (err) {
|
|
1204
|
-
return
|
|
1334
|
+
return fail3(err);
|
|
1205
1335
|
}
|
|
1206
1336
|
}
|
|
1207
1337
|
);
|
|
@@ -1209,18 +1339,18 @@ function registerLinearTools(server) {
|
|
|
1209
1339
|
"linear_update_issue",
|
|
1210
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.',
|
|
1211
1341
|
{
|
|
1212
|
-
id:
|
|
1213
|
-
title:
|
|
1214
|
-
description:
|
|
1215
|
-
state:
|
|
1216
|
-
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(),
|
|
1217
1347
|
priority: prioritySchema
|
|
1218
1348
|
},
|
|
1219
1349
|
async (args) => {
|
|
1220
1350
|
try {
|
|
1221
|
-
return
|
|
1351
|
+
return text3(await updateLinearIssue(args));
|
|
1222
1352
|
} catch (err) {
|
|
1223
|
-
return
|
|
1353
|
+
return fail3(err);
|
|
1224
1354
|
}
|
|
1225
1355
|
}
|
|
1226
1356
|
);
|
|
@@ -1228,29 +1358,29 @@ function registerLinearTools(server) {
|
|
|
1228
1358
|
"linear_comment",
|
|
1229
1359
|
"Add a markdown comment on a Linear issue (uuid or ENG-123).",
|
|
1230
1360
|
{
|
|
1231
|
-
id:
|
|
1232
|
-
body:
|
|
1361
|
+
id: z3.string(),
|
|
1362
|
+
body: z3.string()
|
|
1233
1363
|
},
|
|
1234
1364
|
async (args) => {
|
|
1235
1365
|
try {
|
|
1236
|
-
return
|
|
1366
|
+
return text3(await commentLinearIssue(args));
|
|
1237
1367
|
} catch (err) {
|
|
1238
|
-
return
|
|
1368
|
+
return fail3(err);
|
|
1239
1369
|
}
|
|
1240
1370
|
}
|
|
1241
1371
|
);
|
|
1242
1372
|
}
|
|
1243
1373
|
|
|
1244
1374
|
// src/mcp/schedule-tools.ts
|
|
1245
|
-
import { z as
|
|
1246
|
-
function
|
|
1375
|
+
import { z as z4 } from "zod";
|
|
1376
|
+
function text4(payload, isError = false) {
|
|
1247
1377
|
return {
|
|
1248
1378
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
1249
1379
|
...isError ? { isError: true } : {}
|
|
1250
1380
|
};
|
|
1251
1381
|
}
|
|
1252
|
-
function
|
|
1253
|
-
return
|
|
1382
|
+
function fail4(err) {
|
|
1383
|
+
return text4(
|
|
1254
1384
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
1255
1385
|
true
|
|
1256
1386
|
);
|
|
@@ -1275,9 +1405,9 @@ function registerScheduleTools(server) {
|
|
|
1275
1405
|
{},
|
|
1276
1406
|
async () => {
|
|
1277
1407
|
try {
|
|
1278
|
-
return
|
|
1408
|
+
return text4({ schedules: listSchedules() });
|
|
1279
1409
|
} catch (err) {
|
|
1280
|
-
return
|
|
1410
|
+
return fail4(err);
|
|
1281
1411
|
}
|
|
1282
1412
|
}
|
|
1283
1413
|
);
|
|
@@ -1285,24 +1415,24 @@ function registerScheduleTools(server) {
|
|
|
1285
1415
|
"create_schedule",
|
|
1286
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.",
|
|
1287
1417
|
{
|
|
1288
|
-
prompt:
|
|
1289
|
-
name:
|
|
1290
|
-
at:
|
|
1291
|
-
every:
|
|
1292
|
-
cron:
|
|
1293
|
-
tz:
|
|
1294
|
-
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(
|
|
1295
1425
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
1296
1426
|
),
|
|
1297
|
-
agent:
|
|
1298
|
-
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()
|
|
1299
1429
|
},
|
|
1300
1430
|
async (args) => {
|
|
1301
1431
|
try {
|
|
1302
1432
|
const when = parseWhen(args);
|
|
1303
1433
|
const threadId = resolveScheduleThreadId(args.threadId);
|
|
1304
1434
|
if (args.threadId?.trim().toLowerCase() === "self" && !threadId) {
|
|
1305
|
-
return
|
|
1435
|
+
return fail4(
|
|
1306
1436
|
new Error("threadId=self requires this turn to be an orchestration chat")
|
|
1307
1437
|
);
|
|
1308
1438
|
}
|
|
@@ -1315,12 +1445,12 @@ function registerScheduleTools(server) {
|
|
|
1315
1445
|
model: args.model,
|
|
1316
1446
|
createdBy: "mcp"
|
|
1317
1447
|
});
|
|
1318
|
-
return
|
|
1448
|
+
return text4({
|
|
1319
1449
|
schedule,
|
|
1320
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."
|
|
1321
1451
|
});
|
|
1322
1452
|
} catch (err) {
|
|
1323
|
-
return
|
|
1453
|
+
return fail4(err);
|
|
1324
1454
|
}
|
|
1325
1455
|
}
|
|
1326
1456
|
);
|
|
@@ -1328,17 +1458,17 @@ function registerScheduleTools(server) {
|
|
|
1328
1458
|
"update_schedule",
|
|
1329
1459
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
1330
1460
|
{
|
|
1331
|
-
id:
|
|
1332
|
-
prompt:
|
|
1333
|
-
name:
|
|
1334
|
-
at:
|
|
1335
|
-
every:
|
|
1336
|
-
cron:
|
|
1337
|
-
tz:
|
|
1338
|
-
threadId:
|
|
1339
|
-
agent:
|
|
1340
|
-
model:
|
|
1341
|
-
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()
|
|
1342
1472
|
},
|
|
1343
1473
|
async (args) => {
|
|
1344
1474
|
try {
|
|
@@ -1353,7 +1483,7 @@ function registerScheduleTools(server) {
|
|
|
1353
1483
|
if (args.threadId !== void 0) {
|
|
1354
1484
|
threadId = resolveScheduleThreadId(args.threadId);
|
|
1355
1485
|
if (args.threadId.trim().toLowerCase() === "self" && !threadId) {
|
|
1356
|
-
return
|
|
1486
|
+
return fail4(
|
|
1357
1487
|
new Error("threadId=self requires this turn to be an orchestration chat")
|
|
1358
1488
|
);
|
|
1359
1489
|
}
|
|
@@ -1367,38 +1497,38 @@ function registerScheduleTools(server) {
|
|
|
1367
1497
|
enabled: args.enabled,
|
|
1368
1498
|
model: args.model
|
|
1369
1499
|
});
|
|
1370
|
-
return
|
|
1500
|
+
return text4({ schedule });
|
|
1371
1501
|
} catch (err) {
|
|
1372
|
-
return
|
|
1502
|
+
return fail4(err);
|
|
1373
1503
|
}
|
|
1374
1504
|
}
|
|
1375
1505
|
);
|
|
1376
1506
|
server.tool(
|
|
1377
1507
|
"delete_schedule",
|
|
1378
1508
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
1379
|
-
{ id:
|
|
1509
|
+
{ id: z4.string() },
|
|
1380
1510
|
async ({ id }) => {
|
|
1381
1511
|
try {
|
|
1382
1512
|
deleteSchedule(id);
|
|
1383
|
-
return
|
|
1513
|
+
return text4({ ok: true, id });
|
|
1384
1514
|
} catch (err) {
|
|
1385
|
-
return
|
|
1515
|
+
return fail4(err);
|
|
1386
1516
|
}
|
|
1387
1517
|
}
|
|
1388
1518
|
);
|
|
1389
1519
|
server.tool(
|
|
1390
1520
|
"run_schedule",
|
|
1391
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.",
|
|
1392
|
-
{ id:
|
|
1522
|
+
{ id: z4.string() },
|
|
1393
1523
|
async ({ id }) => {
|
|
1394
1524
|
try {
|
|
1395
1525
|
if (!getSchedule(id)) {
|
|
1396
|
-
return
|
|
1526
|
+
return fail4(new Error(`Schedule not found: ${id}`));
|
|
1397
1527
|
}
|
|
1398
1528
|
const schedule = await fireSchedule(id);
|
|
1399
|
-
return
|
|
1529
|
+
return text4({ schedule });
|
|
1400
1530
|
} catch (err) {
|
|
1401
|
-
return
|
|
1531
|
+
return fail4(err);
|
|
1402
1532
|
}
|
|
1403
1533
|
}
|
|
1404
1534
|
);
|
|
@@ -1509,7 +1639,7 @@ async function loadHomeBoardInputs(workspaces) {
|
|
|
1509
1639
|
const first = await listIssues(paths[0]);
|
|
1510
1640
|
issueSource = first.source;
|
|
1511
1641
|
viewerLogin = first.viewer?.login || first.viewer?.name || void 0;
|
|
1512
|
-
if (first.source
|
|
1642
|
+
if (isAccountWideIssueSource(first.source)) {
|
|
1513
1643
|
const repoPath = paths[0] ?? "";
|
|
1514
1644
|
issues = first.issues.map((issue) => ({
|
|
1515
1645
|
...issue,
|
|
@@ -1737,7 +1867,7 @@ async function startMcpServer() {
|
|
|
1737
1867
|
);
|
|
1738
1868
|
}
|
|
1739
1869
|
try {
|
|
1740
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
1870
|
+
const { maxConcurrentAgents } = await import("../app-settings-YQ7YX2CC.js");
|
|
1741
1871
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
1742
1872
|
} catch {
|
|
1743
1873
|
}
|
|
@@ -1795,13 +1925,13 @@ async function startMcpServer() {
|
|
|
1795
1925
|
"list_board",
|
|
1796
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.",
|
|
1797
1927
|
{
|
|
1798
|
-
query:
|
|
1799
|
-
repoPath:
|
|
1800
|
-
kind:
|
|
1801
|
-
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(
|
|
1802
1932
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
1803
1933
|
),
|
|
1804
|
-
limit:
|
|
1934
|
+
limit: z5.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
1805
1935
|
},
|
|
1806
1936
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
1807
1937
|
const workspaces = orch.listWorkspaces();
|
|
@@ -1839,7 +1969,7 @@ async function startMcpServer() {
|
|
|
1839
1969
|
server.tool(
|
|
1840
1970
|
"get_thread",
|
|
1841
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.",
|
|
1842
|
-
{ ref:
|
|
1972
|
+
{ ref: z5.string() },
|
|
1843
1973
|
async ({ ref }) => {
|
|
1844
1974
|
const t = orch.getThread(ref);
|
|
1845
1975
|
if (!t) {
|
|
@@ -1876,17 +2006,17 @@ async function startMcpServer() {
|
|
|
1876
2006
|
"present_artifact",
|
|
1877
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.",
|
|
1878
2008
|
{
|
|
1879
|
-
title:
|
|
1880
|
-
type:
|
|
2009
|
+
title: z5.string().describe("Short title shown in the artifact pane header"),
|
|
2010
|
+
type: z5.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
1881
2011
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
1882
2012
|
),
|
|
1883
|
-
content:
|
|
2013
|
+
content: z5.string().describe(
|
|
1884
2014
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
1885
2015
|
),
|
|
1886
|
-
artifact_id:
|
|
1887
|
-
status:
|
|
1888
|
-
phase:
|
|
1889
|
-
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")
|
|
1890
2020
|
},
|
|
1891
2021
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
1892
2022
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -1907,15 +2037,15 @@ async function startMcpServer() {
|
|
|
1907
2037
|
"ask_user",
|
|
1908
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.",
|
|
1909
2039
|
{
|
|
1910
|
-
questions:
|
|
1911
|
-
|
|
1912
|
-
question:
|
|
1913
|
-
header:
|
|
1914
|
-
multiSelect:
|
|
1915
|
-
options:
|
|
1916
|
-
|
|
1917
|
-
label:
|
|
1918
|
-
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)")
|
|
1919
2049
|
})
|
|
1920
2050
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
1921
2051
|
})
|
|
@@ -1934,9 +2064,9 @@ async function startMcpServer() {
|
|
|
1934
2064
|
"present_plan",
|
|
1935
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.",
|
|
1936
2066
|
{
|
|
1937
|
-
title:
|
|
1938
|
-
content:
|
|
1939
|
-
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")
|
|
1940
2070
|
},
|
|
1941
2071
|
async ({ title, content, thread_id }) => {
|
|
1942
2072
|
const { writePlanFile } = await import("../plan-file-PHVKUAEE.js");
|
|
@@ -1959,15 +2089,15 @@ async function startMcpServer() {
|
|
|
1959
2089
|
"present_schema",
|
|
1960
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.",
|
|
1961
2091
|
{
|
|
1962
|
-
title:
|
|
1963
|
-
mode:
|
|
1964
|
-
datasource:
|
|
1965
|
-
resource_id:
|
|
1966
|
-
record_id:
|
|
1967
|
-
resource:
|
|
1968
|
-
record:
|
|
1969
|
-
records:
|
|
1970
|
-
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")
|
|
1971
2101
|
},
|
|
1972
2102
|
async (args) => {
|
|
1973
2103
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -1988,10 +2118,10 @@ async function startMcpServer() {
|
|
|
1988
2118
|
"present_files",
|
|
1989
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.",
|
|
1990
2120
|
{
|
|
1991
|
-
title:
|
|
1992
|
-
datasource:
|
|
1993
|
-
path:
|
|
1994
|
-
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")
|
|
1995
2125
|
},
|
|
1996
2126
|
async (args) => {
|
|
1997
2127
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -2009,16 +2139,17 @@ async function startMcpServer() {
|
|
|
2009
2139
|
if (!worktreeProfile) {
|
|
2010
2140
|
registerSlackTools(server);
|
|
2011
2141
|
registerLinearTools(server);
|
|
2142
|
+
registerAbleTimeTools(server);
|
|
2012
2143
|
registerScheduleTools(server);
|
|
2013
2144
|
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-NBRUWYXO.js");
|
|
2014
|
-
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-
|
|
2145
|
+
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-YQ7YX2CC.js");
|
|
2015
2146
|
const accountDefaults = resolveThreadDefaults();
|
|
2016
2147
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
2017
2148
|
server.tool(
|
|
2018
2149
|
"set_caffeinate",
|
|
2019
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.",
|
|
2020
2151
|
{
|
|
2021
|
-
enabled:
|
|
2152
|
+
enabled: z5.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
2022
2153
|
},
|
|
2023
2154
|
async ({ enabled }) => {
|
|
2024
2155
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -2056,18 +2187,18 @@ async function startMcpServer() {
|
|
|
2056
2187
|
"create_thread",
|
|
2057
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.`,
|
|
2058
2189
|
{
|
|
2059
|
-
sourceType:
|
|
2060
|
-
sourceRef:
|
|
2061
|
-
agent:
|
|
2062
|
-
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(
|
|
2063
2194
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
2064
2195
|
),
|
|
2065
|
-
repoPath:
|
|
2066
|
-
title:
|
|
2067
|
-
cowboy:
|
|
2196
|
+
repoPath: z5.string(),
|
|
2197
|
+
title: z5.string().optional(),
|
|
2198
|
+
cowboy: z5.boolean().optional().describe(
|
|
2068
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."
|
|
2069
2200
|
),
|
|
2070
|
-
parentThreadId:
|
|
2201
|
+
parentThreadId: z5.string().optional().describe(
|
|
2071
2202
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
2072
2203
|
)
|
|
2073
2204
|
},
|
|
@@ -2083,10 +2214,10 @@ async function startMcpServer() {
|
|
|
2083
2214
|
"start_board_card",
|
|
2084
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.",
|
|
2085
2216
|
{
|
|
2086
|
-
kind:
|
|
2087
|
-
ref:
|
|
2088
|
-
repoPath:
|
|
2089
|
-
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()
|
|
2090
2221
|
},
|
|
2091
2222
|
async ({ kind, ref, repoPath, title }) => {
|
|
2092
2223
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -2190,9 +2321,9 @@ async function startMcpServer() {
|
|
|
2190
2321
|
"send_to_thread",
|
|
2191
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.',
|
|
2192
2323
|
{
|
|
2193
|
-
ref:
|
|
2194
|
-
prompt:
|
|
2195
|
-
force_stop:
|
|
2324
|
+
ref: z5.string(),
|
|
2325
|
+
prompt: z5.string(),
|
|
2326
|
+
force_stop: z5.boolean().optional()
|
|
2196
2327
|
},
|
|
2197
2328
|
async ({ ref, prompt, force_stop }) => {
|
|
2198
2329
|
if (force_stop) {
|
|
@@ -2221,8 +2352,8 @@ async function startMcpServer() {
|
|
|
2221
2352
|
"wait_for_turn",
|
|
2222
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).",
|
|
2223
2354
|
{
|
|
2224
|
-
ref:
|
|
2225
|
-
timeoutMs:
|
|
2355
|
+
ref: z5.string(),
|
|
2356
|
+
timeoutMs: z5.number().optional()
|
|
2226
2357
|
},
|
|
2227
2358
|
async ({ ref, timeoutMs }) => {
|
|
2228
2359
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -2251,7 +2382,7 @@ async function startMcpServer() {
|
|
|
2251
2382
|
server.tool(
|
|
2252
2383
|
"get_turn_result",
|
|
2253
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).",
|
|
2254
|
-
{ ref:
|
|
2385
|
+
{ ref: z5.string() },
|
|
2255
2386
|
async ({ ref }) => {
|
|
2256
2387
|
const result = orch.getTurnResult(ref);
|
|
2257
2388
|
return {
|
|
@@ -2271,8 +2402,8 @@ async function startMcpServer() {
|
|
|
2271
2402
|
"stop_thread",
|
|
2272
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.",
|
|
2273
2404
|
{
|
|
2274
|
-
ref:
|
|
2275
|
-
force:
|
|
2405
|
+
ref: z5.string(),
|
|
2406
|
+
force: z5.boolean().optional()
|
|
2276
2407
|
},
|
|
2277
2408
|
async ({ ref, force }) => {
|
|
2278
2409
|
const t = orch.getThread(ref);
|
|
@@ -2302,7 +2433,7 @@ async function startMcpServer() {
|
|
|
2302
2433
|
server.tool(
|
|
2303
2434
|
"archive_thread",
|
|
2304
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.",
|
|
2305
|
-
{ ref:
|
|
2436
|
+
{ ref: z5.string() },
|
|
2306
2437
|
async ({ ref }) => {
|
|
2307
2438
|
const t = orch.getThread(ref);
|
|
2308
2439
|
if (!t) {
|
|
@@ -2335,7 +2466,7 @@ async function startMcpServer() {
|
|
|
2335
2466
|
server.tool(
|
|
2336
2467
|
"restore_thread",
|
|
2337
2468
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
2338
|
-
{ ref:
|
|
2469
|
+
{ ref: z5.string() },
|
|
2339
2470
|
async ({ ref }) => {
|
|
2340
2471
|
try {
|
|
2341
2472
|
const restored = await orch.restore(ref);
|
|
@@ -2361,8 +2492,8 @@ async function startMcpServer() {
|
|
|
2361
2492
|
"get_diff",
|
|
2362
2493
|
"Compact diff summary (capped hunks, paginated)",
|
|
2363
2494
|
{
|
|
2364
|
-
ref:
|
|
2365
|
-
maxFiles:
|
|
2495
|
+
ref: z5.string(),
|
|
2496
|
+
maxFiles: z5.number().optional()
|
|
2366
2497
|
},
|
|
2367
2498
|
async ({ ref, maxFiles }) => {
|
|
2368
2499
|
const summary = await orch.diffSummary(ref);
|
|
@@ -2382,7 +2513,7 @@ async function startMcpServer() {
|
|
|
2382
2513
|
server.tool(
|
|
2383
2514
|
"request_review",
|
|
2384
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.',
|
|
2385
|
-
{ ref:
|
|
2516
|
+
{ ref: z5.string().describe("Worktree thread id/ref to review") },
|
|
2386
2517
|
async ({ ref }) => {
|
|
2387
2518
|
try {
|
|
2388
2519
|
const tab = await orch.requestReview(ref);
|
|
@@ -2411,8 +2542,8 @@ async function startMcpServer() {
|
|
|
2411
2542
|
"ask_git",
|
|
2412
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.",
|
|
2413
2544
|
{
|
|
2414
|
-
ref:
|
|
2415
|
-
action:
|
|
2545
|
+
ref: z5.string().describe("Worktree thread id/ref"),
|
|
2546
|
+
action: z5.enum(AGENT_GIT_ACTIONS).describe(
|
|
2416
2547
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
2417
2548
|
)
|
|
2418
2549
|
},
|
|
@@ -2439,7 +2570,7 @@ async function startMcpServer() {
|
|
|
2439
2570
|
}
|
|
2440
2571
|
}
|
|
2441
2572
|
);
|
|
2442
|
-
const agentEnum =
|
|
2573
|
+
const agentEnum = z5.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
2443
2574
|
server.tool(
|
|
2444
2575
|
"list_models",
|
|
2445
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.",
|
|
@@ -2462,11 +2593,11 @@ async function startMcpServer() {
|
|
|
2462
2593
|
"fork_worktree",
|
|
2463
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.",
|
|
2464
2595
|
{
|
|
2465
|
-
ref:
|
|
2466
|
-
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)"),
|
|
2467
2598
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
2468
|
-
model:
|
|
2469
|
-
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()
|
|
2470
2601
|
},
|
|
2471
2602
|
async ({ ref, through_index, agent, model, title }) => {
|
|
2472
2603
|
try {
|
|
@@ -2507,11 +2638,11 @@ async function startMcpServer() {
|
|
|
2507
2638
|
"fork_chat",
|
|
2508
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.",
|
|
2509
2640
|
{
|
|
2510
|
-
ref:
|
|
2511
|
-
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)"),
|
|
2512
2643
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
2513
|
-
model:
|
|
2514
|
-
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()
|
|
2515
2646
|
},
|
|
2516
2647
|
async ({ ref, through_index, agent, model, title }) => {
|
|
2517
2648
|
try {
|
|
@@ -2557,8 +2688,8 @@ async function startMcpServer() {
|
|
|
2557
2688
|
"run_dev_script",
|
|
2558
2689
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
2559
2690
|
{
|
|
2560
|
-
ref:
|
|
2561
|
-
name:
|
|
2691
|
+
ref: z5.string(),
|
|
2692
|
+
name: z5.string().optional()
|
|
2562
2693
|
},
|
|
2563
2694
|
async ({ ref, name }) => {
|
|
2564
2695
|
const result = await orch.startDev(ref, name);
|
|
@@ -2580,7 +2711,7 @@ async function startMcpServer() {
|
|
|
2580
2711
|
server.tool(
|
|
2581
2712
|
"list_run_scripts",
|
|
2582
2713
|
"List named run scripts available for a thread",
|
|
2583
|
-
{ ref:
|
|
2714
|
+
{ ref: z5.string() },
|
|
2584
2715
|
async ({ ref }) => {
|
|
2585
2716
|
const scripts = orch.listThreadRunScripts(ref);
|
|
2586
2717
|
const active = orch.getActiveRuns(ref);
|
|
@@ -2598,8 +2729,8 @@ async function startMcpServer() {
|
|
|
2598
2729
|
"stop_dev_script",
|
|
2599
2730
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
2600
2731
|
{
|
|
2601
|
-
ref:
|
|
2602
|
-
name:
|
|
2732
|
+
ref: z5.string(),
|
|
2733
|
+
name: z5.string().optional()
|
|
2603
2734
|
},
|
|
2604
2735
|
async ({ ref, name }) => {
|
|
2605
2736
|
orch.stopDev(ref, name);
|
|
@@ -2609,7 +2740,7 @@ async function startMcpServer() {
|
|
|
2609
2740
|
server.tool(
|
|
2610
2741
|
"run_setup",
|
|
2611
2742
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
2612
|
-
{ ref:
|
|
2743
|
+
{ ref: z5.string() },
|
|
2613
2744
|
async ({ ref }) => {
|
|
2614
2745
|
const result = await orch.runSetup(ref);
|
|
2615
2746
|
return {
|
|
@@ -2620,7 +2751,7 @@ async function startMcpServer() {
|
|
|
2620
2751
|
server.tool(
|
|
2621
2752
|
"add_workspace",
|
|
2622
2753
|
"Register a git repo as a Sideboard workspace",
|
|
2623
|
-
{ repoPath:
|
|
2754
|
+
{ repoPath: z5.string() },
|
|
2624
2755
|
async ({ repoPath }) => {
|
|
2625
2756
|
const ws = await orch.addWorkspace(repoPath);
|
|
2626
2757
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -2629,7 +2760,7 @@ async function startMcpServer() {
|
|
|
2629
2760
|
server.tool(
|
|
2630
2761
|
"remove_workspace",
|
|
2631
2762
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
2632
|
-
{ repoPath:
|
|
2763
|
+
{ repoPath: z5.string() },
|
|
2633
2764
|
async ({ repoPath }) => {
|
|
2634
2765
|
orch.removeWorkspace(repoPath);
|
|
2635
2766
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -2639,12 +2770,12 @@ async function startMcpServer() {
|
|
|
2639
2770
|
"fanout",
|
|
2640
2771
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
2641
2772
|
{
|
|
2642
|
-
prompt:
|
|
2643
|
-
agents:
|
|
2644
|
-
repoPath:
|
|
2645
|
-
sourceType:
|
|
2646
|
-
sourceRef:
|
|
2647
|
-
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()
|
|
2648
2779
|
},
|
|
2649
2780
|
async (args) => {
|
|
2650
2781
|
const threads = await orch.bestOfN(args);
|
|
@@ -2671,8 +2802,8 @@ async function startMcpServer() {
|
|
|
2671
2802
|
"list_branches",
|
|
2672
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).",
|
|
2673
2804
|
{
|
|
2674
|
-
repoPath:
|
|
2675
|
-
unmergedOnly:
|
|
2805
|
+
repoPath: z5.string(),
|
|
2806
|
+
unmergedOnly: z5.boolean().optional()
|
|
2676
2807
|
},
|
|
2677
2808
|
async ({ repoPath, unmergedOnly }) => {
|
|
2678
2809
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -2692,7 +2823,7 @@ async function startMcpServer() {
|
|
|
2692
2823
|
server.tool(
|
|
2693
2824
|
"list_prs",
|
|
2694
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.",
|
|
2695
|
-
{ repoPath:
|
|
2826
|
+
{ repoPath: z5.string() },
|
|
2696
2827
|
async ({ repoPath }) => {
|
|
2697
2828
|
const root = await resolveRepoRoot(repoPath);
|
|
2698
2829
|
const prs = await listPrs(root);
|
|
@@ -2711,7 +2842,7 @@ async function startMcpServer() {
|
|
|
2711
2842
|
server.tool(
|
|
2712
2843
|
"get_pr_stack",
|
|
2713
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).",
|
|
2714
|
-
{ ref:
|
|
2845
|
+
{ ref: z5.string() },
|
|
2715
2846
|
async ({ ref }) => {
|
|
2716
2847
|
const stack = await orch.getPrStack(ref);
|
|
2717
2848
|
return {
|
|
@@ -2723,8 +2854,8 @@ async function startMcpServer() {
|
|
|
2723
2854
|
"open_pr_stack_layers",
|
|
2724
2855
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
2725
2856
|
{
|
|
2726
|
-
ref:
|
|
2727
|
-
layer:
|
|
2857
|
+
ref: z5.string(),
|
|
2858
|
+
layer: z5.number().int().positive().optional()
|
|
2728
2859
|
},
|
|
2729
2860
|
async ({ ref, layer }) => {
|
|
2730
2861
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -2758,9 +2889,9 @@ async function startMcpServer() {
|
|
|
2758
2889
|
"add_stack_layer",
|
|
2759
2890
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
2760
2891
|
{
|
|
2761
|
-
ref:
|
|
2762
|
-
branchName:
|
|
2763
|
-
title:
|
|
2892
|
+
ref: z5.string(),
|
|
2893
|
+
branchName: z5.string(),
|
|
2894
|
+
title: z5.string().optional()
|
|
2764
2895
|
},
|
|
2765
2896
|
async ({ ref, branchName, title }) => {
|
|
2766
2897
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -2789,11 +2920,11 @@ async function startMcpServer() {
|
|
|
2789
2920
|
"create_pr_stack",
|
|
2790
2921
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
2791
2922
|
{
|
|
2792
|
-
repoPath:
|
|
2793
|
-
branches:
|
|
2794
|
-
agent:
|
|
2795
|
-
base:
|
|
2796
|
-
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()
|
|
2797
2928
|
},
|
|
2798
2929
|
async (args) => {
|
|
2799
2930
|
const result = await orch.createPrStack({
|
|
@@ -2830,8 +2961,8 @@ async function startMcpServer() {
|
|
|
2830
2961
|
);
|
|
2831
2962
|
server.tool(
|
|
2832
2963
|
"list_issues",
|
|
2833
|
-
"List issues from Sideboard Account connections (Linear
|
|
2834
|
-
{ 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() },
|
|
2835
2966
|
async ({ repoPath }) => {
|
|
2836
2967
|
const root = await resolveRepoRoot(repoPath);
|
|
2837
2968
|
const result = await listIssues(root);
|
|
@@ -2842,8 +2973,8 @@ async function startMcpServer() {
|
|
|
2842
2973
|
"list_linear_issues",
|
|
2843
2974
|
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
2844
2975
|
{
|
|
2845
|
-
agent:
|
|
2846
|
-
repoPath:
|
|
2976
|
+
agent: z5.enum(["claude", "codex", "opencode"]),
|
|
2977
|
+
repoPath: z5.string()
|
|
2847
2978
|
},
|
|
2848
2979
|
async ({ agent, repoPath }) => {
|
|
2849
2980
|
const issues = await listLinearIssues(agent, repoPath);
|