@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.
Files changed (43) hide show
  1. package/dist/{agents-KF3J3C53.js → agents-OTBZVTZO.js} +27 -7
  2. package/dist/{agents-QLSFIQAO.js → agents-TS3EI2YI.js} +27 -7
  3. package/dist/{app-settings-LTSFG7QJ.js → app-settings-GJVZGU3M.js} +13 -3
  4. package/dist/{app-settings-HK4KAM4R.js → app-settings-ONKMFSEJ.js} +13 -3
  5. package/dist/{chunk-UGXSSBFJ.js → chunk-4BUCGNZS.js} +1 -1
  6. package/dist/chunk-4UIYN56C.js +173 -0
  7. package/dist/{chunk-NVU7EC2E.js → chunk-57WWNMFP.js} +2 -2
  8. package/dist/{chunk-A6FOKNOU.js → chunk-5HTTCYWA.js} +2 -2
  9. package/dist/{chunk-DXTGDXO2.js → chunk-7ZOQY4A7.js} +31 -6
  10. package/dist/{chunk-ZF5YJCXO.js → chunk-B4PRRKMA.js} +284 -93
  11. package/dist/{chunk-DDN3UF4R.js → chunk-BPOTVI5J.js} +115 -3
  12. package/dist/{chunk-MBAL5FE6.js → chunk-DB3I75FI.js} +4 -4
  13. package/dist/chunk-FGDJOJ2L.js +176 -0
  14. package/dist/{chunk-2FEA4LDA.js → chunk-JWZNK3CQ.js} +31 -6
  15. package/dist/{chunk-2MCNURQV.js → chunk-K7LM4CLD.js} +279 -93
  16. package/dist/{chunk-3KKNTHUC.js → chunk-LW5CMQGZ.js} +1 -1
  17. package/dist/{chunk-JB2H7E7V.js → chunk-M5V4QIWF.js} +1 -1
  18. package/dist/{chunk-6CUJQKTJ.js → chunk-MHR4OGZ4.js} +115 -3
  19. package/dist/{chunk-46KMKHSD.js → chunk-WAFIF22N.js} +2 -2
  20. package/dist/{chunk-27MAWNKY.js → chunk-ZRFAZVGL.js} +2 -2
  21. package/dist/{connected-teams-MKIYKOYA.js → connected-teams-OMDHTL6A.js} +2 -2
  22. package/dist/{connected-teams-RSL4VB3X.js → connected-teams-RUNMOCG5.js} +2 -2
  23. package/dist/{coordinator-prompt-N2R6FGZK.js → coordinator-prompt-3BSMKZL4.js} +4 -4
  24. package/dist/{coordinator-prompt-LKG3SHMI.js → coordinator-prompt-6HMTOX4G.js} +4 -4
  25. package/dist/{global-workspace-LALWQWRJ.js → global-workspace-QS7A23CB.js} +5 -6
  26. package/dist/{global-workspace-YOAUQCMA.js → global-workspace-YZ2NJVLL.js} +5 -6
  27. package/dist/index.cjs +1345 -933
  28. package/dist/index.d.cts +96 -1
  29. package/dist/index.d.ts +96 -1
  30. package/dist/index.js +750 -715
  31. package/dist/mcp/run-stdio.cjs +1293 -977
  32. package/dist/mcp/run-stdio.js +691 -748
  33. package/dist/{run-GPSVV5OH.js → run-JLD2Y52J.js} +1 -1
  34. package/dist/{run-HZTBWGQR.js → run-Y2MI3EVV.js} +1 -1
  35. package/dist/{workspaces-E75POJXP.js → workspaces-AIUYUXB7.js} +6 -7
  36. package/dist/{workspaces-VCAIHVIE.js → workspaces-EBFW7I7S.js} +6 -7
  37. package/dist/{worktree-MV4WRW6G.js → worktree-7NBPIVFZ.js} +2 -2
  38. package/dist/{worktree-LZEGVQSN.js → worktree-WMDJW2BL.js} +2 -2
  39. package/package.json +1 -1
  40. package/dist/chunk-73AT5K4A.js +0 -104
  41. package/dist/chunk-GI7E5733.js +0 -30
  42. package/dist/chunk-RIWUU7FO.js +0 -107
  43. package/dist/chunk-S2LL5HA4.js +0 -33
package/dist/index.js CHANGED
@@ -4,33 +4,14 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-46KMKHSD.js";
8
- import {
9
- CLOUD_COORDINATOR_BUSY_REPLY,
10
- CLOUD_COORDINATOR_STOPPED_REPLY,
11
- CLOUD_COORDINATOR_TIMEOUT_REPLY,
12
- CLOUD_ORCHESTRATOR_GOAL,
13
- GLOBAL_WORKSPACE_ID,
14
- SIDEBOARD_FORCE_STOP,
15
- createGlobalChat,
16
- ensureCloudCoordinator,
17
- healOrchestrationSoccerTitles,
18
- isCloudCoordinatorThread,
19
- isGlobalRepoPath,
20
- isGlobalThread,
21
- isOrchestratorThread,
22
- listGlobalThreads,
23
- orchestrationTitleNeedsSoccerNickname,
24
- orchestratorSessionPoisonedByBuiltins,
25
- parseForceStopMessage,
26
- takenTeamSlugsForOrchestration
27
- } from "./chunk-DXTGDXO2.js";
7
+ } from "./chunk-WAFIF22N.js";
28
8
  import {
29
9
  BRIGHTSY_MCP_ALLOWED_TOOLS,
30
10
  CLAUDE_MODEL_CATALOG,
31
11
  MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
32
12
  PLAN_MODE_INSTRUCTION,
33
13
  SIDEBOARD_MCP_ALLOWED_TOOLS,
14
+ SIDEBOARD_MCP_PROFILE_ENV,
34
15
  allAdapters,
35
16
  brightsyAdapter,
36
17
  brightsyMcpAllowedTools,
@@ -41,6 +22,7 @@ import {
41
22
  countCacheControlBlocks,
42
23
  cursorAdapter,
43
24
  decodeBrightsyTarget,
25
+ dropCachedPrefixOnResume,
44
26
  encodeBrightsyTarget,
45
27
  findInvalidCacheControlTtlOrder,
46
28
  flattenTurnInput,
@@ -66,17 +48,41 @@ import {
66
48
  parseMcpList,
67
49
  parseSessionQuotaResetAt,
68
50
  permissionMode,
51
+ prepareTerminalCommand,
52
+ promptMentionsBrightsy,
69
53
  resolveCursorModelId,
54
+ resolveLoginCommand,
70
55
  resolveQuotaFallbackAgent,
71
56
  sanitizeMcpServerName,
57
+ shouldInjectBrightsyMcp,
58
+ sideboardMcpProfile,
59
+ threadRequestsBrightsyMcp,
72
60
  writeInjectedMcpConfig
73
- } from "./chunk-ZF5YJCXO.js";
61
+ } from "./chunk-B4PRRKMA.js";
74
62
  import {
63
+ CLOUD_COORDINATOR_BUSY_REPLY,
64
+ CLOUD_COORDINATOR_STOPPED_REPLY,
65
+ CLOUD_COORDINATOR_TIMEOUT_REPLY,
66
+ CLOUD_ORCHESTRATOR_GOAL,
67
+ GLOBAL_WORKSPACE_ID,
75
68
  ORCHESTRATOR_AGENT_KINDS,
69
+ SIDEBOARD_FORCE_STOP,
76
70
  assertOrchestratorCapableAgent,
77
71
  coerceOrchestratorAgent,
78
- isOrchestratorCapableAgent
79
- } from "./chunk-GI7E5733.js";
72
+ createGlobalChat,
73
+ ensureCloudCoordinator,
74
+ healOrchestrationSoccerTitles,
75
+ isCloudCoordinatorThread,
76
+ isGlobalRepoPath,
77
+ isGlobalThread,
78
+ isOrchestratorCapableAgent,
79
+ isOrchestratorThread,
80
+ listGlobalThreads,
81
+ orchestrationTitleNeedsSoccerNickname,
82
+ orchestratorSessionPoisonedByBuiltins,
83
+ parseForceStopMessage,
84
+ takenTeamSlugsForOrchestration
85
+ } from "./chunk-7ZOQY4A7.js";
80
86
  import {
81
87
  brightsyConfigPath,
82
88
  brightsyMcpServerName,
@@ -88,7 +94,7 @@ import {
88
94
  loadBrightsyConfig,
89
95
  saveBrightsyConfig,
90
96
  switchBrightsyAccount
91
- } from "./chunk-MBAL5FE6.js";
97
+ } from "./chunk-DB3I75FI.js";
92
98
  import {
93
99
  LEGACY_PLAN_FILE_REL,
94
100
  PLAN_FILE_NAME,
@@ -118,7 +124,7 @@ import {
118
124
  enrichWorkspacesWithGithub,
119
125
  ensureGlobalCoordinatorCwd,
120
126
  formatWorkspaceInventory
121
- } from "./chunk-27MAWNKY.js";
127
+ } from "./chunk-ZRFAZVGL.js";
122
128
  import {
123
129
  HARNESS_ENV_KEYS,
124
130
  appSettingsPath,
@@ -129,6 +135,7 @@ import {
129
135
  autoRunAfterSetupEnabled,
130
136
  brightsyCloudConnectAgent,
131
137
  brightsyCloudConnectEnabled,
138
+ brightsyInjectWorktreeMcpEnabled,
132
139
  caffeinateWhileCloudConnectEnabled,
133
140
  caffeinateWhileRunningEnabled,
134
141
  childEnvWithAppSettings,
@@ -147,18 +154,22 @@ import {
147
154
  maxConcurrentAgents,
148
155
  orchestrationQuotaFallbackAgent,
149
156
  orchestrationQuotaOnLimit,
157
+ resolveAgentExecutable,
150
158
  resolveClaudeExecutable,
151
159
  resolveEffectiveIssueSource,
152
160
  resolveNewThreadOptions,
153
161
  resolveThreadDefaults,
154
162
  saveAppSettings,
155
163
  updateAdvancedSettings,
164
+ updateAgentExecutable,
156
165
  updateAppEnvironment,
157
166
  updateBrightsySettings,
158
167
  updateClaudeSettings,
168
+ updateCodexSettings,
159
169
  updateDefaultsSettings,
160
- updateIntegrationsSettings
161
- } from "./chunk-6CUJQKTJ.js";
170
+ updateIntegrationsSettings,
171
+ updateOpencodeSettings
172
+ } from "./chunk-MHR4OGZ4.js";
162
173
  import {
163
174
  FAMOUS_SOCCER_TEAMS,
164
175
  addPrStackLayer,
@@ -221,7 +232,7 @@ import {
221
232
  worktreeDisplayLabel,
222
233
  worktreeDisplayLabelForGroup,
223
234
  worktreeNameFromPath
224
- } from "./chunk-UGXSSBFJ.js";
235
+ } from "./chunk-4BUCGNZS.js";
225
236
  import {
226
237
  ATTACHMENTS_DIR,
227
238
  LEGACY_ATTACHMENTS_DIR,
@@ -273,12 +284,18 @@ import {
273
284
  worktreesRoot
274
285
  } from "./chunk-M37RITA6.js";
275
286
  import {
287
+ conductorBundledBinDir,
288
+ enrichPathWithNpmGlobalBin,
276
289
  ensureAgentPath,
277
290
  gh,
278
291
  git,
292
+ isConductorBundledCli,
293
+ posixShellSingleQuote,
294
+ resolveCommandBinarySync,
279
295
  resolveGhAuthToken,
280
- run
281
- } from "./chunk-73AT5K4A.js";
296
+ run,
297
+ withExportedPath
298
+ } from "./chunk-4UIYN56C.js";
282
299
 
283
300
  // src/integrations/github.ts
284
301
  async function getGitHubStatus() {
@@ -707,9 +724,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
707
724
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
708
725
  );
709
726
  }
