@sideboard-ai/core 0.1.62 → 0.1.66

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