@sideboard-ai/core 0.1.160 → 0.1.161
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/{chunk-QYKADGPP.js → chunk-EZANYS5D.js} +5 -5
- package/dist/{chunk-BQ4PEXIF.js → chunk-MIX4R72K.js} +5 -5
- package/dist/index.cjs +741 -607
- package/dist/index.js +732 -601
- package/dist/mcp/run-stdio.cjs +741 -607
- package/dist/mcp/run-stdio.js +732 -601
- package/dist/{orchestrator-KVKPIUVU.js → orchestrator-6FZBSP5B.js} +1 -1
- package/dist/{orchestrator-BSVSOTBK.js → orchestrator-QREP2CFO.js} +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -18085,13 +18085,13 @@ function formatStat(files) {
|
|
|
18085
18085
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
18086
18086
|
}
|
|
18087
18087
|
function emptyScopeStats() {
|
|
18088
|
-
const
|
|
18088
|
+
const z9 = emptyStat();
|
|
18089
18089
|
return {
|
|
18090
|
-
commits:
|
|
18091
|
-
uncommitted:
|
|
18092
|
-
staged:
|
|
18093
|
-
unstaged:
|
|
18094
|
-
last_turn:
|
|
18090
|
+
commits: z9,
|
|
18091
|
+
uncommitted: z9,
|
|
18092
|
+
staged: z9,
|
|
18093
|
+
unstaged: z9,
|
|
18094
|
+
last_turn: z9
|
|
18095
18095
|
};
|
|
18096
18096
|
}
|
|
18097
18097
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -19232,17 +19232,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
19232
19232
|
];
|
|
19233
19233
|
if (opts.linear) {
|
|
19234
19234
|
lines.push(
|
|
19235
|
-
"- Linear: `linear_get_issue` (comments), `linear_comment`, `linear_update_issue` (state), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
19235
|
+
"- Linear: `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
19236
19236
|
);
|
|
19237
19237
|
}
|
|
19238
19238
|
if (github) {
|
|
19239
19239
|
lines.push(
|
|
19240
|
-
"- GitHub: `github_get_issue` (comments), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
19240
|
+
"- GitHub: `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
19241
19241
|
);
|
|
19242
19242
|
}
|
|
19243
19243
|
if (opts.abletime) {
|
|
19244
19244
|
lines.push(
|
|
19245
|
-
"- AbleTime: `abletime_get_task` (comments), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
19245
|
+
"- AbleTime: `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
19246
19246
|
);
|
|
19247
19247
|
}
|
|
19248
19248
|
lines.push(
|
|
@@ -24718,7 +24718,7 @@ init_orphan_cleanup();
|
|
|
24718
24718
|
// src/mcp/server.ts
|
|
24719
24719
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
24720
24720
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
24721
|
-
var
|
|
24721
|
+
var import_zod8 = require("zod");
|
|
24722
24722
|
var import_node_crypto13 = require("crypto");
|
|
24723
24723
|
var import_node_path50 = require("path");
|
|
24724
24724
|
init_orchestrator();
|
|
@@ -24999,12 +24999,53 @@ ${link}` : link;
|
|
|
24999
24999
|
// src/mcp/slack-tools.ts
|
|
25000
25000
|
init_outbound_watch();
|
|
25001
25001
|
init_workspaces2();
|
|
25002
|
-
|
|
25002
|
+
|
|
25003
|
+
// src/mcp/issue-list.ts
|
|
25004
|
+
var MCP_ISSUE_LIST_DEFAULT_LIMIT = 40;
|
|
25005
|
+
var MCP_ISSUE_LIST_MAX_LIMIT = 250;
|
|
25006
|
+
function clampMcpIssueLimit(limit) {
|
|
25007
|
+
if (limit == null || !Number.isFinite(limit)) return MCP_ISSUE_LIST_DEFAULT_LIMIT;
|
|
25008
|
+
return Math.max(1, Math.min(MCP_ISSUE_LIST_MAX_LIMIT, Math.floor(limit)));
|
|
25009
|
+
}
|
|
25010
|
+
function applyIssueListWindow(items, limit) {
|
|
25011
|
+
const truncated = items.length > limit;
|
|
25003
25012
|
return {
|
|
25004
|
-
|
|
25013
|
+
items: truncated ? items.slice(0, limit) : items,
|
|
25014
|
+
truncated
|
|
25015
|
+
};
|
|
25016
|
+
}
|
|
25017
|
+
function compactIssueRow(issue) {
|
|
25018
|
+
return {
|
|
25019
|
+
identifier: issue.identifier,
|
|
25020
|
+
title: issue.title,
|
|
25021
|
+
...issue.assignee ? { assignee: issue.assignee } : {},
|
|
25022
|
+
...issue.labels.length > 0 ? { labels: issue.labels } : {},
|
|
25023
|
+
...issue.teamKey ? { team: issue.teamKey } : {},
|
|
25024
|
+
...issue.cycle?.name ? { cycle: issue.cycle.name } : {}
|
|
25025
|
+
};
|
|
25026
|
+
}
|
|
25027
|
+
function formatMcpIssueList(input) {
|
|
25028
|
+
return {
|
|
25029
|
+
...input.source ? { source: input.source } : {},
|
|
25030
|
+
...input.viewer ? { viewer: input.viewer } : {},
|
|
25031
|
+
limit: input.limit,
|
|
25032
|
+
returned: input.issues.length,
|
|
25033
|
+
truncated: input.truncated,
|
|
25034
|
+
...input.truncated ? { hint: "More issues match. Pass a higher limit (max 250) or a tighter query." } : {},
|
|
25035
|
+
issues: input.issues.map(compactIssueRow)
|
|
25036
|
+
};
|
|
25037
|
+
}
|
|
25038
|
+
function mcpJson(payload, isError = false) {
|
|
25039
|
+
return {
|
|
25040
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
25005
25041
|
...isError ? { isError: true } : {}
|
|
25006
25042
|
};
|
|
25007
25043
|
}
|
|
25044
|
+
|
|
25045
|
+
// src/mcp/slack-tools.ts
|
|
25046
|
+
function text(payload, isError = false) {
|
|
25047
|
+
return mcpJson(payload, isError);
|
|
25048
|
+
}
|
|
25008
25049
|
function fail(err) {
|
|
25009
25050
|
return text(
|
|
25010
25051
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
@@ -25273,49 +25314,331 @@ function registerSlackTools(server) {
|
|
|
25273
25314
|
init_app_settings();
|
|
25274
25315
|
|
|
25275
25316
|
// src/mcp/abletime-tools.ts
|
|
25317
|
+
var import_zod3 = require("zod");
|
|
25318
|
+
init_abletime();
|
|
25319
|
+
|
|
25320
|
+
// src/mcp/issue-payload.ts
|
|
25276
25321
|
var import_zod2 = require("zod");
|
|
25277
25322
|
init_abletime();
|
|
25278
25323
|
|
|
25279
|
-
// src/mcp/
|
|
25280
|
-
var
|
|
25281
|
-
var
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25324
|
+
// src/mcp/smart-crush.ts
|
|
25325
|
+
var MUST_KEEP_TEXT_RE = /\b(error|exception|failed|failure|fatal|critical|panic|traceback|npm err|assert)\b/i;
|
|
25326
|
+
var CRUSH_MIN_ITEMS = 5;
|
|
25327
|
+
var CRUSH_MIN_CHARS = 800;
|
|
25328
|
+
var CRUSH_COMMENT_MAX_CHARS = 16e3;
|
|
25329
|
+
var CRUSH_BODY_MIN_CHARS = 12e3;
|
|
25330
|
+
var CRUSH_BODY_MAX_CHARS = 8e3;
|
|
25331
|
+
var NEAR_DUP_JACCARD = 0.92;
|
|
25332
|
+
function looksLikeMustKeep(text7) {
|
|
25333
|
+
return MUST_KEEP_TEXT_RE.test(text7);
|
|
25334
|
+
}
|
|
25335
|
+
function normalizeText(text7) {
|
|
25336
|
+
return text7.replace(/\s+/g, " ").trim().toLowerCase();
|
|
25337
|
+
}
|
|
25338
|
+
function wordSet(text7) {
|
|
25339
|
+
return new Set(
|
|
25340
|
+
text7.toLowerCase().split(/\W+/).filter((w) => w.length > 2)
|
|
25341
|
+
);
|
|
25285
25342
|
}
|
|
25286
|
-
function
|
|
25287
|
-
|
|
25343
|
+
function jaccard(a, b) {
|
|
25344
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
25345
|
+
let inter = 0;
|
|
25346
|
+
for (const w of a) {
|
|
25347
|
+
if (b.has(w)) inter += 1;
|
|
25348
|
+
}
|
|
25349
|
+
const union = a.size + b.size - inter;
|
|
25350
|
+
return union === 0 ? 0 : inter / union;
|
|
25351
|
+
}
|
|
25352
|
+
function importance(text7) {
|
|
25353
|
+
let score = text7.length;
|
|
25354
|
+
if (/```/.test(text7)) score += 400;
|
|
25355
|
+
if (/https?:\/\//.test(text7)) score += 50;
|
|
25356
|
+
if (looksLikeMustKeep(text7)) score += 1e4;
|
|
25357
|
+
return score;
|
|
25358
|
+
}
|
|
25359
|
+
function meanStd(values) {
|
|
25360
|
+
if (values.length === 0) return { mean: 0, std: 0 };
|
|
25361
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
25362
|
+
const variance = values.reduce((a, b) => a + (b - mean) * (b - mean), 0) / values.length;
|
|
25363
|
+
return { mean, std: Math.sqrt(variance) };
|
|
25364
|
+
}
|
|
25365
|
+
function dedupTextItems(items, getText) {
|
|
25366
|
+
const kept = [];
|
|
25367
|
+
const keptNorm = [];
|
|
25368
|
+
const keptWords = [];
|
|
25369
|
+
for (const item of items) {
|
|
25370
|
+
const text7 = getText(item);
|
|
25371
|
+
const norm = normalizeText(text7);
|
|
25372
|
+
if (!norm) {
|
|
25373
|
+
kept.push(item);
|
|
25374
|
+
keptNorm.push(norm);
|
|
25375
|
+
keptWords.push(wordSet(text7));
|
|
25376
|
+
continue;
|
|
25377
|
+
}
|
|
25378
|
+
const words = wordSet(text7);
|
|
25379
|
+
let dupAt = -1;
|
|
25380
|
+
for (let i = 0; i < kept.length; i++) {
|
|
25381
|
+
if (keptNorm[i] === norm) {
|
|
25382
|
+
dupAt = i;
|
|
25383
|
+
break;
|
|
25384
|
+
}
|
|
25385
|
+
if (keptWords[i].size >= 8 && words.size >= 8 && jaccard(keptWords[i], words) >= NEAR_DUP_JACCARD) {
|
|
25386
|
+
dupAt = i;
|
|
25387
|
+
break;
|
|
25388
|
+
}
|
|
25389
|
+
}
|
|
25390
|
+
if (dupAt < 0) {
|
|
25391
|
+
kept.push(item);
|
|
25392
|
+
keptNorm.push(norm);
|
|
25393
|
+
keptWords.push(words);
|
|
25394
|
+
continue;
|
|
25395
|
+
}
|
|
25396
|
+
if (text7.length > getText(kept[dupAt]).length) {
|
|
25397
|
+
kept[dupAt] = item;
|
|
25398
|
+
keptNorm[dupAt] = norm;
|
|
25399
|
+
keptWords[dupAt] = words;
|
|
25400
|
+
}
|
|
25401
|
+
}
|
|
25402
|
+
return { items: kept, dropped: items.length - kept.length };
|
|
25403
|
+
}
|
|
25404
|
+
function crushTextItems(items, getText, opts = {}) {
|
|
25405
|
+
const minItems = opts.minItems ?? CRUSH_MIN_ITEMS;
|
|
25406
|
+
const minChars = opts.minChars ?? CRUSH_MIN_CHARS;
|
|
25407
|
+
const maxChars = opts.maxChars ?? CRUSH_COMMENT_MAX_CHARS;
|
|
25408
|
+
const original = items;
|
|
25409
|
+
const totalChars = items.reduce((n, item) => n + getText(item).length, 0);
|
|
25410
|
+
if (items.length < minItems || totalChars < minChars) {
|
|
25411
|
+
return {
|
|
25412
|
+
items,
|
|
25413
|
+
truncated: false,
|
|
25414
|
+
kept: items.length,
|
|
25415
|
+
dropped: 0,
|
|
25416
|
+
reason: "passthrough"
|
|
25417
|
+
};
|
|
25418
|
+
}
|
|
25419
|
+
const deduped = dedupTextItems(items, getText);
|
|
25420
|
+
const unique = deduped.items;
|
|
25421
|
+
const uniqueChars = unique.reduce((n, item) => n + getText(item).length, 0);
|
|
25422
|
+
if (uniqueChars <= maxChars) {
|
|
25423
|
+
const changed = unique.length < original.length;
|
|
25424
|
+
return {
|
|
25425
|
+
items: unique,
|
|
25426
|
+
truncated: changed,
|
|
25427
|
+
kept: unique.length,
|
|
25428
|
+
dropped: original.length - unique.length,
|
|
25429
|
+
reason: changed ? "dedup" : "passthrough"
|
|
25430
|
+
};
|
|
25431
|
+
}
|
|
25432
|
+
const lengths = unique.map((item) => getText(item).length);
|
|
25433
|
+
const { mean, std } = meanStd(lengths);
|
|
25434
|
+
const outlierFloor = mean + 2 * std;
|
|
25435
|
+
const mustKeep = /* @__PURE__ */ new Set();
|
|
25436
|
+
unique.forEach((item, i) => {
|
|
25437
|
+
const text7 = getText(item);
|
|
25438
|
+
if (looksLikeMustKeep(text7) || text7.length > outlierFloor) mustKeep.add(i);
|
|
25439
|
+
});
|
|
25440
|
+
const remainingIdx = unique.map((_, i) => i).filter((i) => !mustKeep.has(i));
|
|
25441
|
+
let used = [...mustKeep].reduce((n, i) => n + lengths[i], 0);
|
|
25442
|
+
const pick = new Set(mustKeep);
|
|
25443
|
+
const canTake = (i) => {
|
|
25444
|
+
if (pick.has(i)) return false;
|
|
25445
|
+
const next = used + lengths[i];
|
|
25446
|
+
if (next > maxChars && pick.size > mustKeep.size) return false;
|
|
25447
|
+
used = next;
|
|
25448
|
+
pick.add(i);
|
|
25449
|
+
return true;
|
|
25450
|
+
};
|
|
25451
|
+
const k = remainingIdx.length;
|
|
25452
|
+
const headN = Math.max(1, Math.ceil(k * 0.3));
|
|
25453
|
+
const tailN = Math.max(1, Math.ceil(k * 0.15));
|
|
25454
|
+
for (const i of remainingIdx.slice(0, headN)) canTake(i);
|
|
25455
|
+
for (const i of remainingIdx.slice(-tailN).reverse()) canTake(i);
|
|
25456
|
+
const middle = remainingIdx.filter((i) => !pick.has(i)).sort((a, b) => importance(getText(unique[b])) - importance(getText(unique[a])));
|
|
25457
|
+
for (const i of middle) {
|
|
25458
|
+
if (used >= maxChars) break;
|
|
25459
|
+
canTake(i);
|
|
25460
|
+
}
|
|
25461
|
+
const crushed = unique.filter((_, i) => pick.has(i));
|
|
25462
|
+
const crushedChars = crushed.reduce((n, item) => n + getText(item).length, 0);
|
|
25463
|
+
if (crushed.length >= original.length && crushedChars >= totalChars) {
|
|
25464
|
+
return {
|
|
25465
|
+
items: original,
|
|
25466
|
+
truncated: false,
|
|
25467
|
+
kept: original.length,
|
|
25468
|
+
dropped: 0,
|
|
25469
|
+
reason: "passthrough"
|
|
25470
|
+
};
|
|
25471
|
+
}
|
|
25288
25472
|
return {
|
|
25289
|
-
items:
|
|
25290
|
-
truncated
|
|
25473
|
+
items: crushed,
|
|
25474
|
+
truncated: crushed.length < original.length,
|
|
25475
|
+
kept: crushed.length,
|
|
25476
|
+
dropped: original.length - crushed.length,
|
|
25477
|
+
reason: "budget"
|
|
25291
25478
|
};
|
|
25292
25479
|
}
|
|
25293
|
-
function
|
|
25294
|
-
return
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25480
|
+
function splitBlocks(text7) {
|
|
25481
|
+
if (/\n\n/.test(text7)) return text7.split(/\n\n+/);
|
|
25482
|
+
return text7.split("\n");
|
|
25483
|
+
}
|
|
25484
|
+
function capOversizedBlock(block, maxChars) {
|
|
25485
|
+
if (block.length <= maxChars) return block;
|
|
25486
|
+
const head = Math.floor(maxChars * 0.3);
|
|
25487
|
+
const tail = Math.floor(maxChars * 0.15);
|
|
25488
|
+
return `${block.slice(0, head)}
|
|
25489
|
+
|
|
25490
|
+
[\u2026crushed ${block.length - head - tail} chars; pass include=full to read the rest\u2026]
|
|
25491
|
+
|
|
25492
|
+
${block.slice(-tail)}`;
|
|
25493
|
+
}
|
|
25494
|
+
function crushMarkdown(text7, opts) {
|
|
25495
|
+
const original = text7 ?? "";
|
|
25496
|
+
const minChars = opts?.minChars ?? CRUSH_BODY_MIN_CHARS;
|
|
25497
|
+
const maxChars = opts?.maxChars ?? CRUSH_BODY_MAX_CHARS;
|
|
25498
|
+
if (original.length < minChars) {
|
|
25499
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25500
|
+
}
|
|
25501
|
+
const rawBlocks = splitBlocks(original).filter((b) => b.length > 0);
|
|
25502
|
+
const blocks = rawBlocks.map((b) => capOversizedBlock(b, maxChars));
|
|
25503
|
+
const cappedABlock = blocks.some((b, i) => b !== rawBlocks[i]);
|
|
25504
|
+
if (blocks.length <= 1) {
|
|
25505
|
+
const cut = blocks[0] ?? original;
|
|
25506
|
+
if (cut.length >= original.length) {
|
|
25507
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25508
|
+
}
|
|
25509
|
+
return { text: cut, truncated: true, originalChars: original.length };
|
|
25510
|
+
}
|
|
25511
|
+
const must = blocks.filter((b) => looksLikeMustKeep(b));
|
|
25512
|
+
const rest = blocks.filter((b) => !looksLikeMustKeep(b));
|
|
25513
|
+
const marker = `[\u2026crushed body; ${original.length} chars originally. Pass include=full to read the rest.]`;
|
|
25514
|
+
let used = must.reduce((n, b) => n + b.length, 0) + marker.length;
|
|
25515
|
+
const keptRest = [];
|
|
25516
|
+
const headN = Math.max(1, Math.ceil(rest.length * 0.3));
|
|
25517
|
+
const tailN = Math.max(1, Math.ceil(rest.length * 0.15));
|
|
25518
|
+
const head = rest.slice(0, headN);
|
|
25519
|
+
const tail = rest.slice(-tailN);
|
|
25520
|
+
const middle = rest.slice(headN, Math.max(headN, rest.length - tailN));
|
|
25521
|
+
const take = (block) => {
|
|
25522
|
+
if (used + block.length + 2 > maxChars && keptRest.length + must.length > 0) {
|
|
25523
|
+
return false;
|
|
25524
|
+
}
|
|
25525
|
+
used += block.length + 2;
|
|
25526
|
+
keptRest.push(block);
|
|
25527
|
+
return true;
|
|
25301
25528
|
};
|
|
25529
|
+
for (const b of head) take(b);
|
|
25530
|
+
for (const b of tail) take(b);
|
|
25531
|
+
const ranked = [...middle].sort((a, b) => importance(b) - importance(a));
|
|
25532
|
+
for (const b of ranked) {
|
|
25533
|
+
if (used >= maxChars) break;
|
|
25534
|
+
take(b);
|
|
25535
|
+
}
|
|
25536
|
+
const keepSet = /* @__PURE__ */ new Set([...must, ...keptRest]);
|
|
25537
|
+
const ordered = blocks.filter((b) => keepSet.has(b));
|
|
25538
|
+
const omitted = blocks.length - ordered.length;
|
|
25539
|
+
if (omitted <= 0) {
|
|
25540
|
+
const joined = blocks.join("\n\n");
|
|
25541
|
+
if (cappedABlock && joined.length < original.length) {
|
|
25542
|
+
return { text: joined, truncated: true, originalChars: original.length };
|
|
25543
|
+
}
|
|
25544
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25545
|
+
}
|
|
25546
|
+
const out = [];
|
|
25547
|
+
let inserted = false;
|
|
25548
|
+
for (const block of blocks) {
|
|
25549
|
+
if (keepSet.has(block)) {
|
|
25550
|
+
out.push(block);
|
|
25551
|
+
continue;
|
|
25552
|
+
}
|
|
25553
|
+
if (!inserted) {
|
|
25554
|
+
out.push(marker);
|
|
25555
|
+
inserted = true;
|
|
25556
|
+
}
|
|
25557
|
+
}
|
|
25558
|
+
const textOut = out.join("\n\n");
|
|
25559
|
+
if (textOut.length >= original.length) {
|
|
25560
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25561
|
+
}
|
|
25562
|
+
return { text: textOut, truncated: true, originalChars: original.length };
|
|
25302
25563
|
}
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25564
|
+
|
|
25565
|
+
// src/mcp/issue-payload.ts
|
|
25566
|
+
var MCP_ISSUE_INCLUDE_VALUES = ["default", "full"];
|
|
25567
|
+
var mcpIssueIncludeSchema = import_zod2.z.enum(MCP_ISSUE_INCLUDE_VALUES).optional().describe(
|
|
25568
|
+
"default applies SmartCrusher-style crush (dedup + budget; small unique tickets pass through). full returns the uncompressed vendor payload. Re-call with include=full if crush.truncated."
|
|
25569
|
+
);
|
|
25570
|
+
var MCP_ISSUE_CRUSH_HINT = "Pass include=full to re-fetch the uncompressed ticket from the vendor.";
|
|
25571
|
+
function emptyMeta() {
|
|
25572
|
+
return { truncated: true, hint: MCP_ISSUE_CRUSH_HINT };
|
|
25573
|
+
}
|
|
25574
|
+
function withComments(comments, meta) {
|
|
25575
|
+
const crushed = crushTextItems(comments, (c) => c.body);
|
|
25576
|
+
if (crushed.truncated) {
|
|
25577
|
+
meta.comments = {
|
|
25578
|
+
kept: crushed.kept,
|
|
25579
|
+
dropped: crushed.dropped,
|
|
25580
|
+
reason: crushed.reason
|
|
25581
|
+
};
|
|
25582
|
+
}
|
|
25583
|
+
return { comments: crushed.items, meta };
|
|
25584
|
+
}
|
|
25585
|
+
function withBody(text7, meta) {
|
|
25586
|
+
const crushed = crushMarkdown(text7);
|
|
25587
|
+
if (crushed.truncated) {
|
|
25588
|
+
meta.body = { originalChars: crushed.originalChars };
|
|
25589
|
+
return { value: crushed.text, meta };
|
|
25590
|
+
}
|
|
25591
|
+
return { value: text7, meta };
|
|
25592
|
+
}
|
|
25593
|
+
function attachCrush(payload, meta) {
|
|
25594
|
+
if (!meta.comments && !meta.body) return payload;
|
|
25595
|
+
return { ...payload, crush: meta };
|
|
25596
|
+
}
|
|
25597
|
+
function formatLinearIssuePayload(issue, include) {
|
|
25598
|
+
if (include === "full") return issue;
|
|
25599
|
+
const meta = emptyMeta();
|
|
25600
|
+
const comments = withComments(issue.comments, meta);
|
|
25601
|
+
const description = withBody(issue.description, comments.meta);
|
|
25602
|
+
const { team, ...rest } = issue;
|
|
25603
|
+
const next = {
|
|
25604
|
+
...rest,
|
|
25605
|
+
...team ? { team: { id: team.id, key: team.key, name: team.name } } : {},
|
|
25606
|
+
description: description.value,
|
|
25607
|
+
comments: comments.comments
|
|
25312
25608
|
};
|
|
25609
|
+
return attachCrush(next, description.meta);
|
|
25313
25610
|
}
|
|
25314
|
-
function
|
|
25315
|
-
return
|
|
25316
|
-
|
|
25317
|
-
|
|
25611
|
+
function formatGitHubIssuePayload(issue, include) {
|
|
25612
|
+
if (include === "full") return issue;
|
|
25613
|
+
const meta = emptyMeta();
|
|
25614
|
+
const comments = withComments(issue.comments, meta);
|
|
25615
|
+
const body = withBody(issue.body, comments.meta);
|
|
25616
|
+
const next = {
|
|
25617
|
+
...issue,
|
|
25618
|
+
body: body.value,
|
|
25619
|
+
comments: comments.comments
|
|
25620
|
+
};
|
|
25621
|
+
return attachCrush(next, body.meta);
|
|
25622
|
+
}
|
|
25623
|
+
function formatAbleTimeTaskPayload(task, include) {
|
|
25624
|
+
const base = {
|
|
25625
|
+
...toAbleTimeIssueInfo(task),
|
|
25626
|
+
description: task.description,
|
|
25627
|
+
state: task.state,
|
|
25628
|
+
comments: task.comments
|
|
25318
25629
|
};
|
|
25630
|
+
if (include === "full") return base;
|
|
25631
|
+
const meta = emptyMeta();
|
|
25632
|
+
const comments = withComments(task.comments, meta);
|
|
25633
|
+
const description = withBody(task.description, comments.meta);
|
|
25634
|
+
return attachCrush(
|
|
25635
|
+
{
|
|
25636
|
+
...base,
|
|
25637
|
+
description: description.value,
|
|
25638
|
+
comments: comments.comments
|
|
25639
|
+
},
|
|
25640
|
+
description.meta
|
|
25641
|
+
);
|
|
25319
25642
|
}
|
|
25320
25643
|
|
|
25321
25644
|
// src/mcp/abletime-tools.ts
|
|
@@ -25361,7 +25684,7 @@ function registerAbleTimeTools(server) {
|
|
|
25361
25684
|
server.tool(
|
|
25362
25685
|
"abletime_list_tasks",
|
|
25363
25686
|
"List open AbleTime tasks (excludes done/archived). Optional projectId filter.",
|
|
25364
|
-
{ projectId:
|
|
25687
|
+
{ projectId: import_zod3.z.string().optional() },
|
|
25365
25688
|
async ({ projectId }) => {
|
|
25366
25689
|
try {
|
|
25367
25690
|
const tasks = await listAbleTimeTasks({ projectId });
|
|
@@ -25374,7 +25697,7 @@ function registerAbleTimeTools(server) {
|
|
|
25374
25697
|
server.tool(
|
|
25375
25698
|
"abletime_search_tasks",
|
|
25376
25699
|
"Search AbleTime tasks by text, reference, or tag.",
|
|
25377
|
-
{ query:
|
|
25700
|
+
{ query: import_zod3.z.string() },
|
|
25378
25701
|
async ({ query }) => {
|
|
25379
25702
|
try {
|
|
25380
25703
|
const tasks = await searchAbleTimeTasks(query);
|
|
@@ -25386,17 +25709,11 @@ function registerAbleTimeTools(server) {
|
|
|
25386
25709
|
);
|
|
25387
25710
|
server.tool(
|
|
25388
25711
|
"abletime_get_task",
|
|
25389
|
-
"Get one AbleTime task by id or reference (e.g. CRM-232): description, state, comments. Re-fetch to read new comments.",
|
|
25390
|
-
{ id:
|
|
25391
|
-
async ({ id }) => {
|
|
25712
|
+
"Get one AbleTime task by id or reference (e.g. CRM-232): description, state, comments. Re-fetch to read new comments. Default crushes redundant comments and huge pasted bodies (SmartCrusher-style; small unique tickets pass through). Pass include=full for the uncompressed vendor payload.",
|
|
25713
|
+
{ id: import_zod3.z.string(), include: mcpIssueIncludeSchema },
|
|
25714
|
+
async ({ id, include }) => {
|
|
25392
25715
|
try {
|
|
25393
|
-
|
|
25394
|
-
return text2({
|
|
25395
|
-
...toAbleTimeIssueInfo(task),
|
|
25396
|
-
description: task.description,
|
|
25397
|
-
state: task.state,
|
|
25398
|
-
comments: task.comments
|
|
25399
|
-
});
|
|
25716
|
+
return text2(formatAbleTimeTaskPayload(await getAbleTimeTask(id), include));
|
|
25400
25717
|
} catch (err) {
|
|
25401
25718
|
return fail2(err);
|
|
25402
25719
|
}
|
|
@@ -25405,7 +25722,7 @@ function registerAbleTimeTools(server) {
|
|
|
25405
25722
|
server.tool(
|
|
25406
25723
|
"abletime_comment",
|
|
25407
25724
|
"Add a markdown comment on an AbleTime task (id or CRM-232).",
|
|
25408
|
-
{ id:
|
|
25725
|
+
{ id: import_zod3.z.string(), body: import_zod3.z.string() },
|
|
25409
25726
|
async (args) => {
|
|
25410
25727
|
try {
|
|
25411
25728
|
return text2(await commentAbleTimeTask(args));
|
|
@@ -25418,10 +25735,10 @@ function registerAbleTimeTools(server) {
|
|
|
25418
25735
|
"abletime_update_task",
|
|
25419
25736
|
"Update an AbleTime task (id or CRM-232). Pass title, description, and/or state.",
|
|
25420
25737
|
{
|
|
25421
|
-
id:
|
|
25422
|
-
title:
|
|
25423
|
-
description:
|
|
25424
|
-
state:
|
|
25738
|
+
id: import_zod3.z.string(),
|
|
25739
|
+
title: import_zod3.z.string().optional(),
|
|
25740
|
+
description: import_zod3.z.string().optional(),
|
|
25741
|
+
state: import_zod3.z.string().optional()
|
|
25425
25742
|
},
|
|
25426
25743
|
async (args) => {
|
|
25427
25744
|
try {
|
|
@@ -25440,12 +25757,12 @@ function registerAbleTimeTools(server) {
|
|
|
25440
25757
|
"abletime_create_task",
|
|
25441
25758
|
"Create an AbleTime task in a project. Call abletime_list_projects if you do not have a project id. Pass parent (CRM-232) for a spin-off. New tasks start in todo (or backlog).",
|
|
25442
25759
|
{
|
|
25443
|
-
title:
|
|
25444
|
-
description:
|
|
25445
|
-
projectId:
|
|
25446
|
-
categoryId:
|
|
25447
|
-
state:
|
|
25448
|
-
parent:
|
|
25760
|
+
title: import_zod3.z.string(),
|
|
25761
|
+
description: import_zod3.z.string().optional(),
|
|
25762
|
+
projectId: import_zod3.z.string().optional(),
|
|
25763
|
+
categoryId: import_zod3.z.string().optional(),
|
|
25764
|
+
state: import_zod3.z.enum(["backlog", "todo"]).optional(),
|
|
25765
|
+
parent: import_zod3.z.string().optional().describe("Parent task id or reference (CRM-232) for a spin-off.")
|
|
25449
25766
|
},
|
|
25450
25767
|
async (args) => {
|
|
25451
25768
|
try {
|
|
@@ -25459,10 +25776,10 @@ function registerAbleTimeTools(server) {
|
|
|
25459
25776
|
"abletime_ensure_task",
|
|
25460
25777
|
"Find or create an AbleTime task for this work, then create_thread with sourceType=ticket.",
|
|
25461
25778
|
{
|
|
25462
|
-
title:
|
|
25463
|
-
description:
|
|
25464
|
-
projectId:
|
|
25465
|
-
categoryId:
|
|
25779
|
+
title: import_zod3.z.string(),
|
|
25780
|
+
description: import_zod3.z.string().optional(),
|
|
25781
|
+
projectId: import_zod3.z.string().optional(),
|
|
25782
|
+
categoryId: import_zod3.z.string().optional()
|
|
25466
25783
|
},
|
|
25467
25784
|
async (args) => {
|
|
25468
25785
|
try {
|
|
@@ -25476,7 +25793,7 @@ function registerAbleTimeTools(server) {
|
|
|
25476
25793
|
}
|
|
25477
25794
|
|
|
25478
25795
|
// src/mcp/github-tools.ts
|
|
25479
|
-
var
|
|
25796
|
+
var import_zod4 = require("zod");
|
|
25480
25797
|
function text3(payload, isError = false) {
|
|
25481
25798
|
return mcpJson(payload, isError);
|
|
25482
25799
|
}
|
|
@@ -25486,15 +25803,15 @@ function fail3(err) {
|
|
|
25486
25803
|
true
|
|
25487
25804
|
);
|
|
25488
25805
|
}
|
|
25489
|
-
var repoPathSchema =
|
|
25806
|
+
var repoPathSchema = import_zod4.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
|
|
25490
25807
|
function registerGithubIssueTools(server) {
|
|
25491
25808
|
server.tool(
|
|
25492
25809
|
"github_get_issue",
|
|
25493
|
-
"Get a GitHub issue (#123 or URL): body, comments, state. Re-fetch to read new comments. Uses Account gh.",
|
|
25494
|
-
{ id:
|
|
25495
|
-
async ({ id, repoPath }) => {
|
|
25810
|
+
"Get a GitHub issue (#123 or URL): body, comments, state. Re-fetch to read new comments. Default crushes redundant comments and huge pasted bodies (SmartCrusher-style; small unique tickets pass through). Pass include=full for the uncompressed vendor payload. Uses Account gh.",
|
|
25811
|
+
{ id: import_zod4.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
|
|
25812
|
+
async ({ id, include, repoPath }) => {
|
|
25496
25813
|
try {
|
|
25497
|
-
return text3(await getGitHubIssue(id, { repoPath }));
|
|
25814
|
+
return text3(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
|
|
25498
25815
|
} catch (err) {
|
|
25499
25816
|
return fail3(err);
|
|
25500
25817
|
}
|
|
@@ -25503,7 +25820,7 @@ function registerGithubIssueTools(server) {
|
|
|
25503
25820
|
server.tool(
|
|
25504
25821
|
"github_comment",
|
|
25505
25822
|
"Add a markdown comment on a GitHub issue (#123 or URL). Uses Account gh.",
|
|
25506
|
-
{ id:
|
|
25823
|
+
{ id: import_zod4.z.string(), body: import_zod4.z.string(), repoPath: repoPathSchema },
|
|
25507
25824
|
async ({ id, body, repoPath }) => {
|
|
25508
25825
|
try {
|
|
25509
25826
|
return text3(await commentGitHubIssue({ id, body }, { repoPath }));
|
|
@@ -25516,10 +25833,10 @@ function registerGithubIssueTools(server) {
|
|
|
25516
25833
|
"github_update_issue",
|
|
25517
25834
|
"Update a GitHub issue (#123). Pass title, body, and/or state (open|closed).",
|
|
25518
25835
|
{
|
|
25519
|
-
id:
|
|
25520
|
-
title:
|
|
25521
|
-
body:
|
|
25522
|
-
state:
|
|
25836
|
+
id: import_zod4.z.string(),
|
|
25837
|
+
title: import_zod4.z.string().optional(),
|
|
25838
|
+
body: import_zod4.z.string().optional(),
|
|
25839
|
+
state: import_zod4.z.string().optional().describe("open or closed"),
|
|
25523
25840
|
repoPath: repoPathSchema
|
|
25524
25841
|
},
|
|
25525
25842
|
async (args) => {
|
|
@@ -25534,9 +25851,9 @@ function registerGithubIssueTools(server) {
|
|
|
25534
25851
|
"github_create_issue",
|
|
25535
25852
|
"Create a GitHub issue. Pass parent (#123) to mark a spin-off in the body.",
|
|
25536
25853
|
{
|
|
25537
|
-
title:
|
|
25538
|
-
body:
|
|
25539
|
-
parent:
|
|
25854
|
+
title: import_zod4.z.string(),
|
|
25855
|
+
body: import_zod4.z.string().optional(),
|
|
25856
|
+
parent: import_zod4.z.string().optional().describe("Parent issue #123 or URL for a spin-off."),
|
|
25540
25857
|
repoPath: repoPathSchema
|
|
25541
25858
|
},
|
|
25542
25859
|
async (args) => {
|
|
@@ -25550,7 +25867,7 @@ function registerGithubIssueTools(server) {
|
|
|
25550
25867
|
}
|
|
25551
25868
|
|
|
25552
25869
|
// src/mcp/linear-tools.ts
|
|
25553
|
-
var
|
|
25870
|
+
var import_zod5 = require("zod");
|
|
25554
25871
|
function text4(payload, isError = false) {
|
|
25555
25872
|
return mcpJson(payload, isError);
|
|
25556
25873
|
}
|
|
@@ -25560,7 +25877,7 @@ function fail4(err) {
|
|
|
25560
25877
|
true
|
|
25561
25878
|
);
|
|
25562
25879
|
}
|
|
25563
|
-
var prioritySchema =
|
|
25880
|
+
var prioritySchema = import_zod5.z.number().int().min(0).max(4).optional().describe("0 none, 1 urgent, 2 high, 3 medium, 4 low");
|
|
25564
25881
|
function registerLinearTools(server) {
|
|
25565
25882
|
server.tool(
|
|
25566
25883
|
"linear_list_teams",
|
|
@@ -25578,9 +25895,9 @@ function registerLinearTools(server) {
|
|
|
25578
25895
|
"linear_search_issues",
|
|
25579
25896
|
"Search or list open Linear issues. Default 40; pass query and/or limit (max 250) when truncated. assignee: me, unassigned, all (default with query), or a user id/name.",
|
|
25580
25897
|
{
|
|
25581
|
-
query:
|
|
25582
|
-
assignee:
|
|
25583
|
-
limit:
|
|
25898
|
+
query: import_zod5.z.string().optional().describe("Search text (identifier, title, description). Omit to list by assignee only."),
|
|
25899
|
+
assignee: import_zod5.z.string().optional().describe("me, unassigned, all, a Linear user id, or a display name. Default: all when query is set, otherwise me."),
|
|
25900
|
+
limit: import_zod5.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
25584
25901
|
},
|
|
25585
25902
|
async ({ query, assignee, limit }) => {
|
|
25586
25903
|
try {
|
|
@@ -25607,11 +25924,11 @@ function registerLinearTools(server) {
|
|
|
25607
25924
|
);
|
|
25608
25925
|
server.tool(
|
|
25609
25926
|
"linear_get_issue",
|
|
25610
|
-
"Get a Linear issue by uuid or identifier (ENG-123): description, comments, relations, parent/children.",
|
|
25611
|
-
{ id:
|
|
25612
|
-
async ({ id }) => {
|
|
25927
|
+
"Get a Linear issue by uuid or identifier (ENG-123): description, comments, relations, parent/children. Default crushes redundant comments and huge pasted bodies (SmartCrusher-style; small unique tickets pass through). Pass include=full for the uncompressed vendor payload.",
|
|
25928
|
+
{ id: import_zod5.z.string(), include: mcpIssueIncludeSchema },
|
|
25929
|
+
async ({ id, include }) => {
|
|
25613
25930
|
try {
|
|
25614
|
-
return text4(await getLinearIssue(id));
|
|
25931
|
+
return text4(formatLinearIssuePayload(await getLinearIssue(id), include));
|
|
25615
25932
|
} catch (err) {
|
|
25616
25933
|
return fail4(err);
|
|
25617
25934
|
}
|
|
@@ -25621,13 +25938,13 @@ function registerLinearTools(server) {
|
|
|
25621
25938
|
"linear_create_issue",
|
|
25622
25939
|
'Create a Linear issue. Call linear_list_teams first. team is id/key/name; state is name/type/id; assignee is "me" or a user id. Pass parent (ENG-123 or uuid) to nest a spin-off under the current ticket.',
|
|
25623
25940
|
{
|
|
25624
|
-
team:
|
|
25625
|
-
title:
|
|
25626
|
-
description:
|
|
25627
|
-
state:
|
|
25628
|
-
assignee:
|
|
25941
|
+
team: import_zod5.z.string(),
|
|
25942
|
+
title: import_zod5.z.string(),
|
|
25943
|
+
description: import_zod5.z.string().optional(),
|
|
25944
|
+
state: import_zod5.z.string().optional(),
|
|
25945
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
25629
25946
|
priority: prioritySchema,
|
|
25630
|
-
parent:
|
|
25947
|
+
parent: import_zod5.z.string().optional().describe("Parent issue uuid or identifier (ENG-123) for a spin-off / sub-issue.")
|
|
25631
25948
|
},
|
|
25632
25949
|
async (args) => {
|
|
25633
25950
|
try {
|
|
@@ -25641,11 +25958,11 @@ function registerLinearTools(server) {
|
|
|
25641
25958
|
"linear_update_issue",
|
|
25642
25959
|
"Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, and/or priority.",
|
|
25643
25960
|
{
|
|
25644
|
-
id:
|
|
25645
|
-
title:
|
|
25646
|
-
description:
|
|
25647
|
-
state:
|
|
25648
|
-
assignee:
|
|
25961
|
+
id: import_zod5.z.string(),
|
|
25962
|
+
title: import_zod5.z.string().optional(),
|
|
25963
|
+
description: import_zod5.z.string().optional(),
|
|
25964
|
+
state: import_zod5.z.string().optional(),
|
|
25965
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
25649
25966
|
priority: prioritySchema
|
|
25650
25967
|
},
|
|
25651
25968
|
async (args) => {
|
|
@@ -25660,8 +25977,8 @@ function registerLinearTools(server) {
|
|
|
25660
25977
|
"linear_comment",
|
|
25661
25978
|
"Add a markdown comment on a Linear issue (uuid or ENG-123).",
|
|
25662
25979
|
{
|
|
25663
|
-
id:
|
|
25664
|
-
body:
|
|
25980
|
+
id: import_zod5.z.string(),
|
|
25981
|
+
body: import_zod5.z.string()
|
|
25665
25982
|
},
|
|
25666
25983
|
async (args) => {
|
|
25667
25984
|
try {
|
|
@@ -25681,7 +25998,7 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
25681
25998
|
}
|
|
25682
25999
|
|
|
25683
26000
|
// src/mcp/viewer-context-tools.ts
|
|
25684
|
-
var
|
|
26001
|
+
var import_zod6 = require("zod");
|
|
25685
26002
|
init_worktree();
|
|
25686
26003
|
init_app_settings();
|
|
25687
26004
|
init_workspaces();
|
|
@@ -25716,7 +26033,7 @@ function registerViewerContextTools(server) {
|
|
|
25716
26033
|
"get_viewer_context",
|
|
25717
26034
|
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from list_workspaces for project context. Worktree turns may omit repoPath (uses cwd).",
|
|
25718
26035
|
{
|
|
25719
|
-
repoPath:
|
|
26036
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
25720
26037
|
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
25721
26038
|
)
|
|
25722
26039
|
},
|
|
@@ -25736,10 +26053,10 @@ function registerViewerContextTools(server) {
|
|
|
25736
26053
|
"update_viewer_context",
|
|
25737
26054
|
"Replace account or project user context (Settings \u2192 Agents / Projects). Never call until the user confirmed. Show the proposed text, ask_user (Save this context / Do not save), wait, then pass confirmed=true. Empty context clears that field.",
|
|
25738
26055
|
{
|
|
25739
|
-
scope:
|
|
25740
|
-
context:
|
|
25741
|
-
confirmed:
|
|
25742
|
-
repoPath:
|
|
26056
|
+
scope: import_zod6.z.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
26057
|
+
context: import_zod6.z.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
26058
|
+
confirmed: import_zod6.z.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
26059
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
25743
26060
|
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
25744
26061
|
)
|
|
25745
26062
|
},
|
|
@@ -25820,14 +26137,11 @@ init_list_prs();
|
|
|
25820
26137
|
init_app_settings();
|
|
25821
26138
|
|
|
25822
26139
|
// src/mcp/schedule-tools.ts
|
|
25823
|
-
var
|
|
26140
|
+
var import_zod7 = require("zod");
|
|
25824
26141
|
init_schedules();
|
|
25825
26142
|
init_schedule_runner();
|
|
25826
26143
|
function text6(payload, isError = false) {
|
|
25827
|
-
return
|
|
25828
|
-
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
25829
|
-
...isError ? { isError: true } : {}
|
|
25830
|
-
};
|
|
26144
|
+
return mcpJson(payload, isError);
|
|
25831
26145
|
}
|
|
25832
26146
|
function fail5(err) {
|
|
25833
26147
|
return text6(
|
|
@@ -25865,17 +26179,17 @@ function registerScheduleTools(server) {
|
|
|
25865
26179
|
"create_schedule",
|
|
25866
26180
|
"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.",
|
|
25867
26181
|
{
|
|
25868
|
-
prompt:
|
|
25869
|
-
name:
|
|
25870
|
-
at:
|
|
25871
|
-
every:
|
|
25872
|
-
cron:
|
|
25873
|
-
tz:
|
|
25874
|
-
threadId:
|
|
26182
|
+
prompt: import_zod7.z.string().describe("User message / goal queued when the schedule fires"),
|
|
26183
|
+
name: import_zod7.z.string().optional(),
|
|
26184
|
+
at: import_zod7.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
26185
|
+
every: import_zod7.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
26186
|
+
cron: import_zod7.z.string().optional().describe("5-field cron expression"),
|
|
26187
|
+
tz: import_zod7.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
26188
|
+
threadId: import_zod7.z.string().optional().describe(
|
|
25875
26189
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
25876
26190
|
),
|
|
25877
|
-
agent:
|
|
25878
|
-
model:
|
|
26191
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
26192
|
+
model: import_zod7.z.string().optional()
|
|
25879
26193
|
},
|
|
25880
26194
|
async (args) => {
|
|
25881
26195
|
try {
|
|
@@ -25908,17 +26222,17 @@ function registerScheduleTools(server) {
|
|
|
25908
26222
|
"update_schedule",
|
|
25909
26223
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
25910
26224
|
{
|
|
25911
|
-
id:
|
|
25912
|
-
prompt:
|
|
25913
|
-
name:
|
|
25914
|
-
at:
|
|
25915
|
-
every:
|
|
25916
|
-
cron:
|
|
25917
|
-
tz:
|
|
25918
|
-
threadId:
|
|
25919
|
-
agent:
|
|
25920
|
-
model:
|
|
25921
|
-
enabled:
|
|
26225
|
+
id: import_zod7.z.string(),
|
|
26226
|
+
prompt: import_zod7.z.string().optional(),
|
|
26227
|
+
name: import_zod7.z.string().optional(),
|
|
26228
|
+
at: import_zod7.z.string().optional(),
|
|
26229
|
+
every: import_zod7.z.string().optional(),
|
|
26230
|
+
cron: import_zod7.z.string().optional(),
|
|
26231
|
+
tz: import_zod7.z.string().optional(),
|
|
26232
|
+
threadId: import_zod7.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
26233
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
26234
|
+
model: import_zod7.z.string().optional(),
|
|
26235
|
+
enabled: import_zod7.z.boolean().optional()
|
|
25922
26236
|
},
|
|
25923
26237
|
async (args) => {
|
|
25924
26238
|
try {
|
|
@@ -25956,7 +26270,7 @@ function registerScheduleTools(server) {
|
|
|
25956
26270
|
server.tool(
|
|
25957
26271
|
"delete_schedule",
|
|
25958
26272
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
25959
|
-
{ id:
|
|
26273
|
+
{ id: import_zod7.z.string() },
|
|
25960
26274
|
async ({ id }) => {
|
|
25961
26275
|
try {
|
|
25962
26276
|
deleteSchedule(id);
|
|
@@ -25969,7 +26283,7 @@ function registerScheduleTools(server) {
|
|
|
25969
26283
|
server.tool(
|
|
25970
26284
|
"run_schedule",
|
|
25971
26285
|
"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.",
|
|
25972
|
-
{ id:
|
|
26286
|
+
{ id: import_zod7.z.string() },
|
|
25973
26287
|
async ({ id }) => {
|
|
25974
26288
|
try {
|
|
25975
26289
|
if (!getSchedule(id)) {
|
|
@@ -26463,13 +26777,13 @@ async function startMcpServer() {
|
|
|
26463
26777
|
"list_board",
|
|
26464
26778
|
"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.",
|
|
26465
26779
|
{
|
|
26466
|
-
query:
|
|
26467
|
-
repoPath:
|
|
26468
|
-
kind:
|
|
26469
|
-
column:
|
|
26780
|
+
query: import_zod8.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
26781
|
+
repoPath: import_zod8.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
26782
|
+
kind: import_zod8.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
26783
|
+
column: import_zod8.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
26470
26784
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
26471
26785
|
),
|
|
26472
|
-
limit:
|
|
26786
|
+
limit: import_zod8.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
26473
26787
|
},
|
|
26474
26788
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
26475
26789
|
const workspaces = orch.listWorkspaces();
|
|
@@ -26484,30 +26798,19 @@ async function startMcpServer() {
|
|
|
26484
26798
|
limit,
|
|
26485
26799
|
workspaceName: (path2) => names.get(path2) ?? ""
|
|
26486
26800
|
});
|
|
26487
|
-
return {
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
hidden: snap.hidden,
|
|
26495
|
-
totals: snap.totals,
|
|
26496
|
-
columnDefs: BOARD_COLUMN_DEFS,
|
|
26497
|
-
hint: HOME_BOARD_AGENT_HINT
|
|
26498
|
-
},
|
|
26499
|
-
null,
|
|
26500
|
-
2
|
|
26501
|
-
)
|
|
26502
|
-
}
|
|
26503
|
-
]
|
|
26504
|
-
};
|
|
26801
|
+
return mcpJson({
|
|
26802
|
+
columns: snap.columns,
|
|
26803
|
+
hidden: snap.hidden,
|
|
26804
|
+
totals: snap.totals,
|
|
26805
|
+
columnDefs: BOARD_COLUMN_DEFS,
|
|
26806
|
+
hint: HOME_BOARD_AGENT_HINT
|
|
26807
|
+
});
|
|
26505
26808
|
}
|
|
26506
26809
|
);
|
|
26507
26810
|
server.tool(
|
|
26508
26811
|
"get_thread",
|
|
26509
26812
|
"Get a compact thread summary by id/ref. Includes last message preview, parentThreadId, and child worktree threads (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (thread billed token + costUsd totals when providers reported cost) and lastTurnUsage.",
|
|
26510
|
-
{ ref:
|
|
26813
|
+
{ ref: import_zod8.z.string() },
|
|
26511
26814
|
async ({ ref }) => {
|
|
26512
26815
|
const t = orch.getThread(ref);
|
|
26513
26816
|
if (!t) {
|
|
@@ -26541,7 +26844,7 @@ async function startMcpServer() {
|
|
|
26541
26844
|
usage: spend.usage,
|
|
26542
26845
|
lastTurnUsage: spend.lastTurnUsage
|
|
26543
26846
|
};
|
|
26544
|
-
return
|
|
26847
|
+
return mcpJson(summary);
|
|
26545
26848
|
}
|
|
26546
26849
|
);
|
|
26547
26850
|
}
|
|
@@ -26549,17 +26852,17 @@ async function startMcpServer() {
|
|
|
26549
26852
|
"present_artifact",
|
|
26550
26853
|
"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.",
|
|
26551
26854
|
{
|
|
26552
|
-
title:
|
|
26553
|
-
type:
|
|
26855
|
+
title: import_zod8.z.string().describe("Short title shown in the artifact pane header"),
|
|
26856
|
+
type: import_zod8.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
26554
26857
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
26555
26858
|
),
|
|
26556
|
-
content:
|
|
26859
|
+
content: import_zod8.z.string().describe(
|
|
26557
26860
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
26558
26861
|
),
|
|
26559
|
-
artifact_id:
|
|
26560
|
-
status:
|
|
26561
|
-
phase:
|
|
26562
|
-
mode:
|
|
26862
|
+
artifact_id: import_zod8.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
26863
|
+
status: import_zod8.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
26864
|
+
phase: import_zod8.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
26865
|
+
mode: import_zod8.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
26563
26866
|
},
|
|
26564
26867
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
26565
26868
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26573,22 +26876,22 @@ async function startMcpServer() {
|
|
|
26573
26876
|
mode: type === "log" ? mode ?? "append" : void 0,
|
|
26574
26877
|
message: type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
26575
26878
|
};
|
|
26576
|
-
return
|
|
26879
|
+
return mcpJson(payload);
|
|
26577
26880
|
}
|
|
26578
26881
|
);
|
|
26579
26882
|
server.tool(
|
|
26580
26883
|
"ask_user",
|
|
26581
26884
|
"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.",
|
|
26582
26885
|
{
|
|
26583
|
-
questions:
|
|
26584
|
-
|
|
26585
|
-
question:
|
|
26586
|
-
header:
|
|
26587
|
-
multiSelect:
|
|
26588
|
-
options:
|
|
26589
|
-
|
|
26590
|
-
label:
|
|
26591
|
-
description:
|
|
26886
|
+
questions: import_zod8.z.array(
|
|
26887
|
+
import_zod8.z.object({
|
|
26888
|
+
question: import_zod8.z.string().describe("Full question text ending with ?"),
|
|
26889
|
+
header: import_zod8.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
26890
|
+
multiSelect: import_zod8.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
26891
|
+
options: import_zod8.z.array(
|
|
26892
|
+
import_zod8.z.object({
|
|
26893
|
+
label: import_zod8.z.string(),
|
|
26894
|
+
description: import_zod8.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
26592
26895
|
})
|
|
26593
26896
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
26594
26897
|
})
|
|
@@ -26600,16 +26903,16 @@ async function startMcpServer() {
|
|
|
26600
26903
|
questions,
|
|
26601
26904
|
message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
|
|
26602
26905
|
};
|
|
26603
|
-
return
|
|
26906
|
+
return mcpJson(payload);
|
|
26604
26907
|
}
|
|
26605
26908
|
);
|
|
26606
26909
|
server.tool(
|
|
26607
26910
|
"present_plan",
|
|
26608
26911
|
"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.",
|
|
26609
26912
|
{
|
|
26610
|
-
title:
|
|
26611
|
-
content:
|
|
26612
|
-
thread_id:
|
|
26913
|
+
title: import_zod8.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
26914
|
+
content: import_zod8.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
26915
|
+
thread_id: import_zod8.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
26613
26916
|
},
|
|
26614
26917
|
async ({ title, content, thread_id }) => {
|
|
26615
26918
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -26625,22 +26928,22 @@ async function startMcpServer() {
|
|
|
26625
26928
|
title: title?.trim() || "Plan",
|
|
26626
26929
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
26627
26930
|
};
|
|
26628
|
-
return
|
|
26931
|
+
return mcpJson(payload);
|
|
26629
26932
|
}
|
|
26630
26933
|
);
|
|
26631
26934
|
server.tool(
|
|
26632
26935
|
"present_schema",
|
|
26633
26936
|
"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.",
|
|
26634
26937
|
{
|
|
26635
|
-
title:
|
|
26636
|
-
mode:
|
|
26637
|
-
datasource:
|
|
26638
|
-
resource_id:
|
|
26639
|
-
record_id:
|
|
26640
|
-
resource:
|
|
26641
|
-
record:
|
|
26642
|
-
records:
|
|
26643
|
-
pane_id:
|
|
26938
|
+
title: import_zod8.z.string().describe("Pane title"),
|
|
26939
|
+
mode: import_zod8.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
26940
|
+
datasource: import_zod8.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
26941
|
+
resource_id: import_zod8.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
26942
|
+
record_id: import_zod8.z.string().optional().describe("Record id when opening form mode"),
|
|
26943
|
+
resource: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
26944
|
+
record: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
26945
|
+
records: import_zod8.z.array(import_zod8.z.record(import_zod8.z.unknown())).optional().describe("Inline records for table mode"),
|
|
26946
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
26644
26947
|
},
|
|
26645
26948
|
async (args) => {
|
|
26646
26949
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26654,17 +26957,17 @@ async function startMcpServer() {
|
|
|
26654
26957
|
record_id: args.record_id,
|
|
26655
26958
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
26656
26959
|
};
|
|
26657
|
-
return
|
|
26960
|
+
return mcpJson(payload);
|
|
26658
26961
|
}
|
|
26659
26962
|
);
|
|
26660
26963
|
server.tool(
|
|
26661
26964
|
"present_files",
|
|
26662
26965
|
"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.",
|
|
26663
26966
|
{
|
|
26664
|
-
title:
|
|
26665
|
-
datasource:
|
|
26666
|
-
path:
|
|
26667
|
-
pane_id:
|
|
26967
|
+
title: import_zod8.z.string().optional().describe("Pane title (default: Files)"),
|
|
26968
|
+
datasource: import_zod8.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
26969
|
+
path: import_zod8.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
26970
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
26668
26971
|
},
|
|
26669
26972
|
async (args) => {
|
|
26670
26973
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26676,21 +26979,21 @@ async function startMcpServer() {
|
|
|
26676
26979
|
path: args.path,
|
|
26677
26980
|
message: "Files pane accepted. Sideboard desktop opens the Files column beside chat."
|
|
26678
26981
|
};
|
|
26679
|
-
return
|
|
26982
|
+
return mcpJson(payload);
|
|
26680
26983
|
}
|
|
26681
26984
|
);
|
|
26682
26985
|
server.tool(
|
|
26683
26986
|
"wait_for_job",
|
|
26684
26987
|
"Wait on a detached long job (tests, pack, deploy) started with detached-job.js. MCP clients kill tools around 60s, so this returns within 45s. stillRunning is the source of truth \u2014 if true, present_artifact type=log with content=delta (same artifact_id) and call wait_for_job again. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
26685
26988
|
{
|
|
26686
|
-
id:
|
|
26687
|
-
timeoutMs:
|
|
26989
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
26990
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
26688
26991
|
},
|
|
26689
26992
|
async ({ id, timeoutMs }) => {
|
|
26690
26993
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
26691
26994
|
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
26692
26995
|
});
|
|
26693
|
-
return
|
|
26996
|
+
return mcpJson(result);
|
|
26694
26997
|
}
|
|
26695
26998
|
);
|
|
26696
26999
|
if (!worktreeProfile) {
|
|
@@ -26705,56 +27008,44 @@ async function startMcpServer() {
|
|
|
26705
27008
|
"set_caffeinate",
|
|
26706
27009
|
"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.",
|
|
26707
27010
|
{
|
|
26708
|
-
enabled:
|
|
27011
|
+
enabled: import_zod8.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
26709
27012
|
},
|
|
26710
27013
|
async ({ enabled }) => {
|
|
26711
27014
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
26712
27015
|
const state = setCaffeinateHold2(enabled, { threadId });
|
|
26713
27016
|
if (enabled && !state.held) {
|
|
26714
|
-
return
|
|
26715
|
-
content: [
|
|
26716
|
-
{
|
|
26717
|
-
type: "text",
|
|
26718
|
-
text: JSON.stringify({
|
|
26719
|
-
...state,
|
|
26720
|
-
ok: false,
|
|
26721
|
-
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
26722
|
-
})
|
|
26723
|
-
}
|
|
26724
|
-
],
|
|
26725
|
-
isError: true
|
|
26726
|
-
};
|
|
26727
|
-
}
|
|
26728
|
-
return {
|
|
26729
|
-
content: [
|
|
27017
|
+
return mcpJson(
|
|
26730
27018
|
{
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
27019
|
+
...state,
|
|
27020
|
+
ok: false,
|
|
27021
|
+
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
27022
|
+
},
|
|
27023
|
+
true
|
|
27024
|
+
);
|
|
27025
|
+
}
|
|
27026
|
+
return mcpJson({
|
|
27027
|
+
...getCaffeinateHold2(),
|
|
27028
|
+
ok: true,
|
|
27029
|
+
message: state.held ? "Mac will stay awake until you call set_caffeinate with enabled=false, the user says they are done, or this orchestration chat is closed." : "Caffeinate hold released. The Mac can sleep (unless Settings caffeinate toggles are on)."
|
|
27030
|
+
});
|
|
26740
27031
|
}
|
|
26741
27032
|
);
|
|
26742
27033
|
server.tool(
|
|
26743
27034
|
"create_thread",
|
|
26744
27035
|
`Create a worktree thread (chat) from branch, pr, or ticket. A ticket, PR, or named branch may have only one live worktree \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. Creating from the default branch still opens a new isolated worktree. Pass repoPath from list_workspaces. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Then use send_to_thread to chat.`,
|
|
26745
27036
|
{
|
|
26746
|
-
sourceType:
|
|
26747
|
-
sourceRef:
|
|
26748
|
-
agent:
|
|
26749
|
-
model:
|
|
27037
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]),
|
|
27038
|
+
sourceRef: import_zod8.z.string(),
|
|
27039
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
27040
|
+
model: import_zod8.z.string().nullable().optional().describe(
|
|
26750
27041
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
26751
27042
|
),
|
|
26752
|
-
repoPath:
|
|
26753
|
-
title:
|
|
26754
|
-
cowboy:
|
|
27043
|
+
repoPath: import_zod8.z.string(),
|
|
27044
|
+
title: import_zod8.z.string().optional(),
|
|
27045
|
+
cowboy: import_zod8.z.boolean().optional().describe(
|
|
26755
27046
|
"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."
|
|
26756
27047
|
),
|
|
26757
|
-
parentThreadId:
|
|
27048
|
+
parentThreadId: import_zod8.z.string().optional().describe(
|
|
26758
27049
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
26759
27050
|
)
|
|
26760
27051
|
},
|
|
@@ -26770,10 +27061,10 @@ async function startMcpServer() {
|
|
|
26770
27061
|
"start_board_card",
|
|
26771
27062
|
"Same as create_thread for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second worktree when one already matches \u2014 returns that thread (alreadyStarted). Then send_to_thread.",
|
|
26772
27063
|
{
|
|
26773
|
-
kind:
|
|
26774
|
-
ref:
|
|
26775
|
-
repoPath:
|
|
26776
|
-
title:
|
|
27064
|
+
kind: import_zod8.z.enum(["ticket", "pr", "branch"]),
|
|
27065
|
+
ref: import_zod8.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
27066
|
+
repoPath: import_zod8.z.string().describe("Workspace path from list_workspaces / list_board"),
|
|
27067
|
+
title: import_zod8.z.string().optional()
|
|
26777
27068
|
},
|
|
26778
27069
|
async ({ kind, ref, repoPath, title }) => {
|
|
26779
27070
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -26790,20 +27081,13 @@ async function startMcpServer() {
|
|
|
26790
27081
|
}))
|
|
26791
27082
|
);
|
|
26792
27083
|
if (existing) {
|
|
26793
|
-
return {
|
|
26794
|
-
|
|
26795
|
-
|
|
26796
|
-
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
title: existing.title,
|
|
26801
|
-
status: existing.status,
|
|
26802
|
-
link: `sideboard://thread/${existing.id}`
|
|
26803
|
-
})
|
|
26804
|
-
}
|
|
26805
|
-
]
|
|
26806
|
-
};
|
|
27084
|
+
return mcpJson({
|
|
27085
|
+
alreadyStarted: true,
|
|
27086
|
+
id: existing.id,
|
|
27087
|
+
title: existing.title,
|
|
27088
|
+
status: existing.status,
|
|
27089
|
+
link: `sideboard://thread/${existing.id}`
|
|
27090
|
+
});
|
|
26807
27091
|
}
|
|
26808
27092
|
const workspaces = orch.listWorkspaces();
|
|
26809
27093
|
const loaded = await getHomeBoardInputs(workspaces);
|
|
@@ -26877,9 +27161,9 @@ async function startMcpServer() {
|
|
|
26877
27161
|
"send_to_thread",
|
|
26878
27162
|
'Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not force_stop to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that stops the child mid-thought. Call wait_for_turn again instead.',
|
|
26879
27163
|
{
|
|
26880
|
-
ref:
|
|
26881
|
-
prompt:
|
|
26882
|
-
force_stop:
|
|
27164
|
+
ref: import_zod8.z.string(),
|
|
27165
|
+
prompt: import_zod8.z.string(),
|
|
27166
|
+
force_stop: import_zod8.z.boolean().optional()
|
|
26883
27167
|
},
|
|
26884
27168
|
async ({ ref, prompt, force_stop }) => {
|
|
26885
27169
|
if (force_stop) {
|
|
@@ -26889,79 +27173,58 @@ async function startMcpServer() {
|
|
|
26889
27173
|
}
|
|
26890
27174
|
}
|
|
26891
27175
|
const thread = await orch.send(ref, prompt);
|
|
26892
|
-
return {
|
|
26893
|
-
|
|
26894
|
-
|
|
26895
|
-
|
|
26896
|
-
|
|
26897
|
-
|
|
26898
|
-
status: thread.status,
|
|
26899
|
-
queueLength: thread.queue.length,
|
|
26900
|
-
forceStopped: Boolean(force_stop)
|
|
26901
|
-
})
|
|
26902
|
-
}
|
|
26903
|
-
]
|
|
26904
|
-
};
|
|
27176
|
+
return mcpJson({
|
|
27177
|
+
id: thread.id,
|
|
27178
|
+
status: thread.status,
|
|
27179
|
+
queueLength: thread.queue.length,
|
|
27180
|
+
forceStopped: Boolean(force_stop)
|
|
27181
|
+
});
|
|
26905
27182
|
}
|
|
26906
27183
|
);
|
|
26907
27184
|
server.tool(
|
|
26908
27185
|
"wait_for_turn",
|
|
26909
27186
|
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
|
|
26910
27187
|
{
|
|
26911
|
-
ref:
|
|
26912
|
-
timeoutMs:
|
|
27188
|
+
ref: import_zod8.z.string(),
|
|
27189
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
26913
27190
|
},
|
|
26914
27191
|
async ({ ref, timeoutMs }) => {
|
|
26915
27192
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
26916
27193
|
resolveIfStillRunning: true
|
|
26917
27194
|
});
|
|
26918
27195
|
const result = orch.getTurnResult(thread.id);
|
|
26919
|
-
return {
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
|
|
26926
|
-
|
|
26927
|
-
|
|
26928
|
-
|
|
26929
|
-
|
|
26930
|
-
lastActivityAt: result.lastActivityAt,
|
|
26931
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
26932
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
26933
|
-
})
|
|
26934
|
-
}
|
|
26935
|
-
]
|
|
26936
|
-
};
|
|
27196
|
+
return mcpJson({
|
|
27197
|
+
id: thread.id,
|
|
27198
|
+
status: result.status,
|
|
27199
|
+
text: result.text,
|
|
27200
|
+
lastError: result.lastError,
|
|
27201
|
+
stillRunning: result.stillRunning,
|
|
27202
|
+
progress: result.progress,
|
|
27203
|
+
lastActivityAt: result.lastActivityAt,
|
|
27204
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
27205
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
27206
|
+
});
|
|
26937
27207
|
}
|
|
26938
27208
|
);
|
|
26939
27209
|
server.tool(
|
|
26940
27210
|
"get_turn_result",
|
|
26941
27211
|
"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).",
|
|
26942
|
-
{ ref:
|
|
27212
|
+
{ ref: import_zod8.z.string() },
|
|
26943
27213
|
async ({ ref }) => {
|
|
26944
27214
|
const result = orch.getTurnResult(ref);
|
|
26945
|
-
return {
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
...result,
|
|
26951
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
26952
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
26953
|
-
})
|
|
26954
|
-
}
|
|
26955
|
-
]
|
|
26956
|
-
};
|
|
27215
|
+
return mcpJson({
|
|
27216
|
+
...result,
|
|
27217
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
27218
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
27219
|
+
});
|
|
26957
27220
|
}
|
|
26958
27221
|
);
|
|
26959
27222
|
server.tool(
|
|
26960
27223
|
"stop_thread",
|
|
26961
27224
|
"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.",
|
|
26962
27225
|
{
|
|
26963
|
-
ref:
|
|
26964
|
-
force:
|
|
27226
|
+
ref: import_zod8.z.string(),
|
|
27227
|
+
force: import_zod8.z.boolean().optional()
|
|
26965
27228
|
},
|
|
26966
27229
|
async ({ ref, force }) => {
|
|
26967
27230
|
const t = orch.getThread(ref);
|
|
@@ -26974,24 +27237,17 @@ async function startMcpServer() {
|
|
|
26974
27237
|
const clearQueue = force !== false;
|
|
26975
27238
|
const hadQueued = t.queue.length > 0;
|
|
26976
27239
|
const stopped = orch.stop(ref, { clearQueue, notifyParent: false });
|
|
26977
|
-
return {
|
|
26978
|
-
|
|
26979
|
-
|
|
26980
|
-
|
|
26981
|
-
|
|
26982
|
-
id: stopped.id,
|
|
26983
|
-
status: stopped.status,
|
|
26984
|
-
clearedQueue: clearQueue && hadQueued
|
|
26985
|
-
})
|
|
26986
|
-
}
|
|
26987
|
-
]
|
|
26988
|
-
};
|
|
27240
|
+
return mcpJson({
|
|
27241
|
+
id: stopped.id,
|
|
27242
|
+
status: stopped.status,
|
|
27243
|
+
clearedQueue: clearQueue && hadQueued
|
|
27244
|
+
});
|
|
26989
27245
|
}
|
|
26990
27246
|
);
|
|
26991
27247
|
server.tool(
|
|
26992
27248
|
"archive_thread",
|
|
26993
27249
|
"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.",
|
|
26994
|
-
{ ref:
|
|
27250
|
+
{ ref: import_zod8.z.string() },
|
|
26995
27251
|
async ({ ref }) => {
|
|
26996
27252
|
const t = orch.getThread(ref);
|
|
26997
27253
|
if (!t) {
|
|
@@ -27008,38 +27264,24 @@ async function startMcpServer() {
|
|
|
27008
27264
|
};
|
|
27009
27265
|
}
|
|
27010
27266
|
const archived = await orch.archive(ref);
|
|
27011
|
-
return {
|
|
27012
|
-
|
|
27013
|
-
|
|
27014
|
-
|
|
27015
|
-
text: JSON.stringify({
|
|
27016
|
-
id: archived.id,
|
|
27017
|
-
status: archived.status
|
|
27018
|
-
})
|
|
27019
|
-
}
|
|
27020
|
-
]
|
|
27021
|
-
};
|
|
27267
|
+
return mcpJson({
|
|
27268
|
+
id: archived.id,
|
|
27269
|
+
status: archived.status
|
|
27270
|
+
});
|
|
27022
27271
|
}
|
|
27023
27272
|
);
|
|
27024
27273
|
server.tool(
|
|
27025
27274
|
"restore_thread",
|
|
27026
27275
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
27027
|
-
{ ref:
|
|
27276
|
+
{ ref: import_zod8.z.string() },
|
|
27028
27277
|
async ({ ref }) => {
|
|
27029
27278
|
try {
|
|
27030
27279
|
const restored = await orch.restore(ref);
|
|
27031
|
-
return {
|
|
27032
|
-
|
|
27033
|
-
|
|
27034
|
-
|
|
27035
|
-
|
|
27036
|
-
id: restored.id,
|
|
27037
|
-
status: restored.status,
|
|
27038
|
-
worktreePath: restored.worktreePath
|
|
27039
|
-
})
|
|
27040
|
-
}
|
|
27041
|
-
]
|
|
27042
|
-
};
|
|
27280
|
+
return mcpJson({
|
|
27281
|
+
id: restored.id,
|
|
27282
|
+
status: restored.status,
|
|
27283
|
+
worktreePath: restored.worktreePath
|
|
27284
|
+
});
|
|
27043
27285
|
} catch (err) {
|
|
27044
27286
|
const message = err instanceof Error ? err.message : String(err);
|
|
27045
27287
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27050,34 +27292,25 @@ async function startMcpServer() {
|
|
|
27050
27292
|
"get_diff",
|
|
27051
27293
|
"Compact diff summary (capped hunks, paginated)",
|
|
27052
27294
|
{
|
|
27053
|
-
ref:
|
|
27054
|
-
maxFiles:
|
|
27295
|
+
ref: import_zod8.z.string(),
|
|
27296
|
+
maxFiles: import_zod8.z.number().optional()
|
|
27055
27297
|
},
|
|
27056
27298
|
async ({ ref, maxFiles }) => {
|
|
27057
27299
|
const summary = await orch.diffSummary(ref);
|
|
27058
|
-
return {
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
text: JSON.stringify({
|
|
27063
|
-
...summary,
|
|
27064
|
-
files: summary.files.slice(0, maxFiles ?? 10)
|
|
27065
|
-
})
|
|
27066
|
-
}
|
|
27067
|
-
]
|
|
27068
|
-
};
|
|
27300
|
+
return mcpJson({
|
|
27301
|
+
...summary,
|
|
27302
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
27303
|
+
});
|
|
27069
27304
|
}
|
|
27070
27305
|
);
|
|
27071
27306
|
server.tool(
|
|
27072
27307
|
"get_pr_checks",
|
|
27073
27308
|
"Snapshot of GitHub PR checks for a worktree thread (`gh pr checks` plus merge/review gates). null = no PR. If the user gave a goal (Greptile 5/5, CI green), the worktree agent watches with `gh pr checks --watch` via /long-running \u2014 this tool is a snapshot, not a waiter. Coordinators: do not run gh from the orchestration cwd.",
|
|
27074
|
-
{ ref:
|
|
27309
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref") },
|
|
27075
27310
|
async ({ ref }) => {
|
|
27076
27311
|
try {
|
|
27077
27312
|
const checks = await orch.getPrChecks(ref);
|
|
27078
|
-
return
|
|
27079
|
-
content: [{ type: "text", text: JSON.stringify(checks) }]
|
|
27080
|
-
};
|
|
27313
|
+
return mcpJson(checks);
|
|
27081
27314
|
} catch (err) {
|
|
27082
27315
|
const message = err instanceof Error ? err.message : String(err);
|
|
27083
27316
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27087,25 +27320,18 @@ async function startMcpServer() {
|
|
|
27087
27320
|
server.tool(
|
|
27088
27321
|
"request_review",
|
|
27089
27322
|
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
|
|
27090
|
-
{ ref:
|
|
27323
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref to review") },
|
|
27091
27324
|
async ({ ref }) => {
|
|
27092
27325
|
try {
|
|
27093
27326
|
const tab = await orch.requestReview(ref);
|
|
27094
27327
|
const from = orch.getThread(ref);
|
|
27095
|
-
return {
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
|
|
27099
|
-
|
|
27100
|
-
|
|
27101
|
-
|
|
27102
|
-
status: tab.status,
|
|
27103
|
-
fromThreadId: from?.id ?? ref,
|
|
27104
|
-
link: `sideboard://thread/${tab.id}`
|
|
27105
|
-
})
|
|
27106
|
-
}
|
|
27107
|
-
]
|
|
27108
|
-
};
|
|
27328
|
+
return mcpJson({
|
|
27329
|
+
id: tab.id,
|
|
27330
|
+
title: tab.title,
|
|
27331
|
+
status: tab.status,
|
|
27332
|
+
fromThreadId: from?.id ?? ref,
|
|
27333
|
+
link: `sideboard://thread/${tab.id}`
|
|
27334
|
+
});
|
|
27109
27335
|
} catch (err) {
|
|
27110
27336
|
const message = err instanceof Error ? err.message : String(err);
|
|
27111
27337
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27116,53 +27342,37 @@ async function startMcpServer() {
|
|
|
27116
27342
|
"ask_git",
|
|
27117
27343
|
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
|
|
27118
27344
|
{
|
|
27119
|
-
ref:
|
|
27120
|
-
action:
|
|
27345
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref"),
|
|
27346
|
+
action: import_zod8.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
27121
27347
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
27122
27348
|
)
|
|
27123
27349
|
},
|
|
27124
27350
|
async ({ ref, action }) => {
|
|
27125
27351
|
try {
|
|
27126
27352
|
const thread = await orch.askGit(ref, action);
|
|
27127
|
-
return {
|
|
27128
|
-
|
|
27129
|
-
|
|
27130
|
-
|
|
27131
|
-
|
|
27132
|
-
|
|
27133
|
-
|
|
27134
|
-
queueLength: thread.queue.length,
|
|
27135
|
-
action,
|
|
27136
|
-
link: `sideboard://thread/${thread.id}`
|
|
27137
|
-
})
|
|
27138
|
-
}
|
|
27139
|
-
]
|
|
27140
|
-
};
|
|
27353
|
+
return mcpJson({
|
|
27354
|
+
id: thread.id,
|
|
27355
|
+
status: thread.status,
|
|
27356
|
+
queueLength: thread.queue.length,
|
|
27357
|
+
action,
|
|
27358
|
+
link: `sideboard://thread/${thread.id}`
|
|
27359
|
+
});
|
|
27141
27360
|
} catch (err) {
|
|
27142
27361
|
const raw = err instanceof Error ? err.message : String(err);
|
|
27143
27362
|
const message = formatGhLandError(raw);
|
|
27144
|
-
return
|
|
27145
|
-
|
|
27146
|
-
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27150
|
-
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_thread so the worktree agent runs gh pr create --draft -R <origin> --body-file <short.md>. Keep the description short." : void 0
|
|
27154
|
-
},
|
|
27155
|
-
null,
|
|
27156
|
-
2
|
|
27157
|
-
)
|
|
27158
|
-
}
|
|
27159
|
-
],
|
|
27160
|
-
isError: true
|
|
27161
|
-
};
|
|
27363
|
+
return mcpJson(
|
|
27364
|
+
{
|
|
27365
|
+
error: true,
|
|
27366
|
+
action,
|
|
27367
|
+
lastError: message,
|
|
27368
|
+
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_thread so the worktree agent runs gh pr create --draft -R <origin> --body-file <short.md>. Keep the description short." : void 0
|
|
27369
|
+
},
|
|
27370
|
+
true
|
|
27371
|
+
);
|
|
27162
27372
|
}
|
|
27163
27373
|
}
|
|
27164
27374
|
);
|
|
27165
|
-
const agentEnum =
|
|
27375
|
+
const agentEnum = import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
27166
27376
|
server.tool(
|
|
27167
27377
|
"list_models",
|
|
27168
27378
|
"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.",
|
|
@@ -27172,9 +27382,7 @@ async function startMcpServer() {
|
|
|
27172
27382
|
async ({ agent }) => {
|
|
27173
27383
|
try {
|
|
27174
27384
|
const catalogs = await listModelsForAgent(agent);
|
|
27175
|
-
return
|
|
27176
|
-
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
27177
|
-
};
|
|
27385
|
+
return mcpJson(catalogs);
|
|
27178
27386
|
} catch (err) {
|
|
27179
27387
|
const message = err instanceof Error ? err.message : String(err);
|
|
27180
27388
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27185,11 +27393,11 @@ async function startMcpServer() {
|
|
|
27185
27393
|
"fork_worktree",
|
|
27186
27394
|
"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.",
|
|
27187
27395
|
{
|
|
27188
|
-
ref:
|
|
27189
|
-
through_index:
|
|
27396
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref to fork"),
|
|
27397
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27190
27398
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27191
|
-
model:
|
|
27192
|
-
title:
|
|
27399
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27400
|
+
title: import_zod8.z.string().optional()
|
|
27193
27401
|
},
|
|
27194
27402
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27195
27403
|
try {
|
|
@@ -27202,24 +27410,17 @@ async function startMcpServer() {
|
|
|
27202
27410
|
model,
|
|
27203
27411
|
title
|
|
27204
27412
|
});
|
|
27205
|
-
return {
|
|
27206
|
-
|
|
27207
|
-
|
|
27208
|
-
|
|
27209
|
-
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
|
|
27216
|
-
worktreePath: thread.worktreePath,
|
|
27217
|
-
fromThreadId: source?.id ?? ref,
|
|
27218
|
-
link: `sideboard://thread/${thread.id}`
|
|
27219
|
-
})
|
|
27220
|
-
}
|
|
27221
|
-
]
|
|
27222
|
-
};
|
|
27413
|
+
return mcpJson({
|
|
27414
|
+
id: thread.id,
|
|
27415
|
+
title: thread.title,
|
|
27416
|
+
status: thread.status,
|
|
27417
|
+
agent: thread.agent,
|
|
27418
|
+
model: thread.model,
|
|
27419
|
+
branchName: thread.branchName,
|
|
27420
|
+
worktreePath: thread.worktreePath,
|
|
27421
|
+
fromThreadId: source?.id ?? ref,
|
|
27422
|
+
link: `sideboard://thread/${thread.id}`
|
|
27423
|
+
});
|
|
27223
27424
|
} catch (err) {
|
|
27224
27425
|
const message = err instanceof Error ? err.message : String(err);
|
|
27225
27426
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27230,11 +27431,11 @@ async function startMcpServer() {
|
|
|
27230
27431
|
"fork_chat",
|
|
27231
27432
|
"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.",
|
|
27232
27433
|
{
|
|
27233
|
-
ref:
|
|
27234
|
-
through_index:
|
|
27434
|
+
ref: import_zod8.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
27435
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27235
27436
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27236
|
-
model:
|
|
27237
|
-
title:
|
|
27437
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27438
|
+
title: import_zod8.z.string().optional()
|
|
27238
27439
|
},
|
|
27239
27440
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27240
27441
|
try {
|
|
@@ -27252,24 +27453,17 @@ async function startMcpServer() {
|
|
|
27252
27453
|
model,
|
|
27253
27454
|
title
|
|
27254
27455
|
});
|
|
27255
|
-
return {
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
|
|
27263
|
-
|
|
27264
|
-
|
|
27265
|
-
|
|
27266
|
-
worktreePath: tab.worktreePath,
|
|
27267
|
-
fromThreadId: source.id,
|
|
27268
|
-
link: `sideboard://thread/${tab.id}`
|
|
27269
|
-
})
|
|
27270
|
-
}
|
|
27271
|
-
]
|
|
27272
|
-
};
|
|
27456
|
+
return mcpJson({
|
|
27457
|
+
id: tab.id,
|
|
27458
|
+
title: tab.title,
|
|
27459
|
+
status: tab.status,
|
|
27460
|
+
agent: tab.agent,
|
|
27461
|
+
model: tab.model,
|
|
27462
|
+
sourceType: tab.sourceType,
|
|
27463
|
+
worktreePath: tab.worktreePath,
|
|
27464
|
+
fromThreadId: source.id,
|
|
27465
|
+
link: `sideboard://thread/${tab.id}`
|
|
27466
|
+
});
|
|
27273
27467
|
} catch (err) {
|
|
27274
27468
|
const message = err instanceof Error ? err.message : String(err);
|
|
27275
27469
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27280,49 +27474,35 @@ async function startMcpServer() {
|
|
|
27280
27474
|
"run_dev_script",
|
|
27281
27475
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
27282
27476
|
{
|
|
27283
|
-
ref:
|
|
27284
|
-
name:
|
|
27477
|
+
ref: import_zod8.z.string(),
|
|
27478
|
+
name: import_zod8.z.string().optional()
|
|
27285
27479
|
},
|
|
27286
27480
|
async ({ ref, name }) => {
|
|
27287
27481
|
const result = await orch.startDev(ref, name);
|
|
27288
|
-
return {
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
scriptName: result.scriptName,
|
|
27295
|
-
ports: result.ports,
|
|
27296
|
-
url: `http://localhost:${result.port}`
|
|
27297
|
-
})
|
|
27298
|
-
}
|
|
27299
|
-
]
|
|
27300
|
-
};
|
|
27482
|
+
return mcpJson({
|
|
27483
|
+
port: result.port,
|
|
27484
|
+
scriptName: result.scriptName,
|
|
27485
|
+
ports: result.ports,
|
|
27486
|
+
url: `http://localhost:${result.port}`
|
|
27487
|
+
});
|
|
27301
27488
|
}
|
|
27302
27489
|
);
|
|
27303
27490
|
server.tool(
|
|
27304
27491
|
"list_run_scripts",
|
|
27305
27492
|
"List named run scripts available for a thread",
|
|
27306
|
-
{ ref:
|
|
27493
|
+
{ ref: import_zod8.z.string() },
|
|
27307
27494
|
async ({ ref }) => {
|
|
27308
27495
|
const scripts = orch.listThreadRunScripts(ref);
|
|
27309
27496
|
const active = orch.getActiveRuns(ref);
|
|
27310
|
-
return {
|
|
27311
|
-
content: [
|
|
27312
|
-
{
|
|
27313
|
-
type: "text",
|
|
27314
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
27315
|
-
}
|
|
27316
|
-
]
|
|
27317
|
-
};
|
|
27497
|
+
return mcpJson({ scripts, active });
|
|
27318
27498
|
}
|
|
27319
27499
|
);
|
|
27320
27500
|
server.tool(
|
|
27321
27501
|
"stop_dev_script",
|
|
27322
27502
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
27323
27503
|
{
|
|
27324
|
-
ref:
|
|
27325
|
-
name:
|
|
27504
|
+
ref: import_zod8.z.string(),
|
|
27505
|
+
name: import_zod8.z.string().optional()
|
|
27326
27506
|
},
|
|
27327
27507
|
async ({ ref, name }) => {
|
|
27328
27508
|
orch.stopDev(ref, name);
|
|
@@ -27332,27 +27512,25 @@ async function startMcpServer() {
|
|
|
27332
27512
|
server.tool(
|
|
27333
27513
|
"run_setup",
|
|
27334
27514
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
27335
|
-
{ ref:
|
|
27515
|
+
{ ref: import_zod8.z.string() },
|
|
27336
27516
|
async ({ ref }) => {
|
|
27337
27517
|
const result = await orch.runSetup(ref);
|
|
27338
|
-
return
|
|
27339
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
27340
|
-
};
|
|
27518
|
+
return mcpJson(result);
|
|
27341
27519
|
}
|
|
27342
27520
|
);
|
|
27343
27521
|
server.tool(
|
|
27344
27522
|
"add_workspace",
|
|
27345
27523
|
"Register a git repo as a Sideboard workspace",
|
|
27346
|
-
{ repoPath:
|
|
27524
|
+
{ repoPath: import_zod8.z.string() },
|
|
27347
27525
|
async ({ repoPath }) => {
|
|
27348
27526
|
const ws = await orch.addWorkspace(repoPath);
|
|
27349
|
-
return
|
|
27527
|
+
return mcpJson(ws);
|
|
27350
27528
|
}
|
|
27351
27529
|
);
|
|
27352
27530
|
server.tool(
|
|
27353
27531
|
"remove_workspace",
|
|
27354
27532
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
27355
|
-
{ repoPath:
|
|
27533
|
+
{ repoPath: import_zod8.z.string() },
|
|
27356
27534
|
async ({ repoPath }) => {
|
|
27357
27535
|
orch.removeWorkspace(repoPath);
|
|
27358
27536
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -27362,40 +27540,31 @@ async function startMcpServer() {
|
|
|
27362
27540
|
"fanout",
|
|
27363
27541
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
27364
27542
|
{
|
|
27365
|
-
prompt:
|
|
27366
|
-
agents:
|
|
27367
|
-
repoPath:
|
|
27368
|
-
sourceType:
|
|
27369
|
-
sourceRef:
|
|
27370
|
-
title:
|
|
27543
|
+
prompt: import_zod8.z.string(),
|
|
27544
|
+
agents: import_zod8.z.array(import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
27545
|
+
repoPath: import_zod8.z.string(),
|
|
27546
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
27547
|
+
sourceRef: import_zod8.z.string().optional(),
|
|
27548
|
+
title: import_zod8.z.string().optional()
|
|
27371
27549
|
},
|
|
27372
27550
|
async (args) => {
|
|
27373
27551
|
const threads = await orch.bestOfN(args);
|
|
27374
|
-
return
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
branchName: t.branchName,
|
|
27383
|
-
worktreePath: t.worktreePath
|
|
27384
|
-
})),
|
|
27385
|
-
null,
|
|
27386
|
-
2
|
|
27387
|
-
)
|
|
27388
|
-
}
|
|
27389
|
-
]
|
|
27390
|
-
};
|
|
27552
|
+
return mcpJson(
|
|
27553
|
+
threads.map((t) => ({
|
|
27554
|
+
id: t.id,
|
|
27555
|
+
agent: t.agent,
|
|
27556
|
+
branchName: t.branchName,
|
|
27557
|
+
worktreePath: t.worktreePath
|
|
27558
|
+
}))
|
|
27559
|
+
);
|
|
27391
27560
|
}
|
|
27392
27561
|
);
|
|
27393
27562
|
server.tool(
|
|
27394
27563
|
"list_branches",
|
|
27395
27564
|
"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).",
|
|
27396
27565
|
{
|
|
27397
|
-
repoPath:
|
|
27398
|
-
unmergedOnly:
|
|
27566
|
+
repoPath: import_zod8.z.string(),
|
|
27567
|
+
unmergedOnly: import_zod8.z.boolean().optional()
|
|
27399
27568
|
},
|
|
27400
27569
|
async ({ repoPath, unmergedOnly }) => {
|
|
27401
27570
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27416,19 +27585,19 @@ async function startMcpServer() {
|
|
|
27416
27585
|
"list_prs",
|
|
27417
27586
|
'List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_workspaces. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled eng-review with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_thread with sourceType=pr.',
|
|
27418
27587
|
{
|
|
27419
|
-
repoPath:
|
|
27420
|
-
query:
|
|
27421
|
-
queue:
|
|
27588
|
+
repoPath: import_zod8.z.string(),
|
|
27589
|
+
query: import_zod8.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
27590
|
+
queue: import_zod8.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
27422
27591
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
27423
27592
|
),
|
|
27424
|
-
state:
|
|
27425
|
-
label:
|
|
27593
|
+
state: import_zod8.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
27594
|
+
label: import_zod8.z.string().optional().describe(
|
|
27426
27595
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
27427
27596
|
),
|
|
27428
|
-
reviewer:
|
|
27597
|
+
reviewer: import_zod8.z.string().optional().describe(
|
|
27429
27598
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
27430
27599
|
),
|
|
27431
|
-
limit:
|
|
27600
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27432
27601
|
},
|
|
27433
27602
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
27434
27603
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27460,89 +27629,65 @@ async function startMcpServer() {
|
|
|
27460
27629
|
server.tool(
|
|
27461
27630
|
"get_pr_stack",
|
|
27462
27631
|
"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).",
|
|
27463
|
-
{ ref:
|
|
27632
|
+
{ ref: import_zod8.z.string() },
|
|
27464
27633
|
async ({ ref }) => {
|
|
27465
27634
|
const stack = await orch.getPrStack(ref);
|
|
27466
|
-
return
|
|
27467
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
27468
|
-
};
|
|
27635
|
+
return mcpJson(stack);
|
|
27469
27636
|
}
|
|
27470
27637
|
);
|
|
27471
27638
|
server.tool(
|
|
27472
27639
|
"open_pr_stack_layers",
|
|
27473
27640
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
27474
27641
|
{
|
|
27475
|
-
ref:
|
|
27476
|
-
layer:
|
|
27642
|
+
ref: import_zod8.z.string(),
|
|
27643
|
+
layer: import_zod8.z.number().int().positive().optional()
|
|
27477
27644
|
},
|
|
27478
27645
|
async ({ ref, layer }) => {
|
|
27479
27646
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
27480
|
-
return {
|
|
27481
|
-
|
|
27482
|
-
|
|
27483
|
-
|
|
27484
|
-
|
|
27485
|
-
|
|
27486
|
-
|
|
27487
|
-
|
|
27488
|
-
|
|
27489
|
-
|
|
27490
|
-
|
|
27491
|
-
|
|
27492
|
-
|
|
27493
|
-
worktreePath: t.worktreePath,
|
|
27494
|
-
prUrl: t.prUrl,
|
|
27495
|
-
link: `sideboard://thread/${t.id}`
|
|
27496
|
-
}))
|
|
27497
|
-
},
|
|
27498
|
-
null,
|
|
27499
|
-
2
|
|
27500
|
-
)
|
|
27501
|
-
}
|
|
27502
|
-
]
|
|
27503
|
-
};
|
|
27647
|
+
return mcpJson({
|
|
27648
|
+
stackNumber: result.stack.stackNumber,
|
|
27649
|
+
trunk: result.stack.trunk,
|
|
27650
|
+
threads: result.threads.map((t) => ({
|
|
27651
|
+
id: t.id,
|
|
27652
|
+
title: t.title,
|
|
27653
|
+
branchName: t.branchName,
|
|
27654
|
+
stackLayer: t.stackLayer,
|
|
27655
|
+
worktreePath: t.worktreePath,
|
|
27656
|
+
prUrl: t.prUrl,
|
|
27657
|
+
link: `sideboard://thread/${t.id}`
|
|
27658
|
+
}))
|
|
27659
|
+
});
|
|
27504
27660
|
}
|
|
27505
27661
|
);
|
|
27506
27662
|
server.tool(
|
|
27507
27663
|
"add_stack_layer",
|
|
27508
27664
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
27509
27665
|
{
|
|
27510
|
-
ref:
|
|
27511
|
-
branchName:
|
|
27512
|
-
title:
|
|
27666
|
+
ref: import_zod8.z.string(),
|
|
27667
|
+
branchName: import_zod8.z.string(),
|
|
27668
|
+
title: import_zod8.z.string().optional()
|
|
27513
27669
|
},
|
|
27514
27670
|
async ({ ref, branchName, title }) => {
|
|
27515
27671
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
27516
|
-
return {
|
|
27517
|
-
|
|
27518
|
-
|
|
27519
|
-
|
|
27520
|
-
|
|
27521
|
-
|
|
27522
|
-
|
|
27523
|
-
|
|
27524
|
-
branchName: result.thread.branchName,
|
|
27525
|
-
stackLayer: result.thread.stackLayer,
|
|
27526
|
-
worktreePath: result.thread.worktreePath,
|
|
27527
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
27528
|
-
},
|
|
27529
|
-
null,
|
|
27530
|
-
2
|
|
27531
|
-
)
|
|
27532
|
-
}
|
|
27533
|
-
]
|
|
27534
|
-
};
|
|
27672
|
+
return mcpJson({
|
|
27673
|
+
id: result.thread.id,
|
|
27674
|
+
title: result.thread.title,
|
|
27675
|
+
branchName: result.thread.branchName,
|
|
27676
|
+
stackLayer: result.thread.stackLayer,
|
|
27677
|
+
worktreePath: result.thread.worktreePath,
|
|
27678
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
27679
|
+
});
|
|
27535
27680
|
}
|
|
27536
27681
|
);
|
|
27537
27682
|
server.tool(
|
|
27538
27683
|
"create_pr_stack",
|
|
27539
27684
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
27540
27685
|
{
|
|
27541
|
-
repoPath:
|
|
27542
|
-
branches:
|
|
27543
|
-
agent:
|
|
27544
|
-
base:
|
|
27545
|
-
title:
|
|
27686
|
+
repoPath: import_zod8.z.string(),
|
|
27687
|
+
branches: import_zod8.z.array(import_zod8.z.string()).min(1),
|
|
27688
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
27689
|
+
base: import_zod8.z.string().optional(),
|
|
27690
|
+
title: import_zod8.z.string().optional()
|
|
27546
27691
|
},
|
|
27547
27692
|
async (args) => {
|
|
27548
27693
|
const result = await orch.createPrStack({
|
|
@@ -27552,39 +27697,28 @@ async function startMcpServer() {
|
|
|
27552
27697
|
base: args.base,
|
|
27553
27698
|
title: args.title
|
|
27554
27699
|
});
|
|
27555
|
-
return {
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
stackLayer: t.stackLayer,
|
|
27568
|
-
worktreePath: t.worktreePath,
|
|
27569
|
-
link: `sideboard://thread/${t.id}`
|
|
27570
|
-
}))
|
|
27571
|
-
},
|
|
27572
|
-
null,
|
|
27573
|
-
2
|
|
27574
|
-
)
|
|
27575
|
-
}
|
|
27576
|
-
]
|
|
27577
|
-
};
|
|
27700
|
+
return mcpJson({
|
|
27701
|
+
stackNumber: result.stack.stackNumber,
|
|
27702
|
+
trunk: result.stack.trunk,
|
|
27703
|
+
threads: result.threads.map((t) => ({
|
|
27704
|
+
id: t.id,
|
|
27705
|
+
title: t.title,
|
|
27706
|
+
branchName: t.branchName,
|
|
27707
|
+
stackLayer: t.stackLayer,
|
|
27708
|
+
worktreePath: t.worktreePath,
|
|
27709
|
+
link: `sideboard://thread/${t.id}`
|
|
27710
|
+
}))
|
|
27711
|
+
});
|
|
27578
27712
|
}
|
|
27579
27713
|
);
|
|
27580
27714
|
server.tool(
|
|
27581
27715
|
"list_issues",
|
|
27582
27716
|
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Then create_thread with sourceType=ticket.",
|
|
27583
27717
|
{
|
|
27584
|
-
repoPath:
|
|
27585
|
-
query:
|
|
27586
|
-
assignee:
|
|
27587
|
-
limit:
|
|
27718
|
+
repoPath: import_zod8.z.string(),
|
|
27719
|
+
query: import_zod8.z.string().optional().describe("Search title, identifier, or description"),
|
|
27720
|
+
assignee: import_zod8.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
27721
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27588
27722
|
},
|
|
27589
27723
|
async ({ repoPath, query, assignee, limit }) => {
|
|
27590
27724
|
const root = await resolveRepoRoot(repoPath);
|