710
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-LALWQWRJ.js");
727
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-QS7A23CB.js");
711
728
  if (isGlobalThread2(thread)) {
712
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-N2R6FGZK.js");
729
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-3BSMKZL4.js");
713
730
  ensureGlobalCoordinatorCwd2(
714
731
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
715
732
  );
@@ -911,6 +928,9 @@ function formatWorktreeDirective(thread, opts) {
911
928
  );
912
929
  return lines.join("\n");
913
930
  }
931
+ function formatWorktreeReminder() {
932
+ 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.";
933
+ }
914
934
  function formatArtifactDirective() {
915
935
  return [
916
936
  "Sideboard side column (desktop UI):",
@@ -996,11 +1016,13 @@ ${prompt}`;
996
1016
  // src/detect/detect.ts
997
1017
  async function detectAgents() {
998
1018
  ensureAgentPath();
1019
+ enrichPathWithNpmGlobalBin();
999
1020
  return Promise.all(allAdapters().map((a) => a.detect()));
1000
1021
  }
1001
1022
  var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
1002
1023
  async function requireAgent(agent, opts) {
1003
1024
  ensureAgentPath();
1025
+ enrichPathWithNpmGlobalBin();
1004
1026
  const status = await Promise.race([
1005
1027
  getAdapter(agent).detect(),
1006
1028
  new Promise((_, reject) => {
@@ -2842,7 +2864,7 @@ async function previewLand(thread) {
2842
2864
  const target = await resolveDefaultBranch(thread.repoPath);
2843
2865
  if (thread.branchName === target || thread.branchName === "main" || thread.branchName === "master") {
2844
2866
  }
2845
- const { stdout: head } = await import("./run-GPSVV5OH.js").then(
2867
+ const { stdout: head } = await import("./run-JLD2Y52J.js").then(
2846
2868
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], thread.worktreePath)
2847
2869
  );
2848
2870
  const current = head.trim();
@@ -2881,7 +2903,7 @@ async function confirmLand(thread, opts) {
2881
2903
  if (preview.dirty) {
2882
2904
  committed = await commitAll(thread.worktreePath, meta.commitMessage);
2883
2905
  }
2884
- const { git: git2 } = await import("./run-GPSVV5OH.js");
2906
+ const { git: git2 } = await import("./run-JLD2Y52J.js");
2885
2907
  const { stdout: headOut } = await git2(
2886
2908
  ["rev-parse", "--abbrev-ref", "HEAD"],
2887
2909
  thread.worktreePath,
@@ -2979,7 +3001,7 @@ async function createThread(input, _onSetupLine) {
2979
3001
  return readThread(thread.id) ?? thread;
2980
3002
  }
2981
3003
  async function listLinearIssues(agent, repoPath) {
2982
- const { getAdapter: getAdapter2 } = await import("./agents-QLSFIQAO.js");
3004
+ const { getAdapter: getAdapter2 } = await import("./agents-TS3EI2YI.js");
2983
3005
  await requireAgent(agent, { requireLinear: true });
2984
3006
  const adapter = getAdapter2(agent);
2985
3007
  if (!adapter.listLinearIssues) {
@@ -3507,7 +3529,7 @@ async function adoptThread(input) {
3507
3529
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3508
3530
  }
3509
3531
  const repoPath = await resolveRepoRoot(input.worktreePath);
3510
- const { stdout: branch } = await import("./run-GPSVV5OH.js").then(
3532
+ const { stdout: branch } = await import("./run-JLD2Y52J.js").then(
3511
3533
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
3512
3534
  );
3513
3535
  const thread = createEmptyThread({
@@ -3522,7 +3544,7 @@ async function adoptThread(input) {
3522
3544
  messages: input.messages ?? []
3523
3545
  });
3524
3546
  writeThread(thread);
3525
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VCAIHVIE.js");
3547
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-EBFW7I7S.js");
3526
3548
  await ensureWorkspace2(repoPath);
3527
3549
  return thread;
3528
3550
  }
@@ -4473,7 +4495,7 @@ var Orchestrator = class {
4473
4495
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
4474
4496
  });
4475
4497
  }
4476
- const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-HK4KAM4R.js");
4498
+ const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4477
4499
  if (autoCleanupOrphansEnabled2() && shouldRunWorktreeCleanup() && orphans.length > 0) {
4478
4500
  await cleanupOrphanWorktrees({ repoPaths });
4479
4501
  }
@@ -4630,7 +4652,7 @@ var Orchestrator = class {
4630
4652
  }
4631
4653
  async finishCreateThread(threadId, prompt) {
4632
4654
  await this.runSetupAfterCreate(threadId);
4633
- const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-HK4KAM4R.js");
4655
+ const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4634
4656
  if (autoRunAfterSetupEnabled2()) {
4635
4657
  try {
4636
4658
  await this.startDev(threadId);
@@ -4871,13 +4893,14 @@ var Orchestrator = class {
4871
4893
  "Files column: MCP present_files (brightsy account storage or memory).",
4872
4894
  "Do not say artifacts/CMS UI are unavailable."
4873
4895
  ].join(" ") : null;
4896
+ const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
4874
4897
  const agentPrompt = [
4875
4898
  thread.planMode ? PLAN_MODE_INSTRUCTION : null,
4876
4899
  orchestrationReminder,
4900
+ worktreeReminder,
4877
4901
  artifactReminder,
4878
4902
  expandedPrompt
4879
4903
  ].filter(Boolean).join("\n\n");
4880
- const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
4881
4904
  if (thread.attachments.length > 0) {
4882
4905
  updateThread(threadId, { attachments: [] });
4883
4906
  }
@@ -4901,11 +4924,10 @@ var Orchestrator = class {
4901
4924
  });
4902
4925
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
4903
4926
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
4904
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-HK4KAM4R.js");
4927
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4905
4928
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
4906
4929
  customPrompt: settings?.prompts?.renameBranch
4907
4930
  }) : null;
4908
- const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
4909
4931
  let seed = null;
4910
4932
  if (!fresh.sessionId) {
4911
4933
  const prior = fresh.messages.slice(0, -1);
@@ -4926,12 +4948,12 @@ var Orchestrator = class {
4926
4948
  });
4927
4949
  }
4928
4950
  }
4929
- const cachedPrefix = [
4951
+ const cachedPrefix = fresh.sessionId ? "" : [
4930
4952
  coordinatorDirective,
4931
4953
  worktreeDirective,
4932
4954
  artifactDirective,
4933
4955
  renameBranchDirective,
4934
- ...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
4956
+ seed
4935
4957
  ].filter(Boolean).join("\n\n---\n\n");
4936
4958
  try {
4937
4959
  const stderrTail = [];
@@ -5018,15 +5040,11 @@ var Orchestrator = class {
5018
5040
  const retryThread = this.requireThread(threadId);
5019
5041
  const prior = retryThread.messages.slice(0, -1);
5020
5042
  const retrySeed = buildSessionSeed(prior);
5021
- const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
5022
- loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
5023
- );
5024
5043
  const retryPrefix = [
5025
5044
  coordinatorDirective,
5026
5045
  worktreeDirective,
5027
5046
  artifactDirective,
5028
5047
  renameBranchDirective,
5029
- retryInstructions,
5030
5048
  retrySeed
5031
5049
  ].filter(Boolean).join("\n\n---\n\n");
5032
5050
  const retryHandle = await spawnAgentTurn(
@@ -5596,7 +5614,7 @@ var Orchestrator = class {
5596
5614
  updateThread(thread.id, { title: meta.title });
5597
5615
  }
5598
5616
  }
5599
- const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-HK4KAM4R.js");
5617
+ const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
5600
5618
  const latest = this.requireThread(thread.id);
5601
5619
  if (!shouldAutoArchiveOnPrMerge({
5602
5620
  previousPrState: prevState || null,
@@ -5820,7 +5838,7 @@ var Orchestrator = class {
5820
5838
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
5821
5839
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
5822
5840
  try {
5823
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VCAIHVIE.js");
5841
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-EBFW7I7S.js");
5824
5842
  await ensureWorkspace2(thread.repoPath);
5825
5843
  } catch {
5826
5844
  }
@@ -5841,7 +5859,7 @@ var Orchestrator = class {
5841
5859
  await runArchiveScript(thread.repoPath, thread.worktreePath);
5842
5860
  } catch {
5843
5861
  }
5844
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-HK4KAM4R.js");
5862
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
5845
5863
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
5846
5864
  await removeWorktree(thread.repoPath, thread.worktreePath, {
5847
5865
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -5862,7 +5880,7 @@ var Orchestrator = class {
5862
5880
  return restored2;
5863
5881
  }
5864
5882
  if (!existsSync13(thread.worktreePath)) {
5865
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-MV4WRW6G.js");
5883
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-7NBPIVFZ.js");
5866
5884
  const { execa: execa6 } = await import("execa");
5867
5885
  const slug = thread.worktreePath.split("/").pop();
5868
5886
  const dest = thread.worktreePath;
@@ -5971,7 +5989,7 @@ async function startOrchestration(opts) {
5971
5989
  sourceRef: "default",
5972
5990
  ...createOpts
5973
5991
  }).catch(async () => {
5974
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-MV4WRW6G.js");
5992
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-7NBPIVFZ.js");
5975
5993
  const repo = await resolveRepoRoot2(repoPath);
5976
5994
  const def = await resolveDefaultBranch2(repo);
5977
5995
  return createThread({
@@ -6127,7 +6145,7 @@ function withTimeout(promise, ms, label) {
6127
6145
  async function startMcpServer() {
6128
6146
  const orch = getOrchestrator();
6129
6147
  try {
6130
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-HK4KAM4R.js");
6148
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-ONKMFSEJ.js");
6131
6149
  orch.setMaxConcurrent(maxConcurrentAgents2());
6132
6150
  } catch {
6133
6151
  }
@@ -6218,14 +6236,13 @@ async function startMcpServer() {
6218
6236
  ),
6219
6237
  artifact_id: z.string().optional().describe("Stable id when updating the same artifact across turns")
6220
6238
  },
6221
- async ({ title, type, content, artifact_id }) => {
6239
+ async ({ title, type, artifact_id }) => {
6222
6240
  const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
6223
6241
  const payload = {
6224
6242
  ok: true,
6225
6243
  artifact_id: id,
6226
6244
  title,
6227
6245
  type,
6228
- content,
6229
6246
  message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
6230
6247
  };
6231
6248
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6278,7 +6295,6 @@ async function startMcpServer() {
6278
6295
  ok: true,
6279
6296
  path,
6280
6297
  title: title?.trim() || "Plan",
6281
- content,
6282
6298
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
6283
6299
  };
6284
6300
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6308,9 +6324,6 @@ async function startMcpServer() {
6308
6324
  datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
6309
6325
  resource_id: args.resource_id,
6310
6326
  record_id: args.record_id,
6311
- resource: args.resource,
6312
- record: args.record,
6313
- records: args.records,
6314
6327
  message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
6315
6328
  };
6316
6329
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6338,739 +6351,741 @@ async function startMcpServer() {
6338
6351
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6339
6352
  }
6340
6353
  );
6341
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-HK4KAM4R.js");
6342
- const accountDefaults = resolveThreadDefaults2();
6343
- const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
6344
- server.tool(
6345
- "create_thread",
6346
- `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.`,
6347
- {
6348
- sourceType: z.enum(["branch", "pr", "ticket"]),
6349
- sourceRef: z.string(),
6350
- agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
6351
- model: z.string().nullable().optional().describe(
6352
- `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
6353
- ),
6354
- repoPath: z.string(),
6355
- title: z.string().optional(),
6356
- parentThreadId: z.string().optional().describe(
6357
- "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
6358
- )
6359
- },
6360
- async (args) => {
6361
- const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
6362
- let parentId = args.parentThreadId?.trim() || "";
6363
- let parent = parentId ? orch.getThread(parentId) : null;
6364
- let parentCorrectedFrom;
6365
- if (envParentId) {
6366
- const envParent = orch.getThread(envParentId);
6367
- if (envParent) {
6368
- if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
6369
- else if (!parentId) parentCorrectedFrom = void 0;
6370
- parentId = envParentId;
6371
- parent = envParent;
6354
+ if (sideboardMcpProfile() !== "worktree") {
6355
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-ONKMFSEJ.js");
6356
+ const accountDefaults = resolveThreadDefaults2();
6357
+ const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
6358
+ server.tool(
6359
+ "create_thread",
6360
+ `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.`,
6361
+ {
6362
+ sourceType: z.enum(["branch", "pr", "ticket"]),
6363
+ sourceRef: z.string(),
6364
+ agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
6365
+ model: z.string().nullable().optional().describe(
6366
+ `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
6367
+ ),
6368
+ repoPath: z.string(),
6369
+ title: z.string().optional(),
6370
+ parentThreadId: z.string().optional().describe(
6371
+ "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
6372
+ )
6373
+ },
6374
+ async (args) => {
6375
+ const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
6376
+ let parentId = args.parentThreadId?.trim() || "";
6377
+ let parent = parentId ? orch.getThread(parentId) : null;
6378
+ let parentCorrectedFrom;
6379
+ if (envParentId) {
6380
+ const envParent = orch.getThread(envParentId);
6381
+ if (envParent) {
6382
+ if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
6383
+ else if (!parentId) parentCorrectedFrom = void 0;
6384
+ parentId = envParentId;
6385
+ parent = envParent;
6386
+ }
6372
6387
  }
6373
- }
6374
- if (parentId && !parent) {
6375
- parentCorrectedFrom = parentId;
6376
- parentId = "";
6377
- parent = null;
6378
- }
6379
- if (parentId) {
6380
- const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
6381
- if (children.length >= MAX_ORCH_THREADS) {
6388
+ if (parentId && !parent) {
6389
+ parentCorrectedFrom = parentId;
6390
+ parentId = "";
6391
+ parent = null;
6392
+ }
6393
+ if (parentId) {
6394
+ const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
6395
+ if (children.length >= MAX_ORCH_THREADS) {
6396
+ return {
6397
+ content: [
6398
+ {
6399
+ type: "text",
6400
+ text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
6401
+ }
6402
+ ],
6403
+ isError: true
6404
+ };
6405
+ }
6406
+ }
6407
+ let agentArg = args.agent;
6408
+ let agentCoercedFrom;
6409
+ const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
6410
+ if (resolvedProbe === "codex") {
6411
+ agentCoercedFrom = agentArg ?? "codex";
6412
+ const accountAgent = resolveNewThreadOptions2({}).agent;
6413
+ agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
6414
+ }
6415
+ const opts = resolveNewThreadOptions2({
6416
+ agent: agentArg,
6417
+ model: args.model
6418
+ });
6419
+ try {
6420
+ const thread = await withTimeout(
6421
+ orch.createThread({
6422
+ sourceType: args.sourceType,
6423
+ sourceRef: args.sourceRef,
6424
+ agent: opts.agent,
6425
+ model: opts.model,
6426
+ effort: opts.effort,
6427
+ fast: opts.fast,
6428
+ repoPath: args.repoPath,
6429
+ title: args.title,
6430
+ parentThreadId: parentId || null
6431
+ }),
6432
+ CREATE_THREAD_TIMEOUT_MS,
6433
+ "create_thread"
6434
+ );
6435
+ const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
6382
6436
  return {
6383
6437
  content: [
6384
6438
  {
6385
6439
  type: "text",
6386
- text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
6440
+ text: JSON.stringify({
6441
+ id: thread.id,
6442
+ title: thread.title,
6443
+ branchName: thread.branchName,
6444
+ worktreePath: thread.worktreePath,
6445
+ agent: thread.agent,
6446
+ model: thread.model,
6447
+ status: thread.status,
6448
+ link: `sideboard://thread/${thread.id}`,
6449
+ parentThreadId: thread.parentThreadId,
6450
+ ...agentCoercedFrom ? {
6451
+ agentCoercedFrom,
6452
+ note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
6453
+ } : {},
6454
+ ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
6455
+ })
6456
+ }
6457
+ ]
6458
+ };
6459
+ } catch (err) {
6460
+ const message = err instanceof Error ? err.message : String(err);
6461
+ return {
6462
+ content: [
6463
+ {
6464
+ type: "text",
6465
+ text: `create_thread failed: ${message}`
6387
6466
  }
6388
6467
  ],
6389
6468
  isError: true
6390
6469
  };
6391
6470
  }
6392
6471
  }
