@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/index.cjs
CHANGED
|
@@ -10215,6 +10215,17 @@ function partsToAssistantText(parts) {
|
|
|
10215
10215
|
(p) => p.type === "text" && !messagePartParentId(p)
|
|
10216
10216
|
).map((p) => p.text).join("").trim();
|
|
10217
10217
|
}
|
|
10218
|
+
function lastAssistantMessageText(parts) {
|
|
10219
|
+
if (!parts?.length) return "";
|
|
10220
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
10221
|
+
const p = parts[i];
|
|
10222
|
+
if (!p || p.type !== "text" || messagePartParentId(p)) continue;
|
|
10223
|
+
const text7 = p.text.trim();
|
|
10224
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
10225
|
+
return text7;
|
|
10226
|
+
}
|
|
10227
|
+
return "";
|
|
10228
|
+
}
|
|
10218
10229
|
function stripBrightsyNdjsonNoise(text7) {
|
|
10219
10230
|
if (!text7 || !text7.includes('"type"')) return text7;
|
|
10220
10231
|
let out = text7;
|
|
@@ -16094,6 +16105,7 @@ var GLOBAL_WORKSPACE_ID2, BOARD_COLUMN_DEFS, HOME_BOARD_CACHE_TTL_MS, DEFAULT_WO
|
|
|
16094
16105
|
var init_home_board = __esm({
|
|
16095
16106
|
"src/board/home-board.ts"() {
|
|
16096
16107
|
"use strict";
|
|
16108
|
+
init_message_parts();
|
|
16097
16109
|
init_worktree_labels();
|
|
16098
16110
|
GLOBAL_WORKSPACE_ID2 = "__global__";
|
|
16099
16111
|
BOARD_COLUMN_DEFS = [
|
|
@@ -16894,6 +16906,23 @@ var init_create = __esm({
|
|
|
16894
16906
|
}
|
|
16895
16907
|
});
|
|
16896
16908
|
|
|
16909
|
+
// src/threads/implied-first-prompt.ts
|
|
16910
|
+
function resolveCreateFirstPrompt(input) {
|
|
16911
|
+
const typed = input.prompt?.trim();
|
|
16912
|
+
if (typed) return typed;
|
|
16913
|
+
if (input.sourceType !== "ticket") return void 0;
|
|
16914
|
+
if (input.parentThreadId) return void 0;
|
|
16915
|
+
if (input.reused && input.hasUserMessages) return void 0;
|
|
16916
|
+
return IMPLIED_TICKET_RESOLVE_PROMPT;
|
|
16917
|
+
}
|
|
16918
|
+
var IMPLIED_TICKET_RESOLVE_PROMPT;
|
|
16919
|
+
var init_implied_first_prompt = __esm({
|
|
16920
|
+
"src/threads/implied-first-prompt.ts"() {
|
|
16921
|
+
"use strict";
|
|
16922
|
+
IMPLIED_TICKET_RESOLVE_PROMPT = "Resolve this issue.";
|
|
16923
|
+
}
|
|
16924
|
+
});
|
|
16925
|
+
|
|
16897
16926
|
// src/threads/cowboy.ts
|
|
16898
16927
|
function isCowboyThread(thread) {
|
|
16899
16928
|
return Boolean(thread?.cowboy);
|
|
@@ -18085,13 +18114,13 @@ function formatStat(files) {
|
|
|
18085
18114
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
18086
18115
|
}
|
|
18087
18116
|
function emptyScopeStats() {
|
|
18088
|
-
const
|
|
18117
|
+
const z9 = emptyStat();
|
|
18089
18118
|
return {
|
|
18090
|
-
commits:
|
|
18091
|
-
uncommitted:
|
|
18092
|
-
staged:
|
|
18093
|
-
unstaged:
|
|
18094
|
-
last_turn:
|
|
18119
|
+
commits: z9,
|
|
18120
|
+
uncommitted: z9,
|
|
18121
|
+
staged: z9,
|
|
18122
|
+
unstaged: z9,
|
|
18123
|
+
last_turn: z9
|
|
18095
18124
|
};
|
|
18096
18125
|
}
|
|
18097
18126
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -19232,17 +19261,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
19232
19261
|
];
|
|
19233
19262
|
if (opts.linear) {
|
|
19234
19263
|
lines.push(
|
|
19235
|
-
"- Linear: `linear_get_issue` (comments), `linear_comment`, `linear_update_issue` (state), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
19264
|
+
"- Linear: `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
19236
19265
|
);
|
|
19237
19266
|
}
|
|
19238
19267
|
if (github) {
|
|
19239
19268
|
lines.push(
|
|
19240
|
-
"- GitHub: `github_get_issue` (comments), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
19269
|
+
"- GitHub: `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
19241
19270
|
);
|
|
19242
19271
|
}
|
|
19243
19272
|
if (opts.abletime) {
|
|
19244
19273
|
lines.push(
|
|
19245
|
-
"- AbleTime: `abletime_get_task` (comments), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
19274
|
+
"- AbleTime: `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
19246
19275
|
);
|
|
19247
19276
|
}
|
|
19248
19277
|
lines.push(
|
|
@@ -20075,6 +20104,7 @@ var init_orchestrator = __esm({
|
|
|
20075
20104
|
init_child_halt();
|
|
20076
20105
|
init_wait_for_job();
|
|
20077
20106
|
init_create();
|
|
20107
|
+
init_implied_first_prompt();
|
|
20078
20108
|
init_cowboy();
|
|
20079
20109
|
init_orchestrator_capable();
|
|
20080
20110
|
init_chat_tabs();
|
|
@@ -20481,10 +20511,18 @@ var init_orchestrator = __esm({
|
|
|
20481
20511
|
listThreads({ includeArchived: false }).map((t) => t.id)
|
|
20482
20512
|
);
|
|
20483
20513
|
const thread = await createThread(input);
|
|
20484
|
-
|
|
20485
|
-
|
|
20514
|
+
const reused = prior.has(thread.id);
|
|
20515
|
+
const prompt = resolveCreateFirstPrompt({
|
|
20516
|
+
sourceType: input.sourceType,
|
|
20517
|
+
prompt: input.prompt,
|
|
20518
|
+
parentThreadId: input.parentThreadId ?? thread.parentThreadId,
|
|
20519
|
+
reused,
|
|
20520
|
+
hasUserMessages: thread.messages.some((m) => m.role === "user")
|
|
20521
|
+
});
|
|
20522
|
+
if (reused) {
|
|
20523
|
+
if (prompt) {
|
|
20486
20524
|
try {
|
|
20487
|
-
await this.send(thread.id,
|
|
20525
|
+
await this.send(thread.id, prompt);
|
|
20488
20526
|
} catch (err) {
|
|
20489
20527
|
const message = err instanceof Error ? err.message : String(err);
|
|
20490
20528
|
updateThread(thread.id, {
|
|
@@ -20495,7 +20533,7 @@ var init_orchestrator = __esm({
|
|
|
20495
20533
|
return thread;
|
|
20496
20534
|
}
|
|
20497
20535
|
this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
|
|
20498
|
-
void this.finishCreateThread(thread.id,
|
|
20536
|
+
void this.finishCreateThread(thread.id, prompt);
|
|
20499
20537
|
return thread;
|
|
20500
20538
|
}
|
|
20501
20539
|
async finishCreateThread(threadId, prompt) {
|
|
@@ -22339,6 +22377,7 @@ __export(index_exports, {
|
|
|
22339
22377
|
GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
|
|
22340
22378
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
22341
22379
|
HOME_BOARD_CACHE_TTL_MS: () => HOME_BOARD_CACHE_TTL_MS,
|
|
22380
|
+
IMPLIED_TICKET_RESOLVE_PROMPT: () => IMPLIED_TICKET_RESOLVE_PROMPT,
|
|
22342
22381
|
ISSUE_SOURCE_LABELS: () => ISSUE_SOURCE_LABELS,
|
|
22343
22382
|
LEGACY_ATTACHMENTS_DIR: () => LEGACY_ATTACHMENTS_DIR,
|
|
22344
22383
|
LEGACY_DETACHED_JOBS_DIR: () => LEGACY_DETACHED_JOBS_DIR,
|
|
@@ -22741,6 +22780,7 @@ __export(index_exports, {
|
|
|
22741
22780
|
issueMatchesAssignee: () => issueMatchesAssignee,
|
|
22742
22781
|
issueSourceLabel: () => issueSourceLabel,
|
|
22743
22782
|
issueTicketFromThread: () => issueTicketFromThread,
|
|
22783
|
+
lastAssistantMessageText: () => lastAssistantMessageText,
|
|
22744
22784
|
lastRequestOccupancy: () => lastRequestOccupancy,
|
|
22745
22785
|
latestPendingPlanQuestions: () => latestPendingPlanQuestions,
|
|
22746
22786
|
linearAuthorizationHeader: () => linearAuthorizationHeader,
|
|
@@ -22891,6 +22931,7 @@ __export(index_exports, {
|
|
|
22891
22931
|
resolveCodexGitWritableRoots: () => resolveCodexGitWritableRoots,
|
|
22892
22932
|
resolveCommandBinarySync: () => resolveCommandBinarySync,
|
|
22893
22933
|
resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
|
|
22934
|
+
resolveCreateFirstPrompt: () => resolveCreateFirstPrompt,
|
|
22894
22935
|
resolveCursorModelId: () => resolveCursorModelId,
|
|
22895
22936
|
resolveDefaultBranch: () => resolveDefaultBranch,
|
|
22896
22937
|
resolveDetachedJobScript: () => resolveDetachedJobScript,
|
|
@@ -24579,6 +24620,7 @@ init_context_compact();
|
|
|
24579
24620
|
init_summarize();
|
|
24580
24621
|
init_land();
|
|
24581
24622
|
init_create();
|
|
24623
|
+
init_implied_first_prompt();
|
|
24582
24624
|
init_cowboy();
|
|
24583
24625
|
init_chat_tabs();
|
|
24584
24626
|
init_fork_worktree();
|
|
@@ -24718,7 +24760,7 @@ init_orphan_cleanup();
|
|
|
24718
24760
|
// src/mcp/server.ts
|
|
24719
24761
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
24720
24762
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
24721
|
-
var
|
|
24763
|
+
var import_zod8 = require("zod");
|
|
24722
24764
|
var import_node_crypto13 = require("crypto");
|
|
24723
24765
|
var import_node_path50 = require("path");
|
|
24724
24766
|
init_orchestrator();
|
|
@@ -24999,12 +25041,53 @@ ${link}` : link;
|
|
|
24999
25041
|
// src/mcp/slack-tools.ts
|
|
25000
25042
|
init_outbound_watch();
|
|
25001
25043
|
init_workspaces2();
|
|
25002
|
-
|
|
25044
|
+
|
|
25045
|
+
// src/mcp/issue-list.ts
|
|
25046
|
+
var MCP_ISSUE_LIST_DEFAULT_LIMIT = 40;
|
|
25047
|
+
var MCP_ISSUE_LIST_MAX_LIMIT = 250;
|
|
25048
|
+
function clampMcpIssueLimit(limit) {
|
|
25049
|
+
if (limit == null || !Number.isFinite(limit)) return MCP_ISSUE_LIST_DEFAULT_LIMIT;
|
|
25050
|
+
return Math.max(1, Math.min(MCP_ISSUE_LIST_MAX_LIMIT, Math.floor(limit)));
|
|
25051
|
+
}
|
|
25052
|
+
function applyIssueListWindow(items, limit) {
|
|
25053
|
+
const truncated = items.length > limit;
|
|
25054
|
+
return {
|
|
25055
|
+
items: truncated ? items.slice(0, limit) : items,
|
|
25056
|
+
truncated
|
|
25057
|
+
};
|
|
25058
|
+
}
|
|
25059
|
+
function compactIssueRow(issue) {
|
|
25060
|
+
return {
|
|
25061
|
+
identifier: issue.identifier,
|
|
25062
|
+
title: issue.title,
|
|
25063
|
+
...issue.assignee ? { assignee: issue.assignee } : {},
|
|
25064
|
+
...issue.labels.length > 0 ? { labels: issue.labels } : {},
|
|
25065
|
+
...issue.teamKey ? { team: issue.teamKey } : {},
|
|
25066
|
+
...issue.cycle?.name ? { cycle: issue.cycle.name } : {}
|
|
25067
|
+
};
|
|
25068
|
+
}
|
|
25069
|
+
function formatMcpIssueList(input) {
|
|
25003
25070
|
return {
|
|
25004
|
-
|
|
25071
|
+
...input.source ? { source: input.source } : {},
|
|
25072
|
+
...input.viewer ? { viewer: input.viewer } : {},
|
|
25073
|
+
limit: input.limit,
|
|
25074
|
+
returned: input.issues.length,
|
|
25075
|
+
truncated: input.truncated,
|
|
25076
|
+
...input.truncated ? { hint: "More issues match. Pass a higher limit (max 250) or a tighter query." } : {},
|
|
25077
|
+
issues: input.issues.map(compactIssueRow)
|
|
25078
|
+
};
|
|
25079
|
+
}
|
|
25080
|
+
function mcpJson(payload, isError = false) {
|
|
25081
|
+
return {
|
|
25082
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
25005
25083
|
...isError ? { isError: true } : {}
|
|
25006
25084
|
};
|
|
25007
25085
|
}
|
|
25086
|
+
|
|
25087
|
+
// src/mcp/slack-tools.ts
|
|
25088
|
+
function text(payload, isError = false) {
|
|
25089
|
+
return mcpJson(payload, isError);
|
|
25090
|
+
}
|
|
25008
25091
|
function fail(err) {
|
|
25009
25092
|
return text(
|
|
25010
25093
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
@@ -25273,49 +25356,331 @@ function registerSlackTools(server) {
|
|
|
25273
25356
|
init_app_settings();
|
|
25274
25357
|
|
|
25275
25358
|
// src/mcp/abletime-tools.ts
|
|
25359
|
+
var import_zod3 = require("zod");
|
|
25360
|
+
init_abletime();
|
|
25361
|
+
|
|
25362
|
+
// src/mcp/issue-payload.ts
|
|
25276
25363
|
var import_zod2 = require("zod");
|
|
25277
25364
|
init_abletime();
|
|
25278
25365
|
|
|
25279
|
-
// src/mcp/
|
|
25280
|
-
var
|
|
25281
|
-
var
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25366
|
+
// src/mcp/smart-crush.ts
|
|
25367
|
+
var MUST_KEEP_TEXT_RE = /\b(error|exception|failed|failure|fatal|critical|panic|traceback|npm err|assert)\b/i;
|
|
25368
|
+
var CRUSH_MIN_ITEMS = 5;
|
|
25369
|
+
var CRUSH_MIN_CHARS = 800;
|
|
25370
|
+
var CRUSH_COMMENT_MAX_CHARS = 16e3;
|
|
25371
|
+
var CRUSH_BODY_MIN_CHARS = 12e3;
|
|
25372
|
+
var CRUSH_BODY_MAX_CHARS = 8e3;
|
|
25373
|
+
var NEAR_DUP_JACCARD = 0.92;
|
|
25374
|
+
function looksLikeMustKeep(text7) {
|
|
25375
|
+
return MUST_KEEP_TEXT_RE.test(text7);
|
|
25376
|
+
}
|
|
25377
|
+
function normalizeText(text7) {
|
|
25378
|
+
return text7.replace(/\s+/g, " ").trim().toLowerCase();
|
|
25379
|
+
}
|
|
25380
|
+
function wordSet(text7) {
|
|
25381
|
+
return new Set(
|
|
25382
|
+
text7.toLowerCase().split(/\W+/).filter((w) => w.length > 2)
|
|
25383
|
+
);
|
|
25285
25384
|
}
|
|
25286
|
-
function
|
|
25287
|
-
|
|
25385
|
+
function jaccard(a, b) {
|
|
25386
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
25387
|
+
let inter = 0;
|
|
25388
|
+
for (const w of a) {
|
|
25389
|
+
if (b.has(w)) inter += 1;
|
|
25390
|
+
}
|
|
25391
|
+
const union = a.size + b.size - inter;
|
|
25392
|
+
return union === 0 ? 0 : inter / union;
|
|
25393
|
+
}
|
|
25394
|
+
function importance(text7) {
|
|
25395
|
+
let score = text7.length;
|
|
25396
|
+
if (/```/.test(text7)) score += 400;
|
|
25397
|
+
if (/https?:\/\//.test(text7)) score += 50;
|
|
25398
|
+
if (looksLikeMustKeep(text7)) score += 1e4;
|
|
25399
|
+
return score;
|
|
25400
|
+
}
|
|
25401
|
+
function meanStd(values) {
|
|
25402
|
+
if (values.length === 0) return { mean: 0, std: 0 };
|
|
25403
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
25404
|
+
const variance = values.reduce((a, b) => a + (b - mean) * (b - mean), 0) / values.length;
|
|
25405
|
+
return { mean, std: Math.sqrt(variance) };
|
|
25406
|
+
}
|
|
25407
|
+
function dedupTextItems(items, getText) {
|
|
25408
|
+
const kept = [];
|
|
25409
|
+
const keptNorm = [];
|
|
25410
|
+
const keptWords = [];
|
|
25411
|
+
for (const item of items) {
|
|
25412
|
+
const text7 = getText(item);
|
|
25413
|
+
const norm = normalizeText(text7);
|
|
25414
|
+
if (!norm) {
|
|
25415
|
+
kept.push(item);
|
|
25416
|
+
keptNorm.push(norm);
|
|
25417
|
+
keptWords.push(wordSet(text7));
|
|
25418
|
+
continue;
|
|
25419
|
+
}
|
|
25420
|
+
const words = wordSet(text7);
|
|
25421
|
+
let dupAt = -1;
|
|
25422
|
+
for (let i = 0; i < kept.length; i++) {
|
|
25423
|
+
if (keptNorm[i] === norm) {
|
|
25424
|
+
dupAt = i;
|
|
25425
|
+
break;
|
|
25426
|
+
}
|
|
25427
|
+
if (keptWords[i].size >= 8 && words.size >= 8 && jaccard(keptWords[i], words) >= NEAR_DUP_JACCARD) {
|
|
25428
|
+
dupAt = i;
|
|
25429
|
+
break;
|
|
25430
|
+
}
|
|
25431
|
+
}
|
|
25432
|
+
if (dupAt < 0) {
|
|
25433
|
+
kept.push(item);
|
|
25434
|
+
keptNorm.push(norm);
|
|
25435
|
+
keptWords.push(words);
|
|
25436
|
+
continue;
|
|
25437
|
+
}
|
|
25438
|
+
if (text7.length > getText(kept[dupAt]).length) {
|
|
25439
|
+
kept[dupAt] = item;
|
|
25440
|
+
keptNorm[dupAt] = norm;
|
|
25441
|
+
keptWords[dupAt] = words;
|
|
25442
|
+
}
|
|
25443
|
+
}
|
|
25444
|
+
return { items: kept, dropped: items.length - kept.length };
|
|
25445
|
+
}
|
|
25446
|
+
function crushTextItems(items, getText, opts = {}) {
|
|
25447
|
+
const minItems = opts.minItems ?? CRUSH_MIN_ITEMS;
|
|
25448
|
+
const minChars = opts.minChars ?? CRUSH_MIN_CHARS;
|
|
25449
|
+
const maxChars = opts.maxChars ?? CRUSH_COMMENT_MAX_CHARS;
|
|
25450
|
+
const original = items;
|
|
25451
|
+
const totalChars = items.reduce((n, item) => n + getText(item).length, 0);
|
|
25452
|
+
if (items.length < minItems || totalChars < minChars) {
|
|
25453
|
+
return {
|
|
25454
|
+
items,
|
|
25455
|
+
truncated: false,
|
|
25456
|
+
kept: items.length,
|
|
25457
|
+
dropped: 0,
|
|
25458
|
+
reason: "passthrough"
|
|
25459
|
+
};
|
|
25460
|
+
}
|
|
25461
|
+
const deduped = dedupTextItems(items, getText);
|
|
25462
|
+
const unique = deduped.items;
|
|
25463
|
+
const uniqueChars = unique.reduce((n, item) => n + getText(item).length, 0);
|
|
25464
|
+
if (uniqueChars <= maxChars) {
|
|
25465
|
+
const changed = unique.length < original.length;
|
|
25466
|
+
return {
|
|
25467
|
+
items: unique,
|
|
25468
|
+
truncated: changed,
|
|
25469
|
+
kept: unique.length,
|
|
25470
|
+
dropped: original.length - unique.length,
|
|
25471
|
+
reason: changed ? "dedup" : "passthrough"
|
|
25472
|
+
};
|
|
25473
|
+
}
|
|
25474
|
+
const lengths = unique.map((item) => getText(item).length);
|
|
25475
|
+
const { mean, std } = meanStd(lengths);
|
|
25476
|
+
const outlierFloor = mean + 2 * std;
|
|
25477
|
+
const mustKeep = /* @__PURE__ */ new Set();
|
|
25478
|
+
unique.forEach((item, i) => {
|
|
25479
|
+
const text7 = getText(item);
|
|
25480
|
+
if (looksLikeMustKeep(text7) || text7.length > outlierFloor) mustKeep.add(i);
|
|
25481
|
+
});
|
|
25482
|
+
const remainingIdx = unique.map((_, i) => i).filter((i) => !mustKeep.has(i));
|
|
25483
|
+
let used = [...mustKeep].reduce((n, i) => n + lengths[i], 0);
|
|
25484
|
+
const pick = new Set(mustKeep);
|
|
25485
|
+
const canTake = (i) => {
|
|
25486
|
+
if (pick.has(i)) return false;
|
|
25487
|
+
const next = used + lengths[i];
|
|
25488
|
+
if (next > maxChars && pick.size > mustKeep.size) return false;
|
|
25489
|
+
used = next;
|
|
25490
|
+
pick.add(i);
|
|
25491
|
+
return true;
|
|
25492
|
+
};
|
|
25493
|
+
const k = remainingIdx.length;
|
|
25494
|
+
const headN = Math.max(1, Math.ceil(k * 0.3));
|
|
25495
|
+
const tailN = Math.max(1, Math.ceil(k * 0.15));
|
|
25496
|
+
for (const i of remainingIdx.slice(0, headN)) canTake(i);
|
|
25497
|
+
for (const i of remainingIdx.slice(-tailN).reverse()) canTake(i);
|
|
25498
|
+
const middle = remainingIdx.filter((i) => !pick.has(i)).sort((a, b) => importance(getText(unique[b])) - importance(getText(unique[a])));
|
|
25499
|
+
for (const i of middle) {
|
|
25500
|
+
if (used >= maxChars) break;
|
|
25501
|
+
canTake(i);
|
|
25502
|
+
}
|
|
25503
|
+
const crushed = unique.filter((_, i) => pick.has(i));
|
|
25504
|
+
const crushedChars = crushed.reduce((n, item) => n + getText(item).length, 0);
|
|
25505
|
+
if (crushed.length >= original.length && crushedChars >= totalChars) {
|
|
25506
|
+
return {
|
|
25507
|
+
items: original,
|
|
25508
|
+
truncated: false,
|
|
25509
|
+
kept: original.length,
|
|
25510
|
+
dropped: 0,
|
|
25511
|
+
reason: "passthrough"
|
|
25512
|
+
};
|
|
25513
|
+
}
|
|
25288
25514
|
return {
|
|
25289
|
-
items:
|
|
25290
|
-
truncated
|
|
25515
|
+
items: crushed,
|
|
25516
|
+
truncated: crushed.length < original.length,
|
|
25517
|
+
kept: crushed.length,
|
|
25518
|
+
dropped: original.length - crushed.length,
|
|
25519
|
+
reason: "budget"
|
|
25291
25520
|
};
|
|
25292
25521
|
}
|
|
25293
|
-
function
|
|
25294
|
-
return
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25522
|
+
function splitBlocks(text7) {
|
|
25523
|
+
if (/\n\n/.test(text7)) return text7.split(/\n\n+/);
|
|
25524
|
+
return text7.split("\n");
|
|
25525
|
+
}
|
|
25526
|
+
function capOversizedBlock(block, maxChars) {
|
|
25527
|
+
if (block.length <= maxChars) return block;
|
|
25528
|
+
const head = Math.floor(maxChars * 0.3);
|
|
25529
|
+
const tail = Math.floor(maxChars * 0.15);
|
|
25530
|
+
return `${block.slice(0, head)}
|
|
25531
|
+
|
|
25532
|
+
[\u2026crushed ${block.length - head - tail} chars; pass include=full to read the rest\u2026]
|
|
25533
|
+
|
|
25534
|
+
${block.slice(-tail)}`;
|
|
25535
|
+
}
|
|
25536
|
+
function crushMarkdown(text7, opts) {
|
|
25537
|
+
const original = text7 ?? "";
|
|
25538
|
+
const minChars = opts?.minChars ?? CRUSH_BODY_MIN_CHARS;
|
|
25539
|
+
const maxChars = opts?.maxChars ?? CRUSH_BODY_MAX_CHARS;
|
|
25540
|
+
if (original.length < minChars) {
|
|
25541
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25542
|
+
}
|
|
25543
|
+
const rawBlocks = splitBlocks(original).filter((b) => b.length > 0);
|
|
25544
|
+
const blocks = rawBlocks.map((b) => capOversizedBlock(b, maxChars));
|
|
25545
|
+
const cappedABlock = blocks.some((b, i) => b !== rawBlocks[i]);
|
|
25546
|
+
if (blocks.length <= 1) {
|
|
25547
|
+
const cut = blocks[0] ?? original;
|
|
25548
|
+
if (cut.length >= original.length) {
|
|
25549
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25550
|
+
}
|
|
25551
|
+
return { text: cut, truncated: true, originalChars: original.length };
|
|
25552
|
+
}
|
|
25553
|
+
const must = blocks.filter((b) => looksLikeMustKeep(b));
|
|
25554
|
+
const rest = blocks.filter((b) => !looksLikeMustKeep(b));
|
|
25555
|
+
const marker = `[\u2026crushed body; ${original.length} chars originally. Pass include=full to read the rest.]`;
|
|
25556
|
+
let used = must.reduce((n, b) => n + b.length, 0) + marker.length;
|
|
25557
|
+
const keptRest = [];
|
|
25558
|
+
const headN = Math.max(1, Math.ceil(rest.length * 0.3));
|
|
25559
|
+
const tailN = Math.max(1, Math.ceil(rest.length * 0.15));
|
|
25560
|
+
const head = rest.slice(0, headN);
|
|
25561
|
+
const tail = rest.slice(-tailN);
|
|
25562
|
+
const middle = rest.slice(headN, Math.max(headN, rest.length - tailN));
|
|
25563
|
+
const take = (block) => {
|
|
25564
|
+
if (used + block.length + 2 > maxChars && keptRest.length + must.length > 0) {
|
|
25565
|
+
return false;
|
|
25566
|
+
}
|
|
25567
|
+
used += block.length + 2;
|
|
25568
|
+
keptRest.push(block);
|
|
25569
|
+
return true;
|
|
25301
25570
|
};
|
|
25571
|
+
for (const b of head) take(b);
|
|
25572
|
+
for (const b of tail) take(b);
|
|
25573
|
+
const ranked = [...middle].sort((a, b) => importance(b) - importance(a));
|
|
25574
|
+
for (const b of ranked) {
|
|
25575
|
+
if (used >= maxChars) break;
|
|
25576
|
+
take(b);
|
|
25577
|
+
}
|
|
25578
|
+
const keepSet = /* @__PURE__ */ new Set([...must, ...keptRest]);
|
|
25579
|
+
const ordered = blocks.filter((b) => keepSet.has(b));
|
|
25580
|
+
const omitted = blocks.length - ordered.length;
|
|
25581
|
+
if (omitted <= 0) {
|
|
25582
|
+
const joined = blocks.join("\n\n");
|
|
25583
|
+
if (cappedABlock && joined.length < original.length) {
|
|
25584
|
+
return { text: joined, truncated: true, originalChars: original.length };
|
|
25585
|
+
}
|
|
25586
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25587
|
+
}
|
|
25588
|
+
const out = [];
|
|
25589
|
+
let inserted = false;
|
|
25590
|
+
for (const block of blocks) {
|
|
25591
|
+
if (keepSet.has(block)) {
|
|
25592
|
+
out.push(block);
|
|
25593
|
+
continue;
|
|
25594
|
+
}
|
|
25595
|
+
if (!inserted) {
|
|
25596
|
+
out.push(marker);
|
|
25597
|
+
inserted = true;
|
|
25598
|
+
}
|
|
25599
|
+
}
|
|
25600
|
+
const textOut = out.join("\n\n");
|
|
25601
|
+
if (textOut.length >= original.length) {
|
|
25602
|
+
return { text: original, truncated: false, originalChars: original.length };
|
|
25603
|
+
}
|
|
25604
|
+
return { text: textOut, truncated: true, originalChars: original.length };
|
|
25302
25605
|
}
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25606
|
+
|
|
25607
|
+
// src/mcp/issue-payload.ts
|
|
25608
|
+
var MCP_ISSUE_INCLUDE_VALUES = ["default", "full"];
|
|
25609
|
+
var mcpIssueIncludeSchema = import_zod2.z.enum(MCP_ISSUE_INCLUDE_VALUES).optional().describe(
|
|
25610
|
+
"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."
|
|
25611
|
+
);
|
|
25612
|
+
var MCP_ISSUE_CRUSH_HINT = "Pass include=full to re-fetch the uncompressed ticket from the vendor.";
|
|
25613
|
+
function emptyMeta() {
|
|
25614
|
+
return { truncated: true, hint: MCP_ISSUE_CRUSH_HINT };
|
|
25615
|
+
}
|
|
25616
|
+
function withComments(comments, meta) {
|
|
25617
|
+
const crushed = crushTextItems(comments, (c) => c.body);
|
|
25618
|
+
if (crushed.truncated) {
|
|
25619
|
+
meta.comments = {
|
|
25620
|
+
kept: crushed.kept,
|
|
25621
|
+
dropped: crushed.dropped,
|
|
25622
|
+
reason: crushed.reason
|
|
25623
|
+
};
|
|
25624
|
+
}
|
|
25625
|
+
return { comments: crushed.items, meta };
|
|
25626
|
+
}
|
|
25627
|
+
function withBody(text7, meta) {
|
|
25628
|
+
const crushed = crushMarkdown(text7);
|
|
25629
|
+
if (crushed.truncated) {
|
|
25630
|
+
meta.body = { originalChars: crushed.originalChars };
|
|
25631
|
+
return { value: crushed.text, meta };
|
|
25632
|
+
}
|
|
25633
|
+
return { value: text7, meta };
|
|
25634
|
+
}
|
|
25635
|
+
function attachCrush(payload, meta) {
|
|
25636
|
+
if (!meta.comments && !meta.body) return payload;
|
|
25637
|
+
return { ...payload, crush: meta };
|
|
25638
|
+
}
|
|
25639
|
+
function formatLinearIssuePayload(issue, include) {
|
|
25640
|
+
if (include === "full") return issue;
|
|
25641
|
+
const meta = emptyMeta();
|
|
25642
|
+
const comments = withComments(issue.comments, meta);
|
|
25643
|
+
const description = withBody(issue.description, comments.meta);
|
|
25644
|
+
const { team, ...rest } = issue;
|
|
25645
|
+
const next = {
|
|
25646
|
+
...rest,
|
|
25647
|
+
...team ? { team: { id: team.id, key: team.key, name: team.name } } : {},
|
|
25648
|
+
description: description.value,
|
|
25649
|
+
comments: comments.comments
|
|
25312
25650
|
};
|
|
25651
|
+
return attachCrush(next, description.meta);
|
|
25313
25652
|
}
|
|
25314
|
-
function
|
|
25315
|
-
return
|
|
25316
|
-
|
|
25317
|
-
|
|
25653
|
+
function formatGitHubIssuePayload(issue, include) {
|
|
25654
|
+
if (include === "full") return issue;
|
|
25655
|
+
const meta = emptyMeta();
|
|
25656
|
+
const comments = withComments(issue.comments, meta);
|
|
25657
|
+
const body = withBody(issue.body, comments.meta);
|
|
25658
|
+
const next = {
|
|
25659
|
+
...issue,
|
|
25660
|
+
body: body.value,
|
|
25661
|
+
comments: comments.comments
|
|
25318
25662
|
};
|
|
25663
|
+
return attachCrush(next, body.meta);
|
|
25664
|
+
}
|
|
25665
|
+
function formatAbleTimeTaskPayload(task, include) {
|
|
25666
|
+
const base = {
|
|
25667
|
+
...toAbleTimeIssueInfo(task),
|
|
25668
|
+
description: task.description,
|
|
25669
|
+
state: task.state,
|
|
25670
|
+
comments: task.comments
|
|
25671
|
+
};
|
|
25672
|
+
if (include === "full") return base;
|
|
25673
|
+
const meta = emptyMeta();
|
|
25674
|
+
const comments = withComments(task.comments, meta);
|
|
25675
|
+
const description = withBody(task.description, comments.meta);
|
|
25676
|
+
return attachCrush(
|
|
25677
|
+
{
|
|
25678
|
+
...base,
|
|
25679
|
+
description: description.value,
|
|
25680
|
+
comments: comments.comments
|
|
25681
|
+
},
|
|
25682
|
+
description.meta
|
|
25683
|
+
);
|
|
25319
25684
|
}
|
|
25320
25685
|
|
|
25321
25686
|
// src/mcp/abletime-tools.ts
|
|
@@ -25361,7 +25726,7 @@ function registerAbleTimeTools(server) {
|
|
|
25361
25726
|
server.tool(
|
|
25362
25727
|
"abletime_list_tasks",
|
|
25363
25728
|
"List open AbleTime tasks (excludes done/archived). Optional projectId filter.",
|
|
25364
|
-
{ projectId:
|
|
25729
|
+
{ projectId: import_zod3.z.string().optional() },
|
|
25365
25730
|
async ({ projectId }) => {
|
|
25366
25731
|
try {
|
|
25367
25732
|
const tasks = await listAbleTimeTasks({ projectId });
|
|
@@ -25374,7 +25739,7 @@ function registerAbleTimeTools(server) {
|
|
|
25374
25739
|
server.tool(
|
|
25375
25740
|
"abletime_search_tasks",
|
|
25376
25741
|
"Search AbleTime tasks by text, reference, or tag.",
|
|
25377
|
-
{ query:
|
|
25742
|
+
{ query: import_zod3.z.string() },
|
|
25378
25743
|
async ({ query }) => {
|
|
25379
25744
|
try {
|
|
25380
25745
|
const tasks = await searchAbleTimeTasks(query);
|
|
@@ -25386,17 +25751,11 @@ function registerAbleTimeTools(server) {
|
|
|
25386
25751
|
);
|
|
25387
25752
|
server.tool(
|
|
25388
25753
|
"abletime_get_task",
|
|
25389
|
-
"Get one AbleTime task by id or reference (e.g. CRM-232): description, state, comments. Re-fetch to read new comments.",
|
|
25390
|
-
{ id:
|
|
25391
|
-
async ({ id }) => {
|
|
25754
|
+
"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.",
|
|
25755
|
+
{ id: import_zod3.z.string(), include: mcpIssueIncludeSchema },
|
|
25756
|
+
async ({ id, include }) => {
|
|
25392
25757
|
try {
|
|
25393
|
-
|
|
25394
|
-
return text2({
|
|
25395
|
-
...toAbleTimeIssueInfo(task),
|
|
25396
|
-
description: task.description,
|
|
25397
|
-
state: task.state,
|
|
25398
|
-
comments: task.comments
|
|
25399
|
-
});
|
|
25758
|
+
return text2(formatAbleTimeTaskPayload(await getAbleTimeTask(id), include));
|
|
25400
25759
|
} catch (err) {
|
|
25401
25760
|
return fail2(err);
|
|
25402
25761
|
}
|
|
@@ -25405,7 +25764,7 @@ function registerAbleTimeTools(server) {
|
|
|
25405
25764
|
server.tool(
|
|
25406
25765
|
"abletime_comment",
|
|
25407
25766
|
"Add a markdown comment on an AbleTime task (id or CRM-232).",
|
|
25408
|
-
{ id:
|
|
25767
|
+
{ id: import_zod3.z.string(), body: import_zod3.z.string() },
|
|
25409
25768
|
async (args) => {
|
|
25410
25769
|
try {
|
|
25411
25770
|
return text2(await commentAbleTimeTask(args));
|
|
@@ -25418,10 +25777,10 @@ function registerAbleTimeTools(server) {
|
|
|
25418
25777
|
"abletime_update_task",
|
|
25419
25778
|
"Update an AbleTime task (id or CRM-232). Pass title, description, and/or state.",
|
|
25420
25779
|
{
|
|
25421
|
-
id:
|
|
25422
|
-
title:
|
|
25423
|
-
description:
|
|
25424
|
-
state:
|
|
25780
|
+
id: import_zod3.z.string(),
|
|
25781
|
+
title: import_zod3.z.string().optional(),
|
|
25782
|
+
description: import_zod3.z.string().optional(),
|
|
25783
|
+
state: import_zod3.z.string().optional()
|
|
25425
25784
|
},
|
|
25426
25785
|
async (args) => {
|
|
25427
25786
|
try {
|
|
@@ -25440,12 +25799,12 @@ function registerAbleTimeTools(server) {
|
|
|
25440
25799
|
"abletime_create_task",
|
|
25441
25800
|
"Create an AbleTime task in a project. Call abletime_list_projects if you do not have a project id. Pass parent (CRM-232) for a spin-off. New tasks start in todo (or backlog).",
|
|
25442
25801
|
{
|
|
25443
|
-
title:
|
|
25444
|
-
description:
|
|
25445
|
-
projectId:
|
|
25446
|
-
categoryId:
|
|
25447
|
-
state:
|
|
25448
|
-
parent:
|
|
25802
|
+
title: import_zod3.z.string(),
|
|
25803
|
+
description: import_zod3.z.string().optional(),
|
|
25804
|
+
projectId: import_zod3.z.string().optional(),
|
|
25805
|
+
categoryId: import_zod3.z.string().optional(),
|
|
25806
|
+
state: import_zod3.z.enum(["backlog", "todo"]).optional(),
|
|
25807
|
+
parent: import_zod3.z.string().optional().describe("Parent task id or reference (CRM-232) for a spin-off.")
|
|
25449
25808
|
},
|
|
25450
25809
|
async (args) => {
|
|
25451
25810
|
try {
|
|
@@ -25459,10 +25818,10 @@ function registerAbleTimeTools(server) {
|
|
|
25459
25818
|
"abletime_ensure_task",
|
|
25460
25819
|
"Find or create an AbleTime task for this work, then create_thread with sourceType=ticket.",
|
|
25461
25820
|
{
|
|
25462
|
-
title:
|
|
25463
|
-
description:
|
|
25464
|
-
projectId:
|
|
25465
|
-
categoryId:
|
|
25821
|
+
title: import_zod3.z.string(),
|
|
25822
|
+
description: import_zod3.z.string().optional(),
|
|
25823
|
+
projectId: import_zod3.z.string().optional(),
|
|
25824
|
+
categoryId: import_zod3.z.string().optional()
|
|
25466
25825
|
},
|
|
25467
25826
|
async (args) => {
|
|
25468
25827
|
try {
|
|
@@ -25476,7 +25835,7 @@ function registerAbleTimeTools(server) {
|
|
|
25476
25835
|
}
|
|
25477
25836
|
|
|
25478
25837
|
// src/mcp/github-tools.ts
|
|
25479
|
-
var
|
|
25838
|
+
var import_zod4 = require("zod");
|
|
25480
25839
|
function text3(payload, isError = false) {
|
|
25481
25840
|
return mcpJson(payload, isError);
|
|
25482
25841
|
}
|
|
@@ -25486,15 +25845,15 @@ function fail3(err) {
|
|
|
25486
25845
|
true
|
|
25487
25846
|
);
|
|
25488
25847
|
}
|
|
25489
|
-
var repoPathSchema =
|
|
25848
|
+
var repoPathSchema = import_zod4.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
|
|
25490
25849
|
function registerGithubIssueTools(server) {
|
|
25491
25850
|
server.tool(
|
|
25492
25851
|
"github_get_issue",
|
|
25493
|
-
"Get a GitHub issue (#123 or URL): body, comments, state. Re-fetch to read new comments. Uses Account gh.",
|
|
25494
|
-
{ id:
|
|
25495
|
-
async ({ id, repoPath }) => {
|
|
25852
|
+
"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.",
|
|
25853
|
+
{ id: import_zod4.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
|
|
25854
|
+
async ({ id, include, repoPath }) => {
|
|
25496
25855
|
try {
|
|
25497
|
-
return text3(await getGitHubIssue(id, { repoPath }));
|
|
25856
|
+
return text3(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
|
|
25498
25857
|
} catch (err) {
|
|
25499
25858
|
return fail3(err);
|
|
25500
25859
|
}
|
|
@@ -25503,7 +25862,7 @@ function registerGithubIssueTools(server) {
|
|
|
25503
25862
|
server.tool(
|
|
25504
25863
|
"github_comment",
|
|
25505
25864
|
"Add a markdown comment on a GitHub issue (#123 or URL). Uses Account gh.",
|
|
25506
|
-
{ id:
|
|
25865
|
+
{ id: import_zod4.z.string(), body: import_zod4.z.string(), repoPath: repoPathSchema },
|
|
25507
25866
|
async ({ id, body, repoPath }) => {
|
|
25508
25867
|
try {
|
|
25509
25868
|
return text3(await commentGitHubIssue({ id, body }, { repoPath }));
|
|
@@ -25516,10 +25875,10 @@ function registerGithubIssueTools(server) {
|
|
|
25516
25875
|
"github_update_issue",
|
|
25517
25876
|
"Update a GitHub issue (#123). Pass title, body, and/or state (open|closed).",
|
|
25518
25877
|
{
|
|
25519
|
-
id:
|
|
25520
|
-
title:
|
|
25521
|
-
body:
|
|
25522
|
-
state:
|
|
25878
|
+
id: import_zod4.z.string(),
|
|
25879
|
+
title: import_zod4.z.string().optional(),
|
|
25880
|
+
body: import_zod4.z.string().optional(),
|
|
25881
|
+
state: import_zod4.z.string().optional().describe("open or closed"),
|
|
25523
25882
|
repoPath: repoPathSchema
|
|
25524
25883
|
},
|
|
25525
25884
|
async (args) => {
|
|
@@ -25534,9 +25893,9 @@ function registerGithubIssueTools(server) {
|
|
|
25534
25893
|
"github_create_issue",
|
|
25535
25894
|
"Create a GitHub issue. Pass parent (#123) to mark a spin-off in the body.",
|
|
25536
25895
|
{
|
|
25537
|
-
title:
|
|
25538
|
-
body:
|
|
25539
|
-
parent:
|
|
25896
|
+
title: import_zod4.z.string(),
|
|
25897
|
+
body: import_zod4.z.string().optional(),
|
|
25898
|
+
parent: import_zod4.z.string().optional().describe("Parent issue #123 or URL for a spin-off."),
|
|
25540
25899
|
repoPath: repoPathSchema
|
|
25541
25900
|
},
|
|
25542
25901
|
async (args) => {
|
|
@@ -25550,7 +25909,7 @@ function registerGithubIssueTools(server) {
|
|
|
25550
25909
|
}
|
|
25551
25910
|
|
|
25552
25911
|
// src/mcp/linear-tools.ts
|
|
25553
|
-
var
|
|
25912
|
+
var import_zod5 = require("zod");
|
|
25554
25913
|
function text4(payload, isError = false) {
|
|
25555
25914
|
return mcpJson(payload, isError);
|
|
25556
25915
|
}
|
|
@@ -25560,7 +25919,7 @@ function fail4(err) {
|
|
|
25560
25919
|
true
|
|
25561
25920
|
);
|
|
25562
25921
|
}
|
|
25563
|
-
var prioritySchema =
|
|
25922
|
+
var prioritySchema = import_zod5.z.number().int().min(0).max(4).optional().describe("0 none, 1 urgent, 2 high, 3 medium, 4 low");
|
|
25564
25923
|
function registerLinearTools(server) {
|
|
25565
25924
|
server.tool(
|
|
25566
25925
|
"linear_list_teams",
|
|
@@ -25578,9 +25937,9 @@ function registerLinearTools(server) {
|
|
|
25578
25937
|
"linear_search_issues",
|
|
25579
25938
|
"Search or list open Linear issues. Default 40; pass query and/or limit (max 250) when truncated. assignee: me, unassigned, all (default with query), or a user id/name.",
|
|
25580
25939
|
{
|
|
25581
|
-
query:
|
|
25582
|
-
assignee:
|
|
25583
|
-
limit:
|
|
25940
|
+
query: import_zod5.z.string().optional().describe("Search text (identifier, title, description). Omit to list by assignee only."),
|
|
25941
|
+
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."),
|
|
25942
|
+
limit: import_zod5.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
25584
25943
|
},
|
|
25585
25944
|
async ({ query, assignee, limit }) => {
|
|
25586
25945
|
try {
|
|
@@ -25607,11 +25966,11 @@ function registerLinearTools(server) {
|
|
|
25607
25966
|
);
|
|
25608
25967
|
server.tool(
|
|
25609
25968
|
"linear_get_issue",
|
|
25610
|
-
"Get a Linear issue by uuid or identifier (ENG-123): description, comments, relations, parent/children.",
|
|
25611
|
-
{ id:
|
|
25612
|
-
async ({ id }) => {
|
|
25969
|
+
"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.",
|
|
25970
|
+
{ id: import_zod5.z.string(), include: mcpIssueIncludeSchema },
|
|
25971
|
+
async ({ id, include }) => {
|
|
25613
25972
|
try {
|
|
25614
|
-
return text4(await getLinearIssue(id));
|
|
25973
|
+
return text4(formatLinearIssuePayload(await getLinearIssue(id), include));
|
|
25615
25974
|
} catch (err) {
|
|
25616
25975
|
return fail4(err);
|
|
25617
25976
|
}
|
|
@@ -25621,13 +25980,13 @@ function registerLinearTools(server) {
|
|
|
25621
25980
|
"linear_create_issue",
|
|
25622
25981
|
'Create a Linear issue. Call linear_list_teams first. team is id/key/name; state is name/type/id; assignee is "me" or a user id. Pass parent (ENG-123 or uuid) to nest a spin-off under the current ticket.',
|
|
25623
25982
|
{
|
|
25624
|
-
team:
|
|
25625
|
-
title:
|
|
25626
|
-
description:
|
|
25627
|
-
state:
|
|
25628
|
-
assignee:
|
|
25983
|
+
team: import_zod5.z.string(),
|
|
25984
|
+
title: import_zod5.z.string(),
|
|
25985
|
+
description: import_zod5.z.string().optional(),
|
|
25986
|
+
state: import_zod5.z.string().optional(),
|
|
25987
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
25629
25988
|
priority: prioritySchema,
|
|
25630
|
-
parent:
|
|
25989
|
+
parent: import_zod5.z.string().optional().describe("Parent issue uuid or identifier (ENG-123) for a spin-off / sub-issue.")
|
|
25631
25990
|
},
|
|
25632
25991
|
async (args) => {
|
|
25633
25992
|
try {
|
|
@@ -25641,11 +26000,11 @@ function registerLinearTools(server) {
|
|
|
25641
26000
|
"linear_update_issue",
|
|
25642
26001
|
"Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, and/or priority.",
|
|
25643
26002
|
{
|
|
25644
|
-
id:
|
|
25645
|
-
title:
|
|
25646
|
-
description:
|
|
25647
|
-
state:
|
|
25648
|
-
assignee:
|
|
26003
|
+
id: import_zod5.z.string(),
|
|
26004
|
+
title: import_zod5.z.string().optional(),
|
|
26005
|
+
description: import_zod5.z.string().optional(),
|
|
26006
|
+
state: import_zod5.z.string().optional(),
|
|
26007
|
+
assignee: import_zod5.z.string().nullable().optional(),
|
|
25649
26008
|
priority: prioritySchema
|
|
25650
26009
|
},
|
|
25651
26010
|
async (args) => {
|
|
@@ -25660,8 +26019,8 @@ function registerLinearTools(server) {
|
|
|
25660
26019
|
"linear_comment",
|
|
25661
26020
|
"Add a markdown comment on a Linear issue (uuid or ENG-123).",
|
|
25662
26021
|
{
|
|
25663
|
-
id:
|
|
25664
|
-
body:
|
|
26022
|
+
id: import_zod5.z.string(),
|
|
26023
|
+
body: import_zod5.z.string()
|
|
25665
26024
|
},
|
|
25666
26025
|
async (args) => {
|
|
25667
26026
|
try {
|
|
@@ -25681,7 +26040,7 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
25681
26040
|
}
|
|
25682
26041
|
|
|
25683
26042
|
// src/mcp/viewer-context-tools.ts
|
|
25684
|
-
var
|
|
26043
|
+
var import_zod6 = require("zod");
|
|
25685
26044
|
init_worktree();
|
|
25686
26045
|
init_app_settings();
|
|
25687
26046
|
init_workspaces();
|
|
@@ -25716,7 +26075,7 @@ function registerViewerContextTools(server) {
|
|
|
25716
26075
|
"get_viewer_context",
|
|
25717
26076
|
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from list_workspaces for project context. Worktree turns may omit repoPath (uses cwd).",
|
|
25718
26077
|
{
|
|
25719
|
-
repoPath:
|
|
26078
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
25720
26079
|
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
25721
26080
|
)
|
|
25722
26081
|
},
|
|
@@ -25736,10 +26095,10 @@ function registerViewerContextTools(server) {
|
|
|
25736
26095
|
"update_viewer_context",
|
|
25737
26096
|
"Replace account or project user context (Settings \u2192 Agents / Projects). Never call until the user confirmed. Show the proposed text, ask_user (Save this context / Do not save), wait, then pass confirmed=true. Empty context clears that field.",
|
|
25738
26097
|
{
|
|
25739
|
-
scope:
|
|
25740
|
-
context:
|
|
25741
|
-
confirmed:
|
|
25742
|
-
repoPath:
|
|
26098
|
+
scope: import_zod6.z.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
26099
|
+
context: import_zod6.z.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
26100
|
+
confirmed: import_zod6.z.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
26101
|
+
repoPath: import_zod6.z.string().optional().describe(
|
|
25743
26102
|
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
25744
26103
|
)
|
|
25745
26104
|
},
|
|
@@ -25820,14 +26179,11 @@ init_list_prs();
|
|
|
25820
26179
|
init_app_settings();
|
|
25821
26180
|
|
|
25822
26181
|
// src/mcp/schedule-tools.ts
|
|
25823
|
-
var
|
|
26182
|
+
var import_zod7 = require("zod");
|
|
25824
26183
|
init_schedules();
|
|
25825
26184
|
init_schedule_runner();
|
|
25826
26185
|
function text6(payload, isError = false) {
|
|
25827
|
-
return
|
|
25828
|
-
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
25829
|
-
...isError ? { isError: true } : {}
|
|
25830
|
-
};
|
|
26186
|
+
return mcpJson(payload, isError);
|
|
25831
26187
|
}
|
|
25832
26188
|
function fail5(err) {
|
|
25833
26189
|
return text6(
|
|
@@ -25865,17 +26221,17 @@ function registerScheduleTools(server) {
|
|
|
25865
26221
|
"create_schedule",
|
|
25866
26222
|
"Create a local schedule that, when due, sends a prompt to an orchestration chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=self to continue this coordinator. Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without threadId open a new chat each run. Overnight/unattended runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate. Sideboard.app must be running for the job to fire.",
|
|
25867
26223
|
{
|
|
25868
|
-
prompt:
|
|
25869
|
-
name:
|
|
25870
|
-
at:
|
|
25871
|
-
every:
|
|
25872
|
-
cron:
|
|
25873
|
-
tz:
|
|
25874
|
-
threadId:
|
|
26224
|
+
prompt: import_zod7.z.string().describe("User message / goal queued when the schedule fires"),
|
|
26225
|
+
name: import_zod7.z.string().optional(),
|
|
26226
|
+
at: import_zod7.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
26227
|
+
every: import_zod7.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
26228
|
+
cron: import_zod7.z.string().optional().describe("5-field cron expression"),
|
|
26229
|
+
tz: import_zod7.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
26230
|
+
threadId: import_zod7.z.string().optional().describe(
|
|
25875
26231
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
25876
26232
|
),
|
|
25877
|
-
agent:
|
|
25878
|
-
model:
|
|
26233
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
26234
|
+
model: import_zod7.z.string().optional()
|
|
25879
26235
|
},
|
|
25880
26236
|
async (args) => {
|
|
25881
26237
|
try {
|
|
@@ -25908,17 +26264,17 @@ function registerScheduleTools(server) {
|
|
|
25908
26264
|
"update_schedule",
|
|
25909
26265
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
25910
26266
|
{
|
|
25911
|
-
id:
|
|
25912
|
-
prompt:
|
|
25913
|
-
name:
|
|
25914
|
-
at:
|
|
25915
|
-
every:
|
|
25916
|
-
cron:
|
|
25917
|
-
tz:
|
|
25918
|
-
threadId:
|
|
25919
|
-
agent:
|
|
25920
|
-
model:
|
|
25921
|
-
enabled:
|
|
26267
|
+
id: import_zod7.z.string(),
|
|
26268
|
+
prompt: import_zod7.z.string().optional(),
|
|
26269
|
+
name: import_zod7.z.string().optional(),
|
|
26270
|
+
at: import_zod7.z.string().optional(),
|
|
26271
|
+
every: import_zod7.z.string().optional(),
|
|
26272
|
+
cron: import_zod7.z.string().optional(),
|
|
26273
|
+
tz: import_zod7.z.string().optional(),
|
|
26274
|
+
threadId: import_zod7.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
26275
|
+
agent: import_zod7.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
26276
|
+
model: import_zod7.z.string().optional(),
|
|
26277
|
+
enabled: import_zod7.z.boolean().optional()
|
|
25922
26278
|
},
|
|
25923
26279
|
async (args) => {
|
|
25924
26280
|
try {
|
|
@@ -25956,7 +26312,7 @@ function registerScheduleTools(server) {
|
|
|
25956
26312
|
server.tool(
|
|
25957
26313
|
"delete_schedule",
|
|
25958
26314
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
25959
|
-
{ id:
|
|
26315
|
+
{ id: import_zod7.z.string() },
|
|
25960
26316
|
async ({ id }) => {
|
|
25961
26317
|
try {
|
|
25962
26318
|
deleteSchedule(id);
|
|
@@ -25969,7 +26325,7 @@ function registerScheduleTools(server) {
|
|
|
25969
26325
|
server.tool(
|
|
25970
26326
|
"run_schedule",
|
|
25971
26327
|
"Fire a schedule now (does not wait for nextRunAt). Queues the prompt or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
|
|
25972
|
-
{ id:
|
|
26328
|
+
{ id: import_zod7.z.string() },
|
|
25973
26329
|
async ({ id }) => {
|
|
25974
26330
|
try {
|
|
25975
26331
|
if (!getSchedule(id)) {
|
|
@@ -26463,13 +26819,13 @@ async function startMcpServer() {
|
|
|
26463
26819
|
"list_board",
|
|
26464
26820
|
"Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
|
|
26465
26821
|
{
|
|
26466
|
-
query:
|
|
26467
|
-
repoPath:
|
|
26468
|
-
kind:
|
|
26469
|
-
column:
|
|
26822
|
+
query: import_zod8.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
26823
|
+
repoPath: import_zod8.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
26824
|
+
kind: import_zod8.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
26825
|
+
column: import_zod8.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
26470
26826
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
26471
26827
|
),
|
|
26472
|
-
limit:
|
|
26828
|
+
limit: import_zod8.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
26473
26829
|
},
|
|
26474
26830
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
26475
26831
|
const workspaces = orch.listWorkspaces();
|
|
@@ -26484,30 +26840,19 @@ async function startMcpServer() {
|
|
|
26484
26840
|
limit,
|
|
26485
26841
|
workspaceName: (path2) => names.get(path2) ?? ""
|
|
26486
26842
|
});
|
|
26487
|
-
return {
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
hidden: snap.hidden,
|
|
26495
|
-
totals: snap.totals,
|
|
26496
|
-
columnDefs: BOARD_COLUMN_DEFS,
|
|
26497
|
-
hint: HOME_BOARD_AGENT_HINT
|
|
26498
|
-
},
|
|
26499
|
-
null,
|
|
26500
|
-
2
|
|
26501
|
-
)
|
|
26502
|
-
}
|
|
26503
|
-
]
|
|
26504
|
-
};
|
|
26843
|
+
return mcpJson({
|
|
26844
|
+
columns: snap.columns,
|
|
26845
|
+
hidden: snap.hidden,
|
|
26846
|
+
totals: snap.totals,
|
|
26847
|
+
columnDefs: BOARD_COLUMN_DEFS,
|
|
26848
|
+
hint: HOME_BOARD_AGENT_HINT
|
|
26849
|
+
});
|
|
26505
26850
|
}
|
|
26506
26851
|
);
|
|
26507
26852
|
server.tool(
|
|
26508
26853
|
"get_thread",
|
|
26509
26854
|
"Get a compact thread summary by id/ref. Includes last message preview, parentThreadId, and child worktree threads (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (thread billed token + costUsd totals when providers reported cost) and lastTurnUsage.",
|
|
26510
|
-
{ ref:
|
|
26855
|
+
{ ref: import_zod8.z.string() },
|
|
26511
26856
|
async ({ ref }) => {
|
|
26512
26857
|
const t = orch.getThread(ref);
|
|
26513
26858
|
if (!t) {
|
|
@@ -26541,7 +26886,7 @@ async function startMcpServer() {
|
|
|
26541
26886
|
usage: spend.usage,
|
|
26542
26887
|
lastTurnUsage: spend.lastTurnUsage
|
|
26543
26888
|
};
|
|
26544
|
-
return
|
|
26889
|
+
return mcpJson(summary);
|
|
26545
26890
|
}
|
|
26546
26891
|
);
|
|
26547
26892
|
}
|
|
@@ -26549,17 +26894,17 @@ async function startMcpServer() {
|
|
|
26549
26894
|
"present_artifact",
|
|
26550
26895
|
"Show a document or live log in Sideboard\u2019s side column. For html/svg/markdown/react, pass the FULL document and do not also fence that same body in chat. For type=log, pass only NEW lines (same artifact_id appends). Prefer type=log for long-running job output \u2014 do not resend HTML. type=react is a single default-export component (JSX/TSX); only react/react-dom imports.",
|
|
26551
26896
|
{
|
|
26552
|
-
title:
|
|
26553
|
-
type:
|
|
26897
|
+
title: import_zod8.z.string().describe("Short title shown in the artifact pane header"),
|
|
26898
|
+
type: import_zod8.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
26554
26899
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
26555
26900
|
),
|
|
26556
|
-
content:
|
|
26901
|
+
content: import_zod8.z.string().describe(
|
|
26557
26902
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
26558
26903
|
),
|
|
26559
|
-
artifact_id:
|
|
26560
|
-
status:
|
|
26561
|
-
phase:
|
|
26562
|
-
mode:
|
|
26904
|
+
artifact_id: import_zod8.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
26905
|
+
status: import_zod8.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
26906
|
+
phase: import_zod8.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
26907
|
+
mode: import_zod8.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
26563
26908
|
},
|
|
26564
26909
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
26565
26910
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26573,22 +26918,22 @@ async function startMcpServer() {
|
|
|
26573
26918
|
mode: type === "log" ? mode ?? "append" : void 0,
|
|
26574
26919
|
message: type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
26575
26920
|
};
|
|
26576
|
-
return
|
|
26921
|
+
return mcpJson(payload);
|
|
26577
26922
|
}
|
|
26578
26923
|
);
|
|
26579
26924
|
server.tool(
|
|
26580
26925
|
"ask_user",
|
|
26581
26926
|
"Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
|
|
26582
26927
|
{
|
|
26583
|
-
questions:
|
|
26584
|
-
|
|
26585
|
-
question:
|
|
26586
|
-
header:
|
|
26587
|
-
multiSelect:
|
|
26588
|
-
options:
|
|
26589
|
-
|
|
26590
|
-
label:
|
|
26591
|
-
description:
|
|
26928
|
+
questions: import_zod8.z.array(
|
|
26929
|
+
import_zod8.z.object({
|
|
26930
|
+
question: import_zod8.z.string().describe("Full question text ending with ?"),
|
|
26931
|
+
header: import_zod8.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
26932
|
+
multiSelect: import_zod8.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
26933
|
+
options: import_zod8.z.array(
|
|
26934
|
+
import_zod8.z.object({
|
|
26935
|
+
label: import_zod8.z.string(),
|
|
26936
|
+
description: import_zod8.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
26592
26937
|
})
|
|
26593
26938
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
26594
26939
|
})
|
|
@@ -26600,16 +26945,16 @@ async function startMcpServer() {
|
|
|
26600
26945
|
questions,
|
|
26601
26946
|
message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
|
|
26602
26947
|
};
|
|
26603
|
-
return
|
|
26948
|
+
return mcpJson(payload);
|
|
26604
26949
|
}
|
|
26605
26950
|
);
|
|
26606
26951
|
server.tool(
|
|
26607
26952
|
"present_plan",
|
|
26608
26953
|
"Save the implementation plan as markdown to .context/attachments/plan.md and show it in Sideboard chat for user approval (Copy / Hand off / Approve). Call this when the plan is ready \u2014 required in plan mode. Pass the full plan body in content. Then Claude should call ExitPlanMode.",
|
|
26609
26954
|
{
|
|
26610
|
-
title:
|
|
26611
|
-
content:
|
|
26612
|
-
thread_id:
|
|
26955
|
+
title: import_zod8.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
26956
|
+
content: import_zod8.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
26957
|
+
thread_id: import_zod8.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
26613
26958
|
},
|
|
26614
26959
|
async ({ title, content, thread_id }) => {
|
|
26615
26960
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -26625,22 +26970,22 @@ async function startMcpServer() {
|
|
|
26625
26970
|
title: title?.trim() || "Plan",
|
|
26626
26971
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
26627
26972
|
};
|
|
26628
|
-
return
|
|
26973
|
+
return mcpJson(payload);
|
|
26629
26974
|
}
|
|
26630
26975
|
);
|
|
26631
26976
|
server.tool(
|
|
26632
26977
|
"present_schema",
|
|
26633
26978
|
"Open Sideboard\u2019s schema-driven side column (filterable table and/or form) when the user needs to filter, edit, publish, or persist records. Do not call this just to re-display rows you already wrote as a markdown table. If the user asks for an editable / interactive table, call this even if chat already showed those rows. Pass JSON Schema + optional schemaUi. Prefer datasource=inline with embedded resource/records. Use datasource=brightsy with resource_id only when the user is logged into Brightsy.",
|
|
26634
26979
|
{
|
|
26635
|
-
title:
|
|
26636
|
-
mode:
|
|
26637
|
-
datasource:
|
|
26638
|
-
resource_id:
|
|
26639
|
-
record_id:
|
|
26640
|
-
resource:
|
|
26641
|
-
record:
|
|
26642
|
-
records:
|
|
26643
|
-
pane_id:
|
|
26980
|
+
title: import_zod8.z.string().describe("Pane title"),
|
|
26981
|
+
mode: import_zod8.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
26982
|
+
datasource: import_zod8.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
26983
|
+
resource_id: import_zod8.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
26984
|
+
record_id: import_zod8.z.string().optional().describe("Record id when opening form mode"),
|
|
26985
|
+
resource: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
26986
|
+
record: import_zod8.z.record(import_zod8.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
26987
|
+
records: import_zod8.z.array(import_zod8.z.record(import_zod8.z.unknown())).optional().describe("Inline records for table mode"),
|
|
26988
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
26644
26989
|
},
|
|
26645
26990
|
async (args) => {
|
|
26646
26991
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26654,17 +26999,17 @@ async function startMcpServer() {
|
|
|
26654
26999
|
record_id: args.record_id,
|
|
26655
27000
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
26656
27001
|
};
|
|
26657
|
-
return
|
|
27002
|
+
return mcpJson(payload);
|
|
26658
27003
|
}
|
|
26659
27004
|
);
|
|
26660
27005
|
server.tool(
|
|
26661
27006
|
"present_files",
|
|
26662
27007
|
"Open Sideboard\u2019s Files column (CMS-style file manager: browse, upload, pick). Use datasource=brightsy when the user is logged into Brightsy account storage; datasource=memory for a session-local demo store. Prefer this over claiming a file manager UI is unavailable. Pair with present_schema when editing records that need media.",
|
|
26663
27008
|
{
|
|
26664
|
-
title:
|
|
26665
|
-
datasource:
|
|
26666
|
-
path:
|
|
26667
|
-
pane_id:
|
|
27009
|
+
title: import_zod8.z.string().optional().describe("Pane title (default: Files)"),
|
|
27010
|
+
datasource: import_zod8.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
27011
|
+
path: import_zod8.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
27012
|
+
pane_id: import_zod8.z.string().optional().describe("Stable pane id across updates")
|
|
26668
27013
|
},
|
|
26669
27014
|
async (args) => {
|
|
26670
27015
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -26676,21 +27021,21 @@ async function startMcpServer() {
|
|
|
26676
27021
|
path: args.path,
|
|
26677
27022
|
message: "Files pane accepted. Sideboard desktop opens the Files column beside chat."
|
|
26678
27023
|
};
|
|
26679
|
-
return
|
|
27024
|
+
return mcpJson(payload);
|
|
26680
27025
|
}
|
|
26681
27026
|
);
|
|
26682
27027
|
server.tool(
|
|
26683
27028
|
"wait_for_job",
|
|
26684
27029
|
"Wait on a detached long job (tests, pack, deploy) started with detached-job.js. MCP clients kill tools around 60s, so this returns within 45s. stillRunning is the source of truth \u2014 if true, present_artifact type=log with content=delta (same artifact_id) and call wait_for_job again. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
26685
27030
|
{
|
|
26686
|
-
id:
|
|
26687
|
-
timeoutMs:
|
|
27031
|
+
id: import_zod8.z.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
27032
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
26688
27033
|
},
|
|
26689
27034
|
async ({ id, timeoutMs }) => {
|
|
26690
27035
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
26691
27036
|
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
26692
27037
|
});
|
|
26693
|
-
return
|
|
27038
|
+
return mcpJson(result);
|
|
26694
27039
|
}
|
|
26695
27040
|
);
|
|
26696
27041
|
if (!worktreeProfile) {
|
|
@@ -26705,56 +27050,44 @@ async function startMcpServer() {
|
|
|
26705
27050
|
"set_caffeinate",
|
|
26706
27051
|
"Keep this Mac awake with caffeinate (like Claude Code) across turns \u2014 independent of Settings toggles. Turn ON when the user will be away from the keyboard, is driving work from Slack, or asks you to keep the machine awake. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the Mac awake. Closing or archiving this orchestration chat also releases the hold. macOS only.",
|
|
26707
27052
|
{
|
|
26708
|
-
enabled:
|
|
27053
|
+
enabled: import_zod8.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
26709
27054
|
},
|
|
26710
27055
|
async ({ enabled }) => {
|
|
26711
27056
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
26712
27057
|
const state = setCaffeinateHold2(enabled, { threadId });
|
|
26713
27058
|
if (enabled && !state.held) {
|
|
26714
|
-
return
|
|
26715
|
-
content: [
|
|
26716
|
-
{
|
|
26717
|
-
type: "text",
|
|
26718
|
-
text: JSON.stringify({
|
|
26719
|
-
...state,
|
|
26720
|
-
ok: false,
|
|
26721
|
-
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
26722
|
-
})
|
|
26723
|
-
}
|
|
26724
|
-
],
|
|
26725
|
-
isError: true
|
|
26726
|
-
};
|
|
26727
|
-
}
|
|
26728
|
-
return {
|
|
26729
|
-
content: [
|
|
27059
|
+
return mcpJson(
|
|
26730
27060
|
{
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
27061
|
+
...state,
|
|
27062
|
+
ok: false,
|
|
27063
|
+
message: state.platform === "darwin" ? "Could not start caffeinate." : "Caffeinate is macOS only."
|
|
27064
|
+
},
|
|
27065
|
+
true
|
|
27066
|
+
);
|
|
27067
|
+
}
|
|
27068
|
+
return mcpJson({
|
|
27069
|
+
...getCaffeinateHold2(),
|
|
27070
|
+
ok: true,
|
|
27071
|
+
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)."
|
|
27072
|
+
});
|
|
26740
27073
|
}
|
|
26741
27074
|
);
|
|
26742
27075
|
server.tool(
|
|
26743
27076
|
"create_thread",
|
|
26744
27077
|
`Create a worktree thread (chat) from branch, pr, or ticket. A ticket, PR, or named branch may have only one live worktree \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. Creating from the default branch still opens a new isolated worktree. Pass repoPath from list_workspaces. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Then use send_to_thread to chat.`,
|
|
26745
27078
|
{
|
|
26746
|
-
sourceType:
|
|
26747
|
-
sourceRef:
|
|
26748
|
-
agent:
|
|
26749
|
-
model:
|
|
27079
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]),
|
|
27080
|
+
sourceRef: import_zod8.z.string(),
|
|
27081
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
27082
|
+
model: import_zod8.z.string().nullable().optional().describe(
|
|
26750
27083
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
26751
27084
|
),
|
|
26752
|
-
repoPath:
|
|
26753
|
-
title:
|
|
26754
|
-
cowboy:
|
|
27085
|
+
repoPath: import_zod8.z.string(),
|
|
27086
|
+
title: import_zod8.z.string().optional(),
|
|
27087
|
+
cowboy: import_zod8.z.boolean().optional().describe(
|
|
26755
27088
|
"If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
|
|
26756
27089
|
),
|
|
26757
|
-
parentThreadId:
|
|
27090
|
+
parentThreadId: import_zod8.z.string().optional().describe(
|
|
26758
27091
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
26759
27092
|
)
|
|
26760
27093
|
},
|
|
@@ -26770,10 +27103,10 @@ async function startMcpServer() {
|
|
|
26770
27103
|
"start_board_card",
|
|
26771
27104
|
"Same as create_thread for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second worktree when one already matches \u2014 returns that thread (alreadyStarted). Then send_to_thread.",
|
|
26772
27105
|
{
|
|
26773
|
-
kind:
|
|
26774
|
-
ref:
|
|
26775
|
-
repoPath:
|
|
26776
|
-
title:
|
|
27106
|
+
kind: import_zod8.z.enum(["ticket", "pr", "branch"]),
|
|
27107
|
+
ref: import_zod8.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
27108
|
+
repoPath: import_zod8.z.string().describe("Workspace path from list_workspaces / list_board"),
|
|
27109
|
+
title: import_zod8.z.string().optional()
|
|
26777
27110
|
},
|
|
26778
27111
|
async ({ kind, ref, repoPath, title }) => {
|
|
26779
27112
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -26790,20 +27123,13 @@ async function startMcpServer() {
|
|
|
26790
27123
|
}))
|
|
26791
27124
|
);
|
|
26792
27125
|
if (existing) {
|
|
26793
|
-
return {
|
|
26794
|
-
|
|
26795
|
-
|
|
26796
|
-
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
title: existing.title,
|
|
26801
|
-
status: existing.status,
|
|
26802
|
-
link: `sideboard://thread/${existing.id}`
|
|
26803
|
-
})
|
|
26804
|
-
}
|
|
26805
|
-
]
|
|
26806
|
-
};
|
|
27126
|
+
return mcpJson({
|
|
27127
|
+
alreadyStarted: true,
|
|
27128
|
+
id: existing.id,
|
|
27129
|
+
title: existing.title,
|
|
27130
|
+
status: existing.status,
|
|
27131
|
+
link: `sideboard://thread/${existing.id}`
|
|
27132
|
+
});
|
|
26807
27133
|
}
|
|
26808
27134
|
const workspaces = orch.listWorkspaces();
|
|
26809
27135
|
const loaded = await getHomeBoardInputs(workspaces);
|
|
@@ -26877,9 +27203,9 @@ async function startMcpServer() {
|
|
|
26877
27203
|
"send_to_thread",
|
|
26878
27204
|
'Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not force_stop to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that stops the child mid-thought. Call wait_for_turn again instead.',
|
|
26879
27205
|
{
|
|
26880
|
-
ref:
|
|
26881
|
-
prompt:
|
|
26882
|
-
force_stop:
|
|
27206
|
+
ref: import_zod8.z.string(),
|
|
27207
|
+
prompt: import_zod8.z.string(),
|
|
27208
|
+
force_stop: import_zod8.z.boolean().optional()
|
|
26883
27209
|
},
|
|
26884
27210
|
async ({ ref, prompt, force_stop }) => {
|
|
26885
27211
|
if (force_stop) {
|
|
@@ -26889,79 +27215,58 @@ async function startMcpServer() {
|
|
|
26889
27215
|
}
|
|
26890
27216
|
}
|
|
26891
27217
|
const thread = await orch.send(ref, prompt);
|
|
26892
|
-
return {
|
|
26893
|
-
|
|
26894
|
-
|
|
26895
|
-
|
|
26896
|
-
|
|
26897
|
-
|
|
26898
|
-
status: thread.status,
|
|
26899
|
-
queueLength: thread.queue.length,
|
|
26900
|
-
forceStopped: Boolean(force_stop)
|
|
26901
|
-
})
|
|
26902
|
-
}
|
|
26903
|
-
]
|
|
26904
|
-
};
|
|
27218
|
+
return mcpJson({
|
|
27219
|
+
id: thread.id,
|
|
27220
|
+
status: thread.status,
|
|
27221
|
+
queueLength: thread.queue.length,
|
|
27222
|
+
forceStopped: Boolean(force_stop)
|
|
27223
|
+
});
|
|
26905
27224
|
}
|
|
26906
27225
|
);
|
|
26907
27226
|
server.tool(
|
|
26908
27227
|
"wait_for_turn",
|
|
26909
27228
|
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
|
|
26910
27229
|
{
|
|
26911
|
-
ref:
|
|
26912
|
-
timeoutMs:
|
|
27230
|
+
ref: import_zod8.z.string(),
|
|
27231
|
+
timeoutMs: import_zod8.z.number().optional()
|
|
26913
27232
|
},
|
|
26914
27233
|
async ({ ref, timeoutMs }) => {
|
|
26915
27234
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
26916
27235
|
resolveIfStillRunning: true
|
|
26917
27236
|
});
|
|
26918
27237
|
const result = orch.getTurnResult(thread.id);
|
|
26919
|
-
return {
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
|
|
26926
|
-
|
|
26927
|
-
|
|
26928
|
-
|
|
26929
|
-
|
|
26930
|
-
lastActivityAt: result.lastActivityAt,
|
|
26931
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
26932
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
26933
|
-
})
|
|
26934
|
-
}
|
|
26935
|
-
]
|
|
26936
|
-
};
|
|
27238
|
+
return mcpJson({
|
|
27239
|
+
id: thread.id,
|
|
27240
|
+
status: result.status,
|
|
27241
|
+
text: result.text,
|
|
27242
|
+
lastError: result.lastError,
|
|
27243
|
+
stillRunning: result.stillRunning,
|
|
27244
|
+
progress: result.progress,
|
|
27245
|
+
lastActivityAt: result.lastActivityAt,
|
|
27246
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
27247
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
27248
|
+
});
|
|
26937
27249
|
}
|
|
26938
27250
|
);
|
|
26939
27251
|
server.tool(
|
|
26940
27252
|
"get_turn_result",
|
|
26941
27253
|
"Assistant message when the turn finished, or live progress while stillRunning. Not the full transcript. Includes usage for the last agent turn (tokens + costUsd when reported).",
|
|
26942
|
-
{ ref:
|
|
27254
|
+
{ ref: import_zod8.z.string() },
|
|
26943
27255
|
async ({ ref }) => {
|
|
26944
27256
|
const result = orch.getTurnResult(ref);
|
|
26945
|
-
return {
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
...result,
|
|
26951
|
-
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
26952
|
-
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
26953
|
-
})
|
|
26954
|
-
}
|
|
26955
|
-
]
|
|
26956
|
-
};
|
|
27257
|
+
return mcpJson({
|
|
27258
|
+
...result,
|
|
27259
|
+
hint: result.stillRunning ? mcpWaitStillRunningHint(result.status) : mcpWaitFinishedHint(result.status),
|
|
27260
|
+
incomplete: !result.stillRunning && Boolean(mcpWaitFinishedHint(result.status))
|
|
27261
|
+
});
|
|
26957
27262
|
}
|
|
26958
27263
|
);
|
|
26959
27264
|
server.tool(
|
|
26960
27265
|
"stop_thread",
|
|
26961
27266
|
"Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true.",
|
|
26962
27267
|
{
|
|
26963
|
-
ref:
|
|
26964
|
-
force:
|
|
27268
|
+
ref: import_zod8.z.string(),
|
|
27269
|
+
force: import_zod8.z.boolean().optional()
|
|
26965
27270
|
},
|
|
26966
27271
|
async ({ ref, force }) => {
|
|
26967
27272
|
const t = orch.getThread(ref);
|
|
@@ -26974,24 +27279,17 @@ async function startMcpServer() {
|
|
|
26974
27279
|
const clearQueue = force !== false;
|
|
26975
27280
|
const hadQueued = t.queue.length > 0;
|
|
26976
27281
|
const stopped = orch.stop(ref, { clearQueue, notifyParent: false });
|
|
26977
|
-
return {
|
|
26978
|
-
|
|
26979
|
-
|
|
26980
|
-
|
|
26981
|
-
|
|
26982
|
-
id: stopped.id,
|
|
26983
|
-
status: stopped.status,
|
|
26984
|
-
clearedQueue: clearQueue && hadQueued
|
|
26985
|
-
})
|
|
26986
|
-
}
|
|
26987
|
-
]
|
|
26988
|
-
};
|
|
27282
|
+
return mcpJson({
|
|
27283
|
+
id: stopped.id,
|
|
27284
|
+
status: stopped.status,
|
|
27285
|
+
clearedQueue: clearQueue && hadQueued
|
|
27286
|
+
});
|
|
26989
27287
|
}
|
|
26990
27288
|
);
|
|
26991
27289
|
server.tool(
|
|
26992
27290
|
"archive_thread",
|
|
26993
27291
|
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
|
|
26994
|
-
{ ref:
|
|
27292
|
+
{ ref: import_zod8.z.string() },
|
|
26995
27293
|
async ({ ref }) => {
|
|
26996
27294
|
const t = orch.getThread(ref);
|
|
26997
27295
|
if (!t) {
|
|
@@ -27008,38 +27306,24 @@ async function startMcpServer() {
|
|
|
27008
27306
|
};
|
|
27009
27307
|
}
|
|
27010
27308
|
const archived = await orch.archive(ref);
|
|
27011
|
-
return {
|
|
27012
|
-
|
|
27013
|
-
|
|
27014
|
-
|
|
27015
|
-
text: JSON.stringify({
|
|
27016
|
-
id: archived.id,
|
|
27017
|
-
status: archived.status
|
|
27018
|
-
})
|
|
27019
|
-
}
|
|
27020
|
-
]
|
|
27021
|
-
};
|
|
27309
|
+
return mcpJson({
|
|
27310
|
+
id: archived.id,
|
|
27311
|
+
status: archived.status
|
|
27312
|
+
});
|
|
27022
27313
|
}
|
|
27023
27314
|
);
|
|
27024
27315
|
server.tool(
|
|
27025
27316
|
"restore_thread",
|
|
27026
27317
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
27027
|
-
{ ref:
|
|
27318
|
+
{ ref: import_zod8.z.string() },
|
|
27028
27319
|
async ({ ref }) => {
|
|
27029
27320
|
try {
|
|
27030
27321
|
const restored = await orch.restore(ref);
|
|
27031
|
-
return {
|
|
27032
|
-
|
|
27033
|
-
|
|
27034
|
-
|
|
27035
|
-
|
|
27036
|
-
id: restored.id,
|
|
27037
|
-
status: restored.status,
|
|
27038
|
-
worktreePath: restored.worktreePath
|
|
27039
|
-
})
|
|
27040
|
-
}
|
|
27041
|
-
]
|
|
27042
|
-
};
|
|
27322
|
+
return mcpJson({
|
|
27323
|
+
id: restored.id,
|
|
27324
|
+
status: restored.status,
|
|
27325
|
+
worktreePath: restored.worktreePath
|
|
27326
|
+
});
|
|
27043
27327
|
} catch (err) {
|
|
27044
27328
|
const message = err instanceof Error ? err.message : String(err);
|
|
27045
27329
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27050,34 +27334,25 @@ async function startMcpServer() {
|
|
|
27050
27334
|
"get_diff",
|
|
27051
27335
|
"Compact diff summary (capped hunks, paginated)",
|
|
27052
27336
|
{
|
|
27053
|
-
ref:
|
|
27054
|
-
maxFiles:
|
|
27337
|
+
ref: import_zod8.z.string(),
|
|
27338
|
+
maxFiles: import_zod8.z.number().optional()
|
|
27055
27339
|
},
|
|
27056
27340
|
async ({ ref, maxFiles }) => {
|
|
27057
27341
|
const summary = await orch.diffSummary(ref);
|
|
27058
|
-
return {
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
text: JSON.stringify({
|
|
27063
|
-
...summary,
|
|
27064
|
-
files: summary.files.slice(0, maxFiles ?? 10)
|
|
27065
|
-
})
|
|
27066
|
-
}
|
|
27067
|
-
]
|
|
27068
|
-
};
|
|
27342
|
+
return mcpJson({
|
|
27343
|
+
...summary,
|
|
27344
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
27345
|
+
});
|
|
27069
27346
|
}
|
|
27070
27347
|
);
|
|
27071
27348
|
server.tool(
|
|
27072
27349
|
"get_pr_checks",
|
|
27073
27350
|
"Snapshot of GitHub PR checks for a worktree thread (`gh pr checks` plus merge/review gates). null = no PR. If the user gave a goal (Greptile 5/5, CI green), the worktree agent watches with `gh pr checks --watch` via /long-running \u2014 this tool is a snapshot, not a waiter. Coordinators: do not run gh from the orchestration cwd.",
|
|
27074
|
-
{ ref:
|
|
27351
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref") },
|
|
27075
27352
|
async ({ ref }) => {
|
|
27076
27353
|
try {
|
|
27077
27354
|
const checks = await orch.getPrChecks(ref);
|
|
27078
|
-
return
|
|
27079
|
-
content: [{ type: "text", text: JSON.stringify(checks) }]
|
|
27080
|
-
};
|
|
27355
|
+
return mcpJson(checks);
|
|
27081
27356
|
} catch (err) {
|
|
27082
27357
|
const message = err instanceof Error ? err.message : String(err);
|
|
27083
27358
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27087,25 +27362,18 @@ async function startMcpServer() {
|
|
|
27087
27362
|
server.tool(
|
|
27088
27363
|
"request_review",
|
|
27089
27364
|
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
|
|
27090
|
-
{ ref:
|
|
27365
|
+
{ ref: import_zod8.z.string().describe("Worktree thread id/ref to review") },
|
|
27091
27366
|
async ({ ref }) => {
|
|
27092
27367
|
try {
|
|
27093
27368
|
const tab = await orch.requestReview(ref);
|
|
27094
27369
|
const from = orch.getThread(ref);
|
|
27095
|
-
return {
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
|
|
27099
|
-
|
|
27100
|
-
|
|
27101
|
-
|
|
27102
|
-
status: tab.status,
|
|
27103
|
-
fromThreadId: from?.id ?? ref,
|
|
27104
|
-
link: `sideboard://thread/${tab.id}`
|
|
27105
|
-
})
|
|
27106
|
-
}
|
|
27107
|
-
]
|
|
27108
|
-
};
|
|
27370
|
+
return mcpJson({
|
|
27371
|
+
id: tab.id,
|
|
27372
|
+
title: tab.title,
|
|
27373
|
+
status: tab.status,
|
|
27374
|
+
fromThreadId: from?.id ?? ref,
|
|
27375
|
+
link: `sideboard://thread/${tab.id}`
|
|
27376
|
+
});
|
|
27109
27377
|
} catch (err) {
|
|
27110
27378
|
const message = err instanceof Error ? err.message : String(err);
|
|
27111
27379
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27116,53 +27384,37 @@ async function startMcpServer() {
|
|
|
27116
27384
|
"ask_git",
|
|
27117
27385
|
"Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
|
|
27118
27386
|
{
|
|
27119
|
-
ref:
|
|
27120
|
-
action:
|
|
27387
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref"),
|
|
27388
|
+
action: import_zod8.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
27121
27389
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
27122
27390
|
)
|
|
27123
27391
|
},
|
|
27124
27392
|
async ({ ref, action }) => {
|
|
27125
27393
|
try {
|
|
27126
27394
|
const thread = await orch.askGit(ref, action);
|
|
27127
|
-
return {
|
|
27128
|
-
|
|
27129
|
-
|
|
27130
|
-
|
|
27131
|
-
|
|
27132
|
-
|
|
27133
|
-
|
|
27134
|
-
queueLength: thread.queue.length,
|
|
27135
|
-
action,
|
|
27136
|
-
link: `sideboard://thread/${thread.id}`
|
|
27137
|
-
})
|
|
27138
|
-
}
|
|
27139
|
-
]
|
|
27140
|
-
};
|
|
27395
|
+
return mcpJson({
|
|
27396
|
+
id: thread.id,
|
|
27397
|
+
status: thread.status,
|
|
27398
|
+
queueLength: thread.queue.length,
|
|
27399
|
+
action,
|
|
27400
|
+
link: `sideboard://thread/${thread.id}`
|
|
27401
|
+
});
|
|
27141
27402
|
} catch (err) {
|
|
27142
27403
|
const raw = err instanceof Error ? err.message : String(err);
|
|
27143
27404
|
const message = formatGhLandError(raw);
|
|
27144
|
-
return
|
|
27145
|
-
|
|
27146
|
-
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27150
|
-
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_thread so the worktree agent runs gh pr create --draft -R <origin> --body-file <short.md>. Keep the description short." : void 0
|
|
27154
|
-
},
|
|
27155
|
-
null,
|
|
27156
|
-
2
|
|
27157
|
-
)
|
|
27158
|
-
}
|
|
27159
|
-
],
|
|
27160
|
-
isError: true
|
|
27161
|
-
};
|
|
27405
|
+
return mcpJson(
|
|
27406
|
+
{
|
|
27407
|
+
error: true,
|
|
27408
|
+
action,
|
|
27409
|
+
lastError: message,
|
|
27410
|
+
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
|
|
27411
|
+
},
|
|
27412
|
+
true
|
|
27413
|
+
);
|
|
27162
27414
|
}
|
|
27163
27415
|
}
|
|
27164
27416
|
);
|
|
27165
|
-
const agentEnum =
|
|
27417
|
+
const agentEnum = import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
27166
27418
|
server.tool(
|
|
27167
27419
|
"list_models",
|
|
27168
27420
|
"List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
|
|
@@ -27172,9 +27424,7 @@ async function startMcpServer() {
|
|
|
27172
27424
|
async ({ agent }) => {
|
|
27173
27425
|
try {
|
|
27174
27426
|
const catalogs = await listModelsForAgent(agent);
|
|
27175
|
-
return
|
|
27176
|
-
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
27177
|
-
};
|
|
27427
|
+
return mcpJson(catalogs);
|
|
27178
27428
|
} catch (err) {
|
|
27179
27429
|
const message = err instanceof Error ? err.message : String(err);
|
|
27180
27430
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27185,11 +27435,11 @@ async function startMcpServer() {
|
|
|
27185
27435
|
"fork_worktree",
|
|
27186
27436
|
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
27187
27437
|
{
|
|
27188
|
-
ref:
|
|
27189
|
-
through_index:
|
|
27438
|
+
ref: import_zod8.z.string().describe("Worktree thread id/ref to fork"),
|
|
27439
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27190
27440
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27191
|
-
model:
|
|
27192
|
-
title:
|
|
27441
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27442
|
+
title: import_zod8.z.string().optional()
|
|
27193
27443
|
},
|
|
27194
27444
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27195
27445
|
try {
|
|
@@ -27202,24 +27452,17 @@ async function startMcpServer() {
|
|
|
27202
27452
|
model,
|
|
27203
27453
|
title
|
|
27204
27454
|
});
|
|
27205
|
-
return {
|
|
27206
|
-
|
|
27207
|
-
|
|
27208
|
-
|
|
27209
|
-
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
|
|
27216
|
-
worktreePath: thread.worktreePath,
|
|
27217
|
-
fromThreadId: source?.id ?? ref,
|
|
27218
|
-
link: `sideboard://thread/${thread.id}`
|
|
27219
|
-
})
|
|
27220
|
-
}
|
|
27221
|
-
]
|
|
27222
|
-
};
|
|
27455
|
+
return mcpJson({
|
|
27456
|
+
id: thread.id,
|
|
27457
|
+
title: thread.title,
|
|
27458
|
+
status: thread.status,
|
|
27459
|
+
agent: thread.agent,
|
|
27460
|
+
model: thread.model,
|
|
27461
|
+
branchName: thread.branchName,
|
|
27462
|
+
worktreePath: thread.worktreePath,
|
|
27463
|
+
fromThreadId: source?.id ?? ref,
|
|
27464
|
+
link: `sideboard://thread/${thread.id}`
|
|
27465
|
+
});
|
|
27223
27466
|
} catch (err) {
|
|
27224
27467
|
const message = err instanceof Error ? err.message : String(err);
|
|
27225
27468
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27230,11 +27473,11 @@ async function startMcpServer() {
|
|
|
27230
27473
|
"fork_chat",
|
|
27231
27474
|
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn (loop while stillRunning) on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
27232
27475
|
{
|
|
27233
|
-
ref:
|
|
27234
|
-
through_index:
|
|
27476
|
+
ref: import_zod8.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
27477
|
+
through_index: import_zod8.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
27235
27478
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
27236
|
-
model:
|
|
27237
|
-
title:
|
|
27479
|
+
model: import_zod8.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
27480
|
+
title: import_zod8.z.string().optional()
|
|
27238
27481
|
},
|
|
27239
27482
|
async ({ ref, through_index, agent, model, title }) => {
|
|
27240
27483
|
try {
|
|
@@ -27252,24 +27495,17 @@ async function startMcpServer() {
|
|
|
27252
27495
|
model,
|
|
27253
27496
|
title
|
|
27254
27497
|
});
|
|
27255
|
-
return {
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
|
|
27263
|
-
|
|
27264
|
-
|
|
27265
|
-
|
|
27266
|
-
worktreePath: tab.worktreePath,
|
|
27267
|
-
fromThreadId: source.id,
|
|
27268
|
-
link: `sideboard://thread/${tab.id}`
|
|
27269
|
-
})
|
|
27270
|
-
}
|
|
27271
|
-
]
|
|
27272
|
-
};
|
|
27498
|
+
return mcpJson({
|
|
27499
|
+
id: tab.id,
|
|
27500
|
+
title: tab.title,
|
|
27501
|
+
status: tab.status,
|
|
27502
|
+
agent: tab.agent,
|
|
27503
|
+
model: tab.model,
|
|
27504
|
+
sourceType: tab.sourceType,
|
|
27505
|
+
worktreePath: tab.worktreePath,
|
|
27506
|
+
fromThreadId: source.id,
|
|
27507
|
+
link: `sideboard://thread/${tab.id}`
|
|
27508
|
+
});
|
|
27273
27509
|
} catch (err) {
|
|
27274
27510
|
const message = err instanceof Error ? err.message : String(err);
|
|
27275
27511
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -27280,49 +27516,35 @@ async function startMcpServer() {
|
|
|
27280
27516
|
"run_dev_script",
|
|
27281
27517
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
27282
27518
|
{
|
|
27283
|
-
ref:
|
|
27284
|
-
name:
|
|
27519
|
+
ref: import_zod8.z.string(),
|
|
27520
|
+
name: import_zod8.z.string().optional()
|
|
27285
27521
|
},
|
|
27286
27522
|
async ({ ref, name }) => {
|
|
27287
27523
|
const result = await orch.startDev(ref, name);
|
|
27288
|
-
return {
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
scriptName: result.scriptName,
|
|
27295
|
-
ports: result.ports,
|
|
27296
|
-
url: `http://localhost:${result.port}`
|
|
27297
|
-
})
|
|
27298
|
-
}
|
|
27299
|
-
]
|
|
27300
|
-
};
|
|
27524
|
+
return mcpJson({
|
|
27525
|
+
port: result.port,
|
|
27526
|
+
scriptName: result.scriptName,
|
|
27527
|
+
ports: result.ports,
|
|
27528
|
+
url: `http://localhost:${result.port}`
|
|
27529
|
+
});
|
|
27301
27530
|
}
|
|
27302
27531
|
);
|
|
27303
27532
|
server.tool(
|
|
27304
27533
|
"list_run_scripts",
|
|
27305
27534
|
"List named run scripts available for a thread",
|
|
27306
|
-
{ ref:
|
|
27535
|
+
{ ref: import_zod8.z.string() },
|
|
27307
27536
|
async ({ ref }) => {
|
|
27308
27537
|
const scripts = orch.listThreadRunScripts(ref);
|
|
27309
27538
|
const active = orch.getActiveRuns(ref);
|
|
27310
|
-
return {
|
|
27311
|
-
content: [
|
|
27312
|
-
{
|
|
27313
|
-
type: "text",
|
|
27314
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
27315
|
-
}
|
|
27316
|
-
]
|
|
27317
|
-
};
|
|
27539
|
+
return mcpJson({ scripts, active });
|
|
27318
27540
|
}
|
|
27319
27541
|
);
|
|
27320
27542
|
server.tool(
|
|
27321
27543
|
"stop_dev_script",
|
|
27322
27544
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
27323
27545
|
{
|
|
27324
|
-
ref:
|
|
27325
|
-
name:
|
|
27546
|
+
ref: import_zod8.z.string(),
|
|
27547
|
+
name: import_zod8.z.string().optional()
|
|
27326
27548
|
},
|
|
27327
27549
|
async ({ ref, name }) => {
|
|
27328
27550
|
orch.stopDev(ref, name);
|
|
@@ -27332,27 +27554,25 @@ async function startMcpServer() {
|
|
|
27332
27554
|
server.tool(
|
|
27333
27555
|
"run_setup",
|
|
27334
27556
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
27335
|
-
{ ref:
|
|
27557
|
+
{ ref: import_zod8.z.string() },
|
|
27336
27558
|
async ({ ref }) => {
|
|
27337
27559
|
const result = await orch.runSetup(ref);
|
|
27338
|
-
return
|
|
27339
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
27340
|
-
};
|
|
27560
|
+
return mcpJson(result);
|
|
27341
27561
|
}
|
|
27342
27562
|
);
|
|
27343
27563
|
server.tool(
|
|
27344
27564
|
"add_workspace",
|
|
27345
27565
|
"Register a git repo as a Sideboard workspace",
|
|
27346
|
-
{ repoPath:
|
|
27566
|
+
{ repoPath: import_zod8.z.string() },
|
|
27347
27567
|
async ({ repoPath }) => {
|
|
27348
27568
|
const ws = await orch.addWorkspace(repoPath);
|
|
27349
|
-
return
|
|
27569
|
+
return mcpJson(ws);
|
|
27350
27570
|
}
|
|
27351
27571
|
);
|
|
27352
27572
|
server.tool(
|
|
27353
27573
|
"remove_workspace",
|
|
27354
27574
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
27355
|
-
{ repoPath:
|
|
27575
|
+
{ repoPath: import_zod8.z.string() },
|
|
27356
27576
|
async ({ repoPath }) => {
|
|
27357
27577
|
orch.removeWorkspace(repoPath);
|
|
27358
27578
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -27362,40 +27582,31 @@ async function startMcpServer() {
|
|
|
27362
27582
|
"fanout",
|
|
27363
27583
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
27364
27584
|
{
|
|
27365
|
-
prompt:
|
|
27366
|
-
agents:
|
|
27367
|
-
repoPath:
|
|
27368
|
-
sourceType:
|
|
27369
|
-
sourceRef:
|
|
27370
|
-
title:
|
|
27585
|
+
prompt: import_zod8.z.string(),
|
|
27586
|
+
agents: import_zod8.z.array(import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
27587
|
+
repoPath: import_zod8.z.string(),
|
|
27588
|
+
sourceType: import_zod8.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
27589
|
+
sourceRef: import_zod8.z.string().optional(),
|
|
27590
|
+
title: import_zod8.z.string().optional()
|
|
27371
27591
|
},
|
|
27372
27592
|
async (args) => {
|
|
27373
27593
|
const threads = await orch.bestOfN(args);
|
|
27374
|
-
return
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
branchName: t.branchName,
|
|
27383
|
-
worktreePath: t.worktreePath
|
|
27384
|
-
})),
|
|
27385
|
-
null,
|
|
27386
|
-
2
|
|
27387
|
-
)
|
|
27388
|
-
}
|
|
27389
|
-
]
|
|
27390
|
-
};
|
|
27594
|
+
return mcpJson(
|
|
27595
|
+
threads.map((t) => ({
|
|
27596
|
+
id: t.id,
|
|
27597
|
+
agent: t.agent,
|
|
27598
|
+
branchName: t.branchName,
|
|
27599
|
+
worktreePath: t.worktreePath
|
|
27600
|
+
}))
|
|
27601
|
+
);
|
|
27391
27602
|
}
|
|
27392
27603
|
);
|
|
27393
27604
|
server.tool(
|
|
27394
27605
|
"list_branches",
|
|
27395
27606
|
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
27396
27607
|
{
|
|
27397
|
-
repoPath:
|
|
27398
|
-
unmergedOnly:
|
|
27608
|
+
repoPath: import_zod8.z.string(),
|
|
27609
|
+
unmergedOnly: import_zod8.z.boolean().optional()
|
|
27399
27610
|
},
|
|
27400
27611
|
async ({ repoPath, unmergedOnly }) => {
|
|
27401
27612
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27416,19 +27627,19 @@ async function startMcpServer() {
|
|
|
27416
27627
|
"list_prs",
|
|
27417
27628
|
'List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_workspaces. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled eng-review with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_thread with sourceType=pr.',
|
|
27418
27629
|
{
|
|
27419
|
-
repoPath:
|
|
27420
|
-
query:
|
|
27421
|
-
queue:
|
|
27630
|
+
repoPath: import_zod8.z.string(),
|
|
27631
|
+
query: import_zod8.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
27632
|
+
queue: import_zod8.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
27422
27633
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
27423
27634
|
),
|
|
27424
|
-
state:
|
|
27425
|
-
label:
|
|
27635
|
+
state: import_zod8.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
27636
|
+
label: import_zod8.z.string().optional().describe(
|
|
27426
27637
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
27427
27638
|
),
|
|
27428
|
-
reviewer:
|
|
27639
|
+
reviewer: import_zod8.z.string().optional().describe(
|
|
27429
27640
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
27430
27641
|
),
|
|
27431
|
-
limit:
|
|
27642
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27432
27643
|
},
|
|
27433
27644
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
27434
27645
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -27460,89 +27671,65 @@ async function startMcpServer() {
|
|
|
27460
27671
|
server.tool(
|
|
27461
27672
|
"get_pr_stack",
|
|
27462
27673
|
"Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs (and only merge when the user explicitly asked).",
|
|
27463
|
-
{ ref:
|
|
27674
|
+
{ ref: import_zod8.z.string() },
|
|
27464
27675
|
async ({ ref }) => {
|
|
27465
27676
|
const stack = await orch.getPrStack(ref);
|
|
27466
|
-
return
|
|
27467
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
27468
|
-
};
|
|
27677
|
+
return mcpJson(stack);
|
|
27469
27678
|
}
|
|
27470
27679
|
);
|
|
27471
27680
|
server.tool(
|
|
27472
27681
|
"open_pr_stack_layers",
|
|
27473
27682
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
27474
27683
|
{
|
|
27475
|
-
ref:
|
|
27476
|
-
layer:
|
|
27684
|
+
ref: import_zod8.z.string(),
|
|
27685
|
+
layer: import_zod8.z.number().int().positive().optional()
|
|
27477
27686
|
},
|
|
27478
27687
|
async ({ ref, layer }) => {
|
|
27479
27688
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
27480
|
-
return {
|
|
27481
|
-
|
|
27482
|
-
|
|
27483
|
-
|
|
27484
|
-
|
|
27485
|
-
|
|
27486
|
-
|
|
27487
|
-
|
|
27488
|
-
|
|
27489
|
-
|
|
27490
|
-
|
|
27491
|
-
|
|
27492
|
-
|
|
27493
|
-
worktreePath: t.worktreePath,
|
|
27494
|
-
prUrl: t.prUrl,
|
|
27495
|
-
link: `sideboard://thread/${t.id}`
|
|
27496
|
-
}))
|
|
27497
|
-
},
|
|
27498
|
-
null,
|
|
27499
|
-
2
|
|
27500
|
-
)
|
|
27501
|
-
}
|
|
27502
|
-
]
|
|
27503
|
-
};
|
|
27689
|
+
return mcpJson({
|
|
27690
|
+
stackNumber: result.stack.stackNumber,
|
|
27691
|
+
trunk: result.stack.trunk,
|
|
27692
|
+
threads: result.threads.map((t) => ({
|
|
27693
|
+
id: t.id,
|
|
27694
|
+
title: t.title,
|
|
27695
|
+
branchName: t.branchName,
|
|
27696
|
+
stackLayer: t.stackLayer,
|
|
27697
|
+
worktreePath: t.worktreePath,
|
|
27698
|
+
prUrl: t.prUrl,
|
|
27699
|
+
link: `sideboard://thread/${t.id}`
|
|
27700
|
+
}))
|
|
27701
|
+
});
|
|
27504
27702
|
}
|
|
27505
27703
|
);
|
|
27506
27704
|
server.tool(
|
|
27507
27705
|
"add_stack_layer",
|
|
27508
27706
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
27509
27707
|
{
|
|
27510
|
-
ref:
|
|
27511
|
-
branchName:
|
|
27512
|
-
title:
|
|
27708
|
+
ref: import_zod8.z.string(),
|
|
27709
|
+
branchName: import_zod8.z.string(),
|
|
27710
|
+
title: import_zod8.z.string().optional()
|
|
27513
27711
|
},
|
|
27514
27712
|
async ({ ref, branchName, title }) => {
|
|
27515
27713
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
27516
|
-
return {
|
|
27517
|
-
|
|
27518
|
-
|
|
27519
|
-
|
|
27520
|
-
|
|
27521
|
-
|
|
27522
|
-
|
|
27523
|
-
|
|
27524
|
-
branchName: result.thread.branchName,
|
|
27525
|
-
stackLayer: result.thread.stackLayer,
|
|
27526
|
-
worktreePath: result.thread.worktreePath,
|
|
27527
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
27528
|
-
},
|
|
27529
|
-
null,
|
|
27530
|
-
2
|
|
27531
|
-
)
|
|
27532
|
-
}
|
|
27533
|
-
]
|
|
27534
|
-
};
|
|
27714
|
+
return mcpJson({
|
|
27715
|
+
id: result.thread.id,
|
|
27716
|
+
title: result.thread.title,
|
|
27717
|
+
branchName: result.thread.branchName,
|
|
27718
|
+
stackLayer: result.thread.stackLayer,
|
|
27719
|
+
worktreePath: result.thread.worktreePath,
|
|
27720
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
27721
|
+
});
|
|
27535
27722
|
}
|
|
27536
27723
|
);
|
|
27537
27724
|
server.tool(
|
|
27538
27725
|
"create_pr_stack",
|
|
27539
27726
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
27540
27727
|
{
|
|
27541
|
-
repoPath:
|
|
27542
|
-
branches:
|
|
27543
|
-
agent:
|
|
27544
|
-
base:
|
|
27545
|
-
title:
|
|
27728
|
+
repoPath: import_zod8.z.string(),
|
|
27729
|
+
branches: import_zod8.z.array(import_zod8.z.string()).min(1),
|
|
27730
|
+
agent: import_zod8.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
27731
|
+
base: import_zod8.z.string().optional(),
|
|
27732
|
+
title: import_zod8.z.string().optional()
|
|
27546
27733
|
},
|
|
27547
27734
|
async (args) => {
|
|
27548
27735
|
const result = await orch.createPrStack({
|
|
@@ -27552,39 +27739,28 @@ async function startMcpServer() {
|
|
|
27552
27739
|
base: args.base,
|
|
27553
27740
|
title: args.title
|
|
27554
27741
|
});
|
|
27555
|
-
return {
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
stackLayer: t.stackLayer,
|
|
27568
|
-
worktreePath: t.worktreePath,
|
|
27569
|
-
link: `sideboard://thread/${t.id}`
|
|
27570
|
-
}))
|
|
27571
|
-
},
|
|
27572
|
-
null,
|
|
27573
|
-
2
|
|
27574
|
-
)
|
|
27575
|
-
}
|
|
27576
|
-
]
|
|
27577
|
-
};
|
|
27742
|
+
return mcpJson({
|
|
27743
|
+
stackNumber: result.stack.stackNumber,
|
|
27744
|
+
trunk: result.stack.trunk,
|
|
27745
|
+
threads: result.threads.map((t) => ({
|
|
27746
|
+
id: t.id,
|
|
27747
|
+
title: t.title,
|
|
27748
|
+
branchName: t.branchName,
|
|
27749
|
+
stackLayer: t.stackLayer,
|
|
27750
|
+
worktreePath: t.worktreePath,
|
|
27751
|
+
link: `sideboard://thread/${t.id}`
|
|
27752
|
+
}))
|
|
27753
|
+
});
|
|
27578
27754
|
}
|
|
27579
27755
|
);
|
|
27580
27756
|
server.tool(
|
|
27581
27757
|
"list_issues",
|
|
27582
27758
|
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Then create_thread with sourceType=ticket.",
|
|
27583
27759
|
{
|
|
27584
|
-
repoPath:
|
|
27585
|
-
query:
|
|
27586
|
-
assignee:
|
|
27587
|
-
limit:
|
|
27760
|
+
repoPath: import_zod8.z.string(),
|
|
27761
|
+
query: import_zod8.z.string().optional().describe("Search title, identifier, or description"),
|
|
27762
|
+
assignee: import_zod8.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
27763
|
+
limit: import_zod8.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
27588
27764
|
},
|
|
27589
27765
|
async ({ repoPath, query, assignee, limit }) => {
|
|
27590
27766
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -29892,6 +30068,7 @@ init_outbound_watch();
|
|
|
29892
30068
|
GLOBAL_WORKSPACE_ID,
|
|
29893
30069
|
HARNESS_ENV_KEYS,
|
|
29894
30070
|
HOME_BOARD_CACHE_TTL_MS,
|
|
30071
|
+
IMPLIED_TICKET_RESOLVE_PROMPT,
|
|
29895
30072
|
ISSUE_SOURCE_LABELS,
|
|
29896
30073
|
LEGACY_ATTACHMENTS_DIR,
|
|
29897
30074
|
LEGACY_DETACHED_JOBS_DIR,
|
|
@@ -30294,6 +30471,7 @@ init_outbound_watch();
|
|
|
30294
30471
|
issueMatchesAssignee,
|
|
30295
30472
|
issueSourceLabel,
|
|
30296
30473
|
issueTicketFromThread,
|
|
30474
|
+
lastAssistantMessageText,
|
|
30297
30475
|
lastRequestOccupancy,
|
|
30298
30476
|
latestPendingPlanQuestions,
|
|
30299
30477
|
linearAuthorizationHeader,
|
|
@@ -30444,6 +30622,7 @@ init_outbound_watch();
|
|
|
30444
30622
|
resolveCodexGitWritableRoots,
|
|
30445
30623
|
resolveCommandBinarySync,
|
|
30446
30624
|
resolveConductorCursorAgentId,
|
|
30625
|
+
resolveCreateFirstPrompt,
|
|
30447
30626
|
resolveCursorModelId,
|
|
30448
30627
|
resolveDefaultBranch,
|
|
30449
30628
|
resolveDetachedJobScript,
|