@sideboard-ai/core 0.1.286 → 0.1.288
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-I4RODUKT.js → abletime-4WJUVE64.js} +4 -4
- package/dist/{abletime-4CH2IOX5.js → abletime-VFKYNSJT.js} +4 -4
- package/dist/{abletime-oauth-7OZZ3ABX.js → abletime-oauth-FW24YHYX.js} +3 -3
- package/dist/{abletime-oauth-C2HN2Q6F.js → abletime-oauth-ICMB2XV2.js} +3 -3
- package/dist/{abletime-rest-E4LBRFLM.js → abletime-rest-HFMRTUDT.js} +3 -3
- package/dist/{abletime-rest-WFI2HWBU.js → abletime-rest-OGWC7BC6.js} +3 -3
- package/dist/agents/cursor-runner.js +3 -3
- package/dist/{agents-BHUXZ7XO.js → agents-GZLTMGSH.js} +19 -17
- package/dist/{agents-QNIFTZPN.js → agents-UQMHFZSM.js} +17 -15
- package/dist/{app-settings-EQQZQNYD.js → app-settings-EHMXHNG5.js} +1 -1
- package/dist/{app-settings-NI4X5HW3.js → app-settings-MR2U6AXN.js} +1 -1
- package/dist/{chunk-3H5U4QLD.js → chunk-2N77CLRQ.js} +8 -298
- package/dist/{chunk-YNTXWFXT.js → chunk-36J3IZBS.js} +12 -12
- package/dist/{chunk-IJMUYHPT.js → chunk-3E4NOIXN.js} +3 -3
- package/dist/{chunk-5MGLNVNK.js → chunk-3W5SL5EW.js} +1 -1
- package/dist/{chunk-CBIUGGPI.js → chunk-442O6OXM.js} +25 -127
- package/dist/chunk-4BXRRHDC.js +139 -0
- package/dist/{chunk-ETZPTOK7.js → chunk-4NM72HJZ.js} +5 -5
- package/dist/{chunk-PCNH74SF.js → chunk-6NVQNTJO.js} +10 -300
- package/dist/{chunk-VOIWX5HA.js → chunk-7UUILVMV.js} +1 -1
- package/dist/chunk-AF2DMY3K.js +295 -0
- package/dist/{chunk-5QOLFOAX.js → chunk-AFLNFGGB.js} +1 -1
- package/dist/{chunk-5T4YWFLD.js → chunk-AHH5USEA.js} +2 -2
- package/dist/{chunk-6CVVNAIU.js → chunk-B3BPLPKQ.js} +2 -2
- package/dist/{chunk-MGJ7R7UX.js → chunk-DBOFDEQ7.js} +4 -4
- package/dist/{chunk-VQEARG5H.js → chunk-GU6DT6NO.js} +5 -5
- package/dist/{chunk-K5XSTHHP.js → chunk-HNB2HTCV.js} +1 -1
- package/dist/{chunk-ULSY2BJF.js → chunk-IA4MPG7F.js} +4 -4
- package/dist/{chunk-S2OOM7AU.js → chunk-K6IOJI3L.js} +1 -1
- package/dist/{chunk-U6QDXT4K.js → chunk-MIVX7QKI.js} +1 -1
- package/dist/{chunk-WRIMAEF2.js → chunk-MVZO7QZ2.js} +12 -12
- package/dist/{chunk-LS4FFCPL.js → chunk-MYJYINTS.js} +3 -3
- package/dist/{chunk-DEJALWKM.js → chunk-NFPWLDCH.js} +5 -5
- package/dist/chunk-NXXQFLNQ.js +300 -0
- package/dist/chunk-OH4EN323.js +89 -0
- package/dist/chunk-W2KZTDET.js +302 -0
- package/dist/{chunk-ABOH7GBG.js → chunk-WQSIUZEB.js} +6 -6
- package/dist/{chunk-HJNFK5SP.js → chunk-WW5XGEMY.js} +33 -313
- package/dist/{chunk-TMM4FEZ5.js → chunk-Y67TBJD5.js} +3 -3
- package/dist/{chunk-XSDCLM3F.js → chunk-YSLCWCMJ.js} +1 -1
- package/dist/{conductor-RBJYEF7L.js → conductor-MCU63Z5U.js} +5 -5
- package/dist/{conductor-WYOWFB7I.js → conductor-ZLYZVIAV.js} +6 -6
- package/dist/{coordinator-prompt-R42WEL3T.js → coordinator-prompt-KHCXXDRO.js} +3 -3
- package/dist/{coordinator-prompt-5DE3XBNT.js → coordinator-prompt-UV6THLMD.js} +3 -3
- package/dist/{global-workspace-IPIEC6FM.js → global-workspace-AXTSYVAE.js} +3 -3
- package/dist/{global-workspace-NXMYLS34.js → global-workspace-GDHZSFP6.js} +3 -3
- package/dist/index.cjs +470 -275
- package/dist/index.d.cts +29 -9
- package/dist/index.d.ts +29 -9
- package/dist/index.js +369 -256
- package/dist/mcp/run-stdio.cjs +603 -226
- package/dist/mcp/run-stdio.js +333 -187
- package/dist/optional-cli-6SW4OOX3.js +23 -0
- package/dist/optional-cli-FRUZSZ3U.js +98 -0
- package/dist/{orchestrator-YEZV35G7.js → orchestrator-5ZBHR6BJ.js} +9 -7
- package/dist/{orchestrator-65QGA6BB.js → orchestrator-SDH6W6ZL.js} +12 -10
- package/dist/{workspaces-LIFFWK23.js → workspaces-L2UR4KC2.js} +3 -3
- package/dist/{workspaces-5C5ESWPU.js → workspaces-Q2R2XGO3.js} +3 -3
- package/dist/{worktree-NKLNVNXX.js → worktree-2GOK4VYT.js} +2 -2
- package/dist/{worktree-YTZC3UQN.js → worktree-YEUTJ7MZ.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1069,7 +1069,7 @@ function humanizeAgentFailDetail(detail) {
|
|
|
1069
1069
|
return "Cursor local file search failed (ripgrep / resource_exhausted). Wait a minute and retry; if it keeps happening, check Cursor usage.";
|
|
1070
1070
|
}
|
|
1071
1071
|
if (looksLikeCodexMcpError(raw)) {
|
|
1072
|
-
return `${raw} \u2014 skip that vendor MCP and continue (PostHog: Settings \u2192 Connectors
|
|
1072
|
+
return `${raw} \u2014 skip that vendor MCP and continue (PostHog: Settings \u2192 Connectors and posthog_api).`;
|
|
1073
1073
|
}
|
|
1074
1074
|
if (/invalid user api key|invalid api key|not logged in|not authenticated|unauthorized|authentication|please run.*login|codex login|claude auth|cursor api/.test(
|
|
1075
1075
|
lower
|
|
@@ -2313,6 +2313,27 @@ function npmGlobalNodeModules(env = process.env) {
|
|
|
2313
2313
|
const dir = process.platform === "win32" ? (0, import_node_path9.join)(prefix, "node_modules") : (0, import_node_path9.join)(prefix, "lib", "node_modules");
|
|
2314
2314
|
return (0, import_node_fs8.existsSync)(dir) ? dir : null;
|
|
2315
2315
|
}
|
|
2316
|
+
function posixShellSingleQuote(value) {
|
|
2317
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
2318
|
+
}
|
|
2319
|
+
function resolveCommandBinarySync(command, whichPath) {
|
|
2320
|
+
const trimmed = command.trim();
|
|
2321
|
+
if (!trimmed) return trimmed;
|
|
2322
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
2323
|
+
if (!match) return trimmed;
|
|
2324
|
+
const bin = match[1];
|
|
2325
|
+
const rest = match[2] ?? "";
|
|
2326
|
+
if (bin.includes("/") || bin.includes("\\")) return trimmed;
|
|
2327
|
+
const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
|
|
2328
|
+
if (!abs) return trimmed;
|
|
2329
|
+
return `${abs}${rest}`;
|
|
2330
|
+
}
|
|
2331
|
+
function withExportedPath(command, pathValue) {
|
|
2332
|
+
const trimmed = command.trim();
|
|
2333
|
+
if (!trimmed) return trimmed;
|
|
2334
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
2335
|
+
return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
|
|
2336
|
+
}
|
|
2316
2337
|
var import_node_fs8, import_node_child_process2, import_node_os3, import_node_path9, EXTRA_BIN_DIRS;
|
|
2317
2338
|
var init_path = __esm({
|
|
2318
2339
|
"src/agents/path.ts"() {
|
|
@@ -6145,22 +6166,21 @@ function applyAppEnvironment(target = process.env, settings = loadAppSettings())
|
|
|
6145
6166
|
applyOptionalServiceTokens(target, settings.integrations);
|
|
6146
6167
|
return target;
|
|
6147
6168
|
}
|
|
6148
|
-
function
|
|
6169
|
+
function assignEnv(target, key, value) {
|
|
6149
6170
|
const v = value?.trim();
|
|
6150
6171
|
if (!key || !v) return;
|
|
6151
|
-
|
|
6172
|
+
target[key] = v;
|
|
6173
|
+
}
|
|
6174
|
+
function scrubPosthogAgentEnv(target) {
|
|
6175
|
+
delete target.POSTHOG_PERSONAL_API_KEY;
|
|
6176
|
+
delete target.POSTHOG_HOST;
|
|
6152
6177
|
}
|
|
6153
6178
|
function applyOptionalServiceTokens(target, integrations) {
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
"POSTHOG_HOST",
|
|
6160
|
-
integrations.posthogPersonalApiKey ? integrations.posthogHost || "https://us.posthog.com" : void 0
|
|
6161
|
-
);
|
|
6162
|
-
fillEnvGap(target, "SENTRY_AUTH_TOKEN", integrations.sentryAuthToken);
|
|
6163
|
-
fillEnvGap(
|
|
6179
|
+
assignEnv(target, "VERCEL_TOKEN", integrations.vercelToken);
|
|
6180
|
+
assignEnv(target, "SUPABASE_ACCESS_TOKEN", integrations.supabaseAccessToken);
|
|
6181
|
+
scrubPosthogAgentEnv(target);
|
|
6182
|
+
assignEnv(target, "SENTRY_AUTH_TOKEN", integrations.sentryAuthToken);
|
|
6183
|
+
assignEnv(
|
|
6164
6184
|
target,
|
|
6165
6185
|
"SENTRY_URL",
|
|
6166
6186
|
integrations.sentryAuthToken ? integrations.sentryHost || "https://sentry.io" : void 0
|
|
@@ -6175,6 +6195,7 @@ function childEnvWithAppSettings(extra) {
|
|
|
6175
6195
|
if (v != null) env[k] = v;
|
|
6176
6196
|
}
|
|
6177
6197
|
}
|
|
6198
|
+
scrubPosthogAgentEnv(env);
|
|
6178
6199
|
return env;
|
|
6179
6200
|
}
|
|
6180
6201
|
function harnessEnvKey(harness) {
|
|
@@ -9515,7 +9536,7 @@ function isRemovedWorkspace(repoPath) {
|
|
|
9515
9536
|
}
|
|
9516
9537
|
function archivesWithRemovedWorkspace(threadRepoPath, removedPath) {
|
|
9517
9538
|
if (!threadRepoPath || isGlobalRepoPath(threadRepoPath)) return false;
|
|
9518
|
-
return
|
|
9539
|
+
return removedMatches(removedPath, threadRepoPath);
|
|
9519
9540
|
}
|
|
9520
9541
|
function rememberRemoved(repoPath) {
|
|
9521
9542
|
const next = readRemoved();
|
|
@@ -9896,7 +9917,7 @@ var init_coordinator_prompt = __esm({
|
|
|
9896
9917
|
"- linear_* (when Linear is connected) \u2014 list_teams for team key/states; linear_list_projects for project names; get/search include the project a ticket is on; get/create/update/comment with ENG-123. Worktree agents have the same Account tools \u2014 prefer they comment, update status, and create spin-offs (parent=) on their own ticket when they were asked to do the work. When reviewing a PR or ticket, do not comment or update until the user types a next step \u2014 they should work through the feedback in chat first. Do not tell the child to ask_user after the review. Scope errors: reconnect Linear in Account settings. Prefer these over any Linear MCP the CLI may still list.",
|
|
9897
9918
|
"- github_* \u2014 get/comment/update/create GitHub issues via Account `gh` (#123). github_list_projects and get/search include GitHub Projects. projectsError or a missing read:project scope \u2192 `gh auth refresh -s read:project`. Worktree agents have these too. Prefer over any vendor GitHub MCP. Pass parent= for spin-offs. When reviewing a PR or ticket, do not comment, submit `gh pr review`, or update the PR/ticket until the user types a next step.",
|
|
9898
9919
|
"- abletime_* (when AbleTime is connected) \u2014 orientation first; abletime_list_projects lists projects; get/list include the project a task is on; get/comment/update/create (parent= for spin-offs); ensure_task when work has no ticket (or create_workspace from the default branch auto-creates one). Worktree agents have the same Account tools. Same review-write gate as Linear/GitHub.",
|
|
9899
|
-
"- Optional connectors (Vercel, Supabase,
|
|
9920
|
+
"- Optional connectors (Vercel, Supabase, Sentry) in Settings \u2192 Connectors inject tokens into worktree agent env when connected. Prefer official CLIs (`vercel`, `supabase`, `sentry-cli`). PostHog is the Sideboard tool `posthog_api` (GET by default; POST `/api/projects/:id/query/` or `/api/environments/:id/query/` is a read and does not need write=true; other POST and PATCH need write=true only when the user asked to change data). The PostHog key stays in the vault \u2014 do not read it from the environment. If a connector MCP needsAuth or is unauthorized, do not sign in \u2014 use the native CLI or `posthog_api`. Tell any worktree agent (Claude / Cursor / Codex / OpenCode) to write CLI output to `.context/cli/` and read a slice. If a CLI is missing, the user presses Install CLI on that row. Do not add vendor MCPs. Git (`gh`) stays Settings \u2192 Git; issue tracking stays Settings \u2192 Issues. The phone remote is Settings \u2192 Remote \u2014 do not look up Slack users.",
|
|
9900
9921
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
9901
9922
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
9902
9923
|
"- list_chats / get_chat \u2014 live thread list (parent id + last message preview). get_chat on this orchestration chat lists child worktree agents (status + lastText). blockedReason is why that agent is waiting on a person. Also includes usage / lastTurnUsage.",
|
|
@@ -11802,7 +11823,8 @@ var init_injected_mcp = __esm({
|
|
|
11802
11823
|
"mcp__sideboard__create_schedule",
|
|
11803
11824
|
"mcp__sideboard__update_schedule",
|
|
11804
11825
|
"mcp__sideboard__delete_schedule",
|
|
11805
|
-
"mcp__sideboard__run_schedule"
|
|
11826
|
+
"mcp__sideboard__run_schedule",
|
|
11827
|
+
"mcp__sideboard__posthog_api"
|
|
11806
11828
|
];
|
|
11807
11829
|
SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
|
|
11808
11830
|
SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
|
|
@@ -14468,6 +14490,107 @@ var init_claude_usage_client = __esm({
|
|
|
14468
14490
|
});
|
|
14469
14491
|
|
|
14470
14492
|
// src/agents/install.ts
|
|
14493
|
+
async function prepareTerminalCommand(command) {
|
|
14494
|
+
enrichPathWithNpmGlobalBin();
|
|
14495
|
+
const trimmed = command.trim();
|
|
14496
|
+
if (!trimmed) return trimmed;
|
|
14497
|
+
if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
|
|
14498
|
+
const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
|
|
14499
|
+
const bin = match?.[1];
|
|
14500
|
+
let whichPath = null;
|
|
14501
|
+
if (bin && !bin.includes("/") && !bin.includes("\\")) {
|
|
14502
|
+
const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
|
|
14503
|
+
whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
|
|
14504
|
+
}
|
|
14505
|
+
const resolved = resolveCommandBinarySync(trimmed, whichPath);
|
|
14506
|
+
return withExportedPath(resolved, process.env.PATH ?? "");
|
|
14507
|
+
}
|
|
14508
|
+
async function openInSystemTerminal(command) {
|
|
14509
|
+
const trimmed = (await prepareTerminalCommand(command)).trim();
|
|
14510
|
+
if (!trimmed) throw new Error("Command is empty");
|
|
14511
|
+
if (process.platform === "darwin") {
|
|
14512
|
+
const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
14513
|
+
const script = `tell application "Terminal" to do script "${escaped}"`;
|
|
14514
|
+
const result = await run("osascript", ["-e", script], { reject: false });
|
|
14515
|
+
if (result.exitCode !== 0) {
|
|
14516
|
+
throw new Error(result.stderr.trim() || result.stdout.trim() || "Failed to open Terminal");
|
|
14517
|
+
}
|
|
14518
|
+
await run("osascript", ["-e", 'tell application "Terminal" to activate'], {
|
|
14519
|
+
reject: false
|
|
14520
|
+
});
|
|
14521
|
+
return;
|
|
14522
|
+
}
|
|
14523
|
+
if (process.platform === "win32") {
|
|
14524
|
+
const result = await run(
|
|
14525
|
+
"cmd.exe",
|
|
14526
|
+
["/c", "start", "cmd.exe", "/k", trimmed],
|
|
14527
|
+
{ reject: false }
|
|
14528
|
+
);
|
|
14529
|
+
if (result.exitCode !== 0) {
|
|
14530
|
+
throw new Error(result.stderr.trim() || result.stdout.trim() || "Failed to open cmd");
|
|
14531
|
+
}
|
|
14532
|
+
return;
|
|
14533
|
+
}
|
|
14534
|
+
const candidates = [
|
|
14535
|
+
{ file: "gnome-terminal", args: ["--", "bash", "-lc", `${trimmed}; exec bash`] },
|
|
14536
|
+
{ file: "x-terminal-emulator", args: ["-e", "bash", "-lc", `${trimmed}; exec bash`] },
|
|
14537
|
+
{ file: "konsole", args: ["-e", "bash", "-lc", `${trimmed}; exec bash`] },
|
|
14538
|
+
{ file: "xterm", args: ["-e", "bash", "-lc", `${trimmed}; exec bash`] }
|
|
14539
|
+
];
|
|
14540
|
+
for (const c of candidates) {
|
|
14541
|
+
const which = await run("which", [c.file], { reject: false });
|
|
14542
|
+
if (which.exitCode !== 0 || !which.stdout.trim()) continue;
|
|
14543
|
+
const result = await run(c.file, c.args, { reject: false });
|
|
14544
|
+
if (result.exitCode === 0) return;
|
|
14545
|
+
}
|
|
14546
|
+
throw new Error(
|
|
14547
|
+
`No terminal found to run: ${trimmed}. Install gnome-terminal (or similar), or run the command manually.`
|
|
14548
|
+
);
|
|
14549
|
+
}
|
|
14550
|
+
async function whichOnPath(bin) {
|
|
14551
|
+
const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
|
|
14552
|
+
if (which.exitCode !== 0) return null;
|
|
14553
|
+
return which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null;
|
|
14554
|
+
}
|
|
14555
|
+
async function installNpmGlobalPackage(opts) {
|
|
14556
|
+
enrichPathWithNpmGlobalBin();
|
|
14557
|
+
const result = await run("npm", ["install", "-g", opts.npmPackage], { reject: false });
|
|
14558
|
+
const ok = result.exitCode === 0;
|
|
14559
|
+
if (!ok) {
|
|
14560
|
+
try {
|
|
14561
|
+
await openInSystemTerminal(opts.installCommand);
|
|
14562
|
+
return {
|
|
14563
|
+
ok: false,
|
|
14564
|
+
openedTerminal: true,
|
|
14565
|
+
command: opts.installCommand,
|
|
14566
|
+
exitCode: result.exitCode,
|
|
14567
|
+
stdout: result.stdout,
|
|
14568
|
+
stderr: result.stderr,
|
|
14569
|
+
message: `npm install failed (exit ${result.exitCode}). Opened Terminal with: ${opts.installCommand}`
|
|
14570
|
+
};
|
|
14571
|
+
} catch {
|
|
14572
|
+
return {
|
|
14573
|
+
ok: false,
|
|
14574
|
+
command: opts.installCommand,
|
|
14575
|
+
exitCode: result.exitCode,
|
|
14576
|
+
stdout: result.stdout,
|
|
14577
|
+
stderr: result.stderr,
|
|
14578
|
+
message: result.stderr.trim() || result.stdout.trim() || `npm install failed (exit ${result.exitCode})`
|
|
14579
|
+
};
|
|
14580
|
+
}
|
|
14581
|
+
}
|
|
14582
|
+
enrichPathWithNpmGlobalBin();
|
|
14583
|
+
const binPath = opts.cliBin ? await whichOnPath(opts.cliBin) : null;
|
|
14584
|
+
return {
|
|
14585
|
+
ok: true,
|
|
14586
|
+
command: opts.installCommand,
|
|
14587
|
+
exitCode: 0,
|
|
14588
|
+
stdout: result.stdout,
|
|
14589
|
+
stderr: result.stderr,
|
|
14590
|
+
// Library-only packages (`@cursor/sdk`) have no CLI to look for.
|
|
14591
|
+
message: !opts.cliBin ? `Installed ${opts.npmPackage}` : binPath ? `Installed ${opts.npmPackage} \u2192 ${binPath}` : `Installed ${opts.npmPackage} (CLI not yet on PATH)`
|
|
14592
|
+
};
|
|
14593
|
+
}
|
|
14471
14594
|
var import_node_fs41;
|
|
14472
14595
|
var init_install = __esm({
|
|
14473
14596
|
"src/agents/install.ts"() {
|
|
@@ -22487,13 +22610,13 @@ function formatStat(files) {
|
|
|
22487
22610
|
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
22488
22611
|
}
|
|
22489
22612
|
function emptyScopeStats() {
|
|
22490
|
-
const
|
|
22613
|
+
const z11 = emptyStat();
|
|
22491
22614
|
return {
|
|
22492
|
-
commits:
|
|
22493
|
-
uncommitted:
|
|
22494
|
-
staged:
|
|
22495
|
-
unstaged:
|
|
22496
|
-
last_turn:
|
|
22615
|
+
commits: z11,
|
|
22616
|
+
uncommitted: z11,
|
|
22617
|
+
staged: z11,
|
|
22618
|
+
unstaged: z11,
|
|
22619
|
+
last_turn: z11
|
|
22497
22620
|
};
|
|
22498
22621
|
}
|
|
22499
22622
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
@@ -23994,7 +24117,111 @@ var init_instructions = __esm({
|
|
|
23994
24117
|
}
|
|
23995
24118
|
});
|
|
23996
24119
|
|
|
24120
|
+
// src/integrations/optional-cli.ts
|
|
24121
|
+
var optional_cli_exports = {};
|
|
24122
|
+
__export(optional_cli_exports, {
|
|
24123
|
+
clearOptionalCliPresenceCache: () => clearOptionalCliPresenceCache,
|
|
24124
|
+
detectOptionalServiceClis: () => detectOptionalServiceClis,
|
|
24125
|
+
installOptionalServiceCli: () => installOptionalServiceCli,
|
|
24126
|
+
optionalCliPresence: () => optionalCliPresence
|
|
24127
|
+
});
|
|
24128
|
+
async function detectOptionalServiceClis() {
|
|
24129
|
+
enrichPathWithNpmGlobalBin();
|
|
24130
|
+
return Promise.all(
|
|
24131
|
+
OPTIONAL_SERVICES.map(async (spec) => {
|
|
24132
|
+
if (!spec.cli) {
|
|
24133
|
+
return { id: spec.id, cli: null, installed: false, path: null };
|
|
24134
|
+
}
|
|
24135
|
+
const path = await whichOnPath(spec.cli);
|
|
24136
|
+
return {
|
|
24137
|
+
id: spec.id,
|
|
24138
|
+
cli: spec.cli,
|
|
24139
|
+
installed: Boolean(path),
|
|
24140
|
+
path
|
|
24141
|
+
};
|
|
24142
|
+
})
|
|
24143
|
+
);
|
|
24144
|
+
}
|
|
24145
|
+
function clearOptionalCliPresenceCache() {
|
|
24146
|
+
cliPresenceCache.clear();
|
|
24147
|
+
}
|
|
24148
|
+
async function optionalCliPresence(ids) {
|
|
24149
|
+
const presence = {};
|
|
24150
|
+
const now = Date.now();
|
|
24151
|
+
await Promise.all(
|
|
24152
|
+
ids.map(async (id) => {
|
|
24153
|
+
const spec = optionalServiceSpec(id);
|
|
24154
|
+
if (!spec.cli) return;
|
|
24155
|
+
const hit = cliPresenceCache.get(id);
|
|
24156
|
+
if (hit && now - hit.at < CLI_PRESENCE_TTL_MS) {
|
|
24157
|
+
presence[id] = hit.installed;
|
|
24158
|
+
return;
|
|
24159
|
+
}
|
|
24160
|
+
const path = await whichOnPath(spec.cli);
|
|
24161
|
+
const installed = Boolean(path);
|
|
24162
|
+
cliPresenceCache.set(id, { at: now, installed });
|
|
24163
|
+
presence[id] = installed;
|
|
24164
|
+
})
|
|
24165
|
+
);
|
|
24166
|
+
return presence;
|
|
24167
|
+
}
|
|
24168
|
+
async function installOptionalServiceCli(id) {
|
|
24169
|
+
if (!isOptionalServiceId(id)) {
|
|
24170
|
+
return { ok: false, message: "Unknown optional service" };
|
|
24171
|
+
}
|
|
24172
|
+
const spec = optionalServiceSpec(id);
|
|
24173
|
+
if (!spec.cli || !spec.npmPackage) {
|
|
24174
|
+
return {
|
|
24175
|
+
ok: false,
|
|
24176
|
+
message: `${spec.label} has no CLI to install. Agents call posthog_api.`
|
|
24177
|
+
};
|
|
24178
|
+
}
|
|
24179
|
+
enrichPathWithNpmGlobalBin();
|
|
24180
|
+
const existing = await whichOnPath(spec.cli);
|
|
24181
|
+
if (existing) {
|
|
24182
|
+
return {
|
|
24183
|
+
ok: true,
|
|
24184
|
+
command: existing,
|
|
24185
|
+
message: `Already on PATH: ${existing}`
|
|
24186
|
+
};
|
|
24187
|
+
}
|
|
24188
|
+
return installNpmGlobalPackage({
|
|
24189
|
+
npmPackage: spec.npmPackage,
|
|
24190
|
+
installCommand: `npm install -g ${spec.npmPackage}`,
|
|
24191
|
+
cliBin: spec.cli
|
|
24192
|
+
});
|
|
24193
|
+
}
|
|
24194
|
+
var CLI_PRESENCE_TTL_MS, cliPresenceCache;
|
|
24195
|
+
var init_optional_cli = __esm({
|
|
24196
|
+
"src/integrations/optional-cli.ts"() {
|
|
24197
|
+
"use strict";
|
|
24198
|
+
init_install();
|
|
24199
|
+
init_path();
|
|
24200
|
+
init_optional_services();
|
|
24201
|
+
CLI_PRESENCE_TTL_MS = 6e4;
|
|
24202
|
+
cliPresenceCache = /* @__PURE__ */ new Map();
|
|
24203
|
+
}
|
|
24204
|
+
});
|
|
24205
|
+
|
|
23997
24206
|
// src/integrations/optional-services.ts
|
|
24207
|
+
function optionalServiceSpec(id) {
|
|
24208
|
+
const spec = OPTIONAL_SERVICES.find((s) => s.id === id);
|
|
24209
|
+
if (!spec) throw new Error(`Unknown optional service: ${id}`);
|
|
24210
|
+
return spec;
|
|
24211
|
+
}
|
|
24212
|
+
function isOptionalServiceId(value) {
|
|
24213
|
+
return typeof value === "string" && OPTIONAL_SERVICE_IDS.includes(value);
|
|
24214
|
+
}
|
|
24215
|
+
function normalizeServiceOrigin(raw, fallback) {
|
|
24216
|
+
const trimmed = raw?.trim() || fallback;
|
|
24217
|
+
try {
|
|
24218
|
+
const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
|
|
24219
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return fallback;
|
|
24220
|
+
return `${url.protocol}//${url.host}`;
|
|
24221
|
+
} catch {
|
|
24222
|
+
return fallback;
|
|
24223
|
+
}
|
|
24224
|
+
}
|
|
23998
24225
|
function optionalServiceConnected(id, integrations) {
|
|
23999
24226
|
if (id === "vercel") return Boolean(integrations.vercelToken?.trim());
|
|
24000
24227
|
if (id === "supabase") return Boolean(integrations.supabaseAccessToken?.trim());
|
|
@@ -24004,61 +24231,65 @@ function optionalServiceConnected(id, integrations) {
|
|
|
24004
24231
|
function connectedOptionalServices(integrations) {
|
|
24005
24232
|
return OPTIONAL_SERVICES.filter((spec) => optionalServiceConnected(spec.id, integrations));
|
|
24006
24233
|
}
|
|
24007
|
-
function
|
|
24234
|
+
function cliPresencePhrase(spec, cli) {
|
|
24235
|
+
if (!spec.cli || !cli || !(spec.id in cli)) return "";
|
|
24236
|
+
return cli[spec.id] ? " (installed)" : " (not installed \u2014 ask the user to press Install CLI in Settings \u2192 Connectors)";
|
|
24237
|
+
}
|
|
24238
|
+
function connectorDirectiveLine(spec, cli) {
|
|
24239
|
+
const extras = spec.hostEnvKey ? ` / \`${spec.hostEnvKey}\`` : "";
|
|
24240
|
+
if (spec.cli) {
|
|
24241
|
+
return `- ${spec.label}: use \`${spec.cli}\` (\`${spec.envKey}\`${extras})${cliPresencePhrase(spec, cli)}. Do not add a ${spec.label} MCP.`;
|
|
24242
|
+
}
|
|
24243
|
+
return `- ${spec.label}: call \`posthog_api\`. Path must start with \`/api/\`. GET is the default. POST \`/api/projects/:id/query/\` or \`/api/environments/:id/query/\` is a read and does not need \`write=true\`. Other POST and PATCH require \`write=true\` only when the user asked to change data. Do not read the API key from the environment. Do not add a ${spec.label} MCP.`;
|
|
24244
|
+
}
|
|
24245
|
+
function connectorReminderBit(spec, cli) {
|
|
24246
|
+
if (!spec.cli) return `${spec.label}: \`posthog_api\``;
|
|
24247
|
+
const presence = cliPresencePhrase(spec, cli);
|
|
24248
|
+
return `${spec.label}: \`${spec.cli}\`${presence}`;
|
|
24249
|
+
}
|
|
24250
|
+
function formatOptionalServicesDirective(integrations, cli) {
|
|
24008
24251
|
const connected = connectedOptionalServices(integrations);
|
|
24009
24252
|
if (connected.length === 0) return null;
|
|
24253
|
+
const cliConnected = connected.some((spec) => spec.cli);
|
|
24010
24254
|
const lines = [
|
|
24011
|
-
"Optional connectors (connected \u2014 tokens are already in this process env):"
|
|
24255
|
+
cliConnected ? "Optional connectors (connected \u2014 CLI tokens are already in this process env):" : "Optional connectors (connected):",
|
|
24256
|
+
...connected.map((spec) => connectorDirectiveLine(spec, cli)),
|
|
24257
|
+
cliConnected ? "If a connector MCP is listed but needsAuth or returns unauthorized, do not sign in to that MCP. CLI tokens are already in this process. Do not read a PostHog key from the environment." : "If a connector MCP is listed but needsAuth or returns unauthorized, do not sign in to that MCP. Do not read a PostHog key from the environment.",
|
|
24258
|
+
"Do not add a vendor MCP. When a binary is missing, ask the user to press Install CLI in Settings \u2192 Connectors.",
|
|
24259
|
+
"When you shell a connector CLI, write bulky output to `.context/cli/` and read a slice."
|
|
24012
24260
|
];
|
|
24013
|
-
for (const spec of connected) {
|
|
24014
|
-
if (spec.cli) {
|
|
24015
|
-
const extras = spec.hostEnvKey ? ` / \`${spec.hostEnvKey}\`` : "";
|
|
24016
|
-
lines.push(
|
|
24017
|
-
`- ${spec.label}: use the \`${spec.cli}\` CLI (\`${spec.envKey}\`${extras}). Do not add a ${spec.label} MCP.`
|
|
24018
|
-
);
|
|
24019
|
-
} else {
|
|
24020
|
-
const extras = spec.hostEnvKey ? ` / \`${spec.hostEnvKey}\`` : "";
|
|
24021
|
-
lines.push(
|
|
24022
|
-
`- ${spec.label}: no first-class CLI \u2014 call the HTTP API with \`${spec.envKey}\`${extras}. Do not add a ${spec.label} MCP.`
|
|
24023
|
-
);
|
|
24024
|
-
}
|
|
24025
|
-
}
|
|
24026
|
-
lines.push(
|
|
24027
|
-
"If a connector MCP is listed but needsAuth or returns unauthorized, do not sign in to that MCP. Check the native CLI (or the HTTP API when there is no CLI) \u2014 the token is already in this process."
|
|
24028
|
-
);
|
|
24029
|
-
lines.push(
|
|
24030
|
-
"If a CLI is missing, say so or use the HTTP API with the same token. The user can Install CLI from Settings \u2192 Connectors. Do not ask the user to install a vendor MCP."
|
|
24031
|
-
);
|
|
24032
|
-
lines.push(
|
|
24033
|
-
"Safe CLI / HTTP (every connector \u2014 `vercel logs`, Supabase inspect, `sentry-cli`, PostHog API):"
|
|
24034
|
-
);
|
|
24035
|
-
lines.push(
|
|
24036
|
-
'- Never put raw `--json` / `--expand` / huge dumps in the tool result. That crashes any worktree agent mid-turn (Claude / Cursor / Codex / OpenCode). Cursor chat fills with `file://\u2026/cursor-runtime/\u2026/@cursor/sdk/dist/esm/index.js` then minified `importas e from"@bufbuild/protobuf"`.'
|
|
24037
|
-
);
|
|
24038
|
-
lines.push(
|
|
24039
|
-
"- Write output to `.context/cli/` (worktree scratch; not `.context/attachments/`). Then read a slice. Tight window first (`--limit`, recent `--since` / last N events); raise only if empty."
|
|
24040
|
-
);
|
|
24041
|
-
lines.push(
|
|
24042
|
-
"- Filter (`jq`) to the fields you need (timestamp, path, status, message) \u2014 not the full request object."
|
|
24043
|
-
);
|
|
24044
|
-
lines.push(
|
|
24045
|
-
"- One query at a time. After you have the error line, stop fetching. Piping to `head` does not help \u2014 the CLI still buffers until you kill it."
|
|
24046
|
-
);
|
|
24047
|
-
lines.push(
|
|
24048
|
-
"- If a call is still running at ~40s with no useful output, stop waiting on the raw shell; detach (`/long-running`) or `stop_job` if it is hanging / doing the wrong thing."
|
|
24049
|
-
);
|
|
24050
24261
|
return lines.join("\n");
|
|
24051
24262
|
}
|
|
24052
|
-
function formatOptionalServicesReminder(integrations) {
|
|
24053
|
-
|
|
24054
|
-
|
|
24263
|
+
function formatOptionalServicesReminder(integrations, cli) {
|
|
24264
|
+
const connected = connectedOptionalServices(integrations);
|
|
24265
|
+
if (connected.length === 0) return null;
|
|
24266
|
+
const bits = connected.map((spec) => connectorReminderBit(spec, cli)).join(". ");
|
|
24267
|
+
const tokenBit = connected.some((spec) => spec.cli) ? "CLI tokens are already in this process. " : "";
|
|
24268
|
+
return `Settings \u2192 Connectors: ${tokenBit}${bits}. If a connector MCP needsAuth or is unauthorized, ignore it and use the CLI or \`posthog_api\`. Do not add vendor MCPs. Do not read a PostHog key from the environment. Shell dumps from those CLIs go in \`.context/cli/\` \u2014 read a slice.`;
|
|
24269
|
+
}
|
|
24270
|
+
async function connectedCliPresence(integrations) {
|
|
24271
|
+
const ids = connectedOptionalServices(integrations).filter((spec) => spec.cli).map((spec) => spec.id);
|
|
24272
|
+
if (ids.length === 0) return void 0;
|
|
24273
|
+
try {
|
|
24274
|
+
const { optionalCliPresence: optionalCliPresence2 } = await Promise.resolve().then(() => (init_optional_cli(), optional_cli_exports));
|
|
24275
|
+
return await optionalCliPresence2(ids);
|
|
24276
|
+
} catch {
|
|
24277
|
+
return void 0;
|
|
24278
|
+
}
|
|
24279
|
+
}
|
|
24280
|
+
async function formatOptionalServicesDirectiveForTurn(integrations) {
|
|
24281
|
+
return formatOptionalServicesDirective(integrations, await connectedCliPresence(integrations));
|
|
24282
|
+
}
|
|
24283
|
+
async function formatOptionalServicesReminderForTurn(integrations) {
|
|
24284
|
+
return formatOptionalServicesReminder(integrations, await connectedCliPresence(integrations));
|
|
24055
24285
|
}
|
|
24056
|
-
var OPTIONAL_SERVICES;
|
|
24286
|
+
var OPTIONAL_SERVICE_IDS, OPTIONAL_SERVICES;
|
|
24057
24287
|
var init_optional_services = __esm({
|
|
24058
24288
|
"src/integrations/optional-services.ts"() {
|
|
24059
24289
|
"use strict";
|
|
24060
24290
|
init_fetch();
|
|
24061
24291
|
init_app_settings();
|
|
24292
|
+
OPTIONAL_SERVICE_IDS = ["vercel", "supabase", "posthog", "sentry"];
|
|
24062
24293
|
OPTIONAL_SERVICES = [
|
|
24063
24294
|
{
|
|
24064
24295
|
id: "vercel",
|
|
@@ -24083,7 +24314,7 @@ var init_optional_services = __esm({
|
|
|
24083
24314
|
{
|
|
24084
24315
|
id: "posthog",
|
|
24085
24316
|
label: "PostHog",
|
|
24086
|
-
hint: "Product analytics.
|
|
24317
|
+
hint: "Product analytics. Agents call `posthog_api` on the Sideboard MCP server.",
|
|
24087
24318
|
tokenPlaceholder: "phx_\u2026",
|
|
24088
24319
|
tokenDocs: "https://app.posthog.com/settings/user-api-keys",
|
|
24089
24320
|
envKey: "POSTHOG_PERSONAL_API_KEY",
|
|
@@ -26377,7 +26608,7 @@ var init_orchestrator = __esm({
|
|
|
26377
26608
|
const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
|
|
26378
26609
|
const longRunningReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatLongRunningReminder({ activeRuns: worktreeActiveRuns }) : null;
|
|
26379
26610
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
26380
|
-
const optionalServicesReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ?
|
|
26611
|
+
const optionalServicesReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? await formatOptionalServicesReminderForTurn(loadAppSettings().integrations) : null;
|
|
26381
26612
|
const issueTicket = issueTicketFromThread(thread, resolveEffectiveIssueSource());
|
|
26382
26613
|
const issueToolsReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatIssueToolsReminder({
|
|
26383
26614
|
linear: isLinearConnected(),
|
|
@@ -26448,7 +26679,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
26448
26679
|
});
|
|
26449
26680
|
const artifactDirective = isBrightsy ? null : formatArtifactDirective();
|
|
26450
26681
|
const longRunningDirective = isBrightsy || isOrchestration ? null : formatLongRunningDirective();
|
|
26451
|
-
const optionalServicesDirective = isBrightsy || isOrchestration ? null :
|
|
26682
|
+
const optionalServicesDirective = isBrightsy || isOrchestration ? null : await formatOptionalServicesDirectiveForTurn(loadAppSettings().integrations);
|
|
26452
26683
|
const freshIssueTicket = issueTicketFromThread(fresh, resolveEffectiveIssueSource());
|
|
26453
26684
|
const issueToolsDirective = isBrightsy || isOrchestration ? null : formatIssueToolsDirective({
|
|
26454
26685
|
linear: isLinearConnected(),
|
|
@@ -26625,7 +26856,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
26625
26856
|
}
|
|
26626
26857
|
} catch {
|
|
26627
26858
|
}
|
|
26628
|
-
const retryNote = isolateCodexPluginRetry ? "Codex plugin/MCP failed \u2014 continuing without that vendor plugin (Settings \u2192 Connectors
|
|
26859
|
+
const retryNote = isolateCodexPluginRetry ? "Codex plugin/MCP failed \u2014 continuing without that vendor plugin (PostHog: Settings \u2192 Connectors and posthog_api)" : looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : looksLikeV8Oom(detail) ? "Agent ran out of memory \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
|
|
26629
26860
|
pushTurnStderr(stderrTail, retryNote);
|
|
26630
26861
|
this.emit({
|
|
26631
26862
|
type: "turn_output",
|
|
@@ -28249,7 +28480,7 @@ function createClippedMcpServer() {
|
|
|
28249
28480
|
}
|
|
28250
28481
|
|
|
28251
28482
|
// src/mcp/server.ts
|
|
28252
|
-
var
|
|
28483
|
+
var import_zod10 = require("zod");
|
|
28253
28484
|
var import_node_crypto19 = require("crypto");
|
|
28254
28485
|
var import_node_path65 = require("path");
|
|
28255
28486
|
init_orchestrator();
|
|
@@ -31262,6 +31493,151 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
|
|
|
31262
31493
|
if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
|
|
31263
31494
|
}
|
|
31264
31495
|
|
|
31496
|
+
// src/mcp/posthog-tools.ts
|
|
31497
|
+
var import_zod8 = require("zod");
|
|
31498
|
+
init_fetch();
|
|
31499
|
+
init_optional_services();
|
|
31500
|
+
init_app_settings();
|
|
31501
|
+
init_issue_list();
|
|
31502
|
+
var POSTHOG_METHODS = ["GET", "POST", "PATCH"];
|
|
31503
|
+
var POSTHOG_RESPONSE_MAX_CHARS = 256e3;
|
|
31504
|
+
var POSTHOG_TIMEOUT_MS = 2e4;
|
|
31505
|
+
var POSTHOG_READ_QUERY = /^\/api\/(?:projects|environments)\/[^/]+\/query$/;
|
|
31506
|
+
function isPosthogReadQuery(method, path) {
|
|
31507
|
+
if (method !== "POST") return false;
|
|
31508
|
+
return POSTHOG_READ_QUERY.test(path.trim().replace(/\/+$/, ""));
|
|
31509
|
+
}
|
|
31510
|
+
function withPosthogSlash(method, path) {
|
|
31511
|
+
const raw = path.trim();
|
|
31512
|
+
if ((method === "POST" || method === "PATCH") && raw.startsWith("/api/") && !raw.endsWith("/")) {
|
|
31513
|
+
return `${raw}/`;
|
|
31514
|
+
}
|
|
31515
|
+
return raw;
|
|
31516
|
+
}
|
|
31517
|
+
function posthogApiUrl(host, path, query) {
|
|
31518
|
+
const raw = path.trim();
|
|
31519
|
+
if (!raw.startsWith("/api/")) {
|
|
31520
|
+
throw new Error("path must start with /api/");
|
|
31521
|
+
}
|
|
31522
|
+
if (raw.includes("://") || raw.startsWith("//") || /[?#\s\\]/.test(raw) || raw.includes("..")) {
|
|
31523
|
+
throw new Error("path must be a PostHog API path, not a URL");
|
|
31524
|
+
}
|
|
31525
|
+
const origin = normalizeServiceOrigin(host, "https://us.posthog.com");
|
|
31526
|
+
const url = new URL(raw, origin);
|
|
31527
|
+
if (url.origin !== new URL(origin).origin || !url.pathname.startsWith("/api/")) {
|
|
31528
|
+
throw new Error("path must stay on the connected PostHog host");
|
|
31529
|
+
}
|
|
31530
|
+
if (query) {
|
|
31531
|
+
for (const [key, value] of Object.entries(query)) {
|
|
31532
|
+
if (!key.trim()) throw new Error("query keys must be non-empty");
|
|
31533
|
+
url.searchParams.set(key, value);
|
|
31534
|
+
}
|
|
31535
|
+
}
|
|
31536
|
+
return url.toString();
|
|
31537
|
+
}
|
|
31538
|
+
async function runPosthogApi(input) {
|
|
31539
|
+
const method = input.method;
|
|
31540
|
+
if (!POSTHOG_METHODS.includes(method)) {
|
|
31541
|
+
return mcpJson({ ok: false, error: "method must be GET, POST, or PATCH" }, true);
|
|
31542
|
+
}
|
|
31543
|
+
if (method !== "GET" && input.write !== true && !isPosthogReadQuery(method, input.path)) {
|
|
31544
|
+
return mcpJson(
|
|
31545
|
+
{
|
|
31546
|
+
ok: false,
|
|
31547
|
+
error: "POST and PATCH require write=true, and only when the user asked to change PostHog data. POST /api/projects/:id/query/ and POST /api/environments/:id/query/ are reads and do not need write=true."
|
|
31548
|
+
},
|
|
31549
|
+
true
|
|
31550
|
+
);
|
|
31551
|
+
}
|
|
31552
|
+
if (method === "GET" && input.body !== void 0) {
|
|
31553
|
+
return mcpJson({ ok: false, error: "GET does not take a body" }, true);
|
|
31554
|
+
}
|
|
31555
|
+
const integrations = loadAppSettings().integrations;
|
|
31556
|
+
const token = integrations.posthogPersonalApiKey?.trim();
|
|
31557
|
+
if (!token) {
|
|
31558
|
+
return mcpJson(
|
|
31559
|
+
{ ok: false, error: "PostHog is not connected (Settings \u2192 Connectors)." },
|
|
31560
|
+
true
|
|
31561
|
+
);
|
|
31562
|
+
}
|
|
31563
|
+
let url;
|
|
31564
|
+
try {
|
|
31565
|
+
url = posthogApiUrl(
|
|
31566
|
+
integrations.posthogHost || "https://us.posthog.com",
|
|
31567
|
+
withPosthogSlash(method, input.path),
|
|
31568
|
+
input.query
|
|
31569
|
+
);
|
|
31570
|
+
} catch (err) {
|
|
31571
|
+
return mcpJson(
|
|
31572
|
+
{ ok: false, error: err instanceof Error ? err.message : String(err) },
|
|
31573
|
+
true
|
|
31574
|
+
);
|
|
31575
|
+
}
|
|
31576
|
+
const headers = {
|
|
31577
|
+
Authorization: `Bearer ${token}`,
|
|
31578
|
+
Accept: "application/json"
|
|
31579
|
+
};
|
|
31580
|
+
let body;
|
|
31581
|
+
if (input.body !== void 0) {
|
|
31582
|
+
headers["Content-Type"] = "application/json";
|
|
31583
|
+
body = typeof input.body === "string" ? input.body : JSON.stringify(input.body);
|
|
31584
|
+
}
|
|
31585
|
+
try {
|
|
31586
|
+
const res = await httpFetch(url, {
|
|
31587
|
+
method,
|
|
31588
|
+
headers,
|
|
31589
|
+
body,
|
|
31590
|
+
signal: AbortSignal.timeout(POSTHOG_TIMEOUT_MS)
|
|
31591
|
+
});
|
|
31592
|
+
const text7 = await res.text();
|
|
31593
|
+
const truncated = text7.length > POSTHOG_RESPONSE_MAX_CHARS;
|
|
31594
|
+
const slice = truncated ? text7.slice(0, POSTHOG_RESPONSE_MAX_CHARS) : text7;
|
|
31595
|
+
let parsed = slice;
|
|
31596
|
+
if (slice.trim()) {
|
|
31597
|
+
try {
|
|
31598
|
+
parsed = JSON.parse(slice);
|
|
31599
|
+
} catch {
|
|
31600
|
+
parsed = slice;
|
|
31601
|
+
}
|
|
31602
|
+
} else {
|
|
31603
|
+
parsed = null;
|
|
31604
|
+
}
|
|
31605
|
+
return mcpJson(
|
|
31606
|
+
{
|
|
31607
|
+
ok: res.ok,
|
|
31608
|
+
status: res.status,
|
|
31609
|
+
...truncated ? { truncated: true } : {},
|
|
31610
|
+
body: parsed
|
|
31611
|
+
},
|
|
31612
|
+
!res.ok
|
|
31613
|
+
);
|
|
31614
|
+
} catch (err) {
|
|
31615
|
+
return mcpJson(
|
|
31616
|
+
{ ok: false, error: err instanceof Error ? err.message : String(err) },
|
|
31617
|
+
true
|
|
31618
|
+
);
|
|
31619
|
+
}
|
|
31620
|
+
}
|
|
31621
|
+
var POSTHOG_API_DESCRIPTION = "Call the connected PostHog project (Settings \u2192 Connectors). path must start with /api/ and is joined to the stored host. GET is the default. POST /api/projects/:id/query/ and POST /api/environments/:id/query/ are reads (HogQL and insights) and do not need write=true. Other POST and PATCH require write=true, and only when the user asked to change PostHog data. The API key is not in the environment. Do not sign in to a PostHog MCP.";
|
|
31622
|
+
function registerPosthogTools(server) {
|
|
31623
|
+
server.tool(
|
|
31624
|
+
"posthog_api",
|
|
31625
|
+
POSTHOG_API_DESCRIPTION,
|
|
31626
|
+
{
|
|
31627
|
+
method: import_zod8.z.enum(POSTHOG_METHODS).describe(
|
|
31628
|
+
"GET, POST /api/projects|environments/:id/query/ for a read, or POST/PATCH when the user asked for a change"
|
|
31629
|
+
),
|
|
31630
|
+
path: import_zod8.z.string().describe("PostHog path starting with /api/, not a full URL"),
|
|
31631
|
+
query: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.string()).optional().describe("Query string parameters"),
|
|
31632
|
+
body: import_zod8.z.unknown().optional().describe("JSON body for POST or PATCH"),
|
|
31633
|
+
write: import_zod8.z.boolean().optional().describe(
|
|
31634
|
+
"Required true for POST and PATCH that change data. Omit for GET and for POST .../query/."
|
|
31635
|
+
)
|
|
31636
|
+
},
|
|
31637
|
+
async (input) => runPosthogApi(input)
|
|
31638
|
+
);
|
|
31639
|
+
}
|
|
31640
|
+
|
|
31265
31641
|
// src/mcp/server.ts
|
|
31266
31642
|
init_viewer_context_tools();
|
|
31267
31643
|
init_issue_list();
|
|
@@ -31306,7 +31682,7 @@ init_list_prs();
|
|
|
31306
31682
|
init_app_settings();
|
|
31307
31683
|
|
|
31308
31684
|
// src/mcp/schedule-tools.ts
|
|
31309
|
-
var
|
|
31685
|
+
var import_zod9 = require("zod");
|
|
31310
31686
|
init_schedules();
|
|
31311
31687
|
init_schedule_runner();
|
|
31312
31688
|
init_issue_list();
|
|
@@ -31349,18 +31725,18 @@ function registerScheduleTools(server) {
|
|
|
31349
31725
|
"create_schedule",
|
|
31350
31726
|
"Create a local schedule that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
|
|
31351
31727
|
{
|
|
31352
|
-
prompt:
|
|
31353
|
-
name:
|
|
31354
|
-
at:
|
|
31355
|
-
every:
|
|
31356
|
-
cron:
|
|
31357
|
-
tz:
|
|
31358
|
-
chatId:
|
|
31728
|
+
prompt: import_zod9.z.string().describe("User message / goal queued when the schedule fires"),
|
|
31729
|
+
name: import_zod9.z.string().optional(),
|
|
31730
|
+
at: import_zod9.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
31731
|
+
every: import_zod9.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
31732
|
+
cron: import_zod9.z.string().optional().describe("5-field cron expression"),
|
|
31733
|
+
tz: import_zod9.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
31734
|
+
chatId: import_zod9.z.string().optional().describe(
|
|
31359
31735
|
'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
|
|
31360
31736
|
),
|
|
31361
|
-
threadId:
|
|
31362
|
-
agent:
|
|
31363
|
-
model:
|
|
31737
|
+
threadId: import_zod9.z.string().optional().describe("Legacy alias for chatId."),
|
|
31738
|
+
agent: import_zod9.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
31739
|
+
model: import_zod9.z.string().optional()
|
|
31364
31740
|
},
|
|
31365
31741
|
async (args) => {
|
|
31366
31742
|
try {
|
|
@@ -31396,20 +31772,20 @@ function registerScheduleTools(server) {
|
|
|
31396
31772
|
"update_schedule",
|
|
31397
31773
|
"Update a local schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
|
|
31398
31774
|
{
|
|
31399
|
-
id:
|
|
31400
|
-
prompt:
|
|
31401
|
-
name:
|
|
31402
|
-
at:
|
|
31403
|
-
every:
|
|
31404
|
-
cron:
|
|
31405
|
-
tz:
|
|
31406
|
-
chatId:
|
|
31775
|
+
id: import_zod9.z.string(),
|
|
31776
|
+
prompt: import_zod9.z.string().optional(),
|
|
31777
|
+
name: import_zod9.z.string().optional(),
|
|
31778
|
+
at: import_zod9.z.string().optional(),
|
|
31779
|
+
every: import_zod9.z.string().optional(),
|
|
31780
|
+
cron: import_zod9.z.string().optional(),
|
|
31781
|
+
tz: import_zod9.z.string().optional(),
|
|
31782
|
+
chatId: import_zod9.z.string().optional().describe(
|
|
31407
31783
|
'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
|
|
31408
31784
|
),
|
|
31409
|
-
threadId:
|
|
31410
|
-
agent:
|
|
31411
|
-
model:
|
|
31412
|
-
enabled:
|
|
31785
|
+
threadId: import_zod9.z.string().optional().describe("Legacy alias for chatId."),
|
|
31786
|
+
agent: import_zod9.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
31787
|
+
model: import_zod9.z.string().optional(),
|
|
31788
|
+
enabled: import_zod9.z.boolean().optional()
|
|
31413
31789
|
},
|
|
31414
31790
|
async (args) => {
|
|
31415
31791
|
try {
|
|
@@ -31450,7 +31826,7 @@ function registerScheduleTools(server) {
|
|
|
31450
31826
|
server.tool(
|
|
31451
31827
|
"delete_schedule",
|
|
31452
31828
|
"Delete a local schedule. Pass id from list_schedules.",
|
|
31453
|
-
{ id:
|
|
31829
|
+
{ id: import_zod9.z.string() },
|
|
31454
31830
|
async ({ id }) => {
|
|
31455
31831
|
try {
|
|
31456
31832
|
deleteSchedule(id);
|
|
@@ -31463,7 +31839,7 @@ function registerScheduleTools(server) {
|
|
|
31463
31839
|
server.tool(
|
|
31464
31840
|
"run_schedule",
|
|
31465
31841
|
"Fire a schedule now (does not wait for nextRunAt). Queues the prompt on the target chat or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
|
|
31466
|
-
{ id:
|
|
31842
|
+
{ id: import_zod9.z.string() },
|
|
31467
31843
|
async ({ id }) => {
|
|
31468
31844
|
try {
|
|
31469
31845
|
if (!getSchedule(id)) {
|
|
@@ -31814,6 +32190,7 @@ async function startMcpServer() {
|
|
|
31814
32190
|
}
|
|
31815
32191
|
};
|
|
31816
32192
|
registerViewerContextTools(server);
|
|
32193
|
+
registerPosthogTools(server);
|
|
31817
32194
|
if (worktreeProfile) {
|
|
31818
32195
|
registerConnectedIssueVendorTools(server);
|
|
31819
32196
|
}
|
|
@@ -31869,16 +32246,16 @@ async function startMcpServer() {
|
|
|
31869
32246
|
"list_board",
|
|
31870
32247
|
"Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. 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), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
|
|
31871
32248
|
{
|
|
31872
|
-
query:
|
|
31873
|
-
repoPath:
|
|
31874
|
-
kind:
|
|
31875
|
-
ownership:
|
|
32249
|
+
query: import_zod10.z.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
|
|
32250
|
+
repoPath: import_zod10.z.string().optional().describe("Limit to one project path from list_projects"),
|
|
32251
|
+
kind: import_zod10.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
32252
|
+
ownership: import_zod10.z.enum(["all", "mine", "reviewing"]).optional().describe(
|
|
31876
32253
|
"Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
|
|
31877
32254
|
),
|
|
31878
|
-
column:
|
|
32255
|
+
column: import_zod10.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
|
|
31879
32256
|
"Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
|
|
31880
32257
|
),
|
|
31881
|
-
limit:
|
|
32258
|
+
limit: import_zod10.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
|
|
31882
32259
|
},
|
|
31883
32260
|
async ({ query, repoPath, kind, ownership, column, limit }) => {
|
|
31884
32261
|
const workspaces = orch.listWorkspaces();
|
|
@@ -31908,7 +32285,7 @@ async function startMcpServer() {
|
|
|
31908
32285
|
server.tool(
|
|
31909
32286
|
"get_chat",
|
|
31910
32287
|
"Get a compact chat summary by id/ref. Includes last message preview, parentChatId, and child workspace chats (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (billed token + costUsd totals when providers reported cost) and lastTurnUsage. blockedReason is why that agent is waiting on a person (question, plan approval, or a reported block).",
|
|
31911
|
-
{ ref:
|
|
32288
|
+
{ ref: import_zod10.z.string() },
|
|
31912
32289
|
async ({ ref }) => {
|
|
31913
32290
|
const t = orch.getThread(ref);
|
|
31914
32291
|
if (!t) {
|
|
@@ -31955,17 +32332,17 @@ async function startMcpServer() {
|
|
|
31955
32332
|
"present_artifact",
|
|
31956
32333
|
"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.",
|
|
31957
32334
|
{
|
|
31958
|
-
title:
|
|
31959
|
-
type:
|
|
32335
|
+
title: import_zod10.z.string().describe("Short title shown in the artifact pane header"),
|
|
32336
|
+
type: import_zod10.z.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
31960
32337
|
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
31961
32338
|
),
|
|
31962
|
-
content:
|
|
32339
|
+
content: import_zod10.z.string().describe(
|
|
31963
32340
|
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
31964
32341
|
),
|
|
31965
|
-
artifact_id:
|
|
31966
|
-
status:
|
|
31967
|
-
phase:
|
|
31968
|
-
mode:
|
|
32342
|
+
artifact_id: import_zod10.z.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
32343
|
+
status: import_zod10.z.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
32344
|
+
phase: import_zod10.z.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
32345
|
+
mode: import_zod10.z.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
31969
32346
|
},
|
|
31970
32347
|
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
31971
32348
|
return mcpJson(
|
|
@@ -31978,15 +32355,15 @@ async function startMcpServer() {
|
|
|
31978
32355
|
"ask_user",
|
|
31979
32356
|
"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.",
|
|
31980
32357
|
{
|
|
31981
|
-
questions:
|
|
31982
|
-
|
|
31983
|
-
question:
|
|
31984
|
-
header:
|
|
31985
|
-
multiSelect:
|
|
31986
|
-
options:
|
|
31987
|
-
|
|
31988
|
-
label:
|
|
31989
|
-
description:
|
|
32358
|
+
questions: import_zod10.z.array(
|
|
32359
|
+
import_zod10.z.object({
|
|
32360
|
+
question: import_zod10.z.string().describe("Full question text ending with ?"),
|
|
32361
|
+
header: import_zod10.z.string().max(24).optional().describe("Short label shown above the question"),
|
|
32362
|
+
multiSelect: import_zod10.z.boolean().optional().describe("Allow selecting multiple options"),
|
|
32363
|
+
options: import_zod10.z.array(
|
|
32364
|
+
import_zod10.z.object({
|
|
32365
|
+
label: import_zod10.z.string(),
|
|
32366
|
+
description: import_zod10.z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
|
|
31990
32367
|
})
|
|
31991
32368
|
).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
|
|
31992
32369
|
})
|
|
@@ -32012,9 +32389,9 @@ async function startMcpServer() {
|
|
|
32012
32389
|
"present_plan",
|
|
32013
32390
|
PRESENT_PLAN_TOOL_DESCRIPTION,
|
|
32014
32391
|
{
|
|
32015
|
-
title:
|
|
32016
|
-
content:
|
|
32017
|
-
thread_id:
|
|
32392
|
+
title: import_zod10.z.string().optional().describe("Short plan title (defaults to Plan)"),
|
|
32393
|
+
content: import_zod10.z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
|
|
32394
|
+
thread_id: import_zod10.z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
|
|
32018
32395
|
},
|
|
32019
32396
|
async ({ title, content, thread_id }) => {
|
|
32020
32397
|
const { writePlanFile: writePlanFile2 } = await Promise.resolve().then(() => (init_plan_file(), plan_file_exports));
|
|
@@ -32043,15 +32420,15 @@ async function startMcpServer() {
|
|
|
32043
32420
|
"present_schema",
|
|
32044
32421
|
"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.",
|
|
32045
32422
|
{
|
|
32046
|
-
title:
|
|
32047
|
-
mode:
|
|
32048
|
-
datasource:
|
|
32049
|
-
resource_id:
|
|
32050
|
-
record_id:
|
|
32051
|
-
resource:
|
|
32052
|
-
record:
|
|
32053
|
-
records:
|
|
32054
|
-
pane_id:
|
|
32423
|
+
title: import_zod10.z.string().describe("Pane title"),
|
|
32424
|
+
mode: import_zod10.z.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
|
|
32425
|
+
datasource: import_zod10.z.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
|
|
32426
|
+
resource_id: import_zod10.z.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
|
|
32427
|
+
record_id: import_zod10.z.string().optional().describe("Record id when opening form mode"),
|
|
32428
|
+
resource: import_zod10.z.record(import_zod10.z.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
|
|
32429
|
+
record: import_zod10.z.record(import_zod10.z.unknown()).optional().describe("Inline record { id, data, published_at? }"),
|
|
32430
|
+
records: import_zod10.z.array(import_zod10.z.record(import_zod10.z.unknown())).optional().describe("Inline records for table mode"),
|
|
32431
|
+
pane_id: import_zod10.z.string().optional().describe("Stable pane id across updates")
|
|
32055
32432
|
},
|
|
32056
32433
|
async (args) => {
|
|
32057
32434
|
const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -32072,10 +32449,10 @@ async function startMcpServer() {
|
|
|
32072
32449
|
"present_files",
|
|
32073
32450
|
"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.",
|
|
32074
32451
|
{
|
|
32075
|
-
title:
|
|
32076
|
-
datasource:
|
|
32077
|
-
path:
|
|
32078
|
-
pane_id:
|
|
32452
|
+
title: import_zod10.z.string().optional().describe("Pane title (default: Files)"),
|
|
32453
|
+
datasource: import_zod10.z.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
|
|
32454
|
+
path: import_zod10.z.string().optional().describe("Initial folder path (e.g. public)"),
|
|
32455
|
+
pane_id: import_zod10.z.string().optional().describe("Stable pane id across updates")
|
|
32079
32456
|
},
|
|
32080
32457
|
async (args) => {
|
|
32081
32458
|
const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -32095,8 +32472,8 @@ async function startMcpServer() {
|
|
|
32095
32472
|
"wait_for_job",
|
|
32096
32473
|
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. MCP clients kill tools around 60s, so this returns within 45s. Sideboard opens a type=log pane from this result (artifact_id = job id, content = delta). stillRunning is the source of truth \u2014 if true, call wait_for_job again. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
32097
32474
|
{
|
|
32098
|
-
id:
|
|
32099
|
-
timeoutMs:
|
|
32475
|
+
id: import_zod10.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
32476
|
+
timeoutMs: import_zod10.z.number().optional()
|
|
32100
32477
|
},
|
|
32101
32478
|
async ({ id, timeoutMs }) => {
|
|
32102
32479
|
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
@@ -32109,8 +32486,8 @@ async function startMcpServer() {
|
|
|
32109
32486
|
"stop_job",
|
|
32110
32487
|
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
|
|
32111
32488
|
{
|
|
32112
|
-
id:
|
|
32113
|
-
reason:
|
|
32489
|
+
id: import_zod10.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
32490
|
+
reason: import_zod10.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
|
|
32114
32491
|
},
|
|
32115
32492
|
async ({ id, reason }) => {
|
|
32116
32493
|
const result = await stopDetachedJob(process.cwd(), id, { reason });
|
|
@@ -32122,7 +32499,7 @@ async function startMcpServer() {
|
|
|
32122
32499
|
"list_run_scripts",
|
|
32123
32500
|
"List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Each script and active run includes preview: url (open http://localhost:<SIDEBOARD_PORT>) or window (Electron: native window is the app; url is renderer/HMR only). Never guess :3000. Worktree turns may omit ref (uses cwd).",
|
|
32124
32501
|
{
|
|
32125
|
-
ref:
|
|
32502
|
+
ref: import_zod10.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
|
|
32126
32503
|
},
|
|
32127
32504
|
async ({ ref }) => {
|
|
32128
32505
|
try {
|
|
@@ -32152,8 +32529,8 @@ async function startMcpServer() {
|
|
|
32152
32529
|
"run_dev_script",
|
|
32153
32530
|
"Start a .sideboard/.conductor run script (same as the desktop Dev / play button). Several named scripts can run at once; each gets its own port, and starting one does not stop the others. Logs and Stop appear in the thread UI \u2014 do not detached-job or shell-spawn the same command. Returns {port, ports, url, preview}. preview=url \u2192 open url (browser, curl, Playwright). preview=window \u2192 Electron: the native window is the app; url is renderer/HMR only (no preload/IPC in a browser). The port is allocated SIDEBOARD_PORT, not 3000. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
|
|
32154
32531
|
{
|
|
32155
|
-
ref:
|
|
32156
|
-
name:
|
|
32532
|
+
ref: import_zod10.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
32533
|
+
name: import_zod10.z.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
|
|
32157
32534
|
},
|
|
32158
32535
|
async ({ ref, name }) => {
|
|
32159
32536
|
try {
|
|
@@ -32181,8 +32558,8 @@ async function startMcpServer() {
|
|
|
32181
32558
|
"stop_dev_script",
|
|
32182
32559
|
"Stop a run script started with run_dev_script or the desktop Dev button (same as UI Stop). Omit name to stop all scripts on this worktree. Worktree turns may omit ref (uses cwd).",
|
|
32183
32560
|
{
|
|
32184
|
-
ref:
|
|
32185
|
-
name:
|
|
32561
|
+
ref: import_zod10.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
32562
|
+
name: import_zod10.z.string().optional().describe("Script name to stop; omit to stop all active scripts.")
|
|
32186
32563
|
},
|
|
32187
32564
|
async ({ ref, name }) => {
|
|
32188
32565
|
try {
|
|
@@ -32199,9 +32576,9 @@ async function startMcpServer() {
|
|
|
32199
32576
|
"get_run_log",
|
|
32200
32577
|
"Read the Run-tab terminal for a worktree script (same stdout/stderr as the desktop Dev pane). Default is a tail \u2014 raise `tail` only if the error is missing. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
|
|
32201
32578
|
{
|
|
32202
|
-
ref:
|
|
32203
|
-
name:
|
|
32204
|
-
tail:
|
|
32579
|
+
ref: import_zod10.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
|
|
32580
|
+
name: import_zod10.z.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
|
|
32581
|
+
tail: import_zod10.z.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
|
|
32205
32582
|
`Max characters to return from the end (default ${DEFAULT_RUN_LOG_TAIL_CHARS}). Do not request the full buffer unless the tail is truncated and you still need more.`
|
|
32206
32583
|
)
|
|
32207
32584
|
},
|
|
@@ -32239,8 +32616,8 @@ async function startMcpServer() {
|
|
|
32239
32616
|
"notify_orchestrator",
|
|
32240
32617
|
"Wake the parent Global orchestrator with a short status so it does not have to poll wait_for_turn. Use when you are blocked and the parent may have moved on (missing access, waiting on something the coordinator must handle). The message is shown on the sidebar and board until the next user message in this chat. ask_user already notifies for input-required \u2014 do not call this for the same question. Information only: never a git command. Do not use this to steer other worktrees.",
|
|
32241
32618
|
{
|
|
32242
|
-
message:
|
|
32243
|
-
reason:
|
|
32619
|
+
message: import_zod10.z.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
|
|
32620
|
+
reason: import_zod10.z.enum(["input-required", "blocked"]).optional().describe(
|
|
32244
32621
|
"input-required = waiting on the user in this chat; blocked = need the coordinator (default)."
|
|
32245
32622
|
)
|
|
32246
32623
|
},
|
|
@@ -32277,11 +32654,11 @@ async function startMcpServer() {
|
|
|
32277
32654
|
"set_workspace_tags",
|
|
32278
32655
|
worktreeProfile ? "Update tags on this workspace (the checkout you are in). Omit ref to use this chat. mode=replace (default), add, or remove. Short labels \u2014 the same ones the orchestrator used for this task. You cannot retag a different checkout." : "Label a project workspace so related checkouts stay recognizable in the sidebar and on the phone. Pass any chat id on that workspace. Use the same tags on every workspace from one task. mode=replace is the default; add and remove change the set. create_workspace tags= sets them at create time. Not for orchestration chats.",
|
|
32279
32656
|
{
|
|
32280
|
-
ref:
|
|
32657
|
+
ref: import_zod10.z.string().optional().describe(
|
|
32281
32658
|
worktreeProfile ? "Omit for this chat. Another chat id only if it is on this same checkout." : "Chat id on the workspace to label."
|
|
32282
32659
|
),
|
|
32283
|
-
tags:
|
|
32284
|
-
mode:
|
|
32660
|
+
tags: import_zod10.z.union([import_zod10.z.string(), import_zod10.z.array(import_zod10.z.string())]).describe("Labels to set. A string may be comma-separated."),
|
|
32661
|
+
mode: import_zod10.z.enum(["replace", "add", "remove"]).optional()
|
|
32285
32662
|
},
|
|
32286
32663
|
async ({ ref, tags, mode }) => {
|
|
32287
32664
|
try {
|
|
@@ -32315,7 +32692,7 @@ async function startMcpServer() {
|
|
|
32315
32692
|
"set_caffeinate",
|
|
32316
32693
|
"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.",
|
|
32317
32694
|
{
|
|
32318
|
-
enabled:
|
|
32695
|
+
enabled: import_zod10.z.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
|
|
32319
32696
|
},
|
|
32320
32697
|
async ({ enabled }) => {
|
|
32321
32698
|
const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
@@ -32341,24 +32718,24 @@ async function startMcpServer() {
|
|
|
32341
32718
|
"create_workspace",
|
|
32342
32719
|
`Create a workspace (isolated git checkout + first chat) from branch, pr, or ticket. A ticket, PR, or named branch has one checkout \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. That is not one agent: chats lists every agent on that checkout. Add another with fork_chat; send_to_chat only the chat that should do this job. Creating from the default branch still opens a new isolated checkout. Pass repoPath from list_projects. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentChatId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Omit agent/model \u2014 Sideboard applies ${accountDefaultsHint}. Do not pass your own agent or agent=cursor; those are ignored. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Pass the same tags on every workspace you open for one task (short labels such as "phone-sync") so they still match in the sidebar the next day. Reusing a checkout adds those tags.`,
|
|
32343
32720
|
{
|
|
32344
|
-
sourceType:
|
|
32345
|
-
sourceRef:
|
|
32346
|
-
agent:
|
|
32721
|
+
sourceType: import_zod10.z.enum(["branch", "pr", "ticket"]),
|
|
32722
|
+
sourceRef: import_zod10.z.string(),
|
|
32723
|
+
agent: import_zod10.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
|
|
32347
32724
|
`Usually omit. Sideboard uses Account default agent (${accountDefaults.agent}). Passing your own agent or cursor is ignored.`
|
|
32348
32725
|
),
|
|
32349
|
-
model:
|
|
32726
|
+
model: import_zod10.z.string().nullable().optional().describe(
|
|
32350
32727
|
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
32351
32728
|
),
|
|
32352
|
-
repoPath:
|
|
32353
|
-
title:
|
|
32354
|
-
cowboy:
|
|
32729
|
+
repoPath: import_zod10.z.string(),
|
|
32730
|
+
title: import_zod10.z.string().optional(),
|
|
32731
|
+
cowboy: import_zod10.z.boolean().optional().describe(
|
|
32355
32732
|
"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."
|
|
32356
32733
|
),
|
|
32357
|
-
parentChatId:
|
|
32734
|
+
parentChatId: import_zod10.z.string().optional().describe(
|
|
32358
32735
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
32359
32736
|
),
|
|
32360
|
-
parentThreadId:
|
|
32361
|
-
tags:
|
|
32737
|
+
parentThreadId: import_zod10.z.string().optional().describe("Legacy alias for parentChatId."),
|
|
32738
|
+
tags: import_zod10.z.union([import_zod10.z.string(), import_zod10.z.array(import_zod10.z.string())]).optional().describe(
|
|
32362
32739
|
"Short labels for this workspace (or a comma-separated string). Use the same tags on every checkout for one task. Added when the checkout already exists."
|
|
32363
32740
|
)
|
|
32364
32741
|
},
|
|
@@ -32374,11 +32751,11 @@ async function startMcpServer() {
|
|
|
32374
32751
|
"start_board_card",
|
|
32375
32752
|
"Same as create_workspace for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second checkout when one already matches \u2014 returns that workspace (alreadyStarted) and its chats. A workspace can have many agents. fork_chat to add one; send_to_chat only the chat that should do this job.",
|
|
32376
32753
|
{
|
|
32377
|
-
kind:
|
|
32378
|
-
ref:
|
|
32379
|
-
repoPath:
|
|
32380
|
-
title:
|
|
32381
|
-
tags:
|
|
32754
|
+
kind: import_zod10.z.enum(["ticket", "pr", "branch"]),
|
|
32755
|
+
ref: import_zod10.z.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
32756
|
+
repoPath: import_zod10.z.string().describe("Project path from list_projects / list_board"),
|
|
32757
|
+
title: import_zod10.z.string().optional(),
|
|
32758
|
+
tags: import_zod10.z.union([import_zod10.z.string(), import_zod10.z.array(import_zod10.z.string())]).optional().describe(
|
|
32382
32759
|
"Short labels for this workspace. Same tags on every checkout for one task. Added when the checkout already exists."
|
|
32383
32760
|
)
|
|
32384
32761
|
},
|
|
@@ -32483,9 +32860,9 @@ async function startMcpServer() {
|
|
|
32483
32860
|
"send_to_chat",
|
|
32484
32861
|
'Steer a prompt on a workspace chat (Settings \u2192 Follow-up, default steer: interrupt the in-flight turn and start now, same as the workspace composer). Use after create_workspace 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 send_to_chat to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
|
|
32485
32862
|
{
|
|
32486
|
-
ref:
|
|
32487
|
-
prompt:
|
|
32488
|
-
force_stop:
|
|
32863
|
+
ref: import_zod10.z.string(),
|
|
32864
|
+
prompt: import_zod10.z.string(),
|
|
32865
|
+
force_stop: import_zod10.z.boolean().optional()
|
|
32489
32866
|
},
|
|
32490
32867
|
async ({ ref, prompt, force_stop }) => {
|
|
32491
32868
|
if (force_stop) {
|
|
@@ -32510,8 +32887,8 @@ async function startMcpServer() {
|
|
|
32510
32887
|
"wait_for_turn",
|
|
32511
32888
|
"Wait until the chat 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. taskState is the A2A-style lifecycle: submitted (queued, not started), working, input-required (ask_user), completed, failed, canceled. stillRunning is true only for submitted/working. blockedReason is why the child is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. If stillRunning, text and usage are empty (they would be the previous turn) \u2014 read progress and call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts). On failed, lastError/text is the failure. On canceled, the child did not finish \u2014 resume with send_to_chat or tell the user. On input-required, wait for the user in that chat. When finished, text is this turn\u2019s assistant reply and usage is that turn\u2019s tokens + costUsd (this turn, not the Claude session total; sessionCostUsd is the provider session total when present).",
|
|
32512
32889
|
{
|
|
32513
|
-
ref:
|
|
32514
|
-
timeoutMs:
|
|
32890
|
+
ref: import_zod10.z.string(),
|
|
32891
|
+
timeoutMs: import_zod10.z.number().optional()
|
|
32515
32892
|
},
|
|
32516
32893
|
async ({ ref, timeoutMs }) => {
|
|
32517
32894
|
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
@@ -32539,7 +32916,7 @@ async function startMcpServer() {
|
|
|
32539
32916
|
server.tool(
|
|
32540
32917
|
"get_turn_result",
|
|
32541
32918
|
"Assistant message and last-turn usage when the turn finished. While stillRunning (submitted/working), text and usage are empty so a previous reply cannot be mistaken for this turn \u2014 use progress for tools/thinking. Not the full transcript. Includes taskState (A2A lifecycle) and blockedReason (why the agent is waiting on a person).",
|
|
32542
|
-
{ ref:
|
|
32919
|
+
{ ref: import_zod10.z.string() },
|
|
32543
32920
|
async ({ ref }) => {
|
|
32544
32921
|
const result = orch.getTurnResult(ref);
|
|
32545
32922
|
const hint = mcpWaitTaskHint(result.taskState, result.status, result.blockedReason);
|
|
@@ -32555,9 +32932,9 @@ async function startMcpServer() {
|
|
|
32555
32932
|
"stop_chat",
|
|
32556
32933
|
"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. Pass reason \u2014 a short note on why this chat is stopping. It is stored on the thread and kept when the turn unwinds. If you omit it, Sideboard still records that the queue was cleared.",
|
|
32557
32934
|
{
|
|
32558
|
-
ref:
|
|
32559
|
-
force:
|
|
32560
|
-
reason:
|
|
32935
|
+
ref: import_zod10.z.string(),
|
|
32936
|
+
force: import_zod10.z.boolean().optional(),
|
|
32937
|
+
reason: import_zod10.z.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
|
|
32561
32938
|
},
|
|
32562
32939
|
async ({ ref, force, reason }) => {
|
|
32563
32940
|
const t = orch.getThread(ref);
|
|
@@ -32581,7 +32958,7 @@ async function startMcpServer() {
|
|
|
32581
32958
|
server.tool(
|
|
32582
32959
|
"archive_chat",
|
|
32583
32960
|
"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.",
|
|
32584
|
-
{ ref:
|
|
32961
|
+
{ ref: import_zod10.z.string() },
|
|
32585
32962
|
async ({ ref }) => {
|
|
32586
32963
|
const t = orch.getThread(ref);
|
|
32587
32964
|
if (!t) {
|
|
@@ -32607,7 +32984,7 @@ async function startMcpServer() {
|
|
|
32607
32984
|
server.tool(
|
|
32608
32985
|
"restore_chat",
|
|
32609
32986
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
32610
|
-
{ ref:
|
|
32987
|
+
{ ref: import_zod10.z.string() },
|
|
32611
32988
|
async ({ ref }) => {
|
|
32612
32989
|
try {
|
|
32613
32990
|
const restored = await orch.restore(ref);
|
|
@@ -32626,8 +33003,8 @@ async function startMcpServer() {
|
|
|
32626
33003
|
"get_diff",
|
|
32627
33004
|
"Compact diff summary (capped hunks, paginated)",
|
|
32628
33005
|
{
|
|
32629
|
-
ref:
|
|
32630
|
-
maxFiles:
|
|
33006
|
+
ref: import_zod10.z.string(),
|
|
33007
|
+
maxFiles: import_zod10.z.number().optional()
|
|
32631
33008
|
},
|
|
32632
33009
|
async ({ ref, maxFiles }) => {
|
|
32633
33010
|
const summary = await orch.diffSummary(ref);
|
|
@@ -32640,7 +33017,7 @@ async function startMcpServer() {
|
|
|
32640
33017
|
server.tool(
|
|
32641
33018
|
"get_pr_checks",
|
|
32642
33019
|
"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.",
|
|
32643
|
-
{ ref:
|
|
33020
|
+
{ ref: import_zod10.z.string().describe("Worktree thread id/ref") },
|
|
32644
33021
|
async ({ ref }) => {
|
|
32645
33022
|
try {
|
|
32646
33023
|
const checks = await orch.getPrChecks(ref);
|
|
@@ -32654,7 +33031,7 @@ async function startMcpServer() {
|
|
|
32654
33031
|
server.tool(
|
|
32655
33032
|
"request_review",
|
|
32656
33033
|
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). If that worktree has a single unused idle agent (no messages), runs the review there; otherwise 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 in that chat. The review stays there so the user can read it and type next steps \u2014 do not comment on or update the PR or ticket, and do not ask_user after the review, until they ask. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review thread id.',
|
|
32657
|
-
{ ref:
|
|
33034
|
+
{ ref: import_zod10.z.string().describe("Worktree thread id/ref to review") },
|
|
32658
33035
|
async ({ ref }) => {
|
|
32659
33036
|
try {
|
|
32660
33037
|
const tab = await orch.requestReview(ref);
|
|
@@ -32676,8 +33053,8 @@ async function startMcpServer() {
|
|
|
32676
33053
|
"ask_git",
|
|
32677
33054
|
"Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (including Resolve). Always steers the worktree agent; does not push or open the PR locally. 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=ready-for-review and action=merge only when the user explicitly asked (or clicked the desktop button). Do not run git or gh from the orchestration cwd.",
|
|
32678
33055
|
{
|
|
32679
|
-
ref:
|
|
32680
|
-
action:
|
|
33056
|
+
ref: import_zod10.z.string().describe("Worktree thread id/ref"),
|
|
33057
|
+
action: import_zod10.z.enum(AGENT_GIT_ACTIONS).describe(
|
|
32681
33058
|
"commit-push | create-draft | create-web | resolve-conflicts | ready-for-review | merge"
|
|
32682
33059
|
)
|
|
32683
33060
|
},
|
|
@@ -32706,7 +33083,7 @@ async function startMcpServer() {
|
|
|
32706
33083
|
}
|
|
32707
33084
|
}
|
|
32708
33085
|
);
|
|
32709
|
-
const agentEnum =
|
|
33086
|
+
const agentEnum = import_zod10.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
32710
33087
|
server.tool(
|
|
32711
33088
|
"list_models",
|
|
32712
33089
|
"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.",
|
|
@@ -32727,11 +33104,11 @@ async function startMcpServer() {
|
|
|
32727
33104
|
"fork_workspace",
|
|
32728
33105
|
"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_chat / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
32729
33106
|
{
|
|
32730
|
-
ref:
|
|
32731
|
-
through_index:
|
|
33107
|
+
ref: import_zod10.z.string().describe("Worktree thread id/ref to fork"),
|
|
33108
|
+
through_index: import_zod10.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
32732
33109
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
32733
|
-
model:
|
|
32734
|
-
title:
|
|
33110
|
+
model: import_zod10.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
33111
|
+
title: import_zod10.z.string().optional()
|
|
32735
33112
|
},
|
|
32736
33113
|
async ({ ref, through_index, agent, model, title }) => {
|
|
32737
33114
|
try {
|
|
@@ -32765,11 +33142,11 @@ async function startMcpServer() {
|
|
|
32765
33142
|
"fork_chat",
|
|
32766
33143
|
"Add another agent chat on the SAME workspace (same files, Run, and git). Also forks a Global orchestration chat into a new orchestration tab. Pass agent when the new chat should use a different harness. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Use this for a second agent on a checkout (review, another harness, parallel work on the same branch). Do not create_workspace again for that \u2014 it returns the existing checkout. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id. Use fork_workspace only when the work needs its own git checkout.",
|
|
32767
33144
|
{
|
|
32768
|
-
ref:
|
|
32769
|
-
through_index:
|
|
33145
|
+
ref: import_zod10.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
33146
|
+
through_index: import_zod10.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
32770
33147
|
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
32771
|
-
model:
|
|
32772
|
-
title:
|
|
33148
|
+
model: import_zod10.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
33149
|
+
title: import_zod10.z.string().optional()
|
|
32773
33150
|
},
|
|
32774
33151
|
async ({ ref, through_index, agent, model, title }) => {
|
|
32775
33152
|
try {
|
|
@@ -32807,7 +33184,7 @@ async function startMcpServer() {
|
|
|
32807
33184
|
server.tool(
|
|
32808
33185
|
"run_setup",
|
|
32809
33186
|
"Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
|
|
32810
|
-
{ ref:
|
|
33187
|
+
{ ref: import_zod10.z.string() },
|
|
32811
33188
|
async ({ ref }) => {
|
|
32812
33189
|
const result = await orch.runSetup(ref);
|
|
32813
33190
|
return mcpJson(result);
|
|
@@ -32816,7 +33193,7 @@ async function startMcpServer() {
|
|
|
32816
33193
|
server.tool(
|
|
32817
33194
|
"add_project",
|
|
32818
33195
|
"Register a git repo as a Sideboard workspace",
|
|
32819
|
-
{ repoPath:
|
|
33196
|
+
{ repoPath: import_zod10.z.string() },
|
|
32820
33197
|
async ({ repoPath }) => {
|
|
32821
33198
|
const ws = await orch.addWorkspace(repoPath);
|
|
32822
33199
|
return mcpJson(ws);
|
|
@@ -32825,7 +33202,7 @@ async function startMcpServer() {
|
|
|
32825
33202
|
server.tool(
|
|
32826
33203
|
"remove_project",
|
|
32827
33204
|
"Unregister a Sideboard workspace and archive its chats",
|
|
32828
|
-
{ repoPath:
|
|
33205
|
+
{ repoPath: import_zod10.z.string() },
|
|
32829
33206
|
async ({ repoPath }) => {
|
|
32830
33207
|
await orch.removeWorkspace(repoPath);
|
|
32831
33208
|
return { content: [{ type: "text", text: "ok" }] };
|
|
@@ -32835,12 +33212,12 @@ async function startMcpServer() {
|
|
|
32835
33212
|
"fanout",
|
|
32836
33213
|
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
32837
33214
|
{
|
|
32838
|
-
prompt:
|
|
32839
|
-
agents:
|
|
32840
|
-
repoPath:
|
|
32841
|
-
sourceType:
|
|
32842
|
-
sourceRef:
|
|
32843
|
-
title:
|
|
33215
|
+
prompt: import_zod10.z.string(),
|
|
33216
|
+
agents: import_zod10.z.array(import_zod10.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
33217
|
+
repoPath: import_zod10.z.string(),
|
|
33218
|
+
sourceType: import_zod10.z.enum(["branch", "pr", "ticket"]).optional(),
|
|
33219
|
+
sourceRef: import_zod10.z.string().optional(),
|
|
33220
|
+
title: import_zod10.z.string().optional()
|
|
32844
33221
|
},
|
|
32845
33222
|
async (args) => {
|
|
32846
33223
|
const threads = await orch.bestOfN(args);
|
|
@@ -32858,8 +33235,8 @@ async function startMcpServer() {
|
|
|
32858
33235
|
"list_branches",
|
|
32859
33236
|
"List git branches in a registered workspace. Pass repoPath from list_projects (unmerged into the default branch by default \u2014 for create_workspace sourceType=branch).",
|
|
32860
33237
|
{
|
|
32861
|
-
repoPath:
|
|
32862
|
-
unmergedOnly:
|
|
33238
|
+
repoPath: import_zod10.z.string(),
|
|
33239
|
+
unmergedOnly: import_zod10.z.boolean().optional()
|
|
32863
33240
|
},
|
|
32864
33241
|
async ({ repoPath, unmergedOnly }) => {
|
|
32865
33242
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -32880,19 +33257,19 @@ async function startMcpServer() {
|
|
|
32880
33257
|
"list_prs",
|
|
32881
33258
|
`List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_projects. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default 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_workspace with sourceType=pr.`,
|
|
32882
33259
|
{
|
|
32883
|
-
repoPath:
|
|
32884
|
-
query:
|
|
32885
|
-
queue:
|
|
33260
|
+
repoPath: import_zod10.z.string(),
|
|
33261
|
+
query: import_zod10.z.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
33262
|
+
queue: import_zod10.z.enum(["review", "mine", "approved", "changes"]).optional().describe(
|
|
32886
33263
|
"review = unclaimed ready-for-review inbox (Settings \u2192 Projects label, default eng-review). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes."
|
|
32887
33264
|
),
|
|
32888
|
-
state:
|
|
32889
|
-
label:
|
|
33265
|
+
state: import_zod10.z.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
|
|
33266
|
+
label: import_zod10.z.string().optional().describe(
|
|
32890
33267
|
"GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
|
|
32891
33268
|
),
|
|
32892
|
-
reviewer:
|
|
33269
|
+
reviewer: import_zod10.z.string().optional().describe(
|
|
32893
33270
|
"me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
|
|
32894
33271
|
),
|
|
32895
|
-
limit:
|
|
33272
|
+
limit: import_zod10.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
|
|
32896
33273
|
},
|
|
32897
33274
|
async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
|
|
32898
33275
|
const root = await resolveRepoRoot(repoPath);
|
|
@@ -32925,7 +33302,7 @@ async function startMcpServer() {
|
|
|
32925
33302
|
server.tool(
|
|
32926
33303
|
"get_pr_stack",
|
|
32927
33304
|
"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).",
|
|
32928
|
-
{ ref:
|
|
33305
|
+
{ ref: import_zod10.z.string() },
|
|
32929
33306
|
async ({ ref }) => {
|
|
32930
33307
|
const stack = await orch.getPrStack(ref);
|
|
32931
33308
|
return mcpJson(stack);
|
|
@@ -32935,8 +33312,8 @@ async function startMcpServer() {
|
|
|
32935
33312
|
"open_pr_stack_layers",
|
|
32936
33313
|
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
32937
33314
|
{
|
|
32938
|
-
ref:
|
|
32939
|
-
layer:
|
|
33315
|
+
ref: import_zod10.z.string(),
|
|
33316
|
+
layer: import_zod10.z.number().int().positive().optional()
|
|
32940
33317
|
},
|
|
32941
33318
|
async ({ ref, layer }) => {
|
|
32942
33319
|
const result = await orch.openPrStackLayers(ref, { layer });
|
|
@@ -32959,9 +33336,9 @@ async function startMcpServer() {
|
|
|
32959
33336
|
"add_stack_layer",
|
|
32960
33337
|
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
32961
33338
|
{
|
|
32962
|
-
ref:
|
|
32963
|
-
branchName:
|
|
32964
|
-
title:
|
|
33339
|
+
ref: import_zod10.z.string(),
|
|
33340
|
+
branchName: import_zod10.z.string(),
|
|
33341
|
+
title: import_zod10.z.string().optional()
|
|
32965
33342
|
},
|
|
32966
33343
|
async ({ ref, branchName, title }) => {
|
|
32967
33344
|
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
@@ -32979,11 +33356,11 @@ async function startMcpServer() {
|
|
|
32979
33356
|
"create_pr_stack",
|
|
32980
33357
|
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
32981
33358
|
{
|
|
32982
|
-
repoPath:
|
|
32983
|
-
branches:
|
|
32984
|
-
agent:
|
|
32985
|
-
base:
|
|
32986
|
-
title:
|
|
33359
|
+
repoPath: import_zod10.z.string(),
|
|
33360
|
+
branches: import_zod10.z.array(import_zod10.z.string()).min(1),
|
|
33361
|
+
agent: import_zod10.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
33362
|
+
base: import_zod10.z.string().optional(),
|
|
33363
|
+
title: import_zod10.z.string().optional()
|
|
32987
33364
|
},
|
|
32988
33365
|
async (args) => {
|
|
32989
33366
|
const result = await orch.createPrStack({
|
|
@@ -33011,10 +33388,10 @@ async function startMcpServer() {
|
|
|
33011
33388
|
"list_issues",
|
|
33012
33389
|
"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. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
|
|
33013
33390
|
{
|
|
33014
|
-
repoPath:
|
|
33015
|
-
query:
|
|
33016
|
-
assignee:
|
|
33017
|
-
limit:
|
|
33391
|
+
repoPath: import_zod10.z.string(),
|
|
33392
|
+
query: import_zod10.z.string().optional().describe("Search title, identifier, or description"),
|
|
33393
|
+
assignee: import_zod10.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
|
|
33394
|
+
limit: import_zod10.z.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
|
|
33018
33395
|
updatedSince: mcpIssueUpdatedSinceSchema
|
|
33019
33396
|
},
|
|
33020
33397
|
async ({ repoPath, query, assignee, limit, updatedSince }) => {
|