6393
- let agentArg = args.agent;
6394
- let agentCoercedFrom;
6395
- const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
6396
- if (resolvedProbe === "codex") {
6397
- agentCoercedFrom = agentArg ?? "codex";
6398
- const accountAgent = resolveNewThreadOptions2({}).agent;
6399
- agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
6400
- }
6401
- const opts = resolveNewThreadOptions2({
6402
- agent: agentArg,
6403
- model: args.model
6404
- });
6405
- try {
6406
- const thread = await withTimeout(
6407
- orch.createThread({
6408
- sourceType: args.sourceType,
6409
- sourceRef: args.sourceRef,
6410
- agent: opts.agent,
6411
- model: opts.model,
6412
- effort: opts.effort,
6413
- fast: opts.fast,
6414
- repoPath: args.repoPath,
6415
- title: args.title,
6416
- parentThreadId: parentId || null
6417
- }),
6418
- CREATE_THREAD_TIMEOUT_MS,
6419
- "create_thread"
6420
- );
6421
- const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
6472
+ );
6473
+ server.tool(
6474
+ "send_to_thread",
6475
+ "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.",
6476
+ {
6477
+ ref: z.string(),
6478
+ prompt: z.string(),
6479
+ force_stop: z.boolean().optional()
6480
+ },
6481
+ async ({ ref, prompt, force_stop }) => {
6482
+ if (force_stop) {
6483
+ const existing = orch.getThread(ref);
6484
+ if (existing) {
6485
+ orch.stop(ref, { clearQueue: true });
6486
+ }
6487
+ }
6488
+ const thread = await orch.send(ref, prompt);
6422
6489
  return {
6423
6490
  content: [
6424
6491
  {
6425
6492
  type: "text",
6426
6493
  text: JSON.stringify({
6427
6494
  id: thread.id,
6428
- title: thread.title,
6429
- branchName: thread.branchName,
6430
- worktreePath: thread.worktreePath,
6431
- agent: thread.agent,
6432
- model: thread.model,
6433
6495
  status: thread.status,
6434
- link: `sideboard://thread/${thread.id}`,
6435
- parentThreadId: thread.parentThreadId,
6436
- ...agentCoercedFrom ? {
6437
- agentCoercedFrom,
6438
- note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
6439
- } : {},
6440
- ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
6496
+ queueLength: thread.queue.length,
6497
+ forceStopped: Boolean(force_stop)
6441
6498
  })
6442
6499
  }
6443
6500
  ]
6444
6501
  };
6445
- } catch (err) {
6446
- const message = err instanceof Error ? err.message : String(err);
6502
+ }
6503
+ );
6504
+ server.tool(
6505
+ "wait_for_turn",
6506
+ "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
6507
+ {
6508
+ ref: z.string(),
6509
+ timeoutMs: z.number().optional()
6510
+ },
6511
+ async ({ ref, timeoutMs }) => {
6512
+ const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
6513
+ const result = orch.getTurnResult(thread.id);
6447
6514
  return {
6448
6515
  content: [
6449
6516
  {
6450
6517
  type: "text",
6451
- text: `create_thread failed: ${message}`
6518
+ text: JSON.stringify({
6519
+ id: thread.id,
6520
+ status: result.status,
6521
+ text: result.text
6522
+ })
6452
6523
  }
6453
- ],
6454
- isError: true
6524
+ ]
6455
6525
  };
6456
6526
  }
