@sideboard-ai/core 0.1.160 → 0.1.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-6UOXBORU.js → agents-JBASUM7Y.js} +1 -1
- package/dist/{agents-RD774UEV.js → agents-KAWRVTRR.js} +1 -1
- package/dist/{chunk-BQ4PEXIF.js → chunk-MWQOHQX7.js} +32 -11
- package/dist/{chunk-RQNXLIXO.js → chunk-QPJJ7H7C.js} +12 -0
- package/dist/{chunk-QYKADGPP.js → chunk-SAVAC53C.js} +29 -10
- package/dist/index.cjs +790 -611
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +739 -602
- package/dist/mcp/run-stdio.cjs +772 -611
- package/dist/mcp/run-stdio.js +733 -602
- package/dist/{orchestrator-BSVSOTBK.js → orchestrator-6RDRATVE.js} +2 -2
- package/dist/{orchestrator-KVKPIUVU.js → orchestrator-AXE7GCJA.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-7OWRFJK6.js → chunk-GGOU4JJU.js} +0 -0
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -14416,6 +14416,7 @@ var GLOBAL_WORKSPACE_ID2, BOARD_COLUMN_DEFS, HOME_BOARD_CACHE_TTL_MS, DEFAULT_WO
|
|
|
14416
14416
|
var init_home_board = __esm({
|
|
14417
14417
|
"src/board/home-board.ts"() {
|
|
14418
14418
|
"use strict";
|
|
14419
|
+
init_message_parts();
|
|
14419
14420
|
init_worktree_labels();
|
|
14420
14421
|
GLOBAL_WORKSPACE_ID2 = "__global__";
|
|
14421
14422
|
BOARD_COLUMN_DEFS = [
|
|
@@ -15207,6 +15208,23 @@ var init_create = __esm({
|
|
|
15207
15208
|
}
|
|
15208
15209
|
});
|
|
15209
15210
|
|
|
15211
|
+
// src/threads/implied-first-prompt.ts
|
|
15212
|
+
function resolveCreateFirstPrompt(input) {
|
|
15213
|
+
const typed = input.prompt?.trim();
|
|
15214
|
+
if (typed) return typed;
|
|
15215
|
+
if (input.sourceType !== "ticket") return void 0;
|
|
15216
|
+
if (input.parentThreadId) return void 0;
|
|
15217
|
+
if (input.reused && input.hasUserMessages) return void 0;
|
|
15218
|
+
return IMPLIED_TICKET_RESOLVE_PROMPT;
|
|
15219
|
+
}
|
|
15220
|
+
var IMPLIED_TICKET_RESOLVE_PROMPT;
|
|
15221
|
+
var init_implied_first_prompt = __esm({
|
|
15222
|
+
"src/threads/implied-first-prompt.ts"() {
|
|
15223
|
+
"use strict";
|
|
15224
|
+
IMPLIED_TICKET_RESOLVE_PROMPT = "Resolve this issue.";
|
|
15225
|
+
}
|
|
15226
|
+
});
|
|
15227
|
+
|
|
15210
15228
|
// src/threads/cowboy.ts
|
|
15211
15229
|
function isCowboyThread(thread) {
|
|
15212
15230
|
return Boolean(thread?.cowboy);
|
|
@@ -16382,13 +16400,13 @@ function formatStat(files) {
|
|
|
16382
16400
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
16383
16401
|
}
|
|
16384
16402
|
function emptyScopeStats() {
|
|
16385
|
-
const
|
|
16403
|
+
const z9 = emptyStat();
|
|
16386
16404
|
return {
|
|
16387
|
-
commits:
|
|
16388
|
-
uncommitted:
|
|
16389
|
-
staged:
|
|
16390
|
-
unstaged:
|
|
16391
|
-
last_turn:
|
|
16405
|
+
commits: z9,
|
|
16406
|
+
uncommitted: z9,
|
|
16407
|
+
staged: z9,
|
|
16408
|
+
unstaged: z9,
|
|
16409
|
+
last_turn: z9
|
|
16392
16410
|
};
|
|
16393
16411
|
}
|
|
16394
16412
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -17525,17 +17543,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
17525
17543
|
];
|
|
17526
17544
|
if (opts.linear) {
|
|
17527
17545
|
lines.push(
|
|
17528
|
-
"- 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."
|
|
17546
|
+
"- 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."
|
|
17529
17547
|
);
|
|
17530
17548
|
}
|
|
17531
17549
|
if (github) {
|
|
17532
17550
|
lines.push(
|
|
17533
|
-
"- GitHub: `github_get_issue` (comments), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
17551
|
+
"- 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`."
|
|
17534
17552
|
);
|
|
17535
17553
|
}
|
|
17536
17554
|
if (opts.abletime) {
|
|
17537
17555
|
lines.push(
|
|
17538
|
-
"- AbleTime: `abletime_get_task` (comments), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
17556
|
+
"- 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)."
|
|
17539
17557
|
);
|
|
17540
17558
|
}
|
|
17541
17559
|
lines.push(
|
|
@@ -18655,6 +18673,7 @@ var init_orchestrator = __esm({
|
|
|
18655
18673
|
init_child_halt();
|
|
18656
18674
|
init_wait_for_job();
|
|
18657
18675
|
init_create();
|
|
18676
|
+
init_implied_first_prompt();
|
|
18658
18677
|
init_cowboy();
|
|
18659
18678
|
init_orchestrator_capable();
|
|
18660
18679
|
init_chat_tabs();
|
|
@@ -19061,10 +19080,18 @@ var init_orchestrator = __esm({
|
|
|
19061
19080
|
listThreads({ includeArchived: false }).map((t) => t.id)
|
|
19062
19081
|
);
|
|
19063
19082
|
const thread = await createThread(input);
|
|
19064
|
-
|
|
19065
|
-
|
|
19083
|
+
const reused = prior.has(thread.id);
|
|
19084
|
+
const prompt = resolveCreateFirstPrompt({
|
|
19085
|
+
sourceType: input.sourceType,
|
|
19086
|
+
prompt: input.prompt,
|
|
19087
|
+
parentThreadId: input.parentThreadId ?? thread.parentThreadId,
|
|
19088
|
+
reused,
|
|
19089
|
+
hasUserMessages: thread.messages.some((m) => m.role === "user")
|
|
19090
|
+
});
|
|
19091
|
+
if (reused) {
|
|
19092
|
+
if (prompt) {
|
|
19066
19093
|
try {
|
|
19067
|
-
await this.send(thread.id,
|
|
19094
|
+
await this.send(thread.id, prompt);
|
|
19068
19095
|
} catch (err) {
|
|
19069
19096
|
const message = err instanceof Error ? err.message : String(err);
|
|
19070
19097
|
updateThread(thread.id, {
|
|
@@ -19075,7 +19102,7 @@ var init_orchestrator = __esm({
|
|
|
19075
19102
|
return thread;
|
|
19076
19103
|
}
|
|
19077
19104
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
19078
|
-
void this.finishCreateThread(thread.id,
|
|
19105
|
+
void this.finishCreateThread(thread.id, prompt);
|
|
19079
19106
|
return thread;
|
|
19080
19107
|
}
|
|
19081
19108
|
async finishCreateThread(threadId, prompt) {
|
|
@@ -20763,7 +20790,7 @@ init_nested_electron_env();
|
|
|
20763
20790
|
// src/mcp/server.ts
|
|
20764
20791
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
20765
20792
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
20766
|
-
var
|
|
20793
|
+
var import_zod8 = require("zod");
|
|
20767
20794
|
var import_node_crypto12 = require("crypto");
|
|
20768
20795
|
var import_node_path50 = require("path");
|
|
20769
20796
|
init_orchestrator();
|
|
@@ -21876,12 +21903,53 @@ ${link}` : link;
|
|
|
21876
21903
|
// src/mcp/slack-tools.ts
|
|
21877
21904
|
init_outbound_watch();
|
|
21878
21905
|
init_workspaces();
|
|
21879
|
-
|
|
21906
|
+
|
|
21907
|
+
// src/mcp/issue-list.ts
|
|
21908
|
+
var MCP_ISSUE_LIST_DEFAULT_LIMIT = 40;
|
|
21909
|
+
var MCP_ISSUE_LIST_MAX_LIMIT = 250;
|
|
21910
|
+
function clampMcpIssueLimit(limit) {
|
|
21911
|
+
if (limit == null || !Number.isFinite(limit)) return MCP_ISSUE_LIST_DEFAULT_LIMIT;
|
|
21912
|
+
return Math.max(1, Math.min(MCP_ISSUE_LIST_MAX_LIMIT, Math.floor(limit)));
|
|
21913
|
+
}
|
|
21914
|
+
function applyIssueListWindow(items, limit) {
|
|
21915
|
+
const truncated = items.length > limit;
|
|
21916
|
+
return {
|
|
21917
|
+
items: truncated ? items.slice(0, limit) : items,
|
|
21918
|
+
truncated
|
|
21919
|
+
};
|
|
21920
|
+
}
|
|
21921
|
+
function compactIssueRow(issue) {
|
|
21922
|
+
return {
|
|
21923
|
+
identifier: issue.identifier,
|
|
21924
|
+
title: issue.title,
|
|
21925
|
+
...issue.assignee ? { assignee: issue.assignee } : {},
|
|
21926
|
+
...issue.labels.length > 0 ? { labels: issue.labels } : {},
|
|
21927
|
+
...issue.teamKey ? { team: issue.teamKey } : {},
|
|
21928
|
+
...issue.cycle?.name ? { cycle: issue.cycle.name } : {}
|
|
21929
|
+
};
|
|
21930
|
+
}
|
|
21931
|
+
function formatMcpIssueList(input) {
|
|
21880
21932
|
return {
|
|
21881
|
-
|
|
21933
|
+
...input.source ? { source: input.source } : {},
|
|
21934
|
+
...input.viewer ? { viewer: input.viewer } : {},
|
|
21935
|
+
limit: input.limit,
|
|
21936
|
+
returned: input.issues.length,
|
|
21937
|
+
truncated: input.truncated,
|
|
21938
|
+
...input.truncated ? { hint: "More issues match. Pass a higher limit (max 250) or a tighter query." } : {},
|
|
21939
|
+
issues: input.issues.map(compactIssueRow)
|
|
21940
|
+
};
|
|
21941
|
+
}
|
|
21942
|
+
function mcpJson(payload, isError = false) {
|
|
21943
|
+
return {
|
|
21944
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
21882
21945
|
...isError ? { isError: true } : {}
|
|
21883
21946
|
};
|
|
21884
21947
|
}
|
|
21948
|
+
|
|
21949
|
+
// src/mcp/slack-tools.ts
|
|
21950
|
+
function text(payload, isError = false) {
|
|
21951
|
+
return mcpJson(payload, isError);
|
|
21952
|
+
}
|
|
21885
21953
|
function fail(err) {
|
|
21886
21954
|
return text(
|
|
21887
21955
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
@@ -22150,49 +22218,331 @@ function registerSlackTools(server) {
|
|
|
22150
22218
|
init_app_settings();
|
|
22151
22219
|
|
|
22152
22220
|
// src/mcp/abletime-tools.ts
|
|
22221
|
+
var import_zod3 = require("zod");
|
|
22222
|
+
init_abletime();
|
|
22223
|
+
|
|
22224
|
+
// src/mcp/issue-payload.ts
|
|
22153
22225
|
var import_zod2 = require("zod");
|
|
22154
22226
|
init_abletime();
|
|
22155
22227
|
|
|
22156
|
-
// src/mcp/
|
|
22157
|
-
var
|
|
22158
|
-
var
|
|
22159
|
-
|
|
22160
|
-
|
|
22161
|
-
|
|
22228
|
+
// src/mcp/smart-crush.ts
|
|
22229
|
+
var MUST_KEEP_TEXT_RE = /\b(error|exception|failed|failure|fatal|critical|panic|traceback|npm err|assert)\b/i;
|
|
22230
|
+
var CRUSH_MIN_ITEMS = 5;
|
|
22231
|
+
var CRUSH_MIN_CHARS = 800;
|
|
22232
|
+
var CRUSH_COMMENT_MAX_CHARS = 16e3;
|
|
22233
|
+
var CRUSH_BODY_MIN_CHARS = 12e3;
|
|
22234
|
+
var CRUSH_BODY_MAX_CHARS = 8e3;
|
|
22235
|
+
var NEAR_DUP_JACCARD = 0.92;
|
|
22236
|
+
function looksLikeMustKeep(text7) {
|
|
22237
|
+
return MUST_KEEP_TEXT_RE.test(text7);
|
|
22238
|
+
}
|
|
22239
|
+
function normalizeText(text7) {
|
|
22240
|
+
return text7.replace(/\s+/g, " ").trim().toLowerCase();
|
|
22241
|
+
}
|
|
22242
|
+
function wordSet(text7) {
|
|
22243
|
+
return new Set(
|
|
22244
|
+
text7.toLowerCase().split(/\W+/).filter((w) => w.length > 2)
|
|
22245
|
+
);
|
|
22162
22246
|
}
|
|
22163
|
-
function
|
|
22164
|
-
|
|
22247
|
+
function jaccard(a, b) {
|
|
22248
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
22249
|
+
let inter = 0;
|
|
22250
|
+
for (const w of a) {
|
|
22251
|
+
if (b.has(w)) inter += 1;
|
|
22252
|
+
}
|
|
22253
|
+
const union = a.size + b.size - inter;
|
|
22254
|
+
return union === 0 ? 0 : inter / union;
|
|
22255
|
+
}
|
|
22256
|
+
function importance(text7) {
|
|
22257
|
+
let score = text7.length;
|
|
22258
|
+
if (/```/.test(text7)) score += 400;
|
|
22259
|
+
if (/https?:\/\//.test(text7)) score += 50;
|
|
22260
|
+
if (looksLikeMustKeep(text7)) score += 1e4;
|
|
22261
|
+
return score;
|
|
22262
|
+
}
|
|
22263
|
+
function meanStd(values) {
|
|
22264
|
+
if (values.length === 0) return { mean: 0, std: 0 };
|
|
22265
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
22266
|
+
const variance = values.reduce((a, b) => a + (b - mean) * (b - mean), 0) / values.length;
|
|
22267
|
+
return { mean, std: Math.sqrt(variance) };
|
|
22268
|
+
}
|
|
22269
|
+
function dedupTextItems(items, getText) {
|
|
22270
|
+
const kept = [];
|
|
22271
|
+
const keptNorm = [];
|
|
22272
|
+
const keptWords = [];
|
|
22273
|
+
for (const item of items) {
|
|
22274
|
+
const text7 = getText(item);
|
|
22275
|
+
const norm = normalizeText(text7);
|
|
22276
|
+
if (!norm) {
|
|
22277
|
+
kept.push(item);
|
|
22278
|
+
keptNorm.push(norm);
|
|
22279
|
+
keptWords.push(wordSet(text7));
|
|
22280
|
+
continue;
|
|
22281
|
+
}
|
|
22282
|
+
const words = wordSet(text7);
|
|
22283
|
+
let dupAt = -1;
|
|
22284
|
+
for (let i = 0; i < kept.length; i++) {
|
|
22285
|
+
if (keptNorm[i] === norm) {
|
|
22286
|
+
dupAt = i;
|
|
22287
|
+
break;
|
|
22288
|
+
}
|
|
22289
|
+
if (keptWords[i].size >= 8 && words.size >= 8 && jaccard(keptWords[i], words) >= NEAR_DUP_JACCARD) {
|
|
22290
|
+
dupAt = i;
|
|
22291
|
+
break;
|
|
22292
|
+
}
|
|
22293
|
+
}
|
|
22294
|
+
if (dupAt < 0) {
|
|
22295
|
+
kept.push(item);
|
|
22296
|
+
keptNorm.push(norm);
|
|
22297
|
+
keptWords.push(words);
|
|
22298
|
+
continue;
|
|
22299
|
+
}
|
|
22300
|
+
if (text7.length > getText(kept[dupAt]).length) {
|
|
22301
|
+
kept[dupAt] = item;
|
|
22302
|
+
keptNorm[dupAt] = norm;
|
|
22303
|
+
keptWords[dupAt] = words;
|
|
22304
|
+
}
|
|
22305
|
+
}
|
|
22306
|
+
return { items: kept, dropped: items.length - kept.length };
|
|
22307
|
+
}
|
|
22308
|
+
function crushTextItems(items, getText, opts = {}) {
|
|
22309
|
+
const minItems = opts.minItems ?? CRUSH_MIN_ITEMS;
|
|
22310
|
+
const minChars = opts.minChars ?? CRUSH_MIN_CHARS;
|
|
22311
|
+
const maxChars = opts.maxChars ?? CRUSH_COMMENT_MAX_CHARS;
|
|
22312
|
+
const original = items;
|
|
22313
|
+
const totalChars = items.reduce((n, item) => n + getText(item).length, 0);
|
|
22314
|
+
if (items.length < minItems || totalChars < minChars) {
|
|
22315
|
+
return {
|
|
22316
|
+
items,
|
|
22317
|
+
truncated: false,
|
|
22318
|
+
kept: items.length,
|
|
22319
|
+
dropped: 0,
|
|
22320
|
+
reason: "passthrough"
|
|
22321
|
+
};
|
|
22322
|
+
}
|
|
22323
|
+
const deduped = dedupTextItems(items, getText);
|
|
22324
|
+
const unique = deduped.items;
|
|
22325
|
+
const uniqueChars = unique.reduce((n, item) => n + getText(item).length, 0);
|
|
22326
|
+
if (uniqueChars <= maxChars) {
|
|
22327
|
+
const changed = unique.length < original.length;
|
|
22328
|
+
return {
|
|
22329
|
+
items: unique,
|
|
22330
|
+
truncated: changed,
|
|
22331
|
+
kept: unique.length,
|
|
22332
|
+
dropped: original.length - unique.length,
|
|
22333
|
+
reason: changed ? "dedup" : "passthrough"
|
|
22334
|
+
};
|
|
22335
|
+
}
|
|
22336
|
+
const lengths = unique.map((item) => getText(item).length);
|
|
22337
|
+
const { mean, std } = meanStd(lengths);
|
|
22338
|
+
const outlierFloor = mean + 2 * std;
|
|
22339
|
+
const mustKeep = /* @__PURE__ */ new Set();
|
|
22340
|
+
unique.forEach((item, i) => {
|
|
22341
|
+
const text7 = getText(item);
|
|
22342
|
+
if (looksLikeMustKeep(text7) || text7.length > outlierFloor) mustKeep.add(i);
|
|
22343
|
+
});
|
|
22344
|
+
const remainingIdx = unique.map((_, i) => i).filter((i) => !mustKeep.has(i));
|
|
22345
|
+
let used = [...mustKeep].reduce((n, i) => n + lengths[i], 0);
|
|
22346
|
+
const pick = new Set(mustKeep);
|
|
22347
|
+
const canTake = (i) => {
|
|
22348
|
+
if (pick.has(i)) return false;
|
|
22349
|
+
const next = used + lengths[i];
|
|
22350
|
+
if (next > maxChars && pick.size > mustKeep.size) return false;
|
|
22351
|
+
used = next;
|
|
22352
|
+
pick.add(i);
|
|
22353
|
+
return true;
|
|
22354
|
+
};
|
|
22355
|
+
const k = remainingIdx.length;
|
|
22356
|
+
const headN = Math.max(1, Math.ceil(k * 0.3));
|
|
22357
|
+
const tailN = Math.max(1, Math.ceil(k * 0.15));
|
|
22358
|
+
for (const i of remainingIdx.slice(0, headN)) canTake(i);
|
|
22359
|
+
for (const i of remainingIdx.slice(-tailN).reverse()) canTake(i);
|
|
22360
|
+
const middle = remainingIdx.filter((i) => !pick.has(i)).sort((a, b) => importance(getText(unique[b])) - importance(getText(unique[a])));
|
|
22361
|
+
for (const i of middle) {
|
|
22362
|
+
if (used >= maxChars) break;
|
|
22363
|
+
canTake(i);
|
|
22364
|
+
}
|
|
22365
|
+
const crushed = unique.filter((_, i) => pick.has(i));
|
|
22366
|
+
const crushedChars = crushed.reduce((n, item) => n + getText(item).length, 0);
|
|
22367
|
+
if (crushed.length >= original.length && crushedChars >= totalChars) {
|
|
22368
|
+
return {
|
|
22369
|
+
items: original,
|
|
22370
|
+
truncated: false,
|
|
22371
|
+
kept: original.length,
|
|
22372
|
+
dropped: 0,
|
|
22373
|
+
reason: "passthrough"
|
|
22374
|
+
};
|
|
22375
|
+
}
|
|
22165
22376
|
return {
|
|
22166
|
-
items:
|
|
22167
|
-
truncated
|
|
22377
|
+
items: crushed,
|
|
22378
|
+
truncated: crushed.length < original.length,
|
|
22379
|
+
kept: crushed.length,
|
|
22380
|
+
dropped: original.length - crushed.length,
|
|
22381
|
+
reason: "budget"
|
|
22168
22382
|
};
|
|
22169
22383
|
}
|
|
22170
|
-
function
|
|
22171
|
-
return
|
|
22172
|
-
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22384
|
+
function splitBlocks(text7) {
|
|
22385
|
+
if (/\n\n/.test(text7)) return text7.split(/\n\n+/);
|
|
22386
|
+
return text7.split("\n");
|
|
22387
|
+
}
|
|
22388
|
+
function capOversizedBlock(block, maxChars) {
|
|
22389
|
+
if (block.length <= maxChars) return block;
|
|
22390
|
+
const head = Math.floor(maxChars * 0.3);
|
|
22391
|
+
const tail = Math.floor(maxChars * 0.15);
|
|
22392
|
+
return `${block.slice(0, head)}
|
|
22393
|
+
|
|
22394
|
+
[\u2026crushed ${block.length - head - tail} chars; pass include=full to read the rest\u2026]
|
|
22395
|
+
|
|
22396
|
+
${block.slice(-tail)}`;
|
|
22397
|
+
}
|
|
22398
|
+
function crushMarkdown(text7, opts) {
|
|
22399
|
+
const original = text7 ?? "";
|
|
22400
|
+
const minChars = opts?.minChars ?? CRUSH_BODY_MIN_CHARS;
|
|
22401
|
+
const maxChars = opts?.maxChars ?? CRUSH_BODY_MAX_CHARS;
|
|
22402
|
+
if (original.length < minChars) {
|
|
22403
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
22404
|
+
}
|
|
22405
|
+
const rawBlocks = splitBlocks(original).filter((b) => b.length > 0);
|
|
22406
|
+
const blocks = rawBlocks.map((b) => capOversizedBlock(b, maxChars));
|
|
22407
|
+
const cappedABlock = blocks.some((b, i) => b !== rawBlocks[i]);
|
|
22408
|
+
if (blocks.length <= 1) {
|
|
22409
|
+
const cut = blocks[0] ?? original;
|
|
22410
|
+
if (cut.length >= original.length) {
|
|
22411
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
22412
|
+
}
|
|
22413
|
+
return { text: cut, truncated: true, originalChars: original.length };
|
|
22414
|
+
}
|
|
22415
|
+
const must = blocks.filter((b) => looksLikeMustKeep(b));
|
|
22416
|
+
const rest = blocks.filter((b) => !looksLikeMustKeep(b));
|
|
22417
|
+
const marker = `[\u2026crushed body; ${original.length} chars originally. Pass include=full to read the rest.]`;
|
|
22418
|
+
let used = must.reduce((n, b) => n + b.length, 0) + marker.length;
|
|
22419
|
+
const keptRest = [];
|
|
22420
|
+
const headN = Math.max(1, Math.ceil(rest.length * 0.3));
|
|
22421
|
+
const tailN = Math.max(1, Math.ceil(rest.length * 0.15));
|
|
22422
|
+
const head = rest.slice(0, headN);
|
|
22423
|
+
const tail = rest.slice(-tailN);
|
|
22424
|
+
const middle = rest.slice(headN, Math.max(headN, rest.length - tailN));
|
|
22425
|
+
const take = (block) => {
|
|
22426
|
+
if (used + block.length + 2 > maxChars && keptRest.length + must.length > 0) {
|
|
22427
|
+
return false;
|
|
22428
|
+
}
|
|
22429
|
+
used += block.length + 2;
|
|
22430
|
+
keptRest.push(block);
|
|
22431
|
+
return true;
|
|
22178
22432
|
};
|
|
22433
|
+
for (const b of head) take(b);
|
|
22434
|
+
for (const b of tail) take(b);
|
|
22435
|
+
const ranked = [...middle].sort((a, b) => importance(b) - importance(a));
|
|
22436
|
+
for (const b of ranked) {
|
|
22437
|
+
if (used >= maxChars) break;
|
|
22438
|
+
take(b);
|
|
22439
|
+
}
|
|
22440
|
+
const keepSet = /* @__PURE__ */ new Set([...must, ...keptRest]);
|
|
22441
|
+
const ordered = blocks.filter((b) => keepSet.has(b));
|
|
22442
|
+
const omitted = blocks.length - ordered.length;
|
|
22443
|
+
if (omitted <= 0) {
|
|
22444
|
+
const joined = blocks.join("\n\n");
|
|
22445
|
+
if (cappedABlock && joined.length < original.length) {
|
|
22446
|
+
return { text: joined, truncated: true, originalChars: original.length };
|
|
22447
|
+
}
|
|
22448
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
22449
|
+
}
|
|
22450
|
+
const out = [];
|
|
22451
|
+
let inserted = false;
|
|
22452
|
+
for (const block of blocks) {
|
|
22453
|
+
if (keepSet.has(block)) {
|
|
22454
|
+
out.push(block);
|
|
22455
|
+
continue;
|
|
22456
|
+
}
|
|
22457
|
+
if (!inserted) {
|
|
22458
|
+
out.push(marker);
|
|
22459
|
+
inserted = true;
|
|
22460
|
+
}
|
|
22461
|
+
}
|
|
22462
|
+
const textOut = out.join("\n\n");
|
|
22463
|
+
if (textOut.length >= original.length) {
|
|
22464
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
22465
|
+
}
|
|
22466
|
+
return { text: textOut, truncated: true, originalChars: original.length };
|
|
22179
22467
|
}
|
|
22180
|
-
|
|
22181
|
-
|
|
22182
|
-
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22468
|
+
|
|
22469
|
+
// src/mcp/issue-payload.ts
|
|
22470
|
+
var MCP_ISSUE_INCLUDE_VALUES = ["default", "full"];
|
|
22471
|
+
var mcpIssueIncludeSchema = import_zod2.z.enum(MCP_ISSUE_INCLUDE_VALUES).optional().describe(
|
|
22472
|
+
"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."
|
|
22473
|
+
);
|
|
22474
|
+
var MCP_ISSUE_CRUSH_HINT = "Pass include=full to re-fetch the uncompressed ticket from the vendor.";
|
|
22475
|
+
function emptyMeta() {
|
|
22476
|
+
return { truncated: true, hint: MCP_ISSUE_CRUSH_HINT };
|
|
22477
|
+
}
|
|
22478
|
+
function withComments(comments, meta) {
|
|
22479
|
+
const crushed = crushTextItems(comments, (c) => c.body);
|
|
22480
|
+
if (crushed.truncated) {
|
|
22481
|
+
meta.comments = {
|
|
22482
|
+
kept: crushed.kept,
|
|
22483
|
+
dropped: crushed.dropped,
|
|
22484
|
+
reason: crushed.reason
|
|
22485
|
+
};
|
|
22486
|
+
}
|
|
22487
|
+
return { comments: crushed.items, meta };
|
|
22488
|
+
}
|
|
22489
|
+
function withBody(text7, meta) {
|
|
22490
|
+
const crushed = crushMarkdown(text7);
|
|
22491
|
+
if (crushed.truncated) {
|
|
22492
|
+
meta.body = { originalChars: crushed.originalChars };
|
|
22493
|
+
return { value: crushed.text, meta };
|
|
22494
|
+
}
|
|
22495
|
+
return { value: text7, meta };
|
|
22496
|
+
}
|
|
22497
|
+
function attachCrush(payload, meta) {
|
|
22498
|
+
if (!meta.comments && !meta.body) return payload;
|
|
22499
|
+
return { ...payload, crush: meta };
|
|
22500
|
+
}
|
|
22501
|
+
function formatLinearIssuePayload(issue, include) {
|
|
22502
|
+
if (include === "full") return issue;
|
|
22503
|
+
const meta = emptyMeta();
|
|
22504
|
+
const comments = withComments(issue.comments, meta);
|
|
22505
|
+
const description = withBody(issue.description, comments.meta);
|
|
22506
|
+
const { team, ...rest } = issue;
|
|
22507
|
+
const next = {
|
|
22508
|
+
...rest,
|
|
22509
|
+
...team ? { team: { id: team.id, key: team.key, name: team.name } } : {},
|
|
22510
|
+
description: description.value,
|
|
22511
|
+
comments: comments.comments
|
|
22189
22512
|
};
|
|
22513
|
+
return attachCrush(next, description.meta);
|
|
22190
22514
|
}
|
|
22191
|
-
function
|
|
22192
|
-
return
|
|
22193
|
-
|
|
22194
|
-
|
|
22515
|
+
function formatGitHubIssuePayload(issue, include) {
|
|
22516
|
+
if (include === "full") return issue;
|
|
22517
|
+
const meta = emptyMeta();
|
|
22518
|
+
const comments = withComments(issue.comments, meta);
|
|
22519
|
+
const body = withBody(issue.body, comments.meta);
|
|
22520
|
+
const next = {
|
|
22521
|
+
...issue,
|
|
22522
|
+
body: body.value,
|
|
22523
|
+
comments: comments.comments
|
|
22195
22524
|
};
|
|
22525
|
+
return attachCrush(next, body.meta);
|
|
22526
|
+
}
|
|
22527
|
+
function formatAbleTimeTaskPayload(task, include) {
|
|
22528
|
+
const base = {
|
|
22529
|
+
...toAbleTimeIssueInfo(task),
|
|
22530
|
+
description: task.description,
|
|
22531
|
+
state: task.state,
|
|
22532
|
+
comments: task.comments
|
|
22533
|
+
};
|
|
22534
|
+
if (include === "full") return base;
|
|
22535
|
+
const meta = emptyMeta();
|
|
22536
|
+
const comments = withComments(task.comments, meta);
|
|
22537
|
+
const description = withBody(task.description, comments.meta);
|
|
22538
|
+
return attachCrush(
|
|
22539
|
+
{
|
|
22540
|
+
...base,
|
|
22541
|
+
description: description.value,
|
|
22542
|
+
comments: comments.comments
|
|
22543
|
+
},
|
|
22544
|
+
description.meta
|
|
22545
|
+
);
|
|
22196
22546
|
}
|
|
22197
22547
|
|
|
22198
22548
|
// src/mcp/abletime-tools.ts
|
|
@@ -22238,7 +22588,7 @@ function registerAbleTimeTools(server) {
|
|
|
22238
22588
|
server.tool(
|
|
22239
22589
|
"abletime_list_tasks",
|
|
22240
22590
|
"List open AbleTime tasks (excludes done/archived). Optional projectId filter.",
|
|
22241
|
-
{ projectId:
|
|
22591
|
+
{ projectId: import_zod3.z.string().optional() },
|
|
22242
22592
|
async ({ projectId }) => {
|
|
22243
22593
|
try {
|
|
22244
22594
|
const tasks = await listAbleTimeTasks({ projectId });
|
|
@@ -22251,7 +22601,7 @@ function registerAbleTimeTools(server) {
|
|
|
22251
22601
|
server.tool(
|
|
22252
22602
|
"abletime_search_tasks",
|
|
22253
22603
|
"Search AbleTime tasks by text, reference, or tag.",
|
|
22254
|
-
{ query:
|
|
22604
|
+
{ query: import_zod3.z.string() },
|
|
22255
22605
|
async ({ query }) => {
|
|
22256
22606
|
try {
|
|
22257
22607
|
const tasks = await searchAbleTimeTasks(query);
|
|
@@ -22263,17 +22613,11 @@ function registerAbleTimeTools(server) {
|
|
|
22263
22613
|
);
|
|
22264
22614
|
server.tool(
|
|
22265
22615
|
"abletime_get_task",
|
|
22266
|
-
"Get one AbleTime task by id or reference (e.g. CRM-232): description, state, comments. Re-fetch to read new comments.",
|
|
22267
|
-
{ id:
|
|
22268
|
-
async ({ id }) => {
|
|
22616
|
+
"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.",
|
|
22617
|
+
{ id: import_zod3.z.string(), include: mcpIssueIncludeSchema },
|
|
22618
|
+
async ({ id, include }) => {
|
|
22269
22619
|
try {
|
|
22270
|
-
|
|
22271
|
-
return text2({
|
|
22272
|
-
...toAbleTimeIssueInfo(task),
|
|
22273
|
-
description: task.description,
|
|
22274
|
-
state: task.state,
|
|
22275
|
-
comments: task.comments
|
|
22276
|
-
});
|
|
22620
|
+
return text2(formatAbleTimeTaskPayload(await getAbleTimeTask(id), include));
|
|
22277
22621
|
} catch (err) {
|
|
22278
22622
|
return fail2(err);
|
|
22279
22623
|
}
|
|
@@ -22282,7 +22626,7 @@ function registerAbleTimeTools(server) {
|
|
|
22282
22626
|
server.tool(
|
|
22283
22627
|
"abletime_comment",
|
|
22284
22628
|
"Add a markdown comment on an AbleTime task (id or CRM-232).",
|
|
22285
|
-
{ id:
|
|
22629
|
+
{ id: import_zod3.z.string(), body: import_zod3.z.string() },
|
|
22286
22630
|
async (args) => {
|
|
22287
22631
|
try {
|
|
22288
22632
|
return text2(await commentAbleTimeTask(args));
|
|
@@ -22295,10 +22639,10 @@ function registerAbleTimeTools(server) {
|
|
|
22295
22639
|
"abletime_update_task",
|
|
22296
22640
|
"Update an AbleTime task (id or CRM-232). Pass title, description, and/or state.",
|
|
22297
22641
|
{
|
|
22298
|
-
id:
|
|
22299
|
-
title:
|
|
22300
|
-
description:
|
|
22301
|
-
state:
|
|
22642
|
+
id: import_zod3.z.string(),
|
|
22643
|
+
title: import_zod3.z.string().optional(),
|
|
22644
|
+
description: import_zod3.z.string().optional(),
|
|
22645
|
+
state: import_zod3.z.string().optional()
|
|
22302
22646
|
},
|
|
22303
22647
|
async (args) => {
|
|
22304
22648
|
try {
|
|
@@ -22317,12 +22661,12 @@ function registerAbleTimeTools(server) {
|
|
|
22317
22661
|
"abletime_create_task",
|
|
22318
22662
|
"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).",
|
|
22319
22663
|
{
|
|
22320
|
-
title:
|
|
22321
|
-
description:
|
|
22322
|
-
projectId:
|
|
22323
|
-
categoryId:
|
|
22324
|
-
state:
|
|
22325
|
-
parent:
|
|
22664
|
+
title: import_zod3.z.string(),
|
|
22665
|
+
description: import_zod3.z.string().optional(),
|
|
22666
|
+
projectId: import_zod3.z.string().optional(),
|
|
22667
|
+
categoryId: import_zod3.z.string().optional(),
|
|
22668
|
+
state: import_zod3.z.enum(["backlog", "todo"]).optional(),
|
|
22669
|
+
parent: import_zod3.z.string().optional().describe("Parent task id or reference (CRM-232) for a spin-off.")
|
|
22326
22670
|
},
|
|
22327
22671
|
async (args) => {
|
|
22328
22672
|
try {
|
|
@@ -22336,10 +22680,10 @@ function registerAbleTimeTools(server) {
|
|
|
22336
22680
|
"abletime_ensure_task",
|
|
22337
22681
|
"Find or create an AbleTime task for this work, then create_thread with sourceType=ticket.",
|
|
22338
22682
|
{
|
|
22339
|
-
title:
|
|
22340
|
-
description:
|
|
22341
|
-
projectId:
|
|
22342
|
-
categoryId:
|
|
22683
|
+
title: import_zod3.z.string(),
|
|
22684
|
+
description: import_zod3.z.string().optional(),
|
|
22685
|
+
projectId: import_zod3.z.string().optional(),
|
|
22686
|
+
categoryId: import_zod3.z.string().optional()
|
|
22343
22687
|
},
|
|
22344
22688
|
async (args) => {
|
|
22345
22689
|
try {
|
|
@@ -22353,7 +22697,7 @@ function registerAbleTimeTools(server) {
|
|
|
22353
22697
|
}
|
|
22354
22698
|
|
|
22355
22699
|
// src/mcp/github-tools.ts
|
|
22356
|
-
var
|
|
22700
|
+
var import_zod4 = require("zod");
|
|
22357
22701
|
|
|
22358
22702
|
// src/integrations/github-issues.ts
|
|
22359
22703
|
init_worktree();
|
|
@@ -22539,15 +22883,15 @@ function fail3(err) {
|
|
|
22539
22883
|
true
|
|
22540
22884
|
);
|
|
22541
22885
|
}
|
|
22542
|
-
var repoPathSchema =
|
|
22886
|
+
var repoPathSchema = import_zod4.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
|
|
22543
22887
|
function registerGithubIssueTools(server) {
|
|
22544
22888
|
server.tool(
|
|
22545
22889
|
"github_get_issue",
|
|
22546
|
-
"Get a GitHub issue (#123 or URL): body, comments, state. Re-fetch to read new comments. Uses Account gh.",
|
|
22547
|
-
{ id:
|
|
22548
|
-
async ({ id, repoPath }) => {
|
|
22890
|
+
"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.",
|
|
22891
|
+
{ id: import_zod4.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
|
|
22892
|
+
async ({ id, include, repoPath }) => {
|
|
22549
22893
|
try {
|
|
22550
|
-
return text3(await getGitHubIssue(id, { repoPath }));
|
|
22894
|
+
return text3(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
|
|
22551
22895
|
} catch (err) {
|
|
22552
22896
|
return fail3(err);
|
|
22553
22897
|
}
|
|
@@ -22556,7 +22900,7 @@ function registerGithubIssueTools(server) {
|
|
|
22556
22900
|
server.tool(
|
|
22557
22901
|
"github_comment",
|
|
22558
22902
|
"Add a markdown comment on a GitHub issue (#123 or URL). Uses Account gh.",
|
|
22559
|
-
{ id:
|
|
22903
|
+
{ id: import_zod4.z.string(), body: import_zod4.z.string(), repoPath: repoPathSchema },
|
|
22560
22904
|
async ({ id, body, repoPath }) => {
|
|
22561
22905
|
try {
|
|
22562
22906
|
return text3(await commentGitHubIssue({ id, body }, { repoPath }));
|
|
@@ -22569,10 +22913,10 @@ function registerGithubIssueTools(server) {
|
|
|
22569
22913
|
"github_update_issue",
|
|
22570
22914
|
"Update a GitHub issue (#123). Pass title, body, and/or state (open|closed).",
|
|
22571
22915
|
{
|
|
22572
|
-
id:
|
|
22573
|
-
title:
|
|
22574
|
-
body:
|
|
22575
|
-
state:
|
|
22916
|
+
id: import_zod4.z.string(),
|
|
22917
|
+
title: import_zod4.z.string().optional(),
|
|
22918
|
+
body: import_zod4.z.string().optional(),
|
|
22919
|
+
state: import_zod4.z.string().optional().describe("open or closed"),
|
|
22576
22920
|
repoPath: repoPathSchema
|
|
22577
22921
|
},
|
|
22578
22922
|
async (args) => {
|
|
@@ -22587,9 +22931,9 @@ function registerGithubIssueTools(server) {
|
|
|
22587
22931
|
"github_create_issue",
|
|
22588
22932
|
"Create a GitHub issue. Pass parent (#123) to mark a spin-off in the body.",
|
|
22589
22933
|
{
|
|
22590
|
-
title:
|
|
22591
|
-
body:
|
|
22592
|
-
parent:
|
|
22934
|
+
title: import_zod4.z.string(),
|
|
22935
|
+
body: import_zod4.z.string().optional(),
|
|
22936
|
+
parent: import_zod4.z.string().optional().describe("Parent issue #123 or URL for a spin-off."),
|
|
22593
22937
|
repoPath: repoPathSchema
|
|
22594
22938
|
},
|
|
22595
22939
|
async (args) => {
|
|
@@ -22603,7 +22947,7 @@ function registerGithubIssueTools(server) {
|
|
|
22603
22947
|
}
|
|
22604
22948
|
|
|
22605
22949
|
// src/mcp/linear-tools.ts
|
|
22606
|
-
var
|
|
22950
|
+
var import_zod5 = require("zod");
|
|
22607
22951
|
function text4(payload, isError = false) {
|
|
22608
22952
|
return mcpJson(payload, isError);
|
|
22609
22953
|
}
|
|
@@ -22613,7 +22957,7 @@ function fail4(err) {
|
|
|
22613
22957
|
true
|
|
22614
22958
|
);
|
|
22615
22959
|
}
|
|
22616
|
-
var prioritySchema =
|
|
22960
|
+
var prioritySchema = import_zod5.z.number().int().min(0).max(4).optional().describe("0 none, 1 urgent, 2 high, 3 medium, 4 low");
|
|
22617
22961
|
function registerLinearTools(server) {
|
|
22618
22962
|
server.tool(
|
|
22619
22963
|
"linear_list_teams",
|
|
@@ -22631,9 +22975,9 @@ function registerLinearTools(server) {
|
|
|
22631
22975
|
"linear_search_issues",
|
|
22632
22976
|
"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.",
|
|
22633
22977
|
{
|
|
22634
|
-
query:
|
|
22635
|
-
assignee:
|
|
22636
|
-
limit:
|
|
22978
|
+
query: import_zod5.z.string().optional().describe("Search text (identifier, title, description). Omit to list by assignee only."),
|
|
22979
|
+
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."),
|
|
22980
|
+
limit: import_zod5.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
22637
22981
|
},
|
|
22638
22982
|
async ({ query, assignee, limit }) => {
|
|
22639
22983
|
try {
|
|
@@ -22660,11 +23004,11 @@ function registerLinearTools(server) {
|
|
|
22660
23004
|
);
|
|
22661
23005
|
server.tool(
|
|
22662
23006
|
"linear_get_issue",
|
|
22663
|
-
"Get a Linear issue by uuid or identifier (ENG-123): description, comments, relations, parent/children.",
|
|
22664
|
-
{ id:
|
|
22665
|
-
async ({ id }) => {
|
|
23007
|
+
"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.",
|
|
23008
|
+
{ id: import_zod5.z.string(), include: mcpIssueIncludeSchema },
|
|
23009
|
+
async ({ id, include }) => {
|
|
22666
23010
|
try {
|
|
22667
|
-
return text4(await getLinearIssue(id));
|
|
23011
|
+
return text4(formatLinearIssuePayload(await getLinearIssue(id), include));
|
|
22668
23012
|
} catch (err) {
|
|
22669
23013
|
return fail4(err);
|
|
22670
23014
|
}
|
|
@@ -22674,13 +23018,13 @@ function registerLinearTools(server) {
|
|
|
22674
23018
|
"linear_create_issue",
|
|
22675
23019
|
'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.',
|
|
22676
23020
|
{
|
|
22677
|
-
team:
|
|
22678
|
-
title:
|
|
22679
|
-
description:
|
|
22680
|
-
state:
|
|
22681
|
-
assignee:
|
|
23021
|
+
team: import_zod5.z.string(),
|
|
23022
|
+
title: import_zod5.z.string(),
|
|
23023
|
+
description: import_zod5.z.string().optional(),
|
|
23024
|
+
state: import_zod5.z.string().optional(),
|
|
23025
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
22682
23026
|
priority: prioritySchema,
|
|
22683
|
-
parent:
|
|
23027
|
+
parent: import_zod5.z.string().optional().describe("Parent issue uuid or identifier (ENG-123) for a spin-off / sub-issue.")
|
|
22684
23028
|
},
|
|
22685
23029
|
async (args) => {
|
|
22686
23030
|
try {
|
|
@@ -22694,11 +23038,11 @@ function registerLinearTools(server) {
|
|
|
22694
23038
|
"linear_update_issue",
|
|
22695
23039
|
"Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, and/or priority.",
|
|
22696
23040
|
{
|
|
22697
|
-
id:
|
|
22698
|
-
title:
|
|
22699
|
-
description:
|
|
22700
|
-
state:
|
|
22701
|
-
assignee:
|
|
23041
|
+
id: import_zod5.z.string(),
|
|
23042
|
+
title: import_zod5.z.string().optional(),
|
|
23043
|
+
description: import_zod5.z.string().optional(),
|
|
23044
|
+
state: import_zod5.z.string().optional(),
|
|
23045
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
22702
23046
|
priority: prioritySchema
|
|
22703
23047
|
},
|
|
22704
23048
|
async (args) => {
|
|
@@ -22713,8 +23057,8 @@ function registerLinearTools(server) {
|
|
|
22713
23057
|
"linear_comment",
|
|
22714
23058
|
"Add a markdown comment on a Linear issue (uuid or ENG-123).",
|
|
22715
23059
|
{
|
|
22716
|
-
id:
|
|
22717
|
-
body:
|
|
23060
|
+
id: import_zod5.z.string(),
|
|
23061
|
+
body: import_zod5.z.string()
|
|
22718
23062
|
},
|
|
22719
23063
|
async (args) => {
|
|
22720
23064
|
try {
|
|
@@ -22734,7 +23078,7 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
22734
23078
|
}
|
|
22735
23079
|
|
|
22736
23080
|
// src/mcp/viewer-context-tools.ts
|
|
22737
|
-
var
|
|
23081
|
+
var import_zod6 = require("zod");
|
|
22738
23082
|
init_worktree();
|
|
22739
23083
|
init_app_settings();
|
|
22740
23084
|
init_workspaces2();
|
|
@@ -22769,7 +23113,7 @@ function registerViewerContextTools(server) {
|
|
|
22769
23113
|
"get_viewer_context",
|
|
22770
23114
|
"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).",
|
|
22771
23115
|
{
|
|
22772
|
-
repoPath:
|
|
23116
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
22773
23117
|
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
22774
23118
|
)
|
|
22775
23119
|
},
|
|
@@ -22789,10 +23133,10 @@ function registerViewerContextTools(server) {
|
|
|
22789
23133
|
"update_viewer_context",
|
|
22790
23134
|
"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.",
|
|
22791
23135
|
{
|
|
22792
|
-
scope:
|
|
22793
|
-
context:
|
|
22794
|
-
confirmed:
|
|
22795
|
-
repoPath:
|
|
23136
|
+
scope: import_zod6.z.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
23137
|
+
context: import_zod6.z.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
23138
|
+
confirmed: import_zod6.z.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
23139
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
22796
23140
|
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
22797
23141
|
)
|
|
22798
23142
|
},
|
|
@@ -22873,14 +23217,11 @@ init_list_prs();
|
|
|
22873
23217
|
init_app_settings();
|
|
22874
23218
|
|
|
22875
23219
|
// src/mcp/schedule-tools.ts
|
|
22876
|
-
var
|
|
23220
|
+
var import_zod7 = require("zod");
|
|
22877
23221
|
init_schedules();
|
|
22878
23222
|
init_schedule_runner();
|
|
22879
23223
|
function text6(payload, isError = false) {
|
|
22880
|
-
return
|
|
22881
|
-
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
22882
|
-
...isError ? { isError: true } : {}
|
|
22883
|
-
};
|
|
23224
|
+
return mcpJson(payload, isError);
|
|
22884
23225
|
}
|
|
22885
23226
|
function fail5(err) {
|
|
22886
23227
|
return text6(
|
|
@@ -22918,17 +23259,17 @@ function registerScheduleTools(server) {
|
|
|
22918
23259
|
"create_schedule",
|
|
22919
23260
|
"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.",
|
|
22920
23261
|
{
|
|
22921
|
-
prompt:
|
|
22922
|
-
name:
|
|
22923
|
-
at:
|
|
22924
|
-
every:
|
|
22925
|
-
cron:
|
|
22926
|
-
tz:
|
|
22927
|
-
threadId:
|
|
23262
|
+
prompt: import_zod7.z.string().describe("User message / goal queued when the schedule fires"),
|
|
23263
|
+
name: import_zod7.z.string().optional(),
|
|
23264
|
+
at: import_zod7.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
23265
|
+
every: import_zod7.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
23266
|
+
cron: import_zod7.z.string().optional().describe("5-field cron expression"),
|
|
23267
|
+
tz: import_zod7.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
23268
|
+
threadId: import_zod7.z.string().optional().describe(
|
|
22928
23269
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
22929
23270
|
),
|
|
22930
|
-
agent:
|
|
22931
|
-
model:
|
|
23271
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
23272
|
+
model: import_zod7.z.string().optional()
|
|
22932
23273
|
},
|
|
22933
23274
|
async (args) => {
|
|
22934
23275
|
try {
|
|
@@ -22961,17 +23302,17 @@ function registerScheduleTools(server) {
|
|
|
22961
23302
|
"update_schedule",
|
|
22962
23303
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
22963
23304
|
{
|
|
22964
|
-
id:
|
|
22965
|
-
prompt:
|
|
22966
|
-
name:
|
|
22967
|
-
at:
|
|
22968
|
-
every:
|
|
22969
|
-
cron:
|
|
22970
|
-
tz:
|
|
22971
|
-
threadId:
|
|
22972
|
-
agent:
|
|
22973
|
-
model:
|
|
22974
|
-
enabled:
|
|
23305
|
+
id: import_zod7.z.string(),
|
|
23306
|
+
prompt: import_zod7.z.string().optional(),
|
|
23307
|
+
name: import_zod7.z.string().optional(),
|
|
23308
|
+
at: import_zod7.z.string().optional(),
|
|
23309
|
+
every: import_zod7.z.string().optional(),
|
|
23310
|
+
cron: import_zod7.z.string().optional(),
|
|
23311
|
+
tz: import_zod7.z.string().optional(),
|
|
23312
|
+
threadId: import_zod7.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
23313
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
23314
|
+
model: import_zod7.z.string().optional(),
|
|
23315
|
+
enabled: import_zod7.z.boolean().optional()
|
|
22975
23316
|
},
|
|
22976
23317
|
async (args) => {
|
|
22977
23318
|
try {
|
|
@@ -23009,7 +23350,7 @@ function registerScheduleTools(server) {
|
|
|
23009
23350
|
server.tool(
|
|
23010
23351
|
"delete_schedule",
|
|
23011
23352
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
23012
|
-
{ id:
|
|
23353
|
+
{ id: import_zod7.z.string() },
|
|
23013
23354
|
async ({ id }) => {
|
|
23014
23355
|
try {
|
|
23015
23356
|
deleteSchedule(id);
|
|
@@ -23022,7 +23363,7 @@ function registerScheduleTools(server) {
|
|
|
23022
23363
|
server.tool(
|
|
23023
23364
|
"run_schedule",
|
|
23024
23365
|
"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.",
|
|
23025
|
-
{ id:
|
|
23366
|
+
{ id: import_zod7.z.string() },
|
|
23026
23367
|
async ({ id }) => {
|
|
23027
23368
|
try {
|
|
23028
23369
|
if (!getSchedule(id)) {
|
|
@@ -23454,13 +23795,13 @@ async function startMcpServer() {
|
|
|
23454
23795
|
"list_board",
|
|
23455
23796
|
"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.",
|
|
23456
23797
|
{
|
|
23457
|
-
query:
|
|
23458
|
-
repoPath:
|
|
23459
|
-
kind:
|
|
23460
|
-
column:
|
|
23798
|
+
query: import_zod8.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
23799
|
+
repoPath: import_zod8.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
23800
|
+
kind: import_zod8.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
23801
|
+
column: import_zod8.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
23461
23802
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
23462
23803
|
),
|
|
23463
|
-
limit:
|
|
23804
|
+
limit: import_zod8.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
23464
23805
|
},
|
|
23465
23806
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
23466
23807
|
const workspaces = orch.listWorkspaces();
|
|
@@ -23475,30 +23816,19 @@ async function startMcpServer() {
|
|
|
23475
23816
|
limit,
|
|
23476
23817
|
workspaceName: (path) => names.get(path) ?? ""
|
|
23477
23818
|
});
|
|
23478
|
-
return {
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
23485
|
-
hidden: snap.hidden,
|
|
23486
|
-
totals: snap.totals,
|
|
23487
|
-
columnDefs: BOARD_COLUMN_DEFS,
|
|
23488
|
-
hint: HOME_BOARD_AGENT_HINT
|
|
23489
|
-
},
|
|
23490
|
-
null,
|
|
23491
|
-
2
|
|
23492
|
-
)
|
|
23493
|
-
}
|
|
23494
|
-
]
|
|
23495
|
-
};
|
|
23819
|
+
return mcpJson({
|
|
23820
|
+
columns: snap.columns,
|
|
23821
|
+
hidden: snap.hidden,
|
|
23822
|
+
totals: snap.totals,
|
|
23823
|
+
columnDefs: BOARD_COLUMN_DEFS,
|
|
23824
|
+
hint: HOME_BOARD_AGENT_HINT
|
|
23825
|
+
});
|
|
23496
23826
|
}
|
|
23497
23827
|
);
|
|
23498
23828
|
server.tool(
|
|
23499
23829
|
"get_thread",
|
|
23500
23830
|
"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.",
|
|
23501
|
-
{ ref:
|
|
23831
|
+
{ ref: import_zod8.z.string() },
|
|
23502
23832
|
async ({ ref }) => {
|
|
23503
23833
|
const t = orch.getThread(ref);
|
|
23504
23834
|
if (!t) {
|
|
@@ -23532,7 +23862,7 @@ async function startMcpServer() {
|
|
|
23532
23862
|
usage: spend.usage,
|
|
23533
23863
|
lastTurnUsage: spend.lastTurnUsage
|
|
23534
23864
|
};
|
|
23535
|
-
return
|
|
23865
|
+
return mcpJson(summary);
|
|
23536
23866
|
}
|
|
23537
23867
|
);
|
|
23538
23868
|
}
|
|
@@ -23540,17 +23870,17 @@ async function startMcpServer() {
|
|
|
23540
23870
|
"present_artifact",
|
|
23541
23871
|
"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.",
|
|
23542
23872
|
{
|
|
23543
|
-
title:
|
|
23544
|
-
type:
|
|
23873
|
+
title: import_zod8.z.string().describe("Short title shown in the artifact pane header"),
|
|
23874
|
+
type: import_zod8.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
23545
23875
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
23546
23876
|
),
|
|
23547
|
-
content:
|
|
23877
|
+
content: import_zod8.z.string().describe(
|
|
23548
23878
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
23549
23879
|
),
|
|
23550
|
-
artifact_id:
|
|
23551
|
-
status:
|
|
23552
|
-
phase:
|
|
23553
|
-
mode:
|
|
23880
|
+
artifact_id: import_zod8.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
23881
|
+
status: import_zod8.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
23882
|
+
phase: import_zod8.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
23883
|
+
mode: import_zod8.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
23554
23884
|
},
|
|
23555
23885
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
23556
23886
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23564,22 +23894,22 @@ async function startMcpServer() {
|
|
|
23564
23894
|
mode: type === "log" ? mode ?? "append" : void 0,
|
|
23565
23895
|
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."
|
|
23566
23896
|
};
|
|
23567
|
-
return
|
|
23897
|
+
return mcpJson(payload);
|
|
23568
23898
|
}
|
|
23569
23899
|
);
|
|
23570
23900
|
server.tool(
|
|
23571
23901
|
"ask_user",
|
|
23572
23902
|
"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.",
|
|
23573
23903
|
{
|
|
23574
|
-
questions:
|
|
23575
|
-
|
|
23576
|
-
question:
|
|
23577
|
-
header:
|
|
23578
|
-
multiSelect:
|
|
23579
|
-
options:
|
|
23580
|
-
|
|
23581
|
-
label:
|
|
23582
|
-
description:
|
|
23904
|
+
questions: import_zod8.z.array(
|
|
23905
|
+
import_zod8.z.object({
|
|
23906
|
+
question: import_zod8.z.string().describe("Full question text ending with ?"),
|
|
23907
|
+
header: import_zod8.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
23908
|
+
multiSelect: import_zod8.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
23909
|
+
options: import_zod8.z.array(
|
|
23910
|
+
import_zod8.z.object({
|
|
23911
|
+
label: import_zod8.z.string(),
|
|
23912
|
+
description: import_zod8.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
23583
23913
|
})
|
|
23584
23914
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
23585
23915
|
})
|
|
@@ -23591,16 +23921,16 @@ async function startMcpServer() {
|
|
|
23591
23921
|
questions,
|
|
23592
23922
|
message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
|
|
23593
23923
|
};
|
|
23594
|
-
return
|
|
23924
|
+
return mcpJson(payload);
|
|
23595
23925
|
}
|
|
23596
23926
|
);
|
|
23597
23927
|
server.tool(
|
|
23598
23928
|
"present_plan",
|
|
23599
23929
|
"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.",
|
|
23600
23930
|
{
|
|
23601
|
-
title:
|
|
23602
|
-
content:
|
|
23603
|
-
thread_id:
|
|
23931
|
+
title: import_zod8.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
23932
|
+
content: import_zod8.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
23933
|
+
thread_id: import_zod8.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
23604
23934
|
},
|
|
23605
23935
|
async ({ title, content, thread_id }) => {
|
|
23606
23936
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -23616,22 +23946,22 @@ async function startMcpServer() {
|
|
|
23616
23946
|
title: title?.trim() || "Plan",
|
|
23617
23947
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
23618
23948
|
};
|
|
23619
|
-
return
|
|
23949
|
+
return mcpJson(payload);
|
|
23620
23950
|
}
|
|
23621
23951
|
);
|
|
23622
23952
|
server.tool(
|
|
23623
23953
|
"present_schema",
|
|
23624
23954
|
"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.",
|
|
23625
23955
|
{
|
|
23626
|
-
title:
|
|
23627
|
-
mode:
|
|
23628
|
-
datasource:
|
|
23629
|
-
resource_id:
|
|
23630
|
-
record_id:
|
|
23631
|
-
resource:
|
|
23632
|
-
record:
|
|
23633
|
-
records:
|
|
23634
|
-
pane_id:
|
|
23956
|
+
title: import_zod8.z.string().describe("Pane title"),
|
|
23957
|
+
mode: import_zod8.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
23958
|
+
datasource: import_zod8.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
23959
|
+
resource_id: import_zod8.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
23960
|
+
record_id: import_zod8.z.string().optional().describe("Record id when opening form mode"),
|
|
23961
|
+
resource: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
23962
|
+
record: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
23963
|
+
records: import_zod8.z.array(import_zod8.z.record(import_zod8.z.unknown())).optional().describe("Inline records for table mode"),
|
|
23964
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
23635
23965
|
},
|
|
23636
23966
|
async (args) => {
|
|
23637
23967
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23645,17 +23975,17 @@ async function startMcpServer() {
|
|
|
23645
23975
|
record_id: args.record_id,
|
|
23646
23976
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
23647
23977
|
};
|
|
23648
|
-
return
|
|
23978
|
+
return mcpJson(payload);
|
|
23649
23979
|
}
|
|
23650
23980
|
);
|
|
23651
23981
|
server.tool(
|
|
23652
23982
|
"present_files",
|
|
23653
23983
|
"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.",
|
|
23654
23984
|
{
|
|
23655
|
-
title:
|
|
23656
|
-
datasource:
|
|
23657
|
-
path:
|
|
23658
|
-
pane_id:
|
|
23985
|
+
title: import_zod8.z.string().optional().describe("Pane title (default: Files)"),
|
|
23986
|
+
datasource: import_zod8.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
23987
|
+
path: import_zod8.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
23988
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
23659
23989
|
},
|
|
23660
23990
|
async (args) => {
|
|
23661
23991
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -23667,21 +23997,21 @@ async function startMcpServer() {
|
|
|
23667
23997
|
path: args.path,
|
|
23668
23998
|
message: "Files pane accepted. Sideboard desktop opens the Files column beside chat."
|
|
23669
23999
|
};
|
|
23670
|
-
return
|
|
24000
|
+
return mcpJson(payload);
|
|
23671
24001
|
}
|
|
23672
24002
|
);
|
|
23673
24003
|
server.tool(
|
|
23674
24004
|
"wait_for_job",
|
|
23675
24005
|
"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.",
|
|
23676
24006
|
{
|
|
23677
|
-
id:
|
|
23678
|
-
timeoutMs:
|
|
24007
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
24008
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
23679
24009
|
},
|
|
23680
24010
|
async ({ id, timeoutMs }) => {
|
|
23681
24011
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
23682
24012
|
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
23683
24013
|
});
|
|
23684
|
-
return
|
|
24014
|
+
return mcpJson(result);
|
|
23685
24015
|
}
|
|
23686
24016
|
);
|
|
23687
24017
|
if (!worktreeProfile) {
|
|
@@ -23696,56 +24026,44 @@ async function startMcpServer() {
|
|
|
23696
24026
|
"set_caffeinate",
|
|
23697
24027
|
"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.",
|
|
23698
24028
|
{
|
|
23699
|
-
enabled:
|
|
24029
|
+
enabled: import_zod8.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
23700
24030
|
},
|
|
23701
24031
|
async ({ enabled }) => {
|
|
23702
24032
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
23703
24033
|
const state = setCaffeinateHold2(enabled, { threadId });
|
|
23704
24034
|
if (enabled && !state.held) {
|
|
23705
|
-
return
|
|
23706
|
-
content: [
|
|
23707
|
-
{
|
|
23708
|
-
type: "text",
|
|
23709
|
-
text: JSON.stringify({
|
|
23710
|
-
...state,
|
|
23711
|
-
ok: false,
|
|
23712
|
-
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
23713
|
-
})
|
|
23714
|
-
}
|
|
23715
|
-
],
|
|
23716
|
-
isError: true
|
|
23717
|
-
};
|
|
23718
|
-
}
|
|
23719
|
-
return {
|
|
23720
|
-
content: [
|
|
24035
|
+
return mcpJson(
|
|
23721
24036
|
{
|
|
23722
|
-
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
24037
|
+
...state,
|
|
24038
|
+
ok: false,
|
|
24039
|
+
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
24040
|
+
},
|
|
24041
|
+
true
|
|
24042
|
+
);
|
|
24043
|
+
}
|
|
24044
|
+
return mcpJson({
|
|
24045
|
+
...getCaffeinateHold2(),
|
|
24046
|
+
ok: true,
|
|
24047
|
+
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)."
|
|
24048
|
+
});
|
|
23731
24049
|
}
|
|
23732
24050
|
);
|
|
23733
24051
|
server.tool(
|
|
23734
24052
|
"create_thread",
|
|
23735
24053
|
`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.`,
|
|
23736
24054
|
{
|
|
23737
|
-
sourceType:
|
|
23738
|
-
sourceRef:
|
|
23739
|
-
agent:
|
|
23740
|
-
model:
|
|
24055
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]),
|
|
24056
|
+
sourceRef: import_zod8.z.string(),
|
|
24057
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
24058
|
+
model: import_zod8.z.string().nullable().optional().describe(
|
|
23741
24059
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
23742
24060
|
),
|
|
23743
|
-
repoPath:
|
|
23744
|
-
title:
|
|
23745
|
-
cowboy:
|
|
24061
|
+
repoPath: import_zod8.z.string(),
|
|
24062
|
+
title: import_zod8.z.string().optional(),
|
|
24063
|
+
cowboy: import_zod8.z.boolean().optional().describe(
|
|
23746
24064
|
"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."
|
|
23747
24065
|
),
|
|
23748
|
-
parentThreadId:
|
|
24066
|
+
parentThreadId: import_zod8.z.string().optional().describe(
|
|
23749
24067
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
23750
24068
|
)
|
|
23751
24069
|
},
|
|
@@ -23761,10 +24079,10 @@ async function startMcpServer() {
|
|
|
23761
24079
|
"start_board_card",
|
|
23762
24080
|
"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.",
|
|
23763
24081
|
{
|
|
23764
|
-
kind:
|
|
23765
|
-
ref:
|
|
23766
|
-
repoPath:
|
|
23767
|
-
title:
|
|
24082
|
+
kind: import_zod8.z.enum(["ticket", "pr", "branch"]),
|
|
24083
|
+
ref: import_zod8.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
24084
|
+
repoPath: import_zod8.z.string().describe("Workspace path from list_workspaces / list_board"),
|
|
24085
|
+
title: import_zod8.z.string().optional()
|
|
23768
24086
|
},
|
|
23769
24087
|
async ({ kind, ref, repoPath, title }) => {
|
|
23770
24088
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -23781,20 +24099,13 @@ async function startMcpServer() {
|
|
|
23781
24099
|
}))
|
|
23782
24100
|
);
|
|
23783
24101
|
if (existing) {
|
|
23784
|
-
return {
|
|
23785
|
-
|
|
23786
|
-
|
|
23787
|
-
|
|
23788
|
-
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
title: existing.title,
|
|
23792
|
-
status: existing.status,
|
|
23793
|
-
link: `sideboard://thread/${existing.id}`
|
|
23794
|
-
})
|
|
23795
|
-
}
|
|
23796
|
-
]
|
|
23797
|
-
};
|
|
24102
|
+
return mcpJson({
|
|
24103
|
+
alreadyStarted: true,
|
|
24104
|
+
id: existing.id,
|
|
24105
|
+
title: existing.title,
|
|
24106
|
+
status: existing.status,
|
|
24107
|
+
link: `sideboard://thread/${existing.id}`
|
|
24108
|
+
});
|
|
23798
24109
|
}
|
|
23799
24110
|
const workspaces = orch.listWorkspaces();
|
|
23800
24111
|
const loaded = await getHomeBoardInputs(workspaces);
|
|
@@ -23868,9 +24179,9 @@ async function startMcpServer() {
|
|
|
23868
24179
|
"send_to_thread",
|
|
23869
24180
|
'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.',
|
|
23870
24181
|
{
|
|
23871
|
-
ref:
|
|
23872
|
-
prompt:
|
|
23873
|
-
force_stop:
|
|
24182
|
+
ref: import_zod8.z.string(),
|
|
24183
|
+
prompt: import_zod8.z.string(),
|
|
24184
|
+
force_stop: import_zod8.z.boolean().optional()
|
|
23874
24185
|
},
|
|
23875
24186
|
async ({ ref, prompt, force_stop }) => {
|
|
23876
24187
|
if (force_stop) {
|
|
@@ -23880,79 +24191,58 @@ async function startMcpServer() {
|
|
|
23880
24191
|
}
|
|
23881
24192
|
}
|
|
23882
24193
|
const thread = await orch.send(ref, prompt);
|
|
23883
|
-
return {
|
|
23884
|
-
|
|
23885
|
-
|
|
23886
|
-
|
|
23887
|
-
|
|
23888
|
-
|
|
23889
|
-
status: thread.status,
|
|
23890
|
-
queueLength: thread.queue.length,
|
|
23891
|
-
forceStopped: Boolean(force_stop)
|
|
23892
|
-
})
|
|
23893
|
-
}
|
|
23894
|
-
]
|
|
23895
|
-
};
|
|
24194
|
+
return mcpJson({
|
|
24195
|
+
id: thread.id,
|
|
24196
|
+
status: thread.status,
|
|
24197
|
+
queueLength: thread.queue.length,
|
|
24198
|
+
forceStopped: Boolean(force_stop)
|
|
24199
|
+
});
|
|
23896
24200
|
}
|
|
23897
24201
|
);
|
|
23898
24202
|
server.tool(
|
|
23899
24203
|
"wait_for_turn",
|
|
23900
24204
|
"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).",
|
|
23901
24205
|
{
|
|
23902
|
-
ref:
|
|
23903
|
-
timeoutMs:
|
|
24206
|
+
ref: import_zod8.z.string(),
|
|
24207
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
23904
24208
|
},
|
|
23905
24209
|
async ({ ref, timeoutMs }) => {
|
|
23906
24210
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
23907
24211
|
resolveIfStillRunning: true
|
|
23908
24212
|
});
|
|
23909
24213
|
const result = orch.getTurnResult(thread.id);
|
|
23910
|
-
return {
|
|
23911
|
-
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
|
|
23915
|
-
|
|
23916
|
-
|
|
23917
|
-
|
|
23918
|
-
|
|
23919
|
-
|
|
23920
|
-
|
|
23921
|
-
lastActivityAt: result.lastActivityAt,
|
|
23922
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
23923
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
23924
|
-
})
|
|
23925
|
-
}
|
|
23926
|
-
]
|
|
23927
|
-
};
|
|
24214
|
+
return mcpJson({
|
|
24215
|
+
id: thread.id,
|
|
24216
|
+
status: result.status,
|
|
24217
|
+
text: result.text,
|
|
24218
|
+
lastError: result.lastError,
|
|
24219
|
+
stillRunning: result.stillRunning,
|
|
24220
|
+
progress: result.progress,
|
|
24221
|
+
lastActivityAt: result.lastActivityAt,
|
|
24222
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
24223
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
24224
|
+
});
|
|
23928
24225
|
}
|
|
23929
24226
|
);
|
|
23930
24227
|
server.tool(
|
|
23931
24228
|
"get_turn_result",
|
|
23932
24229
|
"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).",
|
|
23933
|
-
{ ref:
|
|
24230
|
+
{ ref: import_zod8.z.string() },
|
|
23934
24231
|
async ({ ref }) => {
|
|
23935
24232
|
const result = orch.getTurnResult(ref);
|
|
23936
|
-
return {
|
|
23937
|
-
|
|
23938
|
-
|
|
23939
|
-
|
|
23940
|
-
|
|
23941
|
-
...result,
|
|
23942
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
23943
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
23944
|
-
})
|
|
23945
|
-
}
|
|
23946
|
-
]
|
|
23947
|
-
};
|
|
24233
|
+
return mcpJson({
|
|
24234
|
+
...result,
|
|
24235
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
24236
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
24237
|
+
});
|
|
23948
24238
|
}
|
|
23949
24239
|
);
|
|
23950
24240
|
server.tool(
|
|
23951
24241
|
"stop_thread",
|
|
23952
24242
|
"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.",
|
|
23953
24243
|
{
|
|
23954
|
-
ref:
|
|
23955
|
-
force:
|
|
24244
|
+
ref: import_zod8.z.string(),
|
|
24245
|
+
force: import_zod8.z.boolean().optional()
|
|
23956
24246
|
},
|
|
23957
24247
|
async ({ ref, force }) => {
|
|
23958
24248
|
const t = orch.getThread(ref);
|
|
@@ -23965,24 +24255,17 @@ async function startMcpServer() {
|
|
|
23965
24255
|
const clearQueue = force !== false;
|
|
23966
24256
|
const hadQueued = t.queue.length > 0;
|
|
23967
24257
|
const stopped = orch.stop(ref, { clearQueue, notifyParent: false });
|
|
23968
|
-
return {
|
|
23969
|
-
|
|
23970
|
-
|
|
23971
|
-
|
|
23972
|
-
|
|
23973
|
-
id: stopped.id,
|
|
23974
|
-
status: stopped.status,
|
|
23975
|
-
clearedQueue: clearQueue && hadQueued
|
|
23976
|
-
})
|
|
23977
|
-
}
|
|
23978
|
-
]
|
|
23979
|
-
};
|
|
24258
|
+
return mcpJson({
|
|
24259
|
+
id: stopped.id,
|
|
24260
|
+
status: stopped.status,
|
|
24261
|
+
clearedQueue: clearQueue && hadQueued
|
|
24262
|
+
});
|
|
23980
24263
|
}
|
|
23981
24264
|
);
|
|
23982
24265
|
server.tool(
|
|
23983
24266
|
"archive_thread",
|
|
23984
24267
|
"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.",
|
|
23985
|
-
{ ref:
|
|
24268
|
+
{ ref: import_zod8.z.string() },
|
|
23986
24269
|
async ({ ref }) => {
|
|
23987
24270
|
const t = orch.getThread(ref);
|
|
23988
24271
|
if (!t) {
|
|
@@ -23999,38 +24282,24 @@ async function startMcpServer() {
|
|
|
23999
24282
|
};
|
|
24000
24283
|
}
|
|
24001
24284
|
const archived = await orch.archive(ref);
|
|
24002
|
-
return {
|
|
24003
|
-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
text: JSON.stringify({
|
|
24007
|
-
id: archived.id,
|
|
24008
|
-
status: archived.status
|
|
24009
|
-
})
|
|
24010
|
-
}
|
|
24011
|
-
]
|
|
24012
|
-
};
|
|
24285
|
+
return mcpJson({
|
|
24286
|
+
id: archived.id,
|
|
24287
|
+
status: archived.status
|
|
24288
|
+
});
|
|
24013
24289
|
}
|
|
24014
24290
|
);
|
|
24015
24291
|
server.tool(
|
|
24016
24292
|
"restore_thread",
|
|
24017
24293
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
24018
|
-
{ ref:
|
|
24294
|
+
{ ref: import_zod8.z.string() },
|
|
24019
24295
|
async ({ ref }) => {
|
|
24020
24296
|
try {
|
|
24021
24297
|
const restored = await orch.restore(ref);
|
|
24022
|
-
return {
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
id: restored.id,
|
|
24028
|
-
status: restored.status,
|
|
24029
|
-
worktreePath: restored.worktreePath
|
|
24030
|
-
})
|
|
24031
|
-
}
|
|
24032
|
-
]
|
|
24033
|
-
};
|
|
24298
|
+
return mcpJson({
|
|
24299
|
+
id: restored.id,
|
|
24300
|
+
status: restored.status,
|
|
24301
|
+
worktreePath: restored.worktreePath
|
|
24302
|
+
});
|
|
24034
24303
|
} catch (err) {
|
|
24035
24304
|
const message = err instanceof Error ? err.message : String(err);
|
|
24036
24305
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24041,34 +24310,25 @@ async function startMcpServer() {
|
|
|
24041
24310
|
"get_diff",
|
|
24042
24311
|
"Compact diff summary (capped hunks, paginated)",
|
|
24043
24312
|
{
|
|
24044
|
-
ref:
|
|
24045
|
-
maxFiles:
|
|
24313
|
+
ref: import_zod8.z.string(),
|
|
24314
|
+
maxFiles: import_zod8.z.number().optional()
|
|
24046
24315
|
},
|
|
24047
24316
|
async ({ ref, maxFiles }) => {
|
|
24048
24317
|
const summary = await orch.diffSummary(ref);
|
|
24049
|
-
return {
|
|
24050
|
-
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
text: JSON.stringify({
|
|
24054
|
-
...summary,
|
|
24055
|
-
files: summary.files.slice(0, maxFiles ?? 10)
|
|
24056
|
-
})
|
|
24057
|
-
}
|
|
24058
|
-
]
|
|
24059
|
-
};
|
|
24318
|
+
return mcpJson({
|
|
24319
|
+
...summary,
|
|
24320
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
24321
|
+
});
|
|
24060
24322
|
}
|
|
24061
24323
|
);
|
|
24062
24324
|
server.tool(
|
|
24063
24325
|
"get_pr_checks",
|
|
24064
24326
|
"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.",
|
|
24065
|
-
{ ref:
|
|
24327
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref") },
|
|
24066
24328
|
async ({ ref }) => {
|
|
24067
24329
|
try {
|
|
24068
24330
|
const checks = await orch.getPrChecks(ref);
|
|
24069
|
-
return
|
|
24070
|
-
content: [{ type: "text", text: JSON.stringify(checks) }]
|
|
24071
|
-
};
|
|
24331
|
+
return mcpJson(checks);
|
|
24072
24332
|
} catch (err) {
|
|
24073
24333
|
const message = err instanceof Error ? err.message : String(err);
|
|
24074
24334
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24078,25 +24338,18 @@ async function startMcpServer() {
|
|
|
24078
24338
|
server.tool(
|
|
24079
24339
|
"request_review",
|
|
24080
24340
|
'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.',
|
|
24081
|
-
{ ref:
|
|
24341
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref to review") },
|
|
24082
24342
|
async ({ ref }) => {
|
|
24083
24343
|
try {
|
|
24084
24344
|
const tab = await orch.requestReview(ref);
|
|
24085
24345
|
const from = orch.getThread(ref);
|
|
24086
|
-
return {
|
|
24087
|
-
|
|
24088
|
-
|
|
24089
|
-
|
|
24090
|
-
|
|
24091
|
-
|
|
24092
|
-
|
|
24093
|
-
status: tab.status,
|
|
24094
|
-
fromThreadId: from?.id ?? ref,
|
|
24095
|
-
link: `sideboard://thread/${tab.id}`
|
|
24096
|
-
})
|
|
24097
|
-
}
|
|
24098
|
-
]
|
|
24099
|
-
};
|
|
24346
|
+
return mcpJson({
|
|
24347
|
+
id: tab.id,
|
|
24348
|
+
title: tab.title,
|
|
24349
|
+
status: tab.status,
|
|
24350
|
+
fromThreadId: from?.id ?? ref,
|
|
24351
|
+
link: `sideboard://thread/${tab.id}`
|
|
24352
|
+
});
|
|
24100
24353
|
} catch (err) {
|
|
24101
24354
|
const message = err instanceof Error ? err.message : String(err);
|
|
24102
24355
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24107,53 +24360,37 @@ async function startMcpServer() {
|
|
|
24107
24360
|
"ask_git",
|
|
24108
24361
|
"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.",
|
|
24109
24362
|
{
|
|
24110
|
-
ref:
|
|
24111
|
-
action:
|
|
24363
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref"),
|
|
24364
|
+
action: import_zod8.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
24112
24365
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
24113
24366
|
)
|
|
24114
24367
|
},
|
|
24115
24368
|
async ({ ref, action }) => {
|
|
24116
24369
|
try {
|
|
24117
24370
|
const thread = await orch.askGit(ref, action);
|
|
24118
|
-
return {
|
|
24119
|
-
|
|
24120
|
-
|
|
24121
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
24124
|
-
|
|
24125
|
-
queueLength: thread.queue.length,
|
|
24126
|
-
action,
|
|
24127
|
-
link: `sideboard://thread/${thread.id}`
|
|
24128
|
-
})
|
|
24129
|
-
}
|
|
24130
|
-
]
|
|
24131
|
-
};
|
|
24371
|
+
return mcpJson({
|
|
24372
|
+
id: thread.id,
|
|
24373
|
+
status: thread.status,
|
|
24374
|
+
queueLength: thread.queue.length,
|
|
24375
|
+
action,
|
|
24376
|
+
link: `sideboard://thread/${thread.id}`
|
|
24377
|
+
});
|
|
24132
24378
|
} catch (err) {
|
|
24133
24379
|
const raw = err instanceof Error ? err.message : String(err);
|
|
24134
24380
|
const message = formatGhLandError(raw);
|
|
24135
|
-
return
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
24139
|
-
|
|
24140
|
-
|
|
24141
|
-
|
|
24142
|
-
|
|
24143
|
-
|
|
24144
|
-
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
|
|
24145
|
-
},
|
|
24146
|
-
null,
|
|
24147
|
-
2
|
|
24148
|
-
)
|
|
24149
|
-
}
|
|
24150
|
-
],
|
|
24151
|
-
isError: true
|
|
24152
|
-
};
|
|
24381
|
+
return mcpJson(
|
|
24382
|
+
{
|
|
24383
|
+
error: true,
|
|
24384
|
+
action,
|
|
24385
|
+
lastError: message,
|
|
24386
|
+
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
|
|
24387
|
+
},
|
|
24388
|
+
true
|
|
24389
|
+
);
|
|
24153
24390
|
}
|
|
24154
24391
|
}
|
|
24155
24392
|
);
|
|
24156
|
-
const agentEnum =
|
|
24393
|
+
const agentEnum = import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
24157
24394
|
server.tool(
|
|
24158
24395
|
"list_models",
|
|
24159
24396
|
"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.",
|
|
@@ -24163,9 +24400,7 @@ async function startMcpServer() {
|
|
|
24163
24400
|
async ({ agent }) => {
|
|
24164
24401
|
try {
|
|
24165
24402
|
const catalogs = await listModelsForAgent(agent);
|
|
24166
|
-
return
|
|
24167
|
-
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
24168
|
-
};
|
|
24403
|
+
return mcpJson(catalogs);
|
|
24169
24404
|
} catch (err) {
|
|
24170
24405
|
const message = err instanceof Error ? err.message : String(err);
|
|
24171
24406
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24176,11 +24411,11 @@ async function startMcpServer() {
|
|
|
24176
24411
|
"fork_worktree",
|
|
24177
24412
|
"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.",
|
|
24178
24413
|
{
|
|
24179
|
-
ref:
|
|
24180
|
-
through_index:
|
|
24414
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref to fork"),
|
|
24415
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
24181
24416
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
24182
|
-
model:
|
|
24183
|
-
title:
|
|
24417
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
24418
|
+
title: import_zod8.z.string().optional()
|
|
24184
24419
|
},
|
|
24185
24420
|
async ({ ref, through_index, agent, model, title }) => {
|
|
24186
24421
|
try {
|
|
@@ -24193,24 +24428,17 @@ async function startMcpServer() {
|
|
|
24193
24428
|
model,
|
|
24194
24429
|
title
|
|
24195
24430
|
});
|
|
24196
|
-
return {
|
|
24197
|
-
|
|
24198
|
-
|
|
24199
|
-
|
|
24200
|
-
|
|
24201
|
-
|
|
24202
|
-
|
|
24203
|
-
|
|
24204
|
-
|
|
24205
|
-
|
|
24206
|
-
|
|
24207
|
-
worktreePath: thread.worktreePath,
|
|
24208
|
-
fromThreadId: source?.id ?? ref,
|
|
24209
|
-
link: `sideboard://thread/${thread.id}`
|
|
24210
|
-
})
|
|
24211
|
-
}
|
|
24212
|
-
]
|
|
24213
|
-
};
|
|
24431
|
+
return mcpJson({
|
|
24432
|
+
id: thread.id,
|
|
24433
|
+
title: thread.title,
|
|
24434
|
+
status: thread.status,
|
|
24435
|
+
agent: thread.agent,
|
|
24436
|
+
model: thread.model,
|
|
24437
|
+
branchName: thread.branchName,
|
|
24438
|
+
worktreePath: thread.worktreePath,
|
|
24439
|
+
fromThreadId: source?.id ?? ref,
|
|
24440
|
+
link: `sideboard://thread/${thread.id}`
|
|
24441
|
+
});
|
|
24214
24442
|
} catch (err) {
|
|
24215
24443
|
const message = err instanceof Error ? err.message : String(err);
|
|
24216
24444
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24221,11 +24449,11 @@ async function startMcpServer() {
|
|
|
24221
24449
|
"fork_chat",
|
|
24222
24450
|
"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.",
|
|
24223
24451
|
{
|
|
24224
|
-
ref:
|
|
24225
|
-
through_index:
|
|
24452
|
+
ref: import_zod8.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
24453
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
24226
24454
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
24227
|
-
model:
|
|
24228
|
-
title:
|
|
24455
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
24456
|
+
title: import_zod8.z.string().optional()
|
|
24229
24457
|
},
|
|
24230
24458
|
async ({ ref, through_index, agent, model, title }) => {
|
|
24231
24459
|
try {
|
|
@@ -24243,24 +24471,17 @@ async function startMcpServer() {
|
|
|
24243
24471
|
model,
|
|
24244
24472
|
title
|
|
24245
24473
|
});
|
|
24246
|
-
return {
|
|
24247
|
-
|
|
24248
|
-
|
|
24249
|
-
|
|
24250
|
-
|
|
24251
|
-
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
worktreePath: tab.worktreePath,
|
|
24258
|
-
fromThreadId: source.id,
|
|
24259
|
-
link: `sideboard://thread/${tab.id}`
|
|
24260
|
-
})
|
|
24261
|
-
}
|
|
24262
|
-
]
|
|
24263
|
-
};
|
|
24474
|
+
return mcpJson({
|
|
24475
|
+
id: tab.id,
|
|
24476
|
+
title: tab.title,
|
|
24477
|
+
status: tab.status,
|
|
24478
|
+
agent: tab.agent,
|
|
24479
|
+
model: tab.model,
|
|
24480
|
+
sourceType: tab.sourceType,
|
|
24481
|
+
worktreePath: tab.worktreePath,
|
|
24482
|
+
fromThreadId: source.id,
|
|
24483
|
+
link: `sideboard://thread/${tab.id}`
|
|
24484
|
+
});
|
|
24264
24485
|
} catch (err) {
|
|
24265
24486
|
const message = err instanceof Error ? err.message : String(err);
|
|
24266
24487
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -24271,49 +24492,35 @@ async function startMcpServer() {
|
|
|
24271
24492
|
"run_dev_script",
|
|
24272
24493
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
24273
24494
|
{
|
|
24274
|
-
ref:
|
|
24275
|
-
name:
|
|
24495
|
+
ref: import_zod8.z.string(),
|
|
24496
|
+
name: import_zod8.z.string().optional()
|
|
24276
24497
|
},
|
|
24277
24498
|
async ({ ref, name }) => {
|
|
24278
24499
|
const result = await orch.startDev(ref, name);
|
|
24279
|
-
return {
|
|
24280
|
-
|
|
24281
|
-
|
|
24282
|
-
|
|
24283
|
-
|
|
24284
|
-
|
|
24285
|
-
scriptName: result.scriptName,
|
|
24286
|
-
ports: result.ports,
|
|
24287
|
-
url: `http://localhost:${result.port}`
|
|
24288
|
-
})
|
|
24289
|
-
}
|
|
24290
|
-
]
|
|
24291
|
-
};
|
|
24500
|
+
return mcpJson({
|
|
24501
|
+
port: result.port,
|
|
24502
|
+
scriptName: result.scriptName,
|
|
24503
|
+
ports: result.ports,
|
|
24504
|
+
url: `http://localhost:${result.port}`
|
|
24505
|
+
});
|
|
24292
24506
|
}
|
|
24293
24507
|
);
|
|
24294
24508
|
server.tool(
|
|
24295
24509
|
"list_run_scripts",
|
|
24296
24510
|
"List named run scripts available for a thread",
|
|
24297
|
-
{ ref:
|
|
24511
|
+
{ ref: import_zod8.z.string() },
|
|
24298
24512
|
async ({ ref }) => {
|
|
24299
24513
|
const scripts = orch.listThreadRunScripts(ref);
|
|
24300
24514
|
const active = orch.getActiveRuns(ref);
|
|
24301
|
-
return {
|
|
24302
|
-
content: [
|
|
24303
|
-
{
|
|
24304
|
-
type: "text",
|
|
24305
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
24306
|
-
}
|
|
24307
|
-
]
|
|
24308
|
-
};
|
|
24515
|
+
return mcpJson({ scripts, active });
|
|
24309
24516
|
}
|
|
24310
24517
|
);
|
|
24311
24518
|
server.tool(
|
|
24312
24519
|
"stop_dev_script",
|
|
24313
24520
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
24314
24521
|
{
|
|
24315
|
-
ref:
|
|
24316
|
-
name:
|
|
24522
|
+
ref: import_zod8.z.string(),
|
|
24523
|
+
name: import_zod8.z.string().optional()
|
|
24317
24524
|
},
|
|
24318
24525
|
async ({ ref, name }) => {
|
|
24319
24526
|
orch.stopDev(ref, name);
|
|
@@ -24323,27 +24530,25 @@ async function startMcpServer() {
|
|
|
24323
24530
|
server.tool(
|
|
24324
24531
|
"run_setup",
|
|
24325
24532
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
24326
|
-
{ ref:
|
|
24533
|
+
{ ref: import_zod8.z.string() },
|
|
24327
24534
|
async ({ ref }) => {
|
|
24328
24535
|
const result = await orch.runSetup(ref);
|
|
24329
|
-
return
|
|
24330
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
24331
|
-
};
|
|
24536
|
+
return mcpJson(result);
|
|
24332
24537
|
}
|
|
24333
24538
|
);
|
|
24334
24539
|
server.tool(
|
|
24335
24540
|
"add_workspace",
|
|
24336
24541
|
"Register a git repo as a Sideboard workspace",
|
|
24337
|
-
{ repoPath:
|
|
24542
|
+
{ repoPath: import_zod8.z.string() },
|
|
24338
24543
|
async ({ repoPath }) => {
|
|
24339
24544
|
const ws = await orch.addWorkspace(repoPath);
|
|
24340
|
-
return
|
|
24545
|
+
return mcpJson(ws);
|
|
24341
24546
|
}
|
|
24342
24547
|
);
|
|
24343
24548
|
server.tool(
|
|
24344
24549
|
"remove_workspace",
|
|
24345
24550
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
24346
|
-
{ repoPath:
|
|
24551
|
+
{ repoPath: import_zod8.z.string() },
|
|
24347
24552
|
async ({ repoPath }) => {
|
|
24348
24553
|
orch.removeWorkspace(repoPath);
|
|
24349
24554
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -24353,40 +24558,31 @@ async function startMcpServer() {
|
|
|
24353
24558
|
"fanout",
|
|
24354
24559
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
24355
24560
|
{
|
|
24356
|
-
prompt:
|
|
24357
|
-
agents:
|
|
24358
|
-
repoPath:
|
|
24359
|
-
sourceType:
|
|
24360
|
-
sourceRef:
|
|
24361
|
-
title:
|
|
24561
|
+
prompt: import_zod8.z.string(),
|
|
24562
|
+
agents: import_zod8.z.array(import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
24563
|
+
repoPath: import_zod8.z.string(),
|
|
24564
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
24565
|
+
sourceRef: import_zod8.z.string().optional(),
|
|
24566
|
+
title: import_zod8.z.string().optional()
|
|
24362
24567
|
},
|
|
24363
24568
|
async (args) => {
|
|
24364
24569
|
const threads = await orch.bestOfN(args);
|
|
24365
|
-
return
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
branchName: t.branchName,
|
|
24374
|
-
worktreePath: t.worktreePath
|
|
24375
|
-
})),
|
|
24376
|
-
null,
|
|
24377
|
-
2
|
|
24378
|
-
)
|
|
24379
|
-
}
|
|
24380
|
-
]
|
|
24381
|
-
};
|
|
24570
|
+
return mcpJson(
|
|
24571
|
+
threads.map((t) => ({
|
|
24572
|
+
id: t.id,
|
|
24573
|
+
agent: t.agent,
|
|
24574
|
+
branchName: t.branchName,
|
|
24575
|
+
worktreePath: t.worktreePath
|
|
24576
|
+
}))
|
|
24577
|
+
);
|
|
24382
24578
|
}
|
|
24383
24579
|
);
|
|
24384
24580
|
server.tool(
|
|
24385
24581
|
"list_branches",
|
|
24386
24582
|
"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).",
|
|
24387
24583
|
{
|
|
24388
|
-
repoPath:
|
|
24389
|
-
unmergedOnly:
|
|
24584
|
+
repoPath: import_zod8.z.string(),
|
|
24585
|
+
unmergedOnly: import_zod8.z.boolean().optional()
|
|
24390
24586
|
},
|
|
24391
24587
|
async ({ repoPath, unmergedOnly }) => {
|
|
24392
24588
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -24407,19 +24603,19 @@ async function startMcpServer() {
|
|
|
24407
24603
|
"list_prs",
|
|
24408
24604
|
'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.',
|
|
24409
24605
|
{
|
|
24410
|
-
repoPath:
|
|
24411
|
-
query:
|
|
24412
|
-
queue:
|
|
24606
|
+
repoPath: import_zod8.z.string(),
|
|
24607
|
+
query: import_zod8.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
24608
|
+
queue: import_zod8.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
24413
24609
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
24414
24610
|
),
|
|
24415
|
-
state:
|
|
24416
|
-
label:
|
|
24611
|
+
state: import_zod8.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
24612
|
+
label: import_zod8.z.string().optional().describe(
|
|
24417
24613
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
24418
24614
|
),
|
|
24419
|
-
reviewer:
|
|
24615
|
+
reviewer: import_zod8.z.string().optional().describe(
|
|
24420
24616
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
24421
24617
|
),
|
|
24422
|
-
limit:
|
|
24618
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
24423
24619
|
},
|
|
24424
24620
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
24425
24621
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -24451,89 +24647,65 @@ async function startMcpServer() {
|
|
|
24451
24647
|
server.tool(
|
|
24452
24648
|
"get_pr_stack",
|
|
24453
24649
|
"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).",
|
|
24454
|
-
{ ref:
|
|
24650
|
+
{ ref: import_zod8.z.string() },
|
|
24455
24651
|
async ({ ref }) => {
|
|
24456
24652
|
const stack = await orch.getPrStack(ref);
|
|
24457
|
-
return
|
|
24458
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
24459
|
-
};
|
|
24653
|
+
return mcpJson(stack);
|
|
24460
24654
|
}
|
|
24461
24655
|
);
|
|
24462
24656
|
server.tool(
|
|
24463
24657
|
"open_pr_stack_layers",
|
|
24464
24658
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
24465
24659
|
{
|
|
24466
|
-
ref:
|
|
24467
|
-
layer:
|
|
24660
|
+
ref: import_zod8.z.string(),
|
|
24661
|
+
layer: import_zod8.z.number().int().positive().optional()
|
|
24468
24662
|
},
|
|
24469
24663
|
async ({ ref, layer }) => {
|
|
24470
24664
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
24471
|
-
return {
|
|
24472
|
-
|
|
24473
|
-
|
|
24474
|
-
|
|
24475
|
-
|
|
24476
|
-
|
|
24477
|
-
|
|
24478
|
-
|
|
24479
|
-
|
|
24480
|
-
|
|
24481
|
-
|
|
24482
|
-
|
|
24483
|
-
|
|
24484
|
-
worktreePath: t.worktreePath,
|
|
24485
|
-
prUrl: t.prUrl,
|
|
24486
|
-
link: `sideboard://thread/${t.id}`
|
|
24487
|
-
}))
|
|
24488
|
-
},
|
|
24489
|
-
null,
|
|
24490
|
-
2
|
|
24491
|
-
)
|
|
24492
|
-
}
|
|
24493
|
-
]
|
|
24494
|
-
};
|
|
24665
|
+
return mcpJson({
|
|
24666
|
+
stackNumber: result.stack.stackNumber,
|
|
24667
|
+
trunk: result.stack.trunk,
|
|
24668
|
+
threads: result.threads.map((t) => ({
|
|
24669
|
+
id: t.id,
|
|
24670
|
+
title: t.title,
|
|
24671
|
+
branchName: t.branchName,
|
|
24672
|
+
stackLayer: t.stackLayer,
|
|
24673
|
+
worktreePath: t.worktreePath,
|
|
24674
|
+
prUrl: t.prUrl,
|
|
24675
|
+
link: `sideboard://thread/${t.id}`
|
|
24676
|
+
}))
|
|
24677
|
+
});
|
|
24495
24678
|
}
|
|
24496
24679
|
);
|
|
24497
24680
|
server.tool(
|
|
24498
24681
|
"add_stack_layer",
|
|
24499
24682
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
24500
24683
|
{
|
|
24501
|
-
ref:
|
|
24502
|
-
branchName:
|
|
24503
|
-
title:
|
|
24684
|
+
ref: import_zod8.z.string(),
|
|
24685
|
+
branchName: import_zod8.z.string(),
|
|
24686
|
+
title: import_zod8.z.string().optional()
|
|
24504
24687
|
},
|
|
24505
24688
|
async ({ ref, branchName, title }) => {
|
|
24506
24689
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
24507
|
-
return {
|
|
24508
|
-
|
|
24509
|
-
|
|
24510
|
-
|
|
24511
|
-
|
|
24512
|
-
|
|
24513
|
-
|
|
24514
|
-
|
|
24515
|
-
branchName: result.thread.branchName,
|
|
24516
|
-
stackLayer: result.thread.stackLayer,
|
|
24517
|
-
worktreePath: result.thread.worktreePath,
|
|
24518
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
24519
|
-
},
|
|
24520
|
-
null,
|
|
24521
|
-
2
|
|
24522
|
-
)
|
|
24523
|
-
}
|
|
24524
|
-
]
|
|
24525
|
-
};
|
|
24690
|
+
return mcpJson({
|
|
24691
|
+
id: result.thread.id,
|
|
24692
|
+
title: result.thread.title,
|
|
24693
|
+
branchName: result.thread.branchName,
|
|
24694
|
+
stackLayer: result.thread.stackLayer,
|
|
24695
|
+
worktreePath: result.thread.worktreePath,
|
|
24696
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
24697
|
+
});
|
|
24526
24698
|
}
|
|
24527
24699
|
);
|
|
24528
24700
|
server.tool(
|
|
24529
24701
|
"create_pr_stack",
|
|
24530
24702
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
24531
24703
|
{
|
|
24532
|
-
repoPath:
|
|
24533
|
-
branches:
|
|
24534
|
-
agent:
|
|
24535
|
-
base:
|
|
24536
|
-
title:
|
|
24704
|
+
repoPath: import_zod8.z.string(),
|
|
24705
|
+
branches: import_zod8.z.array(import_zod8.z.string()).min(1),
|
|
24706
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
24707
|
+
base: import_zod8.z.string().optional(),
|
|
24708
|
+
title: import_zod8.z.string().optional()
|
|
24537
24709
|
},
|
|
24538
24710
|
async (args) => {
|
|
24539
24711
|
const result = await orch.createPrStack({
|
|
@@ -24543,39 +24715,28 @@ async function startMcpServer() {
|
|
|
24543
24715
|
base: args.base,
|
|
24544
24716
|
title: args.title
|
|
24545
24717
|
});
|
|
24546
|
-
return {
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
|
|
24550
|
-
|
|
24551
|
-
|
|
24552
|
-
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
24556
|
-
|
|
24557
|
-
|
|
24558
|
-
stackLayer: t.stackLayer,
|
|
24559
|
-
worktreePath: t.worktreePath,
|
|
24560
|
-
link: `sideboard://thread/${t.id}`
|
|
24561
|
-
}))
|
|
24562
|
-
},
|
|
24563
|
-
null,
|
|
24564
|
-
2
|
|
24565
|
-
)
|
|
24566
|
-
}
|
|
24567
|
-
]
|
|
24568
|
-
};
|
|
24718
|
+
return mcpJson({
|
|
24719
|
+
stackNumber: result.stack.stackNumber,
|
|
24720
|
+
trunk: result.stack.trunk,
|
|
24721
|
+
threads: result.threads.map((t) => ({
|
|
24722
|
+
id: t.id,
|
|
24723
|
+
title: t.title,
|
|
24724
|
+
branchName: t.branchName,
|
|
24725
|
+
stackLayer: t.stackLayer,
|
|
24726
|
+
worktreePath: t.worktreePath,
|
|
24727
|
+
link: `sideboard://thread/${t.id}`
|
|
24728
|
+
}))
|
|
24729
|
+
});
|
|
24569
24730
|
}
|
|
24570
24731
|
);
|
|
24571
24732
|
server.tool(
|
|
24572
24733
|
"list_issues",
|
|
24573
24734
|
"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.",
|
|
24574
24735
|
{
|
|
24575
|
-
repoPath:
|
|
24576
|
-
query:
|
|
24577
|
-
assignee:
|
|
24578
|
-
limit:
|
|
24736
|
+
repoPath: import_zod8.z.string(),
|
|
24737
|
+
query: import_zod8.z.string().optional().describe("Search title, identifier, or description"),
|
|
24738
|
+
assignee: import_zod8.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
24739
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
24579
24740
|
},
|
|
24580
24741
|
async ({ repoPath, query, assignee, limit }) => {
|
|
24581
24742
|
const root = await resolveRepoRoot(repoPath);
|