@sideboard-ai/core 0.1.157 → 0.1.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abletime-BEIDJG7W.js → abletime-HOQBDQC4.js} +2 -2
- package/dist/{abletime-GWZE6OYM.js → abletime-LK7GV556.js} +2 -2
- package/dist/{agents-LCDXN2B2.js → agents-5HY76BCR.js} +4 -4
- package/dist/{agents-C55LAEUR.js → agents-67ZBH5OE.js} +5 -5
- package/dist/{app-settings-VRQI3FIE.js → app-settings-BJ5GFAE5.js} +13 -17
- package/dist/{app-settings-H3INSR67.js → app-settings-YWJ4MVYM.js} +13 -17
- package/dist/{chunk-NVXVEGGM.js → chunk-5U4NZLBH.js} +4 -0
- package/dist/{chunk-DVJBO3M7.js → chunk-AGLPYXDT.js} +1 -1
- package/dist/{chunk-KQ4JOGUH.js → chunk-DWUOKF3W.js} +11 -5
- package/dist/{chunk-D7EGJ3EI.js → chunk-EQIJ5MHR.js} +119 -110
- package/dist/{chunk-3BWVOUQG.js → chunk-FLG4A5FJ.js} +119 -110
- package/dist/{chunk-2SDMENAL.js → chunk-JV4VNFMS.js} +1 -1
- package/dist/{chunk-LQMFCS54.js → chunk-KEDHHXKW.js} +3 -13
- package/dist/{chunk-CJQLPPEJ.js → chunk-MEZXWELT.js} +4 -14
- package/dist/{chunk-CVR4RJ6G.js → chunk-O7RWWPMR.js} +12 -13
- package/dist/{chunk-GWEEGE6L.js → chunk-OYOXLY6B.js} +404 -115
- package/dist/{chunk-4I4VKAPZ.js → chunk-SIMFKJIB.js} +13 -14
- package/dist/{chunk-SUCJAWV4.js → chunk-TGU3LSDG.js} +402 -113
- package/dist/{chunk-N27GVFZY.js → chunk-UBMCEZRE.js} +1 -1
- package/dist/{chunk-TIJ6QVX5.js → chunk-V4R6L2ZL.js} +15 -6
- package/dist/{coordinator-prompt-AM47SATF.js → coordinator-prompt-7NKY7QRK.js} +4 -4
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-CNI6AK65.js} +3 -3
- package/dist/{global-workspace-QV7CC7SK.js → global-workspace-FKAJZHO5.js} +4 -4
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-IJYCUO4M.js} +3 -3
- package/dist/index.cjs +1037 -648
- package/dist/index.d.cts +71 -57
- package/dist/index.d.ts +71 -57
- package/dist/index.js +339 -250
- package/dist/mcp/run-stdio.cjs +967 -575
- package/dist/mcp/run-stdio.js +283 -187
- package/dist/{orchestrator-BUH3LJLL.js → orchestrator-KFLS5BTP.js} +5 -5
- package/dist/{orchestrator-KQO4MXBI.js → orchestrator-NSVP7DJH.js} +7 -7
- package/dist/{plan-file-OZEBFSGQ.js → plan-file-I24VJVP2.js} +2 -2
- package/dist/{workspaces-NYSM2EPX.js → workspaces-D2M3AM2M.js} +3 -3
- package/dist/{workspaces-Q2VCHIAS.js → workspaces-OFSDJ7PZ.js} +4 -4
- package/dist/{worktree-PEWDAX5O.js → worktree-BQE5KLRY.js} +3 -3
- package/dist/{worktree-T4SWFSTT.js → worktree-EA6AD6D6.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -22,6 +22,10 @@ import {
|
|
|
22
22
|
listSchedules,
|
|
23
23
|
listSlackOutboundWatches,
|
|
24
24
|
listSlackWorkspaces,
|
|
25
|
+
mcpWaitFinishedHint,
|
|
26
|
+
mcpWaitForJobTimeoutMs,
|
|
27
|
+
mcpWaitForTurnTimeoutMs,
|
|
28
|
+
mcpWaitStillRunningHint,
|
|
25
29
|
pollSlackOutboundWatches,
|
|
26
30
|
readTurnLive,
|
|
27
31
|
recordSlackOutboundWatch,
|
|
@@ -30,13 +34,14 @@ import {
|
|
|
30
34
|
slackApi,
|
|
31
35
|
slackTokenFor,
|
|
32
36
|
syncBoardPins,
|
|
33
|
-
updateSchedule
|
|
34
|
-
|
|
37
|
+
updateSchedule,
|
|
38
|
+
waitForDetachedJob
|
|
39
|
+
} from "../chunk-TGU3LSDG.js";
|
|
35
40
|
import {
|
|
36
41
|
isInternalAgentStatusText,
|
|
37
42
|
listModelsForAgent,
|
|
38
43
|
sideboardMcpProfile
|
|
39
|
-
} from "../chunk-
|
|
44
|
+
} from "../chunk-DWUOKF3W.js";
|
|
40
45
|
import "../chunk-LZ6QMEQC.js";
|
|
41
46
|
import "../chunk-S42XV45P.js";
|
|
42
47
|
import {
|
|
@@ -51,16 +56,17 @@ import {
|
|
|
51
56
|
searchAbleTimeTasks,
|
|
52
57
|
toAbleTimeIssueInfo,
|
|
53
58
|
updateAbleTimeTask
|
|
54
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-UBMCEZRE.js";
|
|
55
60
|
import {
|
|
56
61
|
httpFetch
|
|
57
62
|
} from "../chunk-6GN5WPYZ.js";
|
|
58
|
-
import "../chunk-
|
|
63
|
+
import "../chunk-JV4VNFMS.js";
|
|
59
64
|
import "../chunk-OCQPTUR7.js";
|
|
60
65
|
import {
|
|
61
66
|
GLOBAL_WORKSPACE_ID,
|
|
62
|
-
isCloudCoordinatorThread
|
|
63
|
-
|
|
67
|
+
isCloudCoordinatorThread,
|
|
68
|
+
listWorkspaces
|
|
69
|
+
} from "../chunk-SIMFKJIB.js";
|
|
64
70
|
import {
|
|
65
71
|
canonicalizeRepoPath,
|
|
66
72
|
formatGhLandError,
|
|
@@ -71,8 +77,8 @@ import {
|
|
|
71
77
|
resolveListPrsOptions,
|
|
72
78
|
resolveRepoRoot,
|
|
73
79
|
warmGithubAgentAuth
|
|
74
|
-
} from "../chunk-
|
|
75
|
-
import "../chunk-
|
|
80
|
+
} from "../chunk-MEZXWELT.js";
|
|
81
|
+
import "../chunk-5U4NZLBH.js";
|
|
76
82
|
import "../chunk-LZQULFIT.js";
|
|
77
83
|
import "../chunk-SYFVUF2C.js";
|
|
78
84
|
import {
|
|
@@ -85,11 +91,13 @@ import {
|
|
|
85
91
|
isAbleTimeConnected,
|
|
86
92
|
isLinearConnected,
|
|
87
93
|
loadAppSettings,
|
|
94
|
+
readViewerContext,
|
|
88
95
|
resolveEffectiveIssueSource,
|
|
89
96
|
resolveViewerProfile,
|
|
90
97
|
resolveViewerProfileForRepo,
|
|
91
|
-
saveLinearOAuth
|
|
92
|
-
|
|
98
|
+
saveLinearOAuth,
|
|
99
|
+
writeViewerContext
|
|
100
|
+
} from "../chunk-EQIJ5MHR.js";
|
|
93
101
|
import "../chunk-NSTQ6QKD.js";
|
|
94
102
|
import "../chunk-KGZBYWZ3.js";
|
|
95
103
|
import {
|
|
@@ -99,7 +107,7 @@ import {
|
|
|
99
107
|
// src/mcp/server.ts
|
|
100
108
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
101
109
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
102
|
-
import { z as
|
|
110
|
+
import { z as z7 } from "zod";
|
|
103
111
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
104
112
|
import { basename } from "path";
|
|
105
113
|
|
|
@@ -930,35 +938,13 @@ function mcpArchiveBlockedReason(thread) {
|
|
|
930
938
|
return null;
|
|
931
939
|
}
|
|
932
940
|
|
|
933
|
-
// src/mcp/wait-for-turn.ts
|
|
934
|
-
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
935
|
-
function mcpWaitForTurnTimeoutMs(requested) {
|
|
936
|
-
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
937
|
-
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
938
|
-
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
939
|
-
}
|
|
940
|
-
var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
|
|
941
|
-
var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
|
|
942
|
-
function mcpWaitStillRunningHint(status) {
|
|
943
|
-
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
944
|
-
}
|
|
945
|
-
var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
|
|
946
|
-
var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
947
|
-
var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
|
|
948
|
-
function mcpWaitFinishedHint(status) {
|
|
949
|
-
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
950
|
-
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
951
|
-
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
952
|
-
return void 0;
|
|
953
|
-
}
|
|
954
|
-
|
|
955
941
|
// src/mcp/thread-visibility.ts
|
|
956
942
|
function lastMessagePreview(messages, max = 160) {
|
|
957
943
|
if (!messages?.length) return null;
|
|
958
944
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
959
|
-
const
|
|
960
|
-
if (!
|
|
961
|
-
const flat =
|
|
945
|
+
const text7 = messages[i]?.text?.trim();
|
|
946
|
+
if (!text7 || isInternalAgentStatusText(text7)) continue;
|
|
947
|
+
const flat = text7.replace(/\s+/g, " ");
|
|
962
948
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
963
949
|
}
|
|
964
950
|
return null;
|
|
@@ -1190,8 +1176,8 @@ function labelGithubUrl(url) {
|
|
|
1190
1176
|
}
|
|
1191
1177
|
return { kind: "other", label: "GitHub", url: raw };
|
|
1192
1178
|
}
|
|
1193
|
-
function appendGithubLink(
|
|
1194
|
-
const body =
|
|
1179
|
+
function appendGithubLink(text7, githubUrl) {
|
|
1180
|
+
const body = text7.trimEnd();
|
|
1195
1181
|
if (!githubUrl?.trim()) return body;
|
|
1196
1182
|
const labeled = labelGithubUrl(githubUrl);
|
|
1197
1183
|
if (!labeled) {
|
|
@@ -2055,6 +2041,101 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
2055
2041
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
2056
2042
|
}
|
|
2057
2043
|
|
|
2044
|
+
// src/mcp/viewer-context-tools.ts
|
|
2045
|
+
import { z as z5 } from "zod";
|
|
2046
|
+
function text5(payload, isError = false) {
|
|
2047
|
+
return mcpJson(payload, isError);
|
|
2048
|
+
}
|
|
2049
|
+
function matchRegisteredWorkspace(repoPath) {
|
|
2050
|
+
const hint = (repoPath ?? "").trim().replace(/\/+$/, "");
|
|
2051
|
+
if (!hint) return null;
|
|
2052
|
+
const match = listWorkspaces().find((w) => {
|
|
2053
|
+
const stored = w.path.replace(/\/+$/, "");
|
|
2054
|
+
return stored === hint || hint.startsWith(`${stored}/`) || stored.startsWith(`${hint}/`);
|
|
2055
|
+
});
|
|
2056
|
+
return match?.path.replace(/\/+$/, "") ?? null;
|
|
2057
|
+
}
|
|
2058
|
+
async function resolveProjectRepoPath(repoPath) {
|
|
2059
|
+
const hint = repoPath?.trim() || process.cwd();
|
|
2060
|
+
let root = hint;
|
|
2061
|
+
try {
|
|
2062
|
+
root = await resolveRepoRoot(hint);
|
|
2063
|
+
} catch {
|
|
2064
|
+
root = hint.replace(/\/+$/, "");
|
|
2065
|
+
}
|
|
2066
|
+
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
2067
|
+
}
|
|
2068
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_workspaces. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
2069
|
+
function registerViewerContextTools(server) {
|
|
2070
|
+
server.tool(
|
|
2071
|
+
"get_viewer_context",
|
|
2072
|
+
"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).",
|
|
2073
|
+
{
|
|
2074
|
+
repoPath: z5.string().optional().describe(
|
|
2075
|
+
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
2076
|
+
)
|
|
2077
|
+
},
|
|
2078
|
+
async ({ repoPath }) => {
|
|
2079
|
+
try {
|
|
2080
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
2081
|
+
return text5(readViewerContext(path ?? void 0));
|
|
2082
|
+
} catch (err) {
|
|
2083
|
+
return text5(
|
|
2084
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
2085
|
+
true
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
);
|
|
2090
|
+
server.tool(
|
|
2091
|
+
"update_viewer_context",
|
|
2092
|
+
"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.",
|
|
2093
|
+
{
|
|
2094
|
+
scope: z5.enum(["account", "project"]).describe("account = Settings \u2192 Agents; project = Settings \u2192 Projects for one repo"),
|
|
2095
|
+
context: z5.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
2096
|
+
confirmed: z5.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
2097
|
+
repoPath: z5.string().optional().describe(
|
|
2098
|
+
"Required for scope=project from orchestration (path from list_workspaces). Worktree turns may omit it (cwd)."
|
|
2099
|
+
)
|
|
2100
|
+
},
|
|
2101
|
+
async ({ scope, context, confirmed, repoPath }) => {
|
|
2102
|
+
try {
|
|
2103
|
+
if (scope === "project") {
|
|
2104
|
+
const path = await resolveProjectRepoPath(repoPath);
|
|
2105
|
+
if (!path) {
|
|
2106
|
+
return text5(
|
|
2107
|
+
{
|
|
2108
|
+
ok: false,
|
|
2109
|
+
error: "repoPath required",
|
|
2110
|
+
message: PROJECT_PATH_HINT
|
|
2111
|
+
},
|
|
2112
|
+
true
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
const result2 = writeViewerContext({
|
|
2116
|
+
scope,
|
|
2117
|
+
context,
|
|
2118
|
+
repoPath: path,
|
|
2119
|
+
confirmed: confirmed === true
|
|
2120
|
+
});
|
|
2121
|
+
return text5(result2, !result2.ok);
|
|
2122
|
+
}
|
|
2123
|
+
const result = writeViewerContext({
|
|
2124
|
+
scope,
|
|
2125
|
+
context,
|
|
2126
|
+
confirmed: confirmed === true
|
|
2127
|
+
});
|
|
2128
|
+
return text5(result, !result.ok);
|
|
2129
|
+
} catch (err) {
|
|
2130
|
+
return text5(
|
|
2131
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
2132
|
+
true
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2058
2139
|
// src/mcp/pr-list.ts
|
|
2059
2140
|
function compactPrRow(pr) {
|
|
2060
2141
|
return {
|
|
@@ -2090,15 +2171,15 @@ function formatMcpPrList(input) {
|
|
|
2090
2171
|
}
|
|
2091
2172
|
|
|
2092
2173
|
// src/mcp/schedule-tools.ts
|
|
2093
|
-
import { z as
|
|
2094
|
-
function
|
|
2174
|
+
import { z as z6 } from "zod";
|
|
2175
|
+
function text6(payload, isError = false) {
|
|
2095
2176
|
return {
|
|
2096
2177
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
2097
2178
|
...isError ? { isError: true } : {}
|
|
2098
2179
|
};
|
|
2099
2180
|
}
|
|
2100
2181
|
function fail5(err) {
|
|
2101
|
-
return
|
|
2182
|
+
return text6(
|
|
2102
2183
|
{ error: err instanceof Error ? err.message : String(err) },
|
|
2103
2184
|
true
|
|
2104
2185
|
);
|
|
@@ -2123,7 +2204,7 @@ function registerScheduleTools(server) {
|
|
|
2123
2204
|
{},
|
|
2124
2205
|
async () => {
|
|
2125
2206
|
try {
|
|
2126
|
-
return
|
|
2207
|
+
return text6({ schedules: listSchedules() });
|
|
2127
2208
|
} catch (err) {
|
|
2128
2209
|
return fail5(err);
|
|
2129
2210
|
}
|
|
@@ -2133,17 +2214,17 @@ function registerScheduleTools(server) {
|
|
|
2133
2214
|
"create_schedule",
|
|
2134
2215
|
"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.",
|
|
2135
2216
|
{
|
|
2136
|
-
prompt:
|
|
2137
|
-
name:
|
|
2138
|
-
at:
|
|
2139
|
-
every:
|
|
2140
|
-
cron:
|
|
2141
|
-
tz:
|
|
2142
|
-
threadId:
|
|
2217
|
+
prompt: z6.string().describe("User message / goal queued when the schedule fires"),
|
|
2218
|
+
name: z6.string().optional(),
|
|
2219
|
+
at: z6.string().optional().describe("ISO datetime for a one-shot"),
|
|
2220
|
+
every: z6.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
2221
|
+
cron: z6.string().optional().describe("5-field cron expression"),
|
|
2222
|
+
tz: z6.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
2223
|
+
threadId: z6.string().optional().describe(
|
|
2143
2224
|
'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
|
|
2144
2225
|
),
|
|
2145
|
-
agent:
|
|
2146
|
-
model:
|
|
2226
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
2227
|
+
model: z6.string().optional()
|
|
2147
2228
|
},
|
|
2148
2229
|
async (args) => {
|
|
2149
2230
|
try {
|
|
@@ -2163,7 +2244,7 @@ function registerScheduleTools(server) {
|
|
|
2163
2244
|
model: args.model,
|
|
2164
2245
|
createdBy: "mcp"
|
|
2165
2246
|
});
|
|
2166
|
-
return
|
|
2247
|
+
return text6({
|
|
2167
2248
|
schedule,
|
|
2168
2249
|
hint: "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Overnight runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate."
|
|
2169
2250
|
});
|
|
@@ -2176,17 +2257,17 @@ function registerScheduleTools(server) {
|
|
|
2176
2257
|
"update_schedule",
|
|
2177
2258
|
"Update a local schedule (prompt, cadence, target thread, enabled). Pass id from list_schedules.",
|
|
2178
2259
|
{
|
|
2179
|
-
id:
|
|
2180
|
-
prompt:
|
|
2181
|
-
name:
|
|
2182
|
-
at:
|
|
2183
|
-
every:
|
|
2184
|
-
cron:
|
|
2185
|
-
tz:
|
|
2186
|
-
threadId:
|
|
2187
|
-
agent:
|
|
2188
|
-
model:
|
|
2189
|
-
enabled:
|
|
2260
|
+
id: z6.string(),
|
|
2261
|
+
prompt: z6.string().optional(),
|
|
2262
|
+
name: z6.string().optional(),
|
|
2263
|
+
at: z6.string().optional(),
|
|
2264
|
+
every: z6.string().optional(),
|
|
2265
|
+
cron: z6.string().optional(),
|
|
2266
|
+
tz: z6.string().optional(),
|
|
2267
|
+
threadId: z6.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
|
|
2268
|
+
agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
2269
|
+
model: z6.string().optional(),
|
|
2270
|
+
enabled: z6.boolean().optional()
|
|
2190
2271
|
},
|
|
2191
2272
|
async (args) => {
|
|
2192
2273
|
try {
|
|
@@ -2215,7 +2296,7 @@ function registerScheduleTools(server) {
|
|
|
2215
2296
|
enabled: args.enabled,
|
|
2216
2297
|
model: args.model
|
|
2217
2298
|
});
|
|
2218
|
-
return
|
|
2299
|
+
return text6({ schedule });
|
|
2219
2300
|
} catch (err) {
|
|
2220
2301
|
return fail5(err);
|
|
2221
2302
|
}
|
|
@@ -2224,11 +2305,11 @@ function registerScheduleTools(server) {
|
|
|
2224
2305
|
server.tool(
|
|
2225
2306
|
"delete_schedule",
|
|
2226
2307
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
2227
|
-
{ id:
|
|
2308
|
+
{ id: z6.string() },
|
|
2228
2309
|
async ({ id }) => {
|
|
2229
2310
|
try {
|
|
2230
2311
|
deleteSchedule(id);
|
|
2231
|
-
return
|
|
2312
|
+
return text6({ ok: true, id });
|
|
2232
2313
|
} catch (err) {
|
|
2233
2314
|
return fail5(err);
|
|
2234
2315
|
}
|
|
@@ -2237,14 +2318,14 @@ function registerScheduleTools(server) {
|
|
|
2237
2318
|
server.tool(
|
|
2238
2319
|
"run_schedule",
|
|
2239
2320
|
"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.",
|
|
2240
|
-
{ id:
|
|
2321
|
+
{ id: z6.string() },
|
|
2241
2322
|
async ({ id }) => {
|
|
2242
2323
|
try {
|
|
2243
2324
|
if (!getSchedule(id)) {
|
|
2244
2325
|
return fail5(new Error(`Schedule not found: ${id}`));
|
|
2245
2326
|
}
|
|
2246
2327
|
const schedule = await fireSchedule(id);
|
|
2247
|
-
return
|
|
2328
|
+
return text6({ schedule });
|
|
2248
2329
|
} catch (err) {
|
|
2249
2330
|
return fail5(err);
|
|
2250
2331
|
}
|
|
@@ -2585,7 +2666,7 @@ async function startMcpServer() {
|
|
|
2585
2666
|
);
|
|
2586
2667
|
}
|
|
2587
2668
|
try {
|
|
2588
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
2669
|
+
const { maxConcurrentAgents } = await import("../app-settings-BJ5GFAE5.js");
|
|
2589
2670
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
2590
2671
|
} catch {
|
|
2591
2672
|
}
|
|
@@ -2602,13 +2683,14 @@ async function startMcpServer() {
|
|
|
2602
2683
|
version: "0.1.0"
|
|
2603
2684
|
});
|
|
2604
2685
|
const worktreeProfile = sideboardMcpProfile() === "worktree";
|
|
2686
|
+
registerViewerContextTools(server);
|
|
2605
2687
|
if (worktreeProfile) {
|
|
2606
2688
|
registerConnectedIssueVendorTools(server);
|
|
2607
2689
|
}
|
|
2608
2690
|
if (!worktreeProfile) {
|
|
2609
2691
|
server.tool(
|
|
2610
2692
|
"list_workspaces",
|
|
2611
|
-
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and
|
|
2693
|
+
"List registered Sideboard workspaces (repos). Each line is name, path, github:owner/repo when resolvable, and project context when set \u2014 use path as repoPath for list_board/list_branches/list_prs/list_issues/create_thread. Account / project context (Settings \u2192 Agents / Projects) says which tickets and review PRs belong to the user.",
|
|
2612
2694
|
{},
|
|
2613
2695
|
async () => {
|
|
2614
2696
|
const workspaces = orch.listWorkspaces();
|
|
@@ -2657,13 +2739,13 @@ async function startMcpServer() {
|
|
|
2657
2739
|
"list_board",
|
|
2658
2740
|
"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.",
|
|
2659
2741
|
{
|
|
2660
|
-
query:
|
|
2661
|
-
repoPath:
|
|
2662
|
-
kind:
|
|
2663
|
-
column:
|
|
2742
|
+
query: z7.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
2743
|
+
repoPath: z7.string().optional().describe("Limit to one workspace path from list_workspaces"),
|
|
2744
|
+
kind: z7.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
2745
|
+
column: z7.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
2664
2746
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
2665
2747
|
),
|
|
2666
|
-
limit:
|
|
2748
|
+
limit: z7.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
2667
2749
|
},
|
|
2668
2750
|
async ({ query, repoPath, kind, column, limit }) => {
|
|
2669
2751
|
const workspaces = orch.listWorkspaces();
|
|
@@ -2701,7 +2783,7 @@ async function startMcpServer() {
|
|
|
2701
2783
|
server.tool(
|
|
2702
2784
|
"get_thread",
|
|
2703
2785
|
"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.",
|
|
2704
|
-
{ ref:
|
|
2786
|
+
{ ref: z7.string() },
|
|
2705
2787
|
async ({ ref }) => {
|
|
2706
2788
|
const t = orch.getThread(ref);
|
|
2707
2789
|
if (!t) {
|
|
@@ -2743,17 +2825,17 @@ async function startMcpServer() {
|
|
|
2743
2825
|
"present_artifact",
|
|
2744
2826
|
"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.",
|
|
2745
2827
|
{
|
|
2746
|
-
title:
|
|
2747
|
-
type:
|
|
2828
|
+
title: z7.string().describe("Short title shown in the artifact pane header"),
|
|
2829
|
+
type: z7.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
2748
2830
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
2749
2831
|
),
|
|
2750
|
-
content:
|
|
2832
|
+
content: z7.string().describe(
|
|
2751
2833
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
2752
2834
|
),
|
|
2753
|
-
artifact_id:
|
|
2754
|
-
status:
|
|
2755
|
-
phase:
|
|
2756
|
-
mode:
|
|
2835
|
+
artifact_id: z7.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
2836
|
+
status: z7.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
2837
|
+
phase: z7.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
2838
|
+
mode: z7.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
2757
2839
|
},
|
|
2758
2840
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
2759
2841
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -2774,15 +2856,15 @@ async function startMcpServer() {
|
|
|
2774
2856
|
"ask_user",
|
|
2775
2857
|
"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.",
|
|
2776
2858
|
{
|
|
2777
|
-
questions:
|
|
2778
|
-
|
|
2779
|
-
question:
|
|
2780
|
-
header:
|
|
2781
|
-
multiSelect:
|
|
2782
|
-
options:
|
|
2783
|
-
|
|
2784
|
-
label:
|
|
2785
|
-
description:
|
|
2859
|
+
questions: z7.array(
|
|
2860
|
+
z7.object({
|
|
2861
|
+
question: z7.string().describe("Full question text ending with ?"),
|
|
2862
|
+
header: z7.string().max(24).optional().describe("Short label shown above the question"),
|
|
2863
|
+
multiSelect: z7.boolean().optional().describe("Allow selecting multiple options"),
|
|
2864
|
+
options: z7.array(
|
|
2865
|
+
z7.object({
|
|
2866
|
+
label: z7.string(),
|
|
2867
|
+
description: z7.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
2786
2868
|
})
|
|
2787
2869
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
2788
2870
|
})
|
|
@@ -2801,12 +2883,12 @@ async function startMcpServer() {
|
|
|
2801
2883
|
"present_plan",
|
|
2802
2884
|
"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.",
|
|
2803
2885
|
{
|
|
2804
|
-
title:
|
|
2805
|
-
content:
|
|
2806
|
-
thread_id:
|
|
2886
|
+
title: z7.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
2887
|
+
content: z7.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
2888
|
+
thread_id: z7.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
2807
2889
|
},
|
|
2808
2890
|
async ({ title, content, thread_id }) => {
|
|
2809
|
-
const { writePlanFile } = await import("../plan-file-
|
|
2891
|
+
const { writePlanFile } = await import("../plan-file-I24VJVP2.js");
|
|
2810
2892
|
let root = process.cwd();
|
|
2811
2893
|
if (thread_id?.trim()) {
|
|
2812
2894
|
const t = orch.getThread(thread_id.trim());
|
|
@@ -2826,15 +2908,15 @@ async function startMcpServer() {
|
|
|
2826
2908
|
"present_schema",
|
|
2827
2909
|
"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.",
|
|
2828
2910
|
{
|
|
2829
|
-
title:
|
|
2830
|
-
mode:
|
|
2831
|
-
datasource:
|
|
2832
|
-
resource_id:
|
|
2833
|
-
record_id:
|
|
2834
|
-
resource:
|
|
2835
|
-
record:
|
|
2836
|
-
records:
|
|
2837
|
-
pane_id:
|
|
2911
|
+
title: z7.string().describe("Pane title"),
|
|
2912
|
+
mode: z7.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
2913
|
+
datasource: z7.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
2914
|
+
resource_id: z7.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
2915
|
+
record_id: z7.string().optional().describe("Record id when opening form mode"),
|
|
2916
|
+
resource: z7.record(z7.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
2917
|
+
record: z7.record(z7.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
2918
|
+
records: z7.array(z7.record(z7.unknown())).optional().describe("Inline records for table mode"),
|
|
2919
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
2838
2920
|
},
|
|
2839
2921
|
async (args) => {
|
|
2840
2922
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -2855,10 +2937,10 @@ async function startMcpServer() {
|
|
|
2855
2937
|
"present_files",
|
|
2856
2938
|
"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.",
|
|
2857
2939
|
{
|
|
2858
|
-
title:
|
|
2859
|
-
datasource:
|
|
2860
|
-
path:
|
|
2861
|
-
pane_id:
|
|
2940
|
+
title: z7.string().optional().describe("Pane title (default: Files)"),
|
|
2941
|
+
datasource: z7.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
2942
|
+
path: z7.string().optional().describe("Initial folder path (e.g. public)"),
|
|
2943
|
+
pane_id: z7.string().optional().describe("Stable pane id across updates")
|
|
2862
2944
|
},
|
|
2863
2945
|
async (args) => {
|
|
2864
2946
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -2873,19 +2955,33 @@ async function startMcpServer() {
|
|
|
2873
2955
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
2874
2956
|
}
|
|
2875
2957
|
);
|
|
2958
|
+
server.tool(
|
|
2959
|
+
"wait_for_job",
|
|
2960
|
+
"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.",
|
|
2961
|
+
{
|
|
2962
|
+
id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.js start)"),
|
|
2963
|
+
timeoutMs: z7.number().optional()
|
|
2964
|
+
},
|
|
2965
|
+
async ({ id, timeoutMs }) => {
|
|
2966
|
+
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
2967
|
+
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
2968
|
+
});
|
|
2969
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
2970
|
+
}
|
|
2971
|
+
);
|
|
2876
2972
|
if (!worktreeProfile) {
|
|
2877
2973
|
registerSlackTools(server);
|
|
2878
2974
|
registerConnectedIssueVendorTools(server);
|
|
2879
2975
|
registerScheduleTools(server);
|
|
2880
2976
|
const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-DBKMRZZO.js");
|
|
2881
|
-
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-
|
|
2977
|
+
const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-BJ5GFAE5.js");
|
|
2882
2978
|
const accountDefaults = resolveThreadDefaults();
|
|
2883
2979
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
2884
2980
|
server.tool(
|
|
2885
2981
|
"set_caffeinate",
|
|
2886
2982
|
"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.",
|
|
2887
2983
|
{
|
|
2888
|
-
enabled:
|
|
2984
|
+
enabled: z7.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
2889
2985
|
},
|
|
2890
2986
|
async ({ enabled }) => {
|
|
2891
2987
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -2923,18 +3019,18 @@ async function startMcpServer() {
|
|
|
2923
3019
|
"create_thread",
|
|
2924
3020
|
`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.`,
|
|
2925
3021
|
{
|
|
2926
|
-
sourceType:
|
|
2927
|
-
sourceRef:
|
|
2928
|
-
agent:
|
|
2929
|
-
model:
|
|
3022
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]),
|
|
3023
|
+
sourceRef: z7.string(),
|
|
3024
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
3025
|
+
model: z7.string().nullable().optional().describe(
|
|
2930
3026
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
2931
3027
|
),
|
|
2932
|
-
repoPath:
|
|
2933
|
-
title:
|
|
2934
|
-
cowboy:
|
|
3028
|
+
repoPath: z7.string(),
|
|
3029
|
+
title: z7.string().optional(),
|
|
3030
|
+
cowboy: z7.boolean().optional().describe(
|
|
2935
3031
|
"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."
|
|
2936
3032
|
),
|
|
2937
|
-
parentThreadId:
|
|
3033
|
+
parentThreadId: z7.string().optional().describe(
|
|
2938
3034
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
2939
3035
|
)
|
|
2940
3036
|
},
|
|
@@ -2950,10 +3046,10 @@ async function startMcpServer() {
|
|
|
2950
3046
|
"start_board_card",
|
|
2951
3047
|
"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.",
|
|
2952
3048
|
{
|
|
2953
|
-
kind:
|
|
2954
|
-
ref:
|
|
2955
|
-
repoPath:
|
|
2956
|
-
title:
|
|
3049
|
+
kind: z7.enum(["ticket", "pr", "branch"]),
|
|
3050
|
+
ref: z7.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
3051
|
+
repoPath: z7.string().describe("Workspace path from list_workspaces / list_board"),
|
|
3052
|
+
title: z7.string().optional()
|
|
2957
3053
|
},
|
|
2958
3054
|
async ({ kind, ref, repoPath, title }) => {
|
|
2959
3055
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -3057,9 +3153,9 @@ async function startMcpServer() {
|
|
|
3057
3153
|
"send_to_thread",
|
|
3058
3154
|
'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.',
|
|
3059
3155
|
{
|
|
3060
|
-
ref:
|
|
3061
|
-
prompt:
|
|
3062
|
-
force_stop:
|
|
3156
|
+
ref: z7.string(),
|
|
3157
|
+
prompt: z7.string(),
|
|
3158
|
+
force_stop: z7.boolean().optional()
|
|
3063
3159
|
},
|
|
3064
3160
|
async ({ ref, prompt, force_stop }) => {
|
|
3065
3161
|
if (force_stop) {
|
|
@@ -3088,8 +3184,8 @@ async function startMcpServer() {
|
|
|
3088
3184
|
"wait_for_turn",
|
|
3089
3185
|
"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).",
|
|
3090
3186
|
{
|
|
3091
|
-
ref:
|
|
3092
|
-
timeoutMs:
|
|
3187
|
+
ref: z7.string(),
|
|
3188
|
+
timeoutMs: z7.number().optional()
|
|
3093
3189
|
},
|
|
3094
3190
|
async ({ ref, timeoutMs }) => {
|
|
3095
3191
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -3119,7 +3215,7 @@ async function startMcpServer() {
|
|
|
3119
3215
|
server.tool(
|
|
3120
3216
|
"get_turn_result",
|
|
3121
3217
|
"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).",
|
|
3122
|
-
{ ref:
|
|
3218
|
+
{ ref: z7.string() },
|
|
3123
3219
|
async ({ ref }) => {
|
|
3124
3220
|
const result = orch.getTurnResult(ref);
|
|
3125
3221
|
return {
|
|
@@ -3140,8 +3236,8 @@ async function startMcpServer() {
|
|
|
3140
3236
|
"stop_thread",
|
|
3141
3237
|
"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.",
|
|
3142
3238
|
{
|
|
3143
|
-
ref:
|
|
3144
|
-
force:
|
|
3239
|
+
ref: z7.string(),
|
|
3240
|
+
force: z7.boolean().optional()
|
|
3145
3241
|
},
|
|
3146
3242
|
async ({ ref, force }) => {
|
|
3147
3243
|
const t = orch.getThread(ref);
|
|
@@ -3171,7 +3267,7 @@ async function startMcpServer() {
|
|
|
3171
3267
|
server.tool(
|
|
3172
3268
|
"archive_thread",
|
|
3173
3269
|
"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.",
|
|
3174
|
-
{ ref:
|
|
3270
|
+
{ ref: z7.string() },
|
|
3175
3271
|
async ({ ref }) => {
|
|
3176
3272
|
const t = orch.getThread(ref);
|
|
3177
3273
|
if (!t) {
|
|
@@ -3204,7 +3300,7 @@ async function startMcpServer() {
|
|
|
3204
3300
|
server.tool(
|
|
3205
3301
|
"restore_thread",
|
|
3206
3302
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
3207
|
-
{ ref:
|
|
3303
|
+
{ ref: z7.string() },
|
|
3208
3304
|
async ({ ref }) => {
|
|
3209
3305
|
try {
|
|
3210
3306
|
const restored = await orch.restore(ref);
|
|
@@ -3230,8 +3326,8 @@ async function startMcpServer() {
|
|
|
3230
3326
|
"get_diff",
|
|
3231
3327
|
"Compact diff summary (capped hunks, paginated)",
|
|
3232
3328
|
{
|
|
3233
|
-
ref:
|
|
3234
|
-
maxFiles:
|
|
3329
|
+
ref: z7.string(),
|
|
3330
|
+
maxFiles: z7.number().optional()
|
|
3235
3331
|
},
|
|
3236
3332
|
async ({ ref, maxFiles }) => {
|
|
3237
3333
|
const summary = await orch.diffSummary(ref);
|
|
@@ -3251,7 +3347,7 @@ async function startMcpServer() {
|
|
|
3251
3347
|
server.tool(
|
|
3252
3348
|
"get_pr_checks",
|
|
3253
3349
|
"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.",
|
|
3254
|
-
{ ref:
|
|
3350
|
+
{ ref: z7.string().describe("Worktree thread id/ref") },
|
|
3255
3351
|
async ({ ref }) => {
|
|
3256
3352
|
try {
|
|
3257
3353
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -3267,7 +3363,7 @@ async function startMcpServer() {
|
|
|
3267
3363
|
server.tool(
|
|
3268
3364
|
"request_review",
|
|
3269
3365
|
'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.',
|
|
3270
|
-
{ ref:
|
|
3366
|
+
{ ref: z7.string().describe("Worktree thread id/ref to review") },
|
|
3271
3367
|
async ({ ref }) => {
|
|
3272
3368
|
try {
|
|
3273
3369
|
const tab = await orch.requestReview(ref);
|
|
@@ -3296,8 +3392,8 @@ async function startMcpServer() {
|
|
|
3296
3392
|
"ask_git",
|
|
3297
3393
|
"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.",
|
|
3298
3394
|
{
|
|
3299
|
-
ref:
|
|
3300
|
-
action:
|
|
3395
|
+
ref: z7.string().describe("Worktree thread id/ref"),
|
|
3396
|
+
action: z7.enum(AGENT_GIT_ACTIONS).describe(
|
|
3301
3397
|
"commit-push | create-draft | create-web | resolve-conflicts | merge"
|
|
3302
3398
|
)
|
|
3303
3399
|
},
|
|
@@ -3342,7 +3438,7 @@ async function startMcpServer() {
|
|
|
3342
3438
|
}
|
|
3343
3439
|
}
|
|
3344
3440
|
);
|
|
3345
|
-
const agentEnum =
|
|
3441
|
+
const agentEnum = z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
3346
3442
|
server.tool(
|
|
3347
3443
|
"list_models",
|
|
3348
3444
|
"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.",
|
|
@@ -3365,11 +3461,11 @@ async function startMcpServer() {
|
|
|
3365
3461
|
"fork_worktree",
|
|
3366
3462
|
"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.",
|
|
3367
3463
|
{
|
|
3368
|
-
ref:
|
|
3369
|
-
through_index:
|
|
3464
|
+
ref: z7.string().describe("Worktree thread id/ref to fork"),
|
|
3465
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
3370
3466
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
3371
|
-
model:
|
|
3372
|
-
title:
|
|
3467
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
3468
|
+
title: z7.string().optional()
|
|
3373
3469
|
},
|
|
3374
3470
|
async ({ ref, through_index, agent, model, title }) => {
|
|
3375
3471
|
try {
|
|
@@ -3410,11 +3506,11 @@ async function startMcpServer() {
|
|
|
3410
3506
|
"fork_chat",
|
|
3411
3507
|
"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.",
|
|
3412
3508
|
{
|
|
3413
|
-
ref:
|
|
3414
|
-
through_index:
|
|
3509
|
+
ref: z7.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
3510
|
+
through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
3415
3511
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
3416
|
-
model:
|
|
3417
|
-
title:
|
|
3512
|
+
model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
3513
|
+
title: z7.string().optional()
|
|
3418
3514
|
},
|
|
3419
3515
|
async ({ ref, through_index, agent, model, title }) => {
|
|
3420
3516
|
try {
|
|
@@ -3460,8 +3556,8 @@ async function startMcpServer() {
|
|
|
3460
3556
|
"run_dev_script",
|
|
3461
3557
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
3462
3558
|
{
|
|
3463
|
-
ref:
|
|
3464
|
-
name:
|
|
3559
|
+
ref: z7.string(),
|
|
3560
|
+
name: z7.string().optional()
|
|
3465
3561
|
},
|
|
3466
3562
|
async ({ ref, name }) => {
|
|
3467
3563
|
const result = await orch.startDev(ref, name);
|
|
@@ -3483,7 +3579,7 @@ async function startMcpServer() {
|
|
|
3483
3579
|
server.tool(
|
|
3484
3580
|
"list_run_scripts",
|
|
3485
3581
|
"List named run scripts available for a thread",
|
|
3486
|
-
{ ref:
|
|
3582
|
+
{ ref: z7.string() },
|
|
3487
3583
|
async ({ ref }) => {
|
|
3488
3584
|
const scripts = orch.listThreadRunScripts(ref);
|
|
3489
3585
|
const active = orch.getActiveRuns(ref);
|
|
@@ -3501,8 +3597,8 @@ async function startMcpServer() {
|
|
|
3501
3597
|
"stop_dev_script",
|
|
3502
3598
|
"Stop a running script for a thread (all scripts if name omitted)",
|
|
3503
3599
|
{
|
|
3504
|
-
ref:
|
|
3505
|
-
name:
|
|
3600
|
+
ref: z7.string(),
|
|
3601
|
+
name: z7.string().optional()
|
|
3506
3602
|
},
|
|
3507
3603
|
async ({ ref, name }) => {
|
|
3508
3604
|
orch.stopDev(ref, name);
|
|
@@ -3512,7 +3608,7 @@ async function startMcpServer() {
|
|
|
3512
3608
|
server.tool(
|
|
3513
3609
|
"run_setup",
|
|
3514
3610
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
3515
|
-
{ ref:
|
|
3611
|
+
{ ref: z7.string() },
|
|
3516
3612
|
async ({ ref }) => {
|
|
3517
3613
|
const result = await orch.runSetup(ref);
|
|
3518
3614
|
return {
|
|
@@ -3523,7 +3619,7 @@ async function startMcpServer() {
|
|
|
3523
3619
|
server.tool(
|
|
3524
3620
|
"add_workspace",
|
|
3525
3621
|
"Register a git repo as a Sideboard workspace",
|
|
3526
|
-
{ repoPath:
|
|
3622
|
+
{ repoPath: z7.string() },
|
|
3527
3623
|
async ({ repoPath }) => {
|
|
3528
3624
|
const ws = await orch.addWorkspace(repoPath);
|
|
3529
3625
|
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
@@ -3532,7 +3628,7 @@ async function startMcpServer() {
|
|
|
3532
3628
|
server.tool(
|
|
3533
3629
|
"remove_workspace",
|
|
3534
3630
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
3535
|
-
{ repoPath:
|
|
3631
|
+
{ repoPath: z7.string() },
|
|
3536
3632
|
async ({ repoPath }) => {
|
|
3537
3633
|
orch.removeWorkspace(repoPath);
|
|
3538
3634
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -3542,12 +3638,12 @@ async function startMcpServer() {
|
|
|
3542
3638
|
"fanout",
|
|
3543
3639
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
3544
3640
|
{
|
|
3545
|
-
prompt:
|
|
3546
|
-
agents:
|
|
3547
|
-
repoPath:
|
|
3548
|
-
sourceType:
|
|
3549
|
-
sourceRef:
|
|
3550
|
-
title:
|
|
3641
|
+
prompt: z7.string(),
|
|
3642
|
+
agents: z7.array(z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
3643
|
+
repoPath: z7.string(),
|
|
3644
|
+
sourceType: z7.enum(["branch", "pr", "ticket"]).optional(),
|
|
3645
|
+
sourceRef: z7.string().optional(),
|
|
3646
|
+
title: z7.string().optional()
|
|
3551
3647
|
},
|
|
3552
3648
|
async (args) => {
|
|
3553
3649
|
const threads = await orch.bestOfN(args);
|
|
@@ -3574,8 +3670,8 @@ async function startMcpServer() {
|
|
|
3574
3670
|
"list_branches",
|
|
3575
3671
|
"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).",
|
|
3576
3672
|
{
|
|
3577
|
-
repoPath:
|
|
3578
|
-
unmergedOnly:
|
|
3673
|
+
repoPath: z7.string(),
|
|
3674
|
+
unmergedOnly: z7.boolean().optional()
|
|
3579
3675
|
},
|
|
3580
3676
|
async ({ repoPath, unmergedOnly }) => {
|
|
3581
3677
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -3596,19 +3692,19 @@ async function startMcpServer() {
|
|
|
3596
3692
|
"list_prs",
|
|
3597
3693
|
'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.',
|
|
3598
3694
|
{
|
|
3599
|
-
repoPath:
|
|
3600
|
-
query:
|
|
3601
|
-
queue:
|
|
3695
|
+
repoPath: z7.string(),
|
|
3696
|
+
query: z7.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
3697
|
+
queue: z7.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
3602
3698
|
'review = unclaimed eng-review inbox (use for "get me N tickets to review"). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes.'
|
|
3603
3699
|
),
|
|
3604
|
-
state:
|
|
3605
|
-
label:
|
|
3700
|
+
state: z7.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
3701
|
+
label: z7.string().optional().describe(
|
|
3606
3702
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
3607
3703
|
),
|
|
3608
|
-
reviewer:
|
|
3704
|
+
reviewer: z7.string().optional().describe(
|
|
3609
3705
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
3610
3706
|
),
|
|
3611
|
-
limit:
|
|
3707
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
3612
3708
|
},
|
|
3613
3709
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
3614
3710
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -3640,7 +3736,7 @@ async function startMcpServer() {
|
|
|
3640
3736
|
server.tool(
|
|
3641
3737
|
"get_pr_stack",
|
|
3642
3738
|
"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).",
|
|
3643
|
-
{ ref:
|
|
3739
|
+
{ ref: z7.string() },
|
|
3644
3740
|
async ({ ref }) => {
|
|
3645
3741
|
const stack = await orch.getPrStack(ref);
|
|
3646
3742
|
return {
|
|
@@ -3652,8 +3748,8 @@ async function startMcpServer() {
|
|
|
3652
3748
|
"open_pr_stack_layers",
|
|
3653
3749
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
3654
3750
|
{
|
|
3655
|
-
ref:
|
|
3656
|
-
layer:
|
|
3751
|
+
ref: z7.string(),
|
|
3752
|
+
layer: z7.number().int().positive().optional()
|
|
3657
3753
|
},
|
|
3658
3754
|
async ({ ref, layer }) => {
|
|
3659
3755
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -3687,9 +3783,9 @@ async function startMcpServer() {
|
|
|
3687
3783
|
"add_stack_layer",
|
|
3688
3784
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
3689
3785
|
{
|
|
3690
|
-
ref:
|
|
3691
|
-
branchName:
|
|
3692
|
-
title:
|
|
3786
|
+
ref: z7.string(),
|
|
3787
|
+
branchName: z7.string(),
|
|
3788
|
+
title: z7.string().optional()
|
|
3693
3789
|
},
|
|
3694
3790
|
async ({ ref, branchName, title }) => {
|
|
3695
3791
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -3718,11 +3814,11 @@ async function startMcpServer() {
|
|
|
3718
3814
|
"create_pr_stack",
|
|
3719
3815
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
3720
3816
|
{
|
|
3721
|
-
repoPath:
|
|
3722
|
-
branches:
|
|
3723
|
-
agent:
|
|
3724
|
-
base:
|
|
3725
|
-
title:
|
|
3817
|
+
repoPath: z7.string(),
|
|
3818
|
+
branches: z7.array(z7.string()).min(1),
|
|
3819
|
+
agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
3820
|
+
base: z7.string().optional(),
|
|
3821
|
+
title: z7.string().optional()
|
|
3726
3822
|
},
|
|
3727
3823
|
async (args) => {
|
|
3728
3824
|
const result = await orch.createPrStack({
|
|
@@ -3761,10 +3857,10 @@ async function startMcpServer() {
|
|
|
3761
3857
|
"list_issues",
|
|
3762
3858
|
"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.",
|
|
3763
3859
|
{
|
|
3764
|
-
repoPath:
|
|
3765
|
-
query:
|
|
3766
|
-
assignee:
|
|
3767
|
-
limit:
|
|
3860
|
+
repoPath: z7.string(),
|
|
3861
|
+
query: z7.string().optional().describe("Search title, identifier, or description"),
|
|
3862
|
+
assignee: z7.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
3863
|
+
limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
3768
3864
|
},
|
|
3769
3865
|
async ({ repoPath, query, assignee, limit }) => {
|
|
3770
3866
|
const root = await resolveRepoRoot(repoPath);
|