6457
- }
6458
- );
6459
- server.tool(
6460
- "send_to_thread",
6461
- "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.",
6462
- {
6463
- ref: z.string(),
6464
- prompt: z.string(),
6465
- force_stop: z.boolean().optional()
6466
- },
6467
- async ({ ref, prompt, force_stop }) => {
6468
- if (force_stop) {
6469
- const existing = orch.getThread(ref);
6470
- if (existing) {
6471
- orch.stop(ref, { clearQueue: true });
6472
- }
6527
+ );
6528
+ server.tool(
6529
+ "get_turn_result",
6530
+ "Final assistant message only (not full transcript)",
6531
+ { ref: z.string() },
6532
+ async ({ ref }) => {
6533
+ const result = orch.getTurnResult(ref);
6534
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
6473
6535
  }
6474
- const thread = await orch.send(ref, prompt);
6475
- return {
6476
- content: [
6477
- {
6478
- type: "text",
6479
- text: JSON.stringify({
6480
- id: thread.id,
6481
- status: thread.status,
6482
- queueLength: thread.queue.length,
6483
- forceStopped: Boolean(force_stop)
6484
- })
6485
- }
6486
- ]
6487
- };
6488
- }
6489
- );
6490
- server.tool(
6491
- "wait_for_turn",
6492
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
6493
- {
6494
- ref: z.string(),
6495
- timeoutMs: z.number().optional()
6496
- },
6497
- async ({ ref, timeoutMs }) => {
6498
- const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
6499
- const result = orch.getTurnResult(thread.id);
6500
- return {
6501
- content: [
6502
- {
6503
- type: "text",
6504
- text: JSON.stringify({
6505
- id: thread.id,
6506
- status: result.status,
6507
- text: result.text
6508
- })
6509
- }
6510
- ]
6511
- };
6512
- }
6513
- );
6514
- server.tool(
6515
- "get_turn_result",
6516
- "Final assistant message only (not full transcript)",
6517
- { ref: z.string() },
6518
- async ({ ref }) => {
6519
- const result = orch.getTurnResult(ref);
6520
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
6521
- }
6522
- );
6523
- server.tool(
6524
- "stop_thread",
6525
- "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.",
6526
- {
6527
- ref: z.string(),
6528
- force: z.boolean().optional()
6529
- },
6530
- async ({ ref, force }) => {
6531
- const t = orch.getThread(ref);
6532
- if (!t) {
6536
+ );
6537
+ server.tool(
6538
+ "stop_thread",
6539
+ "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.",
6540
+ {
6541
+ ref: z.string(),
6542
+ force: z.boolean().optional()
6543
+ },
6544
+ async ({ ref, force }) => {
6545
+ const t = orch.getThread(ref);
6546
+ if (!t) {
6547
+ return {
6548
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
6549
+ isError: true
6550
+ };
6551
+ }
6552
+ const clearQueue = force !== false;
6553
+ const hadQueued = t.queue.length > 0;
6554
+ const stopped = orch.stop(ref, { clearQueue });
6533
6555
  return {
6534
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6535
- isError: true
6556
+ content: [
6557
+ {
6558
+ type: "text",
6559
+ text: JSON.stringify({
6560
+ id: stopped.id,
6561
+ status: stopped.status,
6562
+ clearedQueue: clearQueue && hadQueued
6563
+ })
6564
+ }
6565
+ ]
6536
6566
  };
6537
6567
  }
6538
- const clearQueue = force !== false;
6539
- const hadQueued = t.queue.length > 0;
6540
- const stopped = orch.stop(ref, { clearQueue });
6541
- return {
6542
- content: [
6543
- {
6544
- type: "text",
6545
- text: JSON.stringify({
6546
- id: stopped.id,
6547
- status: stopped.status,
6548
- clearedQueue: clearQueue && hadQueued
6549
- })
6550
- }
6551
- ]
6552
- };
6553
- }
6554
- );
6555
- server.tool(
6556
- "archive_thread",
6557
- "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.",
6558
- { ref: z.string() },
6559
- async ({ ref }) => {
6560
- const t = orch.getThread(ref);
6561
- if (!t) {
6568
+ );
6569
+ server.tool(
6570
+ "archive_thread",
6571
+ "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.",
6572
+ { ref: z.string() },
6573
+ async ({ ref }) => {
6574
+ const t = orch.getThread(ref);
6575
+ if (!t) {
6576
+ return {
6577
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
6578
+ isError: true
6579
+ };
6580
+ }
6581
+ const blocked = mcpArchiveBlockedReason(t);
6582
+ if (blocked) {
6583
+ return {
6584
+ content: [{ type: "text", text: blocked }],
6585
+ isError: true
6586
+ };
6587
+ }
6588
+ const archived = await orch.archive(ref);
6562
6589
  return {
6563
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6564
- isError: true
6590
+ content: [
6591
+ {
6592
+ type: "text",
6593
+ text: JSON.stringify({
6594
+ id: archived.id,
6595
+ status: archived.status
6596
+ })
6597
+ }
6598
+ ]
6565
6599
  };
6566
6600
  }
6567
- const blocked = mcpArchiveBlockedReason(t);
6568
- if (blocked) {
6569
- return {
6570
- content: [{ type: "text", text: blocked }],
6571
- isError: true
6572
- };
6601
+ );
6602
+ server.tool(
6603
+ "restore_thread",
6604
+ "Restore an archived thread (recreates worktree from branch when needed)",
6605
+ { ref: z.string() },
6606
+ async ({ ref }) => {
6607
+ try {
6608
+ const restored = await orch.restore(ref);
6609
+ return {
6610
+ content: [
6611
+ {
6612
+ type: "text",
6613
+ text: JSON.stringify({
6614
+ id: restored.id,
6615
+ status: restored.status,
6616
+ worktreePath: restored.worktreePath
6617
+ })
6618
+ }
6619
+ ]
6620
+ };
6621
+ } catch (err) {
6622
+ const message = err instanceof Error ? err.message : String(err);
6623
+ return { content: [{ type: "text", text: message }], isError: true };
6624
+ }
6573
6625
  }
6574
- const archived = await orch.archive(ref);
6575
- return {
6576
- content: [
6577
- {
6578
- type: "text",
6579
- text: JSON.stringify({
6580
- id: archived.id,
6581
- status: archived.status
6582
- })
6583
- }
6584
- ]
6585
- };
6586
- }
6587
- );
6588
- server.tool(
6589
- "restore_thread",
6590
- "Restore an archived thread (recreates worktree from branch when needed)",
6591
- { ref: z.string() },
6592
- async ({ ref }) => {
6593
- try {
6594
- const restored = await orch.restore(ref);
6626
+ );
6627
+ server.tool(
6628
+ "get_diff",
6629
+ "Compact diff summary (capped hunks, paginated)",
6630
+ {
6631
+ ref: z.string(),
6632
+ maxFiles: z.number().optional()
6633
+ },
6634
+ async ({ ref, maxFiles }) => {
6635
+ const summary = await orch.diffSummary(ref);
6595
6636
  return {
6596
6637
  content: [
6597
6638
  {
6598
6639
  type: "text",
6599
6640
  text: JSON.stringify({
6600
- id: restored.id,
6601
- status: restored.status,
6602
- worktreePath: restored.worktreePath
6641
+ ...summary,
6642
+ files: summary.files.slice(0, maxFiles ?? 10)
6603
6643
  })
6604
6644
  }
6605
6645
  ]
6606
6646
  };
6607
- } catch (err) {
6608
- const message = err instanceof Error ? err.message : String(err);
6609
- return { content: [{ type: "text", text: message }], isError: true };
6610
6647
  }
6611
- }
6612
- );
6613
- server.tool(
6614
- "get_diff",
6615
- "Compact diff summary (capped hunks, paginated)",
6616
- {
6617
- ref: z.string(),
6618
- maxFiles: z.number().optional()
6619
- },
6620
- async ({ ref, maxFiles }) => {
6621
- const summary = await orch.diffSummary(ref);
6622
- return {
6623
- content: [
6624
- {
6625
- type: "text",
6626
- text: JSON.stringify({
6627
- ...summary,
6628
- files: summary.files.slice(0, maxFiles ?? 10)
6629
- })
6648
+ );
6649
+ server.tool(
6650
+ "request_review",
6651
+ '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.',
6652
+ { ref: z.string().describe("Worktree thread id/ref to review") },
6653
+ async ({ ref }) => {
6654
+ try {
6655
+ const tab = await orch.requestReview(ref);
6656
+ const from = orch.getThread(ref);
6657
+ return {
6658
+ content: [
6659
+ {
6660
+ type: "text",
6661
+ text: JSON.stringify({
6662
+ id: tab.id,
6663
+ title: tab.title,
6664
+ status: tab.status,
6665
+ fromThreadId: from?.id ?? ref,
6666
+ link: `sideboard://thread/${tab.id}`
6667
+ })
6668
+ }
6669
+ ]
6670
+ };
6671
+ } catch (err) {
6672
+ const message = err instanceof Error ? err.message : String(err);
6673
+ return { content: [{ type: "text", text: message }], isError: true };
6674
+ }
6675
+ }
6676
+ );
6677
+ const agentEnum = z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6678
+ server.tool(
6679
+ "list_models",
6680
+ "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.",
6681
+ {
6682
+ agent: agentEnum.optional().describe("Limit to one agent; omit for all")
6683
+ },
6684
+ async ({ agent }) => {
6685
+ try {
6686
+ const catalogs = await listModelsForAgent(agent);
6687
+ return {
6688
+ content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
6689
+ };
6690
+ } catch (err) {
6691
+ const message = err instanceof Error ? err.message : String(err);
6692
+ return { content: [{ type: "text", text: message }], isError: true };
6693
+ }
6694
+ }
6695
+ );
6696
+ server.tool(
6697
+ "fork_worktree",
6698
+ "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.",
6699
+ {
6700
+ ref: z.string().describe("Worktree thread id/ref to fork"),
6701
+ through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6702
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6703
+ model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6704
+ title: z.string().optional()
6705
+ },
6706
+ async ({ ref, through_index, agent, model, title }) => {
6707
+ try {
6708
+ const source = orch.getThread(ref);
6709
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
6710
+ const thread = await orch.forkThreadWorktree({
6711
+ threadId: ref,
6712
+ throughIndex: through_index,
6713
+ agent,
6714
+ model,
6715
+ title
6716
+ });
6717
+ return {
6718
+ content: [
6719
+ {
6720
+ type: "text",
6721
+ text: JSON.stringify({
6722
+ id: thread.id,
6723
+ title: thread.title,
6724
+ status: thread.status,
6725
+ agent: thread.agent,
6726
+ model: thread.model,
6727
+ branchName: thread.branchName,
6728
+ worktreePath: thread.worktreePath,
6729
+ fromThreadId: source?.id ?? ref,
6730
+ link: `sideboard://thread/${thread.id}`
6731
+ })
6732
+ }
6733
+ ]
6734
+ };
6735
+ } catch (err) {
6736
+ const message = err instanceof Error ? err.message : String(err);
6737
+ return { content: [{ type: "text", text: message }], isError: true };
6738
+ }
6739
+ }
6740
+ );
6741
+ server.tool(
6742
+ "fork_chat",
6743
+ "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.",
6744
+ {
6745
+ ref: z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6746
+ through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6747
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6748
+ model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6749
+ title: z.string().optional()
6750
+ },
6751
+ async ({ ref, through_index, agent, model, title }) => {
6752
+ try {
6753
+ const source = orch.getThread(ref);
6754
+ if (!source) {
6755
+ return {
6756
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
6757
+ isError: true
6758
+ };
6630
6759
  }
6631
- ]
6632
- };
6633
- }
6634
- );
6635
- server.tool(
6636
- "request_review",
6637
- '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.',
6638
- { ref: z.string().describe("Worktree thread id/ref to review") },
6639
- async ({ ref }) => {
6640
- try {
6641
- const tab = await orch.requestReview(ref);
6642
- const from = orch.getThread(ref);
6760
+ const tab = orch.forkChatTab({
6761
+ threadId: source.id,
6762
+ throughIndex: through_index,
6763
+ agent,
6764
+ model,
6765
+ title
6766
+ });
6767
+ return {
6768
+ content: [
6769
+ {
6770
+ type: "text",
6771
+ text: JSON.stringify({
6772
+ id: tab.id,
6773
+ title: tab.title,
6774
+ status: tab.status,
6775
+ agent: tab.agent,
6776
+ model: tab.model,
6777
+ sourceType: tab.sourceType,
6778
+ worktreePath: tab.worktreePath,
6779
+ fromThreadId: source.id,
6780
+ link: `sideboard://thread/${tab.id}`
6781
+ })
6782
+ }
6783
+ ]
6784
+ };
6785
+ } catch (err) {
6786
+ const message = err instanceof Error ? err.message : String(err);
6787
+ return { content: [{ type: "text", text: message }], isError: true };
6788
+ }
6789
+ }
6790
+ );
6791
+ server.tool(
6792
+ "run_dev_script",
6793
+ "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
6794
+ {
6795
+ ref: z.string(),
6796
+ name: z.string().optional()
6797
+ },
6798
+ async ({ ref, name }) => {
6799
+ const result = await orch.startDev(ref, name);
6643
6800
  return {
6644
6801
  content: [
6645
6802
  {
6646
6803
  type: "text",
6647
6804
  text: JSON.stringify({
6648
- id: tab.id,
6649
- title: tab.title,
6650
- status: tab.status,
6651
- fromThreadId: from?.id ?? ref,
6652
- link: `sideboard://thread/${tab.id}`
6805
+ port: result.port,
6806
+ scriptName: result.scriptName,
6807
+ ports: result.ports,
6808
+ url: `http://localhost:${result.port}`
6653
6809
  })
6654
6810
  }
6655
6811
  ]
6656
6812
  };
6657
- } catch (err) {
6658
- const message = err instanceof Error ? err.message : String(err);
6659
- return { content: [{ type: "text", text: message }], isError: true };
6660
6813
  }
6661
- }
6662
- );
6663
- const agentEnum = z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6664
- server.tool(
6665
- "list_models",
6666
- "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.",
6667
- {
6668
- agent: agentEnum.optional().describe("Limit to one agent; omit for all")
6669
- },
6670
- async ({ agent }) => {
6671
- try {
6672
- const catalogs = await listModelsForAgent(agent);
6814
+ );
6815
+ server.tool(
6816
+ "list_run_scripts",
6817
+ "List named run scripts available for a thread",
6818
+ { ref: z.string() },
6819
+ async ({ ref }) => {
6820
+ const scripts = orch.listThreadRunScripts(ref);
6821
+ const active = orch.getActiveRuns(ref);
6673
6822
  return {
6674
- content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
6823
+ content: [
6824
+ {
6825
+ type: "text",
6826
+ text: JSON.stringify({ scripts, active }, null, 2)
6827
+ }
6828
+ ]
6675
6829
  };
6676
- } catch (err) {
6677
- const message = err instanceof Error ? err.message : String(err);
6678
- return { content: [{ type: "text", text: message }], isError: true };
6679
6830
  }
6680
- }
6681
- );
6682
- server.tool(
6683
- "fork_worktree",
6684
- "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.",
6685
- {
6686
- ref: z.string().describe("Worktree thread id/ref to fork"),
6687
- through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6688
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6689
- model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6690
- title: z.string().optional()
6691
- },
6692
- async ({ ref, through_index, agent, model, title }) => {
6693
- try {
6694
- const source = orch.getThread(ref);
6695
- if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
6696
- const thread = await orch.forkThreadWorktree({
6697
- threadId: ref,
6698
- throughIndex: through_index,
6699
- agent,
6700
- model,
6701
- title
6831
+ );
6832
+ server.tool(
6833
+ "stop_dev_script",
6834
+ "Stop a running script for a thread (all scripts if name omitted)",
6835
+ {
6836
+ ref: z.string(),
6837
+ name: z.string().optional()
6838
+ },
6839
+ async ({ ref, name }) => {
6840
+ orch.stopDev(ref, name);
6841
+ return { content: [{ type: "text", text: "ok" }] };
6842
+ }
6843
+ );
6844
+ server.tool(
6845
+ "run_setup",
6846
+ "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
6847
+ { ref: z.string() },
6848
+ async ({ ref }) => {
6849
+ const result = await orch.runSetup(ref);
6850
+ return {
6851
+ content: [{ type: "text", text: JSON.stringify(result) }]
6852
+ };
6853
+ }
6854
+ );
6855
+ server.tool(
6856
+ "add_workspace",
6857
+ "Register a git repo as a Sideboard workspace",
6858
+ { repoPath: z.string() },
6859
+ async ({ repoPath }) => {
6860
+ const ws = await orch.addWorkspace(repoPath);
6861
+ return { content: [{ type: "text", text: JSON.stringify(ws) }] };
6862
+ }
6863
+ );
6864
+ server.tool(
6865
+ "remove_workspace",
6866
+ "Unregister a Sideboard workspace (does not archive threads)",
6867
+ { repoPath: z.string() },
6868
+ async ({ repoPath }) => {
6869
+ orch.removeWorkspace(repoPath);
6870
+ return { content: [{ type: "text", text: "ok" }] };
6871
+ }
6872
+ );
6873
+ server.tool(
6874
+ "fanout",
6875
+ "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
6876
+ {
6877
+ prompt: z.string(),
6878
+ agents: z.array(z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6879
+ repoPath: z.string(),
6880
+ sourceType: z.enum(["branch", "pr", "ticket"]).optional(),
6881
+ sourceRef: z.string().optional(),
6882
+ title: z.string().optional()
6883
+ },
6884
+ async (args) => {
6885
+ const threads = await orch.bestOfN(args);
6886
+ return {
6887
+ content: [
6888
+ {
6889
+ type: "text",
6890
+ text: JSON.stringify(
6891
+ threads.map((t) => ({
6892
+ id: t.id,
6893
+ agent: t.agent,
6894
+ branchName: t.branchName,
6895
+ worktreePath: t.worktreePath
6896
+ })),
6897
+ null,
6898
+ 2
6899
+ )
6900
+ }
6901
+ ]
6902
+ };
6903
+ }
6904
+ );
6905
+ server.tool(
6906
+ "list_branches",
6907
+ "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).",
6908
+ {
6909
+ repoPath: z.string(),
6910
+ unmergedOnly: z.boolean().optional()
6911
+ },
6912
+ async ({ repoPath, unmergedOnly }) => {
6913
+ const root = await resolveRepoRoot(repoPath);
6914
+ const branches = await listBranches(root, {
6915
+ unmergedOnly: unmergedOnly !== false
6702
6916
  });
6703
6917
  return {
6704
6918
  content: [
6705
6919
  {
6706
6920
  type: "text",
6707
- text: JSON.stringify({
6708
- id: thread.id,
6709
- title: thread.title,
6710
- status: thread.status,
6711
- agent: thread.agent,
6712
- model: thread.model,
6713
- branchName: thread.branchName,
6714
- worktreePath: thread.worktreePath,
6715
- fromThreadId: source?.id ?? ref,
6716
- link: `sideboard://thread/${thread.id}`
6717
- })
6921
+ text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
6718
6922
  }
6719
6923
  ]
6720
6924
  };
6721
- } catch (err) {
6722
- const message = err instanceof Error ? err.message : String(err);
6723
- return { content: [{ type: "text", text: message }], isError: true };
6724
6925
  }
6725
- }
6726
- );
6727
- server.tool(
6728
- "fork_chat",
6729
- "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.",
6730
- {
6731
- ref: z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6732
- through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6733
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6734
- model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6735
- title: z.string().optional()
6736
- },
6737
- async ({ ref, through_index, agent, model, title }) => {
6738
- try {
6739
- const source = orch.getThread(ref);
6740
- if (!source) {
6741
- return {
6742
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6743
- isError: true
6744
- };
6745
- }
6746
- const tab = orch.forkChatTab({
6747
- threadId: source.id,
6748
- throughIndex: through_index,
6749
- agent,
6750
- model,
6751
- title
6926
+ );
6927
+ server.tool(
6928
+ "list_prs",
6929
+ "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.",
6930
+ { repoPath: z.string() },
6931
+ async ({ repoPath }) => {
6932
+ const root = await resolveRepoRoot(repoPath);
6933
+ const prs = await listPrs(root);
6934
+ return {
6935
+ content: [
6936
+ {
6937
+ type: "text",
6938
+ text: prs.map(
6939
+ (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
6940
+ ).join("\n")
6941
+ }
6942
+ ]
6943
+ };
6944
+ }
6945
+ );
6946
+ server.tool(
6947
+ "get_pr_stack",
6948
+ "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.",
6949
+ { ref: z.string() },
6950
+ async ({ ref }) => {
6951
+ const stack = await orch.getPrStack(ref);
6952
+ return {
6953
+ content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
6954
+ };
6955
+ }
6956
+ );
6957
+ server.tool(
6958
+ "open_pr_stack_layers",
6959
+ "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6960
+ {
6961
+ ref: z.string(),
6962
+ layer: z.number().int().positive().optional()
6963
+ },
6964
+ async ({ ref, layer }) => {
6965
+ const result = await orch.openPrStackLayers(ref, { layer });
6966
+ return {
6967
+ content: [
6968
+ {
6969
+ type: "text",
6970
+ text: JSON.stringify(
6971
+ {
6972
+ stackNumber: result.stack.stackNumber,
6973
+ trunk: result.stack.trunk,
6974
+ threads: result.threads.map((t) => ({
6975
+ id: t.id,
6976
+ title: t.title,
6977
+ branchName: t.branchName,
6978
+ stackLayer: t.stackLayer,
6979
+ worktreePath: t.worktreePath,
6980
+ prUrl: t.prUrl,
6981
+ link: `sideboard://thread/${t.id}`
6982
+ }))
6983
+ },
6984
+ null,
6985
+ 2
6986
+ )
6987
+ }
6988
+ ]
6989
+ };
6990
+ }
6991
+ );
6992
+ server.tool(
6993
+ "add_stack_layer",
6994
+ "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6995
+ {
6996
+ ref: z.string(),
6997
+ branchName: z.string(),
6998
+ title: z.string().optional()
6999
+ },
7000
+ async ({ ref, branchName, title }) => {
7001
+ const result = await orch.addStackLayer(ref, branchName, { title });
7002
+ return {
7003
+ content: [
7004
+ {
7005
+ type: "text",
7006
+ text: JSON.stringify(
7007
+ {
7008
+ id: result.thread.id,
7009
+ title: result.thread.title,
7010
+ branchName: result.thread.branchName,
7011
+ stackLayer: result.thread.stackLayer,
7012
+ worktreePath: result.thread.worktreePath,
7013
+ link: `sideboard://thread/${result.thread.id}`
7014
+ },
7015
+ null,
7016
+ 2
7017
+ )
7018
+ }
7019
+ ]
7020
+ };
7021
+ }
7022
+ );
7023
+ server.tool(
7024
+ "create_pr_stack",
7025
+ "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
7026
+ {
7027
+ repoPath: z.string(),
7028
+ branches: z.array(z.string()).min(1),
7029
+ agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
7030
+ base: z.string().optional(),
7031
+ title: z.string().optional()
7032
+ },
7033
+ async (args) => {
7034
+ const result = await orch.createPrStack({
7035
+ repoPath: args.repoPath,
7036
+ branches: args.branches,
7037
+ agent: args.agent,
7038
+ base: args.base,
7039
+ title: args.title
6752
7040
  });
6753
7041
  return {
6754
7042
  content: [
6755
7043
  {
6756
7044
  type: "text",
6757
- text: JSON.stringify({
6758
- id: tab.id,
6759
- title: tab.title,
6760
- status: tab.status,
6761
- agent: tab.agent,
6762
- model: tab.model,
6763
- sourceType: tab.sourceType,
6764
- worktreePath: tab.worktreePath,
6765
- fromThreadId: source.id,
6766
- link: `sideboard://thread/${tab.id}`
6767
- })
7045
+ text: JSON.stringify(
7046
+ {
7047
+ stackNumber: result.stack.stackNumber,
7048
+ trunk: result.stack.trunk,
7049
+ threads: result.threads.map((t) => ({
7050
+ id: t.id,
7051
+ title: t.title,
7052
+ branchName: t.branchName,
7053
+ stackLayer: t.stackLayer,
7054
+ worktreePath: t.worktreePath,
7055
+ link: `sideboard://thread/${t.id}`
7056
+ }))
7057
+ },
7058
+ null,
7059
+ 2
7060
+ )
6768
7061
  }
6769
7062
  ]
6770
7063
  };
6771
- } catch (err) {
6772
- const message = err instanceof Error ? err.message : String(err);
6773
- return { content: [{ type: "text", text: message }], isError: true };
6774
7064
  }
6775
- }
6776
- );
6777
- server.tool(
6778
- "run_dev_script",
6779
- "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
6780
- {
6781
- ref: z.string(),
6782
- name: z.string().optional()
6783
- },
6784
- async ({ ref, name }) => {
6785
- const result = await orch.startDev(ref, name);
6786
- return {
6787
- content: [
6788
- {
6789
- type: "text",
6790
- text: JSON.stringify({
6791
- port: result.port,
6792
- scriptName: result.scriptName,
6793
- ports: result.ports,
6794
- url: `http://localhost:${result.port}`
6795
- })
6796
- }
6797
- ]
6798
- };
6799
- }
6800
- );
6801
- server.tool(
6802
- "list_run_scripts",
6803
- "List named run scripts available for a thread",
6804
- { ref: z.string() },
6805
- async ({ ref }) => {
6806
- const scripts = orch.listThreadRunScripts(ref);
6807
- const active = orch.getActiveRuns(ref);
6808
- return {
6809
- content: [
6810
- {
6811
- type: "text",
6812
- text: JSON.stringify({ scripts, active }, null, 2)
6813
- }
6814
- ]
6815
- };
6816
- }
6817
- );
6818
- server.tool(
6819
- "stop_dev_script",
6820
- "Stop a running script for a thread (all scripts if name omitted)",
6821
- {
6822
- ref: z.string(),
6823
- name: z.string().optional()
6824
- },
6825
- async ({ ref, name }) => {
6826
- orch.stopDev(ref, name);
6827
- return { content: [{ type: "text", text: "ok" }] };
6828
- }
6829
- );
6830
- server.tool(
6831
- "run_setup",
6832
- "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
6833
- { ref: z.string() },
6834
- async ({ ref }) => {
6835
- const result = await orch.runSetup(ref);
6836
- return {
6837
- content: [{ type: "text", text: JSON.stringify(result) }]
6838
- };
6839
- }
6840
- );
6841
- server.tool(
6842
- "add_workspace",
6843
- "Register a git repo as a Sideboard workspace",
6844
- { repoPath: z.string() },
6845
- async ({ repoPath }) => {
6846
- const ws = await orch.addWorkspace(repoPath);
6847
- return { content: [{ type: "text", text: JSON.stringify(ws) }] };
6848
- }
6849
- );
6850
- server.tool(
6851
- "remove_workspace",
6852
- "Unregister a Sideboard workspace (does not archive threads)",
6853
- { repoPath: z.string() },
6854
- async ({ repoPath }) => {
6855
- orch.removeWorkspace(repoPath);
6856
- return { content: [{ type: "text", text: "ok" }] };
6857
- }
6858
- );
6859
- server.tool(
6860
- "fanout",
6861
- "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
6862
- {
6863
- prompt: z.string(),
6864
- agents: z.array(z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6865
- repoPath: z.string(),
6866
- sourceType: z.enum(["branch", "pr", "ticket"]).optional(),
6867
- sourceRef: z.string().optional(),
6868
- title: z.string().optional()
6869
- },
6870
- async (args) => {
6871
- const threads = await orch.bestOfN(args);
6872
- return {
6873
- content: [
6874
- {
6875
- type: "text",
6876
- text: JSON.stringify(
6877
- threads.map((t) => ({
6878
- id: t.id,
6879
- agent: t.agent,
6880
- branchName: t.branchName,
6881
- worktreePath: t.worktreePath
6882
- })),
6883
- null,
6884
- 2
6885
- )
6886
- }
6887
- ]
6888
- };
6889
- }
6890
- );
6891
- server.tool(
6892
- "list_branches",
6893
- "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).",
6894
- {
6895
- repoPath: z.string(),
6896
- unmergedOnly: z.boolean().optional()
6897
- },
6898
- async ({ repoPath, unmergedOnly }) => {
6899
- const root = await resolveRepoRoot(repoPath);
6900
- const branches = await listBranches(root, {
6901
- unmergedOnly: unmergedOnly !== false
6902
- });
6903
- return {
6904
- content: [
6905
- {
6906
- type: "text",
6907
- text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
6908
- }
6909
- ]
6910
- };
6911
- }
6912
- );
6913
- server.tool(
6914
- "list_prs",
6915
- "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.",
6916
- { repoPath: z.string() },
6917
- async ({ repoPath }) => {
6918
- const root = await resolveRepoRoot(repoPath);
6919
- const prs = await listPrs(root);
6920
- return {
6921
- content: [
6922
- {
6923
- type: "text",
6924
- text: prs.map(
6925
- (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
6926
- ).join("\n")
6927
- }
6928
- ]
6929
- };
6930
- }
6931
- );
6932
- server.tool(
6933
- "get_pr_stack",
6934
- "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.",
6935
- { ref: z.string() },
6936
- async ({ ref }) => {
6937
- const stack = await orch.getPrStack(ref);
6938
- return {
6939
- content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
6940
- };
6941
- }
6942
- );
6943
- server.tool(
6944
- "open_pr_stack_layers",
6945
- "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6946
- {
6947
- ref: z.string(),
6948
- layer: z.number().int().positive().optional()
6949
- },
6950
- async ({ ref, layer }) => {
6951
- const result = await orch.openPrStackLayers(ref, { layer });
6952
- return {
6953
- content: [
6954
- {
6955
- type: "text",
6956
- text: JSON.stringify(
6957
- {
6958
- stackNumber: result.stack.stackNumber,
6959
- trunk: result.stack.trunk,
6960
- threads: result.threads.map((t) => ({
6961
- id: t.id,
6962
- title: t.title,
6963
- branchName: t.branchName,
6964
- stackLayer: t.stackLayer,
6965
- worktreePath: t.worktreePath,
6966
- prUrl: t.prUrl,
6967
- link: `sideboard://thread/${t.id}`
6968
- }))
6969
- },
6970
- null,
6971
- 2
6972
- )
6973
- }
6974
- ]
6975
- };
6976
- }
6977
- );
6978
- server.tool(
6979
- "add_stack_layer",
6980
- "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6981
- {
6982
- ref: z.string(),
6983
- branchName: z.string(),
6984
- title: z.string().optional()
6985
- },
6986
- async ({ ref, branchName, title }) => {
6987
- const result = await orch.addStackLayer(ref, branchName, { title });
6988
- return {
6989
- content: [
6990
- {
6991
- type: "text",
6992
- text: JSON.stringify(
6993
- {
6994
- id: result.thread.id,
6995
- title: result.thread.title,
6996
- branchName: result.thread.branchName,
6997
- stackLayer: result.thread.stackLayer,
6998
- worktreePath: result.thread.worktreePath,
6999
- link: `sideboard://thread/${result.thread.id}`
7000
- },
7001
- null,
7002
- 2
7003
- )
7004
- }
7005
- ]
7006
- };
7007
- }
7008
- );
7009
- server.tool(
7010
- "create_pr_stack",
7011
- "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
7012
- {
7013
- repoPath: z.string(),
7014
- branches: z.array(z.string()).min(1),
7015
- agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
7016
- base: z.string().optional(),
7017
- title: z.string().optional()
7018
- },
7019
- async (args) => {
7020
- const result = await orch.createPrStack({
7021
- repoPath: args.repoPath,
7022
- branches: args.branches,
7023
- agent: args.agent,
7024
- base: args.base,
7025
- title: args.title
7026
- });
7027
- return {
7028
- content: [
7029
- {
7030
- type: "text",
7031
- text: JSON.stringify(
7032
- {
7033
- stackNumber: result.stack.stackNumber,
7034
- trunk: result.stack.trunk,
7035
- threads: result.threads.map((t) => ({
7036
- id: t.id,
7037
- title: t.title,
7038
- branchName: t.branchName,
7039
- stackLayer: t.stackLayer,
7040
- worktreePath: t.worktreePath,
7041
- link: `sideboard://thread/${t.id}`
7042
- }))
7043
- },
7044
- null,
7045
- 2
7046
- )
7047
- }
7048
- ]
7049
- };
7050
- }
7051
- );
7052
- server.tool(
7053
- "list_issues",
7054
- "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.",
7055
- { repoPath: z.string() },
7056
- async ({ repoPath }) => {
7057
- const root = await resolveRepoRoot(repoPath);
7058
- const result = await listIssues(root);
7059
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
7060
- }
7061
- );
7062
- server.tool(
7063
- "list_linear_issues",
7064
- "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
7065
- {
7066
- agent: z.enum(["claude", "codex", "opencode"]),
7067
- repoPath: z.string()
7068
- },
7069
- async ({ agent, repoPath }) => {
7070
- const issues = await listLinearIssues(agent, repoPath);
7071
- return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
7072
- }
7073
- );
7065
+ );
7066
+ server.tool(
7067
+ "list_issues",
7068
+ "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.",
7069
+ { repoPath: z.string() },
7070
+ async ({ repoPath }) => {
7071
+ const root = await resolveRepoRoot(repoPath);
7072
+ const result = await listIssues(root);
7073
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
7074
+ }
7075
+ );
7076
+ server.tool(
7077
+ "list_linear_issues",
7078
+ "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
7079
+ {
7080
+ agent: z.enum(["claude", "codex", "opencode"]),
7081
+ repoPath: z.string()
7082
+ },
7083
+ async ({ agent, repoPath }) => {
7084
+ const issues = await listLinearIssues(agent, repoPath);
7085
+ return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
7086
+ }
7087
+ );
7088
+ }
7074
7089
  const transport = new StdioServerTransport();
7075
7090
  await server.connect(transport);
7076
7091
  }
@@ -7436,6 +7451,7 @@ export {
7436
7451
  REVIEW_REQUEST_PREFILL,
7437
7452
  SIDEBOARD_FORCE_STOP,
7438
7453
  SIDEBOARD_MCP_ALLOWED_TOOLS,
7454
+ SIDEBOARD_MCP_PROFILE_ENV,
7439
7455
  THINKING_EFFORTS,
7440
7456
  addPrStackLayer,
7441
7457
  addStackLayerFromThread,
@@ -7467,6 +7483,7 @@ export {
7467
7483
  brightsyCloudConnectAgent,
7468
7484
  brightsyCloudConnectEnabled,
7469
7485
  brightsyConfigPath,
7486
+ brightsyInjectWorktreeMcpEnabled,
7470
7487
  brightsyMcpAllowedTools,
7471
7488
  brightsyMcpServerName,
7472
7489
  buildCachedUserContent,
@@ -7492,6 +7509,7 @@ export {
7492
7509
  coerceOrchestratorAgent,
7493
7510
  collectTakenTeamSlugs,
7494
7511
  commitAll,
7512
+ conductorBundledBinDir,
7495
7513
  conductorDbPath,
7496
7514
  confirmLand,
7497
7515
  connectBrightsyTeam,
@@ -7518,7 +7536,9 @@ export {
7518
7536
  detectLocalMergeConflicts,
7519
7537
  disconnectBrightsyTeam,
7520
7538
  discoverSkills,
7539
+ dropCachedPrefixOnResume,
7521
7540
  encodeBrightsyTarget,
7541
+ enrichPathWithNpmGlobalBin,
7522
7542
  enrichWorkspacesWithGithub,
7523
7543
  ensureAgentPath,
7524
7544
  ensureCloudCoordinator,
@@ -7556,6 +7576,7 @@ export {
7556
7576
  formatTranscriptMarkdown,
7557
7577
  formatWorkspaceInventory,
7558
7578
  formatWorktreeDirective,
7579
+ formatWorktreeReminder,
7559
7580
  getAdapter,
7560
7581
  getAgentSetupInfo,
7561
7582
  getBrightsySession,
@@ -7600,6 +7621,7 @@ export {
7600
7621
  isBrightsyConnected,
7601
7622
  isBrightsyNdjsonLine,
7602
7623
  isCloudCoordinatorThread,
7624
+ isConductorBundledCli,
7603
7625
  isCursorAutoModel,
7604
7626
  isDirty,
7605
7627
  isGhRateLimitError,
@@ -7682,7 +7704,10 @@ export {
7682
7704
  pastedTextStats,
7683
7705
  permissionMode,
7684
7706
  planFileAbs,
7707
+ posixShellSingleQuote,
7708
+ prepareTerminalCommand,
7685
7709
  previewLand,
7710
+ promptMentionsBrightsy,
7686
7711
  pushBranch,
7687
7712
  readExistingReviewRequestFile,
7688
7713
  readPlanFile,
@@ -7698,7 +7723,9 @@ export {
7698
7723
  requestReview,
7699
7724
  requireAgent,
7700
7725
  resetGhStackDetectCache,
7726
+ resolveAgentExecutable,
7701
7727
  resolveClaudeExecutable,
7728
+ resolveCommandBinarySync,
7702
7729
  resolveConductorCursorAgentId,
7703
7730
  resolveCursorModelId,
7704
7731
  resolveDefaultBranch,
@@ -7707,6 +7734,7 @@ export {
7707
7734
  resolveFilesToCopy,
7708
7735
  resolveGhAuthToken,
7709
7736
  resolveGithubRepoSlug,
7737
+ resolveLoginCommand,
7710
7738
  resolveNewThreadOptions,
7711
7739
  resolvePlanMarkdown,
7712
7740
  resolvePrSelector,
@@ -7728,9 +7756,11 @@ export {
7728
7756
  settingsSourceLabel,
7729
7757
  shouldAttachPastedText,
7730
7758
  shouldCompactContext,
7759
+ shouldInjectBrightsyMcp,
7731
7760
  shouldRefreshReviewRequestTemplate,
7732
7761
  shouldRunWorktreeCleanup,
7733
7762
  sideboardHomeDir,
7763
+ sideboardMcpProfile,
7734
7764
  sideboardReposDir,
7735
7765
  sideboardWorkspacesDir,
7736
7766
  slugify,
@@ -7758,6 +7788,7 @@ export {
7758
7788
  threadDisplayLabel,
7759
7789
  threadFilePath,
7760
7790
  threadLockPath,
7791
+ threadRequestsBrightsyMcp,
7761
7792
  threadsDir,
7762
7793
  threadsSharingWorktree,
7763
7794
  toolDescription,
@@ -7765,14 +7796,18 @@ export {
7765
7796
  toolFilePath,
7766
7797
  totalTokens,
7767
7798
  updateAdvancedSettings,
7799
+ updateAgentExecutable,
7768
7800
  updateAppEnvironment,
7769
7801
  updateBrightsySettings,
7770
7802
  updateClaudeSettings,
7803
+ updateCodexSettings,
7771
7804
  updateDefaultsSettings,
7772
7805
  updateIntegrationsSettings,
7806
+ updateOpencodeSettings,
7773
7807
  updateThread,
7774
7808
  validateLinearApiKey,
7775
7809
  withAgentInstructions,
7810
+ withExportedPath,
7776
7811
  withThreadLock,
7777
7812
  workspaceSettingsSourceLabel,
7778
7813
  worktreeCleanupSettings,