@sideboard-ai/core 0.1.62 → 0.1.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-KF3J3C53.js → agents-OTBZVTZO.js} +27 -7
- package/dist/{agents-QLSFIQAO.js → agents-TS3EI2YI.js} +27 -7
- package/dist/{app-settings-LTSFG7QJ.js → app-settings-GJVZGU3M.js} +13 -3
- package/dist/{app-settings-HK4KAM4R.js → app-settings-ONKMFSEJ.js} +13 -3
- package/dist/{chunk-UGXSSBFJ.js → chunk-4BUCGNZS.js} +1 -1
- package/dist/chunk-4UIYN56C.js +173 -0
- package/dist/{chunk-NVU7EC2E.js → chunk-57WWNMFP.js} +2 -2
- package/dist/{chunk-A6FOKNOU.js → chunk-5HTTCYWA.js} +2 -2
- package/dist/{chunk-DXTGDXO2.js → chunk-7ZOQY4A7.js} +31 -6
- package/dist/{chunk-ZF5YJCXO.js → chunk-B4PRRKMA.js} +284 -93
- package/dist/{chunk-DDN3UF4R.js → chunk-BPOTVI5J.js} +115 -3
- package/dist/{chunk-MBAL5FE6.js → chunk-DB3I75FI.js} +4 -4
- package/dist/chunk-FGDJOJ2L.js +176 -0
- package/dist/{chunk-2FEA4LDA.js → chunk-JWZNK3CQ.js} +31 -6
- package/dist/{chunk-2MCNURQV.js → chunk-K7LM4CLD.js} +279 -93
- package/dist/{chunk-3KKNTHUC.js → chunk-LW5CMQGZ.js} +1 -1
- package/dist/{chunk-JB2H7E7V.js → chunk-M5V4QIWF.js} +1 -1
- package/dist/{chunk-6CUJQKTJ.js → chunk-MHR4OGZ4.js} +115 -3
- package/dist/{chunk-46KMKHSD.js → chunk-WAFIF22N.js} +2 -2
- package/dist/{chunk-27MAWNKY.js → chunk-ZRFAZVGL.js} +2 -2
- package/dist/{connected-teams-MKIYKOYA.js → connected-teams-OMDHTL6A.js} +2 -2
- package/dist/{connected-teams-RSL4VB3X.js → connected-teams-RUNMOCG5.js} +2 -2
- package/dist/{coordinator-prompt-N2R6FGZK.js → coordinator-prompt-3BSMKZL4.js} +4 -4
- package/dist/{coordinator-prompt-LKG3SHMI.js → coordinator-prompt-6HMTOX4G.js} +4 -4
- package/dist/{global-workspace-LALWQWRJ.js → global-workspace-QS7A23CB.js} +5 -6
- package/dist/{global-workspace-YOAUQCMA.js → global-workspace-YZ2NJVLL.js} +5 -6
- package/dist/index.cjs +1345 -933
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +750 -715
- package/dist/mcp/run-stdio.cjs +1293 -977
- package/dist/mcp/run-stdio.js +691 -748
- package/dist/{run-GPSVV5OH.js → run-JLD2Y52J.js} +1 -1
- package/dist/{run-HZTBWGQR.js → run-Y2MI3EVV.js} +1 -1
- package/dist/{workspaces-E75POJXP.js → workspaces-AIUYUXB7.js} +6 -7
- package/dist/{workspaces-VCAIHVIE.js → workspaces-EBFW7I7S.js} +6 -7
- package/dist/{worktree-MV4WRW6G.js → worktree-7NBPIVFZ.js} +2 -2
- package/dist/{worktree-LZEGVQSN.js → worktree-WMDJW2BL.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-73AT5K4A.js +0 -104
- package/dist/chunk-GI7E5733.js +0 -30
- package/dist/chunk-RIWUU7FO.js +0 -107
- package/dist/chunk-S2LL5HA4.js +0 -33
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -14,15 +14,16 @@ import {
|
|
|
14
14
|
parseSessionQuotaResetAt,
|
|
15
15
|
pushTurnStderr,
|
|
16
16
|
resolveQuotaFallbackAgent,
|
|
17
|
+
sideboardMcpProfile,
|
|
17
18
|
summarizeTurnStderr
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import "../chunk-
|
|
19
|
+
} from "../chunk-K7LM4CLD.js";
|
|
20
|
+
import "../chunk-M5V4QIWF.js";
|
|
20
21
|
import {
|
|
21
22
|
addWorkspace,
|
|
22
23
|
ensureWorkspace,
|
|
23
24
|
removeWorkspace,
|
|
24
25
|
syncWorkspacesFromThreads
|
|
25
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-5HTTCYWA.js";
|
|
26
27
|
import {
|
|
27
28
|
extractPresentedPlan,
|
|
28
29
|
readPlanFile,
|
|
@@ -30,22 +31,20 @@ import {
|
|
|
30
31
|
} from "../chunk-N5PM7HGQ.js";
|
|
31
32
|
import {
|
|
32
33
|
GLOBAL_WORKSPACE_ID,
|
|
34
|
+
assertOrchestratorCapableAgent,
|
|
33
35
|
healOrchestrationSoccerTitles,
|
|
34
36
|
isCloudCoordinatorThread,
|
|
35
37
|
isGlobalRepoPath,
|
|
36
38
|
isGlobalThread,
|
|
37
39
|
isOrchestratorThread,
|
|
38
40
|
orchestratorSessionPoisonedByBuiltins
|
|
39
|
-
} from "../chunk-
|
|
40
|
-
import {
|
|
41
|
-
assertOrchestratorCapableAgent
|
|
42
|
-
} from "../chunk-S2LL5HA4.js";
|
|
41
|
+
} from "../chunk-JWZNK3CQ.js";
|
|
43
42
|
import {
|
|
44
43
|
coordinatorSystemPrompt,
|
|
45
44
|
coordinatorTurnReminder,
|
|
46
45
|
enrichWorkspacesWithGithub,
|
|
47
46
|
ensureGlobalCoordinatorCwd
|
|
48
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-57WWNMFP.js";
|
|
49
48
|
import {
|
|
50
49
|
addPrStackLayer,
|
|
51
50
|
allocateTeamName,
|
|
@@ -82,7 +81,7 @@ import {
|
|
|
82
81
|
takenSlugsFromThread,
|
|
83
82
|
threadDisplayLabel,
|
|
84
83
|
worktreeNameFromPath
|
|
85
|
-
} from "../chunk-
|
|
84
|
+
} from "../chunk-LW5CMQGZ.js";
|
|
86
85
|
import {
|
|
87
86
|
ATTACHMENTS_DIR,
|
|
88
87
|
LEGACY_ATTACHMENTS_DIR,
|
|
@@ -101,11 +100,12 @@ import {
|
|
|
101
100
|
writeThread
|
|
102
101
|
} from "../chunk-7FD7COKE.js";
|
|
103
102
|
import {
|
|
103
|
+
enrichPathWithNpmGlobalBin,
|
|
104
104
|
ensureAgentPath,
|
|
105
105
|
gh,
|
|
106
106
|
git,
|
|
107
107
|
run
|
|
108
|
-
} from "../chunk-
|
|
108
|
+
} from "../chunk-FGDJOJ2L.js";
|
|
109
109
|
import {
|
|
110
110
|
childEnvWithAppSettings,
|
|
111
111
|
getLinearApiKey,
|
|
@@ -116,7 +116,7 @@ import {
|
|
|
116
116
|
resolveEffectiveIssueSource,
|
|
117
117
|
resolveNewThreadOptions,
|
|
118
118
|
updateAdvancedSettings
|
|
119
|
-
} from "../chunk-
|
|
119
|
+
} from "../chunk-BPOTVI5J.js";
|
|
120
120
|
import "../chunk-KGZBYWZ3.js";
|
|
121
121
|
import {
|
|
122
122
|
loadRepoSettings,
|
|
@@ -391,9 +391,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
391
391
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
392
392
|
);
|
|
393
393
|
}
|
|
394
|
-
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-
|
|
394
|
+
const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-YZ2NJVLL.js");
|
|
395
395
|
if (isGlobalThread2(thread)) {
|
|
396
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-
|
|
396
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-6HMTOX4G.js");
|
|
397
397
|
ensureGlobalCoordinatorCwd2(
|
|
398
398
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
399
399
|
);
|
|
@@ -1117,6 +1117,7 @@ import { existsSync as existsSync5 } from "fs";
|
|
|
1117
1117
|
var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
|
|
1118
1118
|
async function requireAgent(agent, opts) {
|
|
1119
1119
|
ensureAgentPath();
|
|
1120
|
+
enrichPathWithNpmGlobalBin();
|
|
1120
1121
|
const status = await Promise.race([
|
|
1121
1122
|
getAdapter(agent).detect(),
|
|
1122
1123
|
new Promise((_, reject) => {
|
|
@@ -1208,7 +1209,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
1208
1209
|
return readThread(thread.id) ?? thread;
|
|
1209
1210
|
}
|
|
1210
1211
|
async function listLinearIssues(agent, repoPath) {
|
|
1211
|
-
const { getAdapter: getAdapter2 } = await import("../agents-
|
|
1212
|
+
const { getAdapter: getAdapter2 } = await import("../agents-OTBZVTZO.js");
|
|
1212
1213
|
await requireAgent(agent, { requireLinear: true });
|
|
1213
1214
|
const adapter = getAdapter2(agent);
|
|
1214
1215
|
if (!adapter.listLinearIssues) {
|
|
@@ -2057,7 +2058,7 @@ async function adoptThread(input) {
|
|
|
2057
2058
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
2058
2059
|
}
|
|
2059
2060
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
2060
|
-
const { stdout: branch } = await import("../run-
|
|
2061
|
+
const { stdout: branch } = await import("../run-Y2MI3EVV.js").then(
|
|
2061
2062
|
({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
|
|
2062
2063
|
);
|
|
2063
2064
|
const thread = createEmptyThread({
|
|
@@ -2072,7 +2073,7 @@ async function adoptThread(input) {
|
|
|
2072
2073
|
messages: input.messages ?? []
|
|
2073
2074
|
});
|
|
2074
2075
|
writeThread(thread);
|
|
2075
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
2076
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-AIUYUXB7.js");
|
|
2076
2077
|
await ensureWorkspace2(repoPath);
|
|
2077
2078
|
return thread;
|
|
2078
2079
|
}
|
|
@@ -3178,7 +3179,7 @@ async function previewLand(thread) {
|
|
|
3178
3179
|
const target = await resolveDefaultBranch(thread.repoPath);
|
|
3179
3180
|
if (thread.branchName === target || thread.branchName === "main" || thread.branchName === "master") {
|
|
3180
3181
|
}
|
|
3181
|
-
const { stdout: head } = await import("../run-
|
|
3182
|
+
const { stdout: head } = await import("../run-Y2MI3EVV.js").then(
|
|
3182
3183
|
({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], thread.worktreePath)
|
|
3183
3184
|
);
|
|
3184
3185
|
const current = head.trim();
|
|
@@ -3217,7 +3218,7 @@ async function confirmLand(thread, opts) {
|
|
|
3217
3218
|
if (preview.dirty) {
|
|
3218
3219
|
committed = await commitAll(thread.worktreePath, meta.commitMessage);
|
|
3219
3220
|
}
|
|
3220
|
-
const { git: git2 } = await import("../run-
|
|
3221
|
+
const { git: git2 } = await import("../run-Y2MI3EVV.js");
|
|
3221
3222
|
const { stdout: headOut } = await git2(
|
|
3222
3223
|
["rev-parse", "--abbrev-ref", "HEAD"],
|
|
3223
3224
|
thread.worktreePath,
|
|
@@ -3754,6 +3755,9 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
3754
3755
|
);
|
|
3755
3756
|
return lines.join("\n");
|
|
3756
3757
|
}
|
|
3758
|
+
function formatWorktreeReminder() {
|
|
3759
|
+
return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout.";
|
|
3760
|
+
}
|
|
3757
3761
|
function formatArtifactDirective() {
|
|
3758
3762
|
return [
|
|
3759
3763
|
"Sideboard side column (desktop UI):",
|
|
@@ -3774,60 +3778,6 @@ function formatArtifactDirective() {
|
|
|
3774
3778
|
"Never say artifacts, CMS UI, or the Files column are unavailable. Prefer present_schema for list/edit/publish; present_files for storage UI; html fences for standalone pages."
|
|
3775
3779
|
].join("\n");
|
|
3776
3780
|
}
|
|
3777
|
-
var FILES_BY_AGENT = {
|
|
3778
|
-
claude: [
|
|
3779
|
-
"CLAUDE.md",
|
|
3780
|
-
".claude/CLAUDE.md",
|
|
3781
|
-
"CLAUDE.local.md",
|
|
3782
|
-
"AGENTS.md",
|
|
3783
|
-
"AGENT.md"
|
|
3784
|
-
],
|
|
3785
|
-
codex: ["AGENTS.md", "AGENT.md", ".codex/AGENTS.md", "CLAUDE.md"],
|
|
3786
|
-
opencode: ["AGENTS.md", "AGENT.md", "OPENCODE.md", "CLAUDE.md"],
|
|
3787
|
-
brightsy: ["AGENTS.md", "AGENT.md", "CLAUDE.md"],
|
|
3788
|
-
cursor: ["AGENTS.md", "AGENT.md", ".cursor/rules", "CLAUDE.md"]
|
|
3789
|
-
};
|
|
3790
|
-
var MAX_CHARS_PER_FILE = 48e3;
|
|
3791
|
-
function loadAgentInstructions(worktreePath, agent) {
|
|
3792
|
-
const candidates = FILES_BY_AGENT[agent] ?? FILES_BY_AGENT.claude;
|
|
3793
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3794
|
-
const out = [];
|
|
3795
|
-
for (const rel of candidates) {
|
|
3796
|
-
if (seen.has(rel)) continue;
|
|
3797
|
-
const abs = join10(worktreePath, rel);
|
|
3798
|
-
if (!existsSync12(abs)) continue;
|
|
3799
|
-
try {
|
|
3800
|
-
if (!statSync5(abs).isFile()) continue;
|
|
3801
|
-
let content = readFileSync8(abs, "utf8");
|
|
3802
|
-
if (!content.trim()) continue;
|
|
3803
|
-
if (content.length > MAX_CHARS_PER_FILE) {
|
|
3804
|
-
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
3805
|
-
|
|
3806
|
-
\u2026(truncated)`;
|
|
3807
|
-
}
|
|
3808
|
-
seen.add(rel);
|
|
3809
|
-
out.push({ relativePath: rel, content });
|
|
3810
|
-
} catch {
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3813
|
-
return out;
|
|
3814
|
-
}
|
|
3815
|
-
function formatAgentInstructions(files) {
|
|
3816
|
-
if (files.length === 0) return null;
|
|
3817
|
-
const parts = [
|
|
3818
|
-
"Project agent instructions (from the worktree \u2014 follow these):",
|
|
3819
|
-
""
|
|
3820
|
-
];
|
|
3821
|
-
for (const f of files) {
|
|
3822
|
-
parts.push(`## ${f.relativePath}`);
|
|
3823
|
-
parts.push("");
|
|
3824
|
-
parts.push(f.content.trim());
|
|
3825
|
-
parts.push("");
|
|
3826
|
-
parts.push("---");
|
|
3827
|
-
parts.push("");
|
|
3828
|
-
}
|
|
3829
|
-
return parts.join("\n").trimEnd();
|
|
3830
|
-
}
|
|
3831
3781
|
|
|
3832
3782
|
// src/threads/sync-branch.ts
|
|
3833
3783
|
async function syncThreadBranchFromGit(threadId) {
|
|
@@ -3960,7 +3910,7 @@ var Orchestrator = class {
|
|
|
3960
3910
|
orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
|
|
3961
3911
|
});
|
|
3962
3912
|
}
|
|
3963
|
-
const { autoCleanupOrphansEnabled } = await import("../app-settings-
|
|
3913
|
+
const { autoCleanupOrphansEnabled } = await import("../app-settings-GJVZGU3M.js");
|
|
3964
3914
|
if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
|
|
3965
3915
|
await cleanupOrphanWorktrees({ repoPaths });
|
|
3966
3916
|
}
|
|
@@ -4117,7 +4067,7 @@ var Orchestrator = class {
|
|
|
4117
4067
|
}
|
|
4118
4068
|
async finishCreateThread(threadId, prompt) {
|
|
4119
4069
|
await this.runSetupAfterCreate(threadId);
|
|
4120
|
-
const { autoRunAfterSetupEnabled } = await import("../app-settings-
|
|
4070
|
+
const { autoRunAfterSetupEnabled } = await import("../app-settings-GJVZGU3M.js");
|
|
4121
4071
|
if (autoRunAfterSetupEnabled()) {
|
|
4122
4072
|
try {
|
|
4123
4073
|
await this.startDev(threadId);
|
|
@@ -4358,13 +4308,14 @@ var Orchestrator = class {
|
|
|
4358
4308
|
"Files column: MCP present_files (brightsy account storage or memory).",
|
|
4359
4309
|
"Do not say artifacts/CMS UI are unavailable."
|
|
4360
4310
|
].join(" ") : null;
|
|
4311
|
+
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
4361
4312
|
const agentPrompt = [
|
|
4362
4313
|
thread.planMode ? PLAN_MODE_INSTRUCTION : null,
|
|
4363
4314
|
orchestrationReminder,
|
|
4315
|
+
worktreeReminder,
|
|
4364
4316
|
artifactReminder,
|
|
4365
4317
|
expandedPrompt
|
|
4366
4318
|
].filter(Boolean).join("\n\n");
|
|
4367
|
-
const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
|
|
4368
4319
|
if (thread.attachments.length > 0) {
|
|
4369
4320
|
updateThread(threadId, { attachments: [] });
|
|
4370
4321
|
}
|
|
@@ -4388,11 +4339,10 @@ var Orchestrator = class {
|
|
|
4388
4339
|
});
|
|
4389
4340
|
const artifactDirective = isBrightsy ? null : formatArtifactDirective();
|
|
4390
4341
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
4391
|
-
const { autoRenameBranchEnabled } = await import("../app-settings-
|
|
4342
|
+
const { autoRenameBranchEnabled } = await import("../app-settings-GJVZGU3M.js");
|
|
4392
4343
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
|
|
4393
4344
|
customPrompt: settings?.prompts?.renameBranch
|
|
4394
4345
|
}) : null;
|
|
4395
|
-
const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
|
|
4396
4346
|
let seed = null;
|
|
4397
4347
|
if (!fresh.sessionId) {
|
|
4398
4348
|
const prior = fresh.messages.slice(0, -1);
|
|
@@ -4413,12 +4363,12 @@ var Orchestrator = class {
|
|
|
4413
4363
|
});
|
|
4414
4364
|
}
|
|
4415
4365
|
}
|
|
4416
|
-
const cachedPrefix = [
|
|
4366
|
+
const cachedPrefix = fresh.sessionId ? "" : [
|
|
4417
4367
|
coordinatorDirective,
|
|
4418
4368
|
worktreeDirective,
|
|
4419
4369
|
artifactDirective,
|
|
4420
4370
|
renameBranchDirective,
|
|
4421
|
-
|
|
4371
|
+
seed
|
|
4422
4372
|
].filter(Boolean).join("\n\n---\n\n");
|
|
4423
4373
|
try {
|
|
4424
4374
|
const stderrTail = [];
|
|
@@ -4505,15 +4455,11 @@ var Orchestrator = class {
|
|
|
4505
4455
|
const retryThread = this.requireThread(threadId);
|
|
4506
4456
|
const prior = retryThread.messages.slice(0, -1);
|
|
4507
4457
|
const retrySeed = buildSessionSeed(prior);
|
|
4508
|
-
const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
|
|
4509
|
-
loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
|
|
4510
|
-
);
|
|
4511
4458
|
const retryPrefix = [
|
|
4512
4459
|
coordinatorDirective,
|
|
4513
4460
|
worktreeDirective,
|
|
4514
4461
|
artifactDirective,
|
|
4515
4462
|
renameBranchDirective,
|
|
4516
|
-
retryInstructions,
|
|
4517
4463
|
retrySeed
|
|
4518
4464
|
].filter(Boolean).join("\n\n---\n\n");
|
|
4519
4465
|
const retryHandle = await spawnAgentTurn(
|
|
@@ -5083,7 +5029,7 @@ var Orchestrator = class {
|
|
|
5083
5029
|
updateThread(thread.id, { title: meta.title });
|
|
5084
5030
|
}
|
|
5085
5031
|
}
|
|
5086
|
-
const { autoArchiveOnMergeEnabled } = await import("../app-settings-
|
|
5032
|
+
const { autoArchiveOnMergeEnabled } = await import("../app-settings-GJVZGU3M.js");
|
|
5087
5033
|
const latest = this.requireThread(thread.id);
|
|
5088
5034
|
if (!shouldAutoArchiveOnPrMerge({
|
|
5089
5035
|
previousPrState: prevState || null,
|
|
@@ -5307,7 +5253,7 @@ var Orchestrator = class {
|
|
|
5307
5253
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
5308
5254
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
5309
5255
|
try {
|
|
5310
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-
|
|
5256
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-AIUYUXB7.js");
|
|
5311
5257
|
await ensureWorkspace2(thread.repoPath);
|
|
5312
5258
|
} catch {
|
|
5313
5259
|
}
|
|
@@ -5328,7 +5274,7 @@ var Orchestrator = class {
|
|
|
5328
5274
|
await runArchiveScript(thread.repoPath, thread.worktreePath);
|
|
5329
5275
|
} catch {
|
|
5330
5276
|
}
|
|
5331
|
-
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-
|
|
5277
|
+
const { deleteBranchOnPurgeEnabled } = await import("../app-settings-GJVZGU3M.js");
|
|
5332
5278
|
const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
|
|
5333
5279
|
await removeWorktree(thread.repoPath, thread.worktreePath, {
|
|
5334
5280
|
deleteBranch: deleteBranch ? thread.branchName : void 0
|
|
@@ -5349,7 +5295,7 @@ var Orchestrator = class {
|
|
|
5349
5295
|
return restored2;
|
|
5350
5296
|
}
|
|
5351
5297
|
if (!existsSync13(thread.worktreePath)) {
|
|
5352
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-
|
|
5298
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-WMDJW2BL.js");
|
|
5353
5299
|
const { execa: execa6 } = await import("execa");
|
|
5354
5300
|
const slug = thread.worktreePath.split("/").pop();
|
|
5355
5301
|
const dest = thread.worktreePath;
|
|
@@ -5565,7 +5511,7 @@ function withTimeout(promise, ms, label) {
|
|
|
5565
5511
|
async function startMcpServer() {
|
|
5566
5512
|
const orch = getOrchestrator();
|
|
5567
5513
|
try {
|
|
5568
|
-
const { maxConcurrentAgents } = await import("../app-settings-
|
|
5514
|
+
const { maxConcurrentAgents } = await import("../app-settings-GJVZGU3M.js");
|
|
5569
5515
|
orch.setMaxConcurrent(maxConcurrentAgents());
|
|
5570
5516
|
} catch {
|
|
5571
5517
|
}
|
|
@@ -5656,14 +5602,13 @@ async function startMcpServer() {
|
|
|
5656
5602
|
),
|
|
5657
5603
|
artifact_id: z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
5658
5604
|
},
|
|
5659
|
-
async ({ title, type,
|
|
5605
|
+
async ({ title, type, artifact_id }) => {
|
|
5660
5606
|
const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5661
5607
|
const payload = {
|
|
5662
5608
|
ok: true,
|
|
5663
5609
|
artifact_id: id,
|
|
5664
5610
|
title,
|
|
5665
5611
|
type,
|
|
5666
|
-
content,
|
|
5667
5612
|
message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
5668
5613
|
};
|
|
5669
5614
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -5716,7 +5661,6 @@ async function startMcpServer() {
|
|
|
5716
5661
|
ok: true,
|
|
5717
5662
|
path,
|
|
5718
5663
|
title: title?.trim() || "Plan",
|
|
5719
|
-
content,
|
|
5720
5664
|
message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
|
|
5721
5665
|
};
|
|
5722
5666
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -5746,9 +5690,6 @@ async function startMcpServer() {
|
|
|
5746
5690
|
datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
|
|
5747
5691
|
resource_id: args.resource_id,
|
|
5748
5692
|
record_id: args.record_id,
|
|
5749
|
-
resource: args.resource,
|
|
5750
|
-
record: args.record,
|
|
5751
|
-
records: args.records,
|
|
5752
5693
|
message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
|
|
5753
5694
|
};
|
|
5754
5695
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -5776,739 +5717,741 @@ async function startMcpServer() {
|
|
|
5776
5717
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
5777
5718
|
}
|
|
5778
5719
|
);
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
if (
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5720
|
+
if (sideboardMcpProfile() !== "worktree") {
|
|
5721
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults } = await import("../app-settings-GJVZGU3M.js");
|
|
5722
|
+
const accountDefaults = resolveThreadDefaults();
|
|
5723
|
+
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
5724
|
+
server.tool(
|
|
5725
|
+
"create_thread",
|
|
5726
|
+
`Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces. From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Then use send_to_thread to chat.`,
|
|
5727
|
+
{
|
|
5728
|
+
sourceType: z.enum(["branch", "pr", "ticket"]),
|
|
5729
|
+
sourceRef: z.string(),
|
|
5730
|
+
agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
|
|
5731
|
+
model: z.string().nullable().optional().describe(
|
|
5732
|
+
`Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
|
|
5733
|
+
),
|
|
5734
|
+
repoPath: z.string(),
|
|
5735
|
+
title: z.string().optional(),
|
|
5736
|
+
parentThreadId: z.string().optional().describe(
|
|
5737
|
+
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
5738
|
+
)
|
|
5739
|
+
},
|
|
5740
|
+
async (args) => {
|
|
5741
|
+
const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
|
|
5742
|
+
let parentId = args.parentThreadId?.trim() || "";
|
|
5743
|
+
let parent = parentId ? orch.getThread(parentId) : null;
|
|
5744
|
+
let parentCorrectedFrom;
|
|
5745
|
+
if (envParentId) {
|
|
5746
|
+
const envParent = orch.getThread(envParentId);
|
|
5747
|
+
if (envParent) {
|
|
5748
|
+
if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
|
|
5749
|
+
else if (!parentId) parentCorrectedFrom = void 0;
|
|
5750
|
+
parentId = envParentId;
|
|
5751
|
+
parent = envParent;
|
|
5752
|
+
}
|
|
5810
5753
|
}
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5754
|
+
if (parentId && !parent) {
|
|
5755
|
+
parentCorrectedFrom = parentId;
|
|
5756
|
+
parentId = "";
|
|
5757
|
+
parent = null;
|
|
5758
|
+
}
|
|
5759
|
+
if (parentId) {
|
|
5760
|
+
const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
|
|
5761
|
+
if (children.length >= MAX_ORCH_THREADS) {
|
|
5762
|
+
return {
|
|
5763
|
+
content: [
|
|
5764
|
+
{
|
|
5765
|
+
type: "text",
|
|
5766
|
+
text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
|
|
5767
|
+
}
|
|
5768
|
+
],
|
|
5769
|
+
isError: true
|
|
5770
|
+
};
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
let agentArg = args.agent;
|
|
5774
|
+
let agentCoercedFrom;
|
|
5775
|
+
const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
|
|
5776
|
+
if (resolvedProbe === "codex") {
|
|
5777
|
+
agentCoercedFrom = agentArg ?? "codex";
|
|
5778
|
+
const accountAgent = resolveNewThreadOptions2({}).agent;
|
|
5779
|
+
agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
|
|
5780
|
+
}
|
|
5781
|
+
const opts = resolveNewThreadOptions2({
|
|
5782
|
+
agent: agentArg,
|
|
5783
|
+
model: args.model
|
|
5784
|
+
});
|
|
5785
|
+
try {
|
|
5786
|
+
const thread = await withTimeout(
|
|
5787
|
+
orch.createThread({
|
|
5788
|
+
sourceType: args.sourceType,
|
|
5789
|
+
sourceRef: args.sourceRef,
|
|
5790
|
+
agent: opts.agent,
|
|
5791
|
+
model: opts.model,
|
|
5792
|
+
effort: opts.effort,
|
|
5793
|
+
fast: opts.fast,
|
|
5794
|
+
repoPath: args.repoPath,
|
|
5795
|
+
title: args.title,
|
|
5796
|
+
parentThreadId: parentId || null
|
|
5797
|
+
}),
|
|
5798
|
+
CREATE_THREAD_TIMEOUT_MS,
|
|
5799
|
+
"create_thread"
|
|
5800
|
+
);
|
|
5801
|
+
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
5802
|
+
return {
|
|
5803
|
+
content: [
|
|
5804
|
+
{
|
|
5805
|
+
type: "text",
|
|
5806
|
+
text: JSON.stringify({
|
|
5807
|
+
id: thread.id,
|
|
5808
|
+
title: thread.title,
|
|
5809
|
+
branchName: thread.branchName,
|
|
5810
|
+
worktreePath: thread.worktreePath,
|
|
5811
|
+
agent: thread.agent,
|
|
5812
|
+
model: thread.model,
|
|
5813
|
+
status: thread.status,
|
|
5814
|
+
link: `sideboard://thread/${thread.id}`,
|
|
5815
|
+
parentThreadId: thread.parentThreadId,
|
|
5816
|
+
...agentCoercedFrom ? {
|
|
5817
|
+
agentCoercedFrom,
|
|
5818
|
+
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
5819
|
+
} : {},
|
|
5820
|
+
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
5821
|
+
})
|
|
5822
|
+
}
|
|
5823
|
+
]
|
|
5824
|
+
};
|
|
5825
|
+
} catch (err) {
|
|
5826
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5820
5827
|
return {
|
|
5821
5828
|
content: [
|
|
5822
5829
|
{
|
|
5823
5830
|
type: "text",
|
|
5824
|
-
text: `
|
|
5831
|
+
text: `create_thread failed: ${message}`
|
|
5825
5832
|
}
|
|
5826
5833
|
],
|
|
5827
5834
|
isError: true
|
|
5828
5835
|
};
|
|
5829
5836
|
}
|
|
5830
5837
|
}
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
agent: opts.agent,
|
|
5849
|
-
model: opts.model,
|
|
5850
|
-
effort: opts.effort,
|
|
5851
|
-
fast: opts.fast,
|
|
5852
|
-
repoPath: args.repoPath,
|
|
5853
|
-
title: args.title,
|
|
5854
|
-
parentThreadId: parentId || null
|
|
5855
|
-
}),
|
|
5856
|
-
CREATE_THREAD_TIMEOUT_MS,
|
|
5857
|
-
"create_thread"
|
|
5858
|
-
);
|
|
5859
|
-
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
5838
|
+
);
|
|
5839
|
+
server.tool(
|
|
5840
|
+
"send_to_thread",
|
|
5841
|
+
"Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.",
|
|
5842
|
+
{
|
|
5843
|
+
ref: z.string(),
|
|
5844
|
+
prompt: z.string(),
|
|
5845
|
+
force_stop: z.boolean().optional()
|
|
5846
|
+
},
|
|
5847
|
+
async ({ ref, prompt, force_stop }) => {
|
|
5848
|
+
if (force_stop) {
|
|
5849
|
+
const existing = orch.getThread(ref);
|
|
5850
|
+
if (existing) {
|
|
5851
|
+
orch.stop(ref, { clearQueue: true });
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
const thread = await orch.send(ref, prompt);
|
|
5860
5855
|
return {
|
|
5861
5856
|
content: [
|
|
5862
5857
|
{
|
|
5863
5858
|
type: "text",
|
|
5864
5859
|
text: JSON.stringify({
|
|
5865
5860
|
id: thread.id,
|
|
5866
|
-
title: thread.title,
|
|
5867
|
-
branchName: thread.branchName,
|
|
5868
|
-
worktreePath: thread.worktreePath,
|
|
5869
|
-
agent: thread.agent,
|
|
5870
|
-
model: thread.model,
|
|
5871
5861
|
status: thread.status,
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
...agentCoercedFrom ? {
|
|
5875
|
-
agentCoercedFrom,
|
|
5876
|
-
note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
|
|
5877
|
-
} : {},
|
|
5878
|
-
...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
|
|
5862
|
+
queueLength: thread.queue.length,
|
|
5863
|
+
forceStopped: Boolean(force_stop)
|
|
5879
5864
|
})
|
|
5880
5865
|
}
|
|
5881
5866
|
]
|
|
5882
5867
|
};
|
|
5883
|
-
}
|
|
5884
|
-
|
|
5868
|
+
}
|
|
5869
|
+
);
|
|
5870
|
+
server.tool(
|
|
5871
|
+
"wait_for_turn",
|
|
5872
|
+
"Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
|
|
5873
|
+
{
|
|
5874
|
+
ref: z.string(),
|
|
5875
|
+
timeoutMs: z.number().optional()
|
|
5876
|
+
},
|
|
5877
|
+
async ({ ref, timeoutMs }) => {
|
|
5878
|
+
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
5879
|
+
const result = orch.getTurnResult(thread.id);
|
|
5885
5880
|
return {
|
|
5886
5881
|
content: [
|
|
5887
5882
|
{
|
|
5888
5883
|
type: "text",
|
|
5889
|
-
text:
|
|
5884
|
+
text: JSON.stringify({
|
|
5885
|
+
id: thread.id,
|
|
5886
|
+
status: result.status,
|
|
5887
|
+
text: result.text
|
|
5888
|
+
})
|
|
5890
5889
|
}
|
|
5891
|
-
]
|
|
5892
|
-
isError: true
|
|
5890
|
+
]
|
|
5893
5891
|
};
|
|
5894
5892
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
force_stop: z.boolean().optional()
|
|
5904
|
-
},
|
|
5905
|
-
async ({ ref, prompt, force_stop }) => {
|
|
5906
|
-
if (force_stop) {
|
|
5907
|
-
const existing = orch.getThread(ref);
|
|
5908
|
-
if (existing) {
|
|
5909
|
-
orch.stop(ref, { clearQueue: true });
|
|
5910
|
-
}
|
|
5893
|
+
);
|
|
5894
|
+
server.tool(
|
|
5895
|
+
"get_turn_result",
|
|
5896
|
+
"Final assistant message only (not full transcript)",
|
|
5897
|
+
{ ref: z.string() },
|
|
5898
|
+
async ({ ref }) => {
|
|
5899
|
+
const result = orch.getTurnResult(ref);
|
|
5900
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
5911
5901
|
}
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
{
|
|
5932
|
-
ref: z.string(),
|
|
5933
|
-
timeoutMs: z.number().optional()
|
|
5934
|
-
},
|
|
5935
|
-
async ({ ref, timeoutMs }) => {
|
|
5936
|
-
const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
|
|
5937
|
-
const result = orch.getTurnResult(thread.id);
|
|
5938
|
-
return {
|
|
5939
|
-
content: [
|
|
5940
|
-
{
|
|
5941
|
-
type: "text",
|
|
5942
|
-
text: JSON.stringify({
|
|
5943
|
-
id: thread.id,
|
|
5944
|
-
status: result.status,
|
|
5945
|
-
text: result.text
|
|
5946
|
-
})
|
|
5947
|
-
}
|
|
5948
|
-
]
|
|
5949
|
-
};
|
|
5950
|
-
}
|
|
5951
|
-
);
|
|
5952
|
-
server.tool(
|
|
5953
|
-
"get_turn_result",
|
|
5954
|
-
"Final assistant message only (not full transcript)",
|
|
5955
|
-
{ ref: z.string() },
|
|
5956
|
-
async ({ ref }) => {
|
|
5957
|
-
const result = orch.getTurnResult(ref);
|
|
5958
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
5959
|
-
}
|
|
5960
|
-
);
|
|
5961
|
-
server.tool(
|
|
5962
|
-
"stop_thread",
|
|
5963
|
-
"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.",
|
|
5964
|
-
{
|
|
5965
|
-
ref: z.string(),
|
|
5966
|
-
force: z.boolean().optional()
|
|
5967
|
-
},
|
|
5968
|
-
async ({ ref, force }) => {
|
|
5969
|
-
const t = orch.getThread(ref);
|
|
5970
|
-
if (!t) {
|
|
5902
|
+
);
|
|
5903
|
+
server.tool(
|
|
5904
|
+
"stop_thread",
|
|
5905
|
+
"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.",
|
|
5906
|
+
{
|
|
5907
|
+
ref: z.string(),
|
|
5908
|
+
force: z.boolean().optional()
|
|
5909
|
+
},
|
|
5910
|
+
async ({ ref, force }) => {
|
|
5911
|
+
const t = orch.getThread(ref);
|
|
5912
|
+
if (!t) {
|
|
5913
|
+
return {
|
|
5914
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
5915
|
+
isError: true
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
const clearQueue = force !== false;
|
|
5919
|
+
const hadQueued = t.queue.length > 0;
|
|
5920
|
+
const stopped = orch.stop(ref, { clearQueue });
|
|
5971
5921
|
return {
|
|
5972
|
-
content: [
|
|
5973
|
-
|
|
5922
|
+
content: [
|
|
5923
|
+
{
|
|
5924
|
+
type: "text",
|
|
5925
|
+
text: JSON.stringify({
|
|
5926
|
+
id: stopped.id,
|
|
5927
|
+
status: stopped.status,
|
|
5928
|
+
clearedQueue: clearQueue && hadQueued
|
|
5929
|
+
})
|
|
5930
|
+
}
|
|
5931
|
+
]
|
|
5974
5932
|
};
|
|
5975
5933
|
}
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
async ({ ref }) => {
|
|
5998
|
-
const t = orch.getThread(ref);
|
|
5999
|
-
if (!t) {
|
|
5934
|
+
);
|
|
5935
|
+
server.tool(
|
|
5936
|
+
"archive_thread",
|
|
5937
|
+
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Cannot archive the cloud coordinator \u2014 use the Sideboard UI for that.",
|
|
5938
|
+
{ ref: z.string() },
|
|
5939
|
+
async ({ ref }) => {
|
|
5940
|
+
const t = orch.getThread(ref);
|
|
5941
|
+
if (!t) {
|
|
5942
|
+
return {
|
|
5943
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
5944
|
+
isError: true
|
|
5945
|
+
};
|
|
5946
|
+
}
|
|
5947
|
+
const blocked = mcpArchiveBlockedReason(t);
|
|
5948
|
+
if (blocked) {
|
|
5949
|
+
return {
|
|
5950
|
+
content: [{ type: "text", text: blocked }],
|
|
5951
|
+
isError: true
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
const archived = await orch.archive(ref);
|
|
6000
5955
|
return {
|
|
6001
|
-
content: [
|
|
6002
|
-
|
|
5956
|
+
content: [
|
|
5957
|
+
{
|
|
5958
|
+
type: "text",
|
|
5959
|
+
text: JSON.stringify({
|
|
5960
|
+
id: archived.id,
|
|
5961
|
+
status: archived.status
|
|
5962
|
+
})
|
|
5963
|
+
}
|
|
5964
|
+
]
|
|
6003
5965
|
};
|
|
6004
5966
|
}
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
5967
|
+
);
|
|
5968
|
+
server.tool(
|
|
5969
|
+
"restore_thread",
|
|
5970
|
+
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
5971
|
+
{ ref: z.string() },
|
|
5972
|
+
async ({ ref }) => {
|
|
5973
|
+
try {
|
|
5974
|
+
const restored = await orch.restore(ref);
|
|
5975
|
+
return {
|
|
5976
|
+
content: [
|
|
5977
|
+
{
|
|
5978
|
+
type: "text",
|
|
5979
|
+
text: JSON.stringify({
|
|
5980
|
+
id: restored.id,
|
|
5981
|
+
status: restored.status,
|
|
5982
|
+
worktreePath: restored.worktreePath
|
|
5983
|
+
})
|
|
5984
|
+
}
|
|
5985
|
+
]
|
|
5986
|
+
};
|
|
5987
|
+
} catch (err) {
|
|
5988
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5989
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
5990
|
+
}
|
|
6011
5991
|
}
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
]
|
|
6023
|
-
};
|
|
6024
|
-
}
|
|
6025
|
-
);
|
|
6026
|
-
server.tool(
|
|
6027
|
-
"restore_thread",
|
|
6028
|
-
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
6029
|
-
{ ref: z.string() },
|
|
6030
|
-
async ({ ref }) => {
|
|
6031
|
-
try {
|
|
6032
|
-
const restored = await orch.restore(ref);
|
|
5992
|
+
);
|
|
5993
|
+
server.tool(
|
|
5994
|
+
"get_diff",
|
|
5995
|
+
"Compact diff summary (capped hunks, paginated)",
|
|
5996
|
+
{
|
|
5997
|
+
ref: z.string(),
|
|
5998
|
+
maxFiles: z.number().optional()
|
|
5999
|
+
},
|
|
6000
|
+
async ({ ref, maxFiles }) => {
|
|
6001
|
+
const summary = await orch.diffSummary(ref);
|
|
6033
6002
|
return {
|
|
6034
6003
|
content: [
|
|
6035
6004
|
{
|
|
6036
6005
|
type: "text",
|
|
6037
6006
|
text: JSON.stringify({
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
worktreePath: restored.worktreePath
|
|
6007
|
+
...summary,
|
|
6008
|
+
files: summary.files.slice(0, maxFiles ?? 10)
|
|
6041
6009
|
})
|
|
6042
6010
|
}
|
|
6043
6011
|
]
|
|
6044
6012
|
};
|
|
6045
|
-
} catch (err) {
|
|
6046
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6047
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
6048
6013
|
}
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6014
|
+
);
|
|
6015
|
+
server.tool(
|
|
6016
|
+
"request_review",
|
|
6017
|
+
'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
|
|
6018
|
+
{ ref: z.string().describe("Worktree thread id/ref to review") },
|
|
6019
|
+
async ({ ref }) => {
|
|
6020
|
+
try {
|
|
6021
|
+
const tab = await orch.requestReview(ref);
|
|
6022
|
+
const from = orch.getThread(ref);
|
|
6023
|
+
return {
|
|
6024
|
+
content: [
|
|
6025
|
+
{
|
|
6026
|
+
type: "text",
|
|
6027
|
+
text: JSON.stringify({
|
|
6028
|
+
id: tab.id,
|
|
6029
|
+
title: tab.title,
|
|
6030
|
+
status: tab.status,
|
|
6031
|
+
fromThreadId: from?.id ?? ref,
|
|
6032
|
+
link: `sideboard://thread/${tab.id}`
|
|
6033
|
+
})
|
|
6034
|
+
}
|
|
6035
|
+
]
|
|
6036
|
+
};
|
|
6037
|
+
} catch (err) {
|
|
6038
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6039
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
);
|
|
6043
|
+
const agentEnum = z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
6044
|
+
server.tool(
|
|
6045
|
+
"list_models",
|
|
6046
|
+
"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.",
|
|
6047
|
+
{
|
|
6048
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
6049
|
+
},
|
|
6050
|
+
async ({ agent }) => {
|
|
6051
|
+
try {
|
|
6052
|
+
const catalogs = await listModelsForAgent(agent);
|
|
6053
|
+
return {
|
|
6054
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
6055
|
+
};
|
|
6056
|
+
} catch (err) {
|
|
6057
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6058
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
);
|
|
6062
|
+
server.tool(
|
|
6063
|
+
"fork_worktree",
|
|
6064
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
6065
|
+
{
|
|
6066
|
+
ref: z.string().describe("Worktree thread id/ref to fork"),
|
|
6067
|
+
through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
6068
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
6069
|
+
model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
6070
|
+
title: z.string().optional()
|
|
6071
|
+
},
|
|
6072
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
6073
|
+
try {
|
|
6074
|
+
const source = orch.getThread(ref);
|
|
6075
|
+
if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
|
|
6076
|
+
const thread = await orch.forkThreadWorktree({
|
|
6077
|
+
threadId: ref,
|
|
6078
|
+
throughIndex: through_index,
|
|
6079
|
+
agent,
|
|
6080
|
+
model,
|
|
6081
|
+
title
|
|
6082
|
+
});
|
|
6083
|
+
return {
|
|
6084
|
+
content: [
|
|
6085
|
+
{
|
|
6086
|
+
type: "text",
|
|
6087
|
+
text: JSON.stringify({
|
|
6088
|
+
id: thread.id,
|
|
6089
|
+
title: thread.title,
|
|
6090
|
+
status: thread.status,
|
|
6091
|
+
agent: thread.agent,
|
|
6092
|
+
model: thread.model,
|
|
6093
|
+
branchName: thread.branchName,
|
|
6094
|
+
worktreePath: thread.worktreePath,
|
|
6095
|
+
fromThreadId: source?.id ?? ref,
|
|
6096
|
+
link: `sideboard://thread/${thread.id}`
|
|
6097
|
+
})
|
|
6098
|
+
}
|
|
6099
|
+
]
|
|
6100
|
+
};
|
|
6101
|
+
} catch (err) {
|
|
6102
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6103
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
);
|
|
6107
|
+
server.tool(
|
|
6108
|
+
"fork_chat",
|
|
6109
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Remote coordinators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
6110
|
+
{
|
|
6111
|
+
ref: z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
6112
|
+
through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
6113
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
6114
|
+
model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
6115
|
+
title: z.string().optional()
|
|
6116
|
+
},
|
|
6117
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
6118
|
+
try {
|
|
6119
|
+
const source = orch.getThread(ref);
|
|
6120
|
+
if (!source) {
|
|
6121
|
+
return {
|
|
6122
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
6123
|
+
isError: true
|
|
6124
|
+
};
|
|
6068
6125
|
}
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6126
|
+
const tab = orch.forkChatTab({
|
|
6127
|
+
threadId: source.id,
|
|
6128
|
+
throughIndex: through_index,
|
|
6129
|
+
agent,
|
|
6130
|
+
model,
|
|
6131
|
+
title
|
|
6132
|
+
});
|
|
6133
|
+
return {
|
|
6134
|
+
content: [
|
|
6135
|
+
{
|
|
6136
|
+
type: "text",
|
|
6137
|
+
text: JSON.stringify({
|
|
6138
|
+
id: tab.id,
|
|
6139
|
+
title: tab.title,
|
|
6140
|
+
status: tab.status,
|
|
6141
|
+
agent: tab.agent,
|
|
6142
|
+
model: tab.model,
|
|
6143
|
+
sourceType: tab.sourceType,
|
|
6144
|
+
worktreePath: tab.worktreePath,
|
|
6145
|
+
fromThreadId: source.id,
|
|
6146
|
+
link: `sideboard://thread/${tab.id}`
|
|
6147
|
+
})
|
|
6148
|
+
}
|
|
6149
|
+
]
|
|
6150
|
+
};
|
|
6151
|
+
} catch (err) {
|
|
6152
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6153
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
);
|
|
6157
|
+
server.tool(
|
|
6158
|
+
"run_dev_script",
|
|
6159
|
+
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
6160
|
+
{
|
|
6161
|
+
ref: z.string(),
|
|
6162
|
+
name: z.string().optional()
|
|
6163
|
+
},
|
|
6164
|
+
async ({ ref, name }) => {
|
|
6165
|
+
const result = await orch.startDev(ref, name);
|
|
6081
6166
|
return {
|
|
6082
6167
|
content: [
|
|
6083
6168
|
{
|
|
6084
6169
|
type: "text",
|
|
6085
6170
|
text: JSON.stringify({
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
link: `sideboard://thread/${tab.id}`
|
|
6171
|
+
port: result.port,
|
|
6172
|
+
scriptName: result.scriptName,
|
|
6173
|
+
ports: result.ports,
|
|
6174
|
+
url: `http://localhost:${result.port}`
|
|
6091
6175
|
})
|
|
6092
6176
|
}
|
|
6093
6177
|
]
|
|
6094
6178
|
};
|
|
6095
|
-
} catch (err) {
|
|
6096
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6097
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
6098
6179
|
}
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
},
|
|
6108
|
-
async ({ agent }) => {
|
|
6109
|
-
try {
|
|
6110
|
-
const catalogs = await listModelsForAgent(agent);
|
|
6180
|
+
);
|
|
6181
|
+
server.tool(
|
|
6182
|
+
"list_run_scripts",
|
|
6183
|
+
"List named run scripts available for a thread",
|
|
6184
|
+
{ ref: z.string() },
|
|
6185
|
+
async ({ ref }) => {
|
|
6186
|
+
const scripts = orch.listThreadRunScripts(ref);
|
|
6187
|
+
const active = orch.getActiveRuns(ref);
|
|
6111
6188
|
return {
|
|
6112
|
-
content: [
|
|
6189
|
+
content: [
|
|
6190
|
+
{
|
|
6191
|
+
type: "text",
|
|
6192
|
+
text: JSON.stringify({ scripts, active }, null, 2)
|
|
6193
|
+
}
|
|
6194
|
+
]
|
|
6113
6195
|
};
|
|
6114
|
-
} catch (err) {
|
|
6115
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6116
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
6117
6196
|
}
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6197
|
+
);
|
|
6198
|
+
server.tool(
|
|
6199
|
+
"stop_dev_script",
|
|
6200
|
+
"Stop a running script for a thread (all scripts if name omitted)",
|
|
6201
|
+
{
|
|
6202
|
+
ref: z.string(),
|
|
6203
|
+
name: z.string().optional()
|
|
6204
|
+
},
|
|
6205
|
+
async ({ ref, name }) => {
|
|
6206
|
+
orch.stopDev(ref, name);
|
|
6207
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
6208
|
+
}
|
|
6209
|
+
);
|
|
6210
|
+
server.tool(
|
|
6211
|
+
"run_setup",
|
|
6212
|
+
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
6213
|
+
{ ref: z.string() },
|
|
6214
|
+
async ({ ref }) => {
|
|
6215
|
+
const result = await orch.runSetup(ref);
|
|
6216
|
+
return {
|
|
6217
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
6218
|
+
};
|
|
6219
|
+
}
|
|
6220
|
+
);
|
|
6221
|
+
server.tool(
|
|
6222
|
+
"add_workspace",
|
|
6223
|
+
"Register a git repo as a Sideboard workspace",
|
|
6224
|
+
{ repoPath: z.string() },
|
|
6225
|
+
async ({ repoPath }) => {
|
|
6226
|
+
const ws = await orch.addWorkspace(repoPath);
|
|
6227
|
+
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
6228
|
+
}
|
|
6229
|
+
);
|
|
6230
|
+
server.tool(
|
|
6231
|
+
"remove_workspace",
|
|
6232
|
+
"Unregister a Sideboard workspace (does not archive threads)",
|
|
6233
|
+
{ repoPath: z.string() },
|
|
6234
|
+
async ({ repoPath }) => {
|
|
6235
|
+
orch.removeWorkspace(repoPath);
|
|
6236
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
6237
|
+
}
|
|
6238
|
+
);
|
|
6239
|
+
server.tool(
|
|
6240
|
+
"fanout",
|
|
6241
|
+
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
6242
|
+
{
|
|
6243
|
+
prompt: z.string(),
|
|
6244
|
+
agents: z.array(z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
6245
|
+
repoPath: z.string(),
|
|
6246
|
+
sourceType: z.enum(["branch", "pr", "ticket"]).optional(),
|
|
6247
|
+
sourceRef: z.string().optional(),
|
|
6248
|
+
title: z.string().optional()
|
|
6249
|
+
},
|
|
6250
|
+
async (args) => {
|
|
6251
|
+
const threads = await orch.bestOfN(args);
|
|
6252
|
+
return {
|
|
6253
|
+
content: [
|
|
6254
|
+
{
|
|
6255
|
+
type: "text",
|
|
6256
|
+
text: JSON.stringify(
|
|
6257
|
+
threads.map((t) => ({
|
|
6258
|
+
id: t.id,
|
|
6259
|
+
agent: t.agent,
|
|
6260
|
+
branchName: t.branchName,
|
|
6261
|
+
worktreePath: t.worktreePath
|
|
6262
|
+
})),
|
|
6263
|
+
null,
|
|
6264
|
+
2
|
|
6265
|
+
)
|
|
6266
|
+
}
|
|
6267
|
+
]
|
|
6268
|
+
};
|
|
6269
|
+
}
|
|
6270
|
+
);
|
|
6271
|
+
server.tool(
|
|
6272
|
+
"list_branches",
|
|
6273
|
+
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
6274
|
+
{
|
|
6275
|
+
repoPath: z.string(),
|
|
6276
|
+
unmergedOnly: z.boolean().optional()
|
|
6277
|
+
},
|
|
6278
|
+
async ({ repoPath, unmergedOnly }) => {
|
|
6279
|
+
const root = await resolveRepoRoot(repoPath);
|
|
6280
|
+
const branches = await listBranches(root, {
|
|
6281
|
+
unmergedOnly: unmergedOnly !== false
|
|
6140
6282
|
});
|
|
6141
6283
|
return {
|
|
6142
6284
|
content: [
|
|
6143
6285
|
{
|
|
6144
6286
|
type: "text",
|
|
6145
|
-
text:
|
|
6146
|
-
id: thread.id,
|
|
6147
|
-
title: thread.title,
|
|
6148
|
-
status: thread.status,
|
|
6149
|
-
agent: thread.agent,
|
|
6150
|
-
model: thread.model,
|
|
6151
|
-
branchName: thread.branchName,
|
|
6152
|
-
worktreePath: thread.worktreePath,
|
|
6153
|
-
fromThreadId: source?.id ?? ref,
|
|
6154
|
-
link: `sideboard://thread/${thread.id}`
|
|
6155
|
-
})
|
|
6287
|
+
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
6156
6288
|
}
|
|
6157
6289
|
]
|
|
6158
6290
|
};
|
|
6159
|
-
} catch (err) {
|
|
6160
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6161
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
6162
6291
|
}
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6292
|
+
);
|
|
6293
|
+
server.tool(
|
|
6294
|
+
"list_prs",
|
|
6295
|
+
"List open GitHub PRs for a registered workspace. Pass repoPath from list_workspaces (uses gh against that repo remote). Then create_thread with sourceType=pr.",
|
|
6296
|
+
{ repoPath: z.string() },
|
|
6297
|
+
async ({ repoPath }) => {
|
|
6298
|
+
const root = await resolveRepoRoot(repoPath);
|
|
6299
|
+
const prs = await listPrs(root);
|
|
6300
|
+
return {
|
|
6301
|
+
content: [
|
|
6302
|
+
{
|
|
6303
|
+
type: "text",
|
|
6304
|
+
text: prs.map(
|
|
6305
|
+
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
6306
|
+
).join("\n")
|
|
6307
|
+
}
|
|
6308
|
+
]
|
|
6309
|
+
};
|
|
6310
|
+
}
|
|
6311
|
+
);
|
|
6312
|
+
server.tool(
|
|
6313
|
+
"get_pr_stack",
|
|
6314
|
+
"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 mergePr on stacked PRs.",
|
|
6315
|
+
{ ref: z.string() },
|
|
6316
|
+
async ({ ref }) => {
|
|
6317
|
+
const stack = await orch.getPrStack(ref);
|
|
6318
|
+
return {
|
|
6319
|
+
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
6320
|
+
};
|
|
6321
|
+
}
|
|
6322
|
+
);
|
|
6323
|
+
server.tool(
|
|
6324
|
+
"open_pr_stack_layers",
|
|
6325
|
+
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
6326
|
+
{
|
|
6327
|
+
ref: z.string(),
|
|
6328
|
+
layer: z.number().int().positive().optional()
|
|
6329
|
+
},
|
|
6330
|
+
async ({ ref, layer }) => {
|
|
6331
|
+
const result = await orch.openPrStackLayers(ref, { layer });
|
|
6332
|
+
return {
|
|
6333
|
+
content: [
|
|
6334
|
+
{
|
|
6335
|
+
type: "text",
|
|
6336
|
+
text: JSON.stringify(
|
|
6337
|
+
{
|
|
6338
|
+
stackNumber: result.stack.stackNumber,
|
|
6339
|
+
trunk: result.stack.trunk,
|
|
6340
|
+
threads: result.threads.map((t) => ({
|
|
6341
|
+
id: t.id,
|
|
6342
|
+
title: t.title,
|
|
6343
|
+
branchName: t.branchName,
|
|
6344
|
+
stackLayer: t.stackLayer,
|
|
6345
|
+
worktreePath: t.worktreePath,
|
|
6346
|
+
prUrl: t.prUrl,
|
|
6347
|
+
link: `sideboard://thread/${t.id}`
|
|
6348
|
+
}))
|
|
6349
|
+
},
|
|
6350
|
+
null,
|
|
6351
|
+
2
|
|
6352
|
+
)
|
|
6353
|
+
}
|
|
6354
|
+
]
|
|
6355
|
+
};
|
|
6356
|
+
}
|
|
6357
|
+
);
|
|
6358
|
+
server.tool(
|
|
6359
|
+
"add_stack_layer",
|
|
6360
|
+
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
6361
|
+
{
|
|
6362
|
+
ref: z.string(),
|
|
6363
|
+
branchName: z.string(),
|
|
6364
|
+
title: z.string().optional()
|
|
6365
|
+
},
|
|
6366
|
+
async ({ ref, branchName, title }) => {
|
|
6367
|
+
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
6368
|
+
return {
|
|
6369
|
+
content: [
|
|
6370
|
+
{
|
|
6371
|
+
type: "text",
|
|
6372
|
+
text: JSON.stringify(
|
|
6373
|
+
{
|
|
6374
|
+
id: result.thread.id,
|
|
6375
|
+
title: result.thread.title,
|
|
6376
|
+
branchName: result.thread.branchName,
|
|
6377
|
+
stackLayer: result.thread.stackLayer,
|
|
6378
|
+
worktreePath: result.thread.worktreePath,
|
|
6379
|
+
link: `sideboard://thread/${result.thread.id}`
|
|
6380
|
+
},
|
|
6381
|
+
null,
|
|
6382
|
+
2
|
|
6383
|
+
)
|
|
6384
|
+
}
|
|
6385
|
+
]
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6388
|
+
);
|
|
6389
|
+
server.tool(
|
|
6390
|
+
"create_pr_stack",
|
|
6391
|
+
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
6392
|
+
{
|
|
6393
|
+
repoPath: z.string(),
|
|
6394
|
+
branches: z.array(z.string()).min(1),
|
|
6395
|
+
agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
6396
|
+
base: z.string().optional(),
|
|
6397
|
+
title: z.string().optional()
|
|
6398
|
+
},
|
|
6399
|
+
async (args) => {
|
|
6400
|
+
const result = await orch.createPrStack({
|
|
6401
|
+
repoPath: args.repoPath,
|
|
6402
|
+
branches: args.branches,
|
|
6403
|
+
agent: args.agent,
|
|
6404
|
+
base: args.base,
|
|
6405
|
+
title: args.title
|
|
6190
6406
|
});
|
|
6191
6407
|
return {
|
|
6192
6408
|
content: [
|
|
6193
6409
|
{
|
|
6194
6410
|
type: "text",
|
|
6195
|
-
text: JSON.stringify(
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6411
|
+
text: JSON.stringify(
|
|
6412
|
+
{
|
|
6413
|
+
stackNumber: result.stack.stackNumber,
|
|
6414
|
+
trunk: result.stack.trunk,
|
|
6415
|
+
threads: result.threads.map((t) => ({
|
|
6416
|
+
id: t.id,
|
|
6417
|
+
title: t.title,
|
|
6418
|
+
branchName: t.branchName,
|
|
6419
|
+
stackLayer: t.stackLayer,
|
|
6420
|
+
worktreePath: t.worktreePath,
|
|
6421
|
+
link: `sideboard://thread/${t.id}`
|
|
6422
|
+
}))
|
|
6423
|
+
},
|
|
6424
|
+
null,
|
|
6425
|
+
2
|
|
6426
|
+
)
|
|
6206
6427
|
}
|
|
6207
6428
|
]
|
|
6208
6429
|
};
|
|
6209
|
-
} catch (err) {
|
|
6210
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6211
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
6212
6430
|
}
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
}
|
|
6238
|
-
);
|
|
6239
|
-
server.tool(
|
|
6240
|
-
"list_run_scripts",
|
|
6241
|
-
"List named run scripts available for a thread",
|
|
6242
|
-
{ ref: z.string() },
|
|
6243
|
-
async ({ ref }) => {
|
|
6244
|
-
const scripts = orch.listThreadRunScripts(ref);
|
|
6245
|
-
const active = orch.getActiveRuns(ref);
|
|
6246
|
-
return {
|
|
6247
|
-
content: [
|
|
6248
|
-
{
|
|
6249
|
-
type: "text",
|
|
6250
|
-
text: JSON.stringify({ scripts, active }, null, 2)
|
|
6251
|
-
}
|
|
6252
|
-
]
|
|
6253
|
-
};
|
|
6254
|
-
}
|
|
6255
|
-
);
|
|
6256
|
-
server.tool(
|
|
6257
|
-
"stop_dev_script",
|
|
6258
|
-
"Stop a running script for a thread (all scripts if name omitted)",
|
|
6259
|
-
{
|
|
6260
|
-
ref: z.string(),
|
|
6261
|
-
name: z.string().optional()
|
|
6262
|
-
},
|
|
6263
|
-
async ({ ref, name }) => {
|
|
6264
|
-
orch.stopDev(ref, name);
|
|
6265
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
6266
|
-
}
|
|
6267
|
-
);
|
|
6268
|
-
server.tool(
|
|
6269
|
-
"run_setup",
|
|
6270
|
-
"Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
|
|
6271
|
-
{ ref: z.string() },
|
|
6272
|
-
async ({ ref }) => {
|
|
6273
|
-
const result = await orch.runSetup(ref);
|
|
6274
|
-
return {
|
|
6275
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
6276
|
-
};
|
|
6277
|
-
}
|
|
6278
|
-
);
|
|
6279
|
-
server.tool(
|
|
6280
|
-
"add_workspace",
|
|
6281
|
-
"Register a git repo as a Sideboard workspace",
|
|
6282
|
-
{ repoPath: z.string() },
|
|
6283
|
-
async ({ repoPath }) => {
|
|
6284
|
-
const ws = await orch.addWorkspace(repoPath);
|
|
6285
|
-
return { content: [{ type: "text", text: JSON.stringify(ws) }] };
|
|
6286
|
-
}
|
|
6287
|
-
);
|
|
6288
|
-
server.tool(
|
|
6289
|
-
"remove_workspace",
|
|
6290
|
-
"Unregister a Sideboard workspace (does not archive threads)",
|
|
6291
|
-
{ repoPath: z.string() },
|
|
6292
|
-
async ({ repoPath }) => {
|
|
6293
|
-
orch.removeWorkspace(repoPath);
|
|
6294
|
-
return { content: [{ type: "text", text: "ok" }] };
|
|
6295
|
-
}
|
|
6296
|
-
);
|
|
6297
|
-
server.tool(
|
|
6298
|
-
"fanout",
|
|
6299
|
-
"Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
|
|
6300
|
-
{
|
|
6301
|
-
prompt: z.string(),
|
|
6302
|
-
agents: z.array(z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
|
|
6303
|
-
repoPath: z.string(),
|
|
6304
|
-
sourceType: z.enum(["branch", "pr", "ticket"]).optional(),
|
|
6305
|
-
sourceRef: z.string().optional(),
|
|
6306
|
-
title: z.string().optional()
|
|
6307
|
-
},
|
|
6308
|
-
async (args) => {
|
|
6309
|
-
const threads = await orch.bestOfN(args);
|
|
6310
|
-
return {
|
|
6311
|
-
content: [
|
|
6312
|
-
{
|
|
6313
|
-
type: "text",
|
|
6314
|
-
text: JSON.stringify(
|
|
6315
|
-
threads.map((t) => ({
|
|
6316
|
-
id: t.id,
|
|
6317
|
-
agent: t.agent,
|
|
6318
|
-
branchName: t.branchName,
|
|
6319
|
-
worktreePath: t.worktreePath
|
|
6320
|
-
})),
|
|
6321
|
-
null,
|
|
6322
|
-
2
|
|
6323
|
-
)
|
|
6324
|
-
}
|
|
6325
|
-
]
|
|
6326
|
-
};
|
|
6327
|
-
}
|
|
6328
|
-
);
|
|
6329
|
-
server.tool(
|
|
6330
|
-
"list_branches",
|
|
6331
|
-
"List git branches in a registered workspace. Pass repoPath from list_workspaces (unmerged into the default branch by default \u2014 for create_thread sourceType=branch).",
|
|
6332
|
-
{
|
|
6333
|
-
repoPath: z.string(),
|
|
6334
|
-
unmergedOnly: z.boolean().optional()
|
|
6335
|
-
},
|
|
6336
|
-
async ({ repoPath, unmergedOnly }) => {
|
|
6337
|
-
const root = await resolveRepoRoot(repoPath);
|
|
6338
|
-
const branches = await listBranches(root, {
|
|
6339
|
-
unmergedOnly: unmergedOnly !== false
|
|
6340
|
-
});
|
|
6341
|
-
return {
|
|
6342
|
-
content: [
|
|
6343
|
-
{
|
|
6344
|
-
type: "text",
|
|
6345
|
-
text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
|
|
6346
|
-
}
|
|
6347
|
-
]
|
|
6348
|
-
};
|
|
6349
|
-
}
|
|
6350
|
-
);
|
|
6351
|
-
server.tool(
|
|
6352
|
-
"list_prs",
|
|
6353
|
-
"List open GitHub PRs for a registered workspace. Pass repoPath from list_workspaces (uses gh against that repo remote). Then create_thread with sourceType=pr.",
|
|
6354
|
-
{ repoPath: z.string() },
|
|
6355
|
-
async ({ repoPath }) => {
|
|
6356
|
-
const root = await resolveRepoRoot(repoPath);
|
|
6357
|
-
const prs = await listPrs(root);
|
|
6358
|
-
return {
|
|
6359
|
-
content: [
|
|
6360
|
-
{
|
|
6361
|
-
type: "text",
|
|
6362
|
-
text: prs.map(
|
|
6363
|
-
(p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
|
|
6364
|
-
).join("\n")
|
|
6365
|
-
}
|
|
6366
|
-
]
|
|
6367
|
-
};
|
|
6368
|
-
}
|
|
6369
|
-
);
|
|
6370
|
-
server.tool(
|
|
6371
|
-
"get_pr_stack",
|
|
6372
|
-
"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 mergePr on stacked PRs.",
|
|
6373
|
-
{ ref: z.string() },
|
|
6374
|
-
async ({ ref }) => {
|
|
6375
|
-
const stack = await orch.getPrStack(ref);
|
|
6376
|
-
return {
|
|
6377
|
-
content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
|
|
6378
|
-
};
|
|
6379
|
-
}
|
|
6380
|
-
);
|
|
6381
|
-
server.tool(
|
|
6382
|
-
"open_pr_stack_layers",
|
|
6383
|
-
"Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
|
|
6384
|
-
{
|
|
6385
|
-
ref: z.string(),
|
|
6386
|
-
layer: z.number().int().positive().optional()
|
|
6387
|
-
},
|
|
6388
|
-
async ({ ref, layer }) => {
|
|
6389
|
-
const result = await orch.openPrStackLayers(ref, { layer });
|
|
6390
|
-
return {
|
|
6391
|
-
content: [
|
|
6392
|
-
{
|
|
6393
|
-
type: "text",
|
|
6394
|
-
text: JSON.stringify(
|
|
6395
|
-
{
|
|
6396
|
-
stackNumber: result.stack.stackNumber,
|
|
6397
|
-
trunk: result.stack.trunk,
|
|
6398
|
-
threads: result.threads.map((t) => ({
|
|
6399
|
-
id: t.id,
|
|
6400
|
-
title: t.title,
|
|
6401
|
-
branchName: t.branchName,
|
|
6402
|
-
stackLayer: t.stackLayer,
|
|
6403
|
-
worktreePath: t.worktreePath,
|
|
6404
|
-
prUrl: t.prUrl,
|
|
6405
|
-
link: `sideboard://thread/${t.id}`
|
|
6406
|
-
}))
|
|
6407
|
-
},
|
|
6408
|
-
null,
|
|
6409
|
-
2
|
|
6410
|
-
)
|
|
6411
|
-
}
|
|
6412
|
-
]
|
|
6413
|
-
};
|
|
6414
|
-
}
|
|
6415
|
-
);
|
|
6416
|
-
server.tool(
|
|
6417
|
-
"add_stack_layer",
|
|
6418
|
-
"Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
|
|
6419
|
-
{
|
|
6420
|
-
ref: z.string(),
|
|
6421
|
-
branchName: z.string(),
|
|
6422
|
-
title: z.string().optional()
|
|
6423
|
-
},
|
|
6424
|
-
async ({ ref, branchName, title }) => {
|
|
6425
|
-
const result = await orch.addStackLayer(ref, branchName, { title });
|
|
6426
|
-
return {
|
|
6427
|
-
content: [
|
|
6428
|
-
{
|
|
6429
|
-
type: "text",
|
|
6430
|
-
text: JSON.stringify(
|
|
6431
|
-
{
|
|
6432
|
-
id: result.thread.id,
|
|
6433
|
-
title: result.thread.title,
|
|
6434
|
-
branchName: result.thread.branchName,
|
|
6435
|
-
stackLayer: result.thread.stackLayer,
|
|
6436
|
-
worktreePath: result.thread.worktreePath,
|
|
6437
|
-
link: `sideboard://thread/${result.thread.id}`
|
|
6438
|
-
},
|
|
6439
|
-
null,
|
|
6440
|
-
2
|
|
6441
|
-
)
|
|
6442
|
-
}
|
|
6443
|
-
]
|
|
6444
|
-
};
|
|
6445
|
-
}
|
|
6446
|
-
);
|
|
6447
|
-
server.tool(
|
|
6448
|
-
"create_pr_stack",
|
|
6449
|
-
"Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
|
|
6450
|
-
{
|
|
6451
|
-
repoPath: z.string(),
|
|
6452
|
-
branches: z.array(z.string()).min(1),
|
|
6453
|
-
agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
|
|
6454
|
-
base: z.string().optional(),
|
|
6455
|
-
title: z.string().optional()
|
|
6456
|
-
},
|
|
6457
|
-
async (args) => {
|
|
6458
|
-
const result = await orch.createPrStack({
|
|
6459
|
-
repoPath: args.repoPath,
|
|
6460
|
-
branches: args.branches,
|
|
6461
|
-
agent: args.agent,
|
|
6462
|
-
base: args.base,
|
|
6463
|
-
title: args.title
|
|
6464
|
-
});
|
|
6465
|
-
return {
|
|
6466
|
-
content: [
|
|
6467
|
-
{
|
|
6468
|
-
type: "text",
|
|
6469
|
-
text: JSON.stringify(
|
|
6470
|
-
{
|
|
6471
|
-
stackNumber: result.stack.stackNumber,
|
|
6472
|
-
trunk: result.stack.trunk,
|
|
6473
|
-
threads: result.threads.map((t) => ({
|
|
6474
|
-
id: t.id,
|
|
6475
|
-
title: t.title,
|
|
6476
|
-
branchName: t.branchName,
|
|
6477
|
-
stackLayer: t.stackLayer,
|
|
6478
|
-
worktreePath: t.worktreePath,
|
|
6479
|
-
link: `sideboard://thread/${t.id}`
|
|
6480
|
-
}))
|
|
6481
|
-
},
|
|
6482
|
-
null,
|
|
6483
|
-
2
|
|
6484
|
-
)
|
|
6485
|
-
}
|
|
6486
|
-
]
|
|
6487
|
-
};
|
|
6488
|
-
}
|
|
6489
|
-
);
|
|
6490
|
-
server.tool(
|
|
6491
|
-
"list_issues",
|
|
6492
|
-
"List issues from Sideboard Account connections (Linear API or GitHub Issues; Linear\u2192GitHub fallback when Linear is not connected). Pass repoPath from list_workspaces \u2014 GitHub Issues are scoped to that repo. Then create_thread with sourceType=ticket.",
|
|
6493
|
-
{ repoPath: z.string() },
|
|
6494
|
-
async ({ repoPath }) => {
|
|
6495
|
-
const root = await resolveRepoRoot(repoPath);
|
|
6496
|
-
const result = await listIssues(root);
|
|
6497
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6498
|
-
}
|
|
6499
|
-
);
|
|
6500
|
-
server.tool(
|
|
6501
|
-
"list_linear_issues",
|
|
6502
|
-
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
6503
|
-
{
|
|
6504
|
-
agent: z.enum(["claude", "codex", "opencode"]),
|
|
6505
|
-
repoPath: z.string()
|
|
6506
|
-
},
|
|
6507
|
-
async ({ agent, repoPath }) => {
|
|
6508
|
-
const issues = await listLinearIssues(agent, repoPath);
|
|
6509
|
-
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
6510
|
-
}
|
|
6511
|
-
);
|
|
6431
|
+
);
|
|
6432
|
+
server.tool(
|
|
6433
|
+
"list_issues",
|
|
6434
|
+
"List issues from Sideboard Account connections (Linear API or GitHub Issues; Linear\u2192GitHub fallback when Linear is not connected). Pass repoPath from list_workspaces \u2014 GitHub Issues are scoped to that repo. Then create_thread with sourceType=ticket.",
|
|
6435
|
+
{ repoPath: z.string() },
|
|
6436
|
+
async ({ repoPath }) => {
|
|
6437
|
+
const root = await resolveRepoRoot(repoPath);
|
|
6438
|
+
const result = await listIssues(root);
|
|
6439
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6440
|
+
}
|
|
6441
|
+
);
|
|
6442
|
+
server.tool(
|
|
6443
|
+
"list_linear_issues",
|
|
6444
|
+
"Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
|
|
6445
|
+
{
|
|
6446
|
+
agent: z.enum(["claude", "codex", "opencode"]),
|
|
6447
|
+
repoPath: z.string()
|
|
6448
|
+
},
|
|
6449
|
+
async ({ agent, repoPath }) => {
|
|
6450
|
+
const issues = await listLinearIssues(agent, repoPath);
|
|
6451
|
+
return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
|
|
6452
|
+
}
|
|
6453
|
+
);
|
|
6454
|
+
}
|
|
6512
6455
|
const transport = new StdioServerTransport();
|
|
6513
6456
|
await server.connect(transport);
|
|
6514
6457
|
}
|