@sideboard-ai/core 0.1.61 → 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 (45) hide show
  1. package/dist/{agents-B2723EOF.js → agents-EWFD765A.js} +23 -7
  2. package/dist/{agents-EADRAAPG.js → agents-MD7SCFYX.js} +23 -7
  3. package/dist/{app-settings-YBNZ4XSR.js → app-settings-GJVZGU3M.js} +15 -3
  4. package/dist/{app-settings-TBHTJWLQ.js → app-settings-ONKMFSEJ.js} +15 -3
  5. package/dist/{chunk-RIWUU7FO.js → chunk-5AG5BUQN.js} +61 -3
  6. package/dist/{chunk-HLEX5AQ6.js → chunk-7FD7COKE.js} +4 -0
  7. package/dist/{chunk-XWPKLRB3.js → chunk-AKPEXIKK.js} +2 -2
  8. package/dist/{chunk-MX7UH5AT.js → chunk-BBEH2CJE.js} +264 -93
  9. package/dist/{chunk-FGNBTFRD.js → chunk-BPOTVI5J.js} +125 -3
  10. package/dist/{chunk-CHGOMFTA.js → chunk-DNPP7QMK.js} +32 -7
  11. package/dist/{chunk-MBAL5FE6.js → chunk-DZYMNW2A.js} +4 -4
  12. package/dist/{chunk-JB2H7E7V.js → chunk-EVD4KNKR.js} +1 -1
  13. package/dist/{chunk-TSRXOSVD.js → chunk-FWJYLBO6.js} +4 -0
  14. package/dist/{chunk-QJBC4BRK.js → chunk-HTOTTJJX.js} +2 -2
  15. package/dist/{chunk-MFGGIRUA.js → chunk-I2KGRAXC.js} +2 -2
  16. package/dist/{chunk-ZXDOI6IH.js → chunk-IFAU2DEY.js} +269 -93
  17. package/dist/{chunk-NTYRBRE7.js → chunk-LRBRQFBU.js} +2 -2
  18. package/dist/{chunk-75QR74KM.js → chunk-MHR4OGZ4.js} +125 -3
  19. package/dist/{chunk-2P2UK7E7.js → chunk-PESJDZTB.js} +2 -2
  20. package/dist/{chunk-P7EQUFB6.js → chunk-R7GUDEPY.js} +2 -2
  21. package/dist/{chunk-73AT5K4A.js → chunk-TXQ6K2GR.js} +61 -3
  22. package/dist/{chunk-MLAST5BB.js → chunk-YTNLOJPK.js} +32 -7
  23. package/dist/{connected-teams-RSL4VB3X.js → connected-teams-CHXO5FFA.js} +2 -2
  24. package/dist/{connected-teams-MKIYKOYA.js → connected-teams-GJTESSTX.js} +2 -2
  25. package/dist/{coordinator-prompt-4P23VVFC.js → coordinator-prompt-E5UGSJES.js} +5 -5
  26. package/dist/{coordinator-prompt-LNWHZ64D.js → coordinator-prompt-ILSULI7C.js} +5 -5
  27. package/dist/{global-workspace-TMMAVRVW.js → global-workspace-44UG64NA.js} +6 -7
  28. package/dist/{global-workspace-BHT4XUAA.js → global-workspace-5PM5LA6E.js} +6 -7
  29. package/dist/index.cjs +1433 -944
  30. package/dist/index.d.cts +117 -2
  31. package/dist/index.d.ts +117 -2
  32. package/dist/index.js +859 -736
  33. package/dist/mcp/run-stdio.cjs +1388 -993
  34. package/dist/mcp/run-stdio.js +798 -765
  35. package/dist/{run-HZTBWGQR.js → run-74S2NZWB.js} +1 -1
  36. package/dist/{run-GPSVV5OH.js → run-JLN5JK7Q.js} +1 -1
  37. package/dist/{thread-store-GHOADGL2.js → thread-store-BUBR24YA.js} +1 -1
  38. package/dist/{thread-store-UJIGMI5J.js → thread-store-XTPFCJAI.js} +1 -1
  39. package/dist/{workspaces-3GO3EDQG.js → workspaces-OVBCSS5Y.js} +7 -8
  40. package/dist/{workspaces-GFPGDGCS.js → workspaces-RXFXQRRF.js} +7 -8
  41. package/dist/{worktree-GBLHEFHR.js → worktree-K2JB53R5.js} +3 -3
  42. package/dist/{worktree-3L2G6PCM.js → worktree-S2N63FLV.js} +3 -3
  43. package/package.json +1 -1
  44. package/dist/chunk-GI7E5733.js +0 -30
  45. 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-2P2UK7E7.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-MLAST5BB.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-ZXDOI6IH.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,16 +124,18 @@ import {
118
124
  enrichWorkspacesWithGithub,
119
125
  ensureGlobalCoordinatorCwd,
120
126
  formatWorkspaceInventory
121
- } from "./chunk-XWPKLRB3.js";
127
+ } from "./chunk-AKPEXIKK.js";
122
128
  import {
123
129
  HARNESS_ENV_KEYS,
124
130
  appSettingsPath,
125
131
  applyAppEnvironment,
132
+ autoArchiveOnMergeEnabled,
126
133
  autoCleanupOrphansEnabled,
127
134
  autoRenameBranchEnabled,
128
135
  autoRunAfterSetupEnabled,
129
136
  brightsyCloudConnectAgent,
130
137
  brightsyCloudConnectEnabled,
138
+ brightsyInjectWorktreeMcpEnabled,
131
139
  caffeinateWhileCloudConnectEnabled,
132
140
  caffeinateWhileRunningEnabled,
133
141
  childEnvWithAppSettings,
@@ -146,18 +154,22 @@ import {
146
154
  maxConcurrentAgents,
147
155
  orchestrationQuotaFallbackAgent,
148
156
  orchestrationQuotaOnLimit,
157
+ resolveAgentExecutable,
149
158
  resolveClaudeExecutable,
150
159
  resolveEffectiveIssueSource,
151
160
  resolveNewThreadOptions,
152
161
  resolveThreadDefaults,
153
162
  saveAppSettings,
154
163
  updateAdvancedSettings,
164
+ updateAgentExecutable,
155
165
  updateAppEnvironment,
156
166
  updateBrightsySettings,
157
167
  updateClaudeSettings,
168
+ updateCodexSettings,
158
169
  updateDefaultsSettings,
159
- updateIntegrationsSettings
160
- } from "./chunk-75QR74KM.js";
170
+ updateIntegrationsSettings,
171
+ updateOpencodeSettings
172
+ } from "./chunk-MHR4OGZ4.js";
161
173
  import {
162
174
  FAMOUS_SOCCER_TEAMS,
163
175
  addPrStackLayer,
@@ -220,7 +232,7 @@ import {
220
232
  worktreeDisplayLabel,
221
233
  worktreeDisplayLabelForGroup,
222
234
  worktreeNameFromPath
223
- } from "./chunk-P7EQUFB6.js";
235
+ } from "./chunk-R7GUDEPY.js";
224
236
  import {
225
237
  ATTACHMENTS_DIR,
226
238
  LEGACY_ATTACHMENTS_DIR,
@@ -240,7 +252,7 @@ import {
240
252
  updateThread,
241
253
  withThreadLock,
242
254
  writeThread
243
- } from "./chunk-TSRXOSVD.js";
255
+ } from "./chunk-FWJYLBO6.js";
244
256
  import {
245
257
  THINKING_EFFORTS,
246
258
  isThinkingEffort,
@@ -272,12 +284,16 @@ import {
272
284
  worktreesRoot
273
285
  } from "./chunk-M37RITA6.js";
274
286
  import {
287
+ enrichPathWithNpmGlobalBin,
275
288
  ensureAgentPath,
276
289
  gh,
277
290
  git,
291
+ posixShellSingleQuote,
292
+ resolveCommandBinarySync,
278
293
  resolveGhAuthToken,
279
- run
280
- } from "./chunk-73AT5K4A.js";
294
+ run,
295
+ withExportedPath
296
+ } from "./chunk-TXQ6K2GR.js";
281
297
 
282
298
  // src/integrations/github.ts
283
299
  async function getGitHubStatus() {
@@ -706,9 +722,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
706
722
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
707
723
  );
708
724
  }
709
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-TMMAVRVW.js");
725
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-44UG64NA.js");
710
726
  if (isGlobalThread2(thread)) {
711
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LNWHZ64D.js");
727
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ILSULI7C.js");
712
728
  ensureGlobalCoordinatorCwd2(
713
729
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
714
730
  );
@@ -910,6 +926,9 @@ function formatWorktreeDirective(thread, opts) {
910
926
  );
911
927
  return lines.join("\n");
912
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
+ }
913
932
  function formatArtifactDirective() {
914
933
  return [
915
934
  "Sideboard side column (desktop UI):",
@@ -995,11 +1014,13 @@ ${prompt}`;
995
1014
  // src/detect/detect.ts
996
1015
  async function detectAgents() {
997
1016
  ensureAgentPath();
1017
+ enrichPathWithNpmGlobalBin();
998
1018
  return Promise.all(allAdapters().map((a) => a.detect()));
999
1019
  }
1000
1020
  var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
1001
1021
  async function requireAgent(agent, opts) {
1002
1022
  ensureAgentPath();
1023
+ enrichPathWithNpmGlobalBin();
1003
1024
  const status = await Promise.race([
1004
1025
  getAdapter(agent).detect(),
1005
1026
  new Promise((_, reject) => {
@@ -2841,7 +2862,7 @@ async function previewLand(thread) {
2841
2862
  const target = await resolveDefaultBranch(thread.repoPath);
2842
2863
  if (thread.branchName === target || thread.branchName === "main" || thread.branchName === "master") {
2843
2864
  }
2844
- const { stdout: head } = await import("./run-GPSVV5OH.js").then(
2865
+ const { stdout: head } = await import("./run-JLN5JK7Q.js").then(
2845
2866
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], thread.worktreePath)
2846
2867
  );
2847
2868
  const current = head.trim();
@@ -2880,7 +2901,7 @@ async function confirmLand(thread, opts) {
2880
2901
  if (preview.dirty) {
2881
2902
  committed = await commitAll(thread.worktreePath, meta.commitMessage);
2882
2903
  }
2883
- const { git: git2 } = await import("./run-GPSVV5OH.js");
2904
+ const { git: git2 } = await import("./run-JLN5JK7Q.js");
2884
2905
  const { stdout: headOut } = await git2(
2885
2906
  ["rev-parse", "--abbrev-ref", "HEAD"],
2886
2907
  thread.worktreePath,
@@ -2978,7 +2999,7 @@ async function createThread(input, _onSetupLine) {
2978
2999
  return readThread(thread.id) ?? thread;
2979
3000
  }
2980
3001
  async function listLinearIssues(agent, repoPath) {
2981
- const { getAdapter: getAdapter2 } = await import("./agents-B2723EOF.js");
3002
+ const { getAdapter: getAdapter2 } = await import("./agents-EWFD765A.js");
2982
3003
  await requireAgent(agent, { requireLinear: true });
2983
3004
  const adapter = getAdapter2(agent);
2984
3005
  if (!adapter.listLinearIssues) {
@@ -3022,6 +3043,8 @@ function worktreeBindingFrom(from) {
3022
3043
  parentThreadId: from.parentThreadId,
3023
3044
  prUrl: from.prUrl,
3024
3045
  prTitle: from.prTitle,
3046
+ prState: from.prState,
3047
+ skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
3025
3048
  stackId: from.stackId,
3026
3049
  stackLayer: from.stackLayer
3027
3050
  };
@@ -3504,7 +3527,7 @@ async function adoptThread(input) {
3504
3527
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3505
3528
  }
3506
3529
  const repoPath = await resolveRepoRoot(input.worktreePath);
3507
- const { stdout: branch } = await import("./run-GPSVV5OH.js").then(
3530
+ const { stdout: branch } = await import("./run-JLN5JK7Q.js").then(
3508
3531
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
3509
3532
  );
3510
3533
  const thread = createEmptyThread({
@@ -3519,7 +3542,7 @@ async function adoptThread(input) {
3519
3542
  messages: input.messages ?? []
3520
3543
  });
3521
3544
  writeThread(thread);
3522
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-3GO3EDQG.js");
3545
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-OVBCSS5Y.js");
3523
3546
  await ensureWorkspace2(repoPath);
3524
3547
  return thread;
3525
3548
  }
@@ -3721,6 +3744,20 @@ async function importConductorWorkspaceAsync(workspaceId) {
3721
3744
  import { EventEmitter } from "events";
3722
3745
  import { existsSync as existsSync13 } from "fs";
3723
3746
 
3747
+ // src/git/pr-merge-archive.ts
3748
+ function normalizePrState(state) {
3749
+ return (state ?? "").trim().toUpperCase();
3750
+ }
3751
+ function shouldAutoArchiveOnPrMerge(opts) {
3752
+ if (!opts.autoArchiveEnabled) return false;
3753
+ if (opts.isGlobal) return false;
3754
+ if (opts.skipAutoArchiveOnMerge) return false;
3755
+ if (opts.threadStatus === "archived") return false;
3756
+ const next = normalizePrState(opts.nextPrState);
3757
+ if (next !== "MERGED") return false;
3758
+ return normalizePrState(opts.previousPrState) !== "MERGED";
3759
+ }
3760
+
3724
3761
  // src/git/orphan-cleanup.ts
3725
3762
  import { existsSync as existsSync10, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3726
3763
  import { join as join8 } from "path";
@@ -4456,7 +4493,7 @@ var Orchestrator = class {
4456
4493
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
4457
4494
  });
4458
4495
  }
4459
- const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4496
+ const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4460
4497
  if (autoCleanupOrphansEnabled2() && shouldRunWorktreeCleanup() && orphans.length > 0) {
4461
4498
  await cleanupOrphanWorktrees({ repoPaths });
4462
4499
  }
@@ -4613,7 +4650,7 @@ var Orchestrator = class {
4613
4650
  }
4614
4651
  async finishCreateThread(threadId, prompt) {
4615
4652
  await this.runSetupAfterCreate(threadId);
4616
- const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4653
+ const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4617
4654
  if (autoRunAfterSetupEnabled2()) {
4618
4655
  try {
4619
4656
  await this.startDev(threadId);
@@ -4854,13 +4891,14 @@ var Orchestrator = class {
4854
4891
  "Files column: MCP present_files (brightsy account storage or memory).",
4855
4892
  "Do not say artifacts/CMS UI are unavailable."
4856
4893
  ].join(" ") : null;
4894
+ const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
4857
4895
  const agentPrompt = [
4858
4896
  thread.planMode ? PLAN_MODE_INSTRUCTION : null,
4859
4897
  orchestrationReminder,
4898
+ worktreeReminder,
4860
4899
  artifactReminder,
4861
4900
  expandedPrompt
4862
4901
  ].filter(Boolean).join("\n\n");
4863
- const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
4864
4902
  if (thread.attachments.length > 0) {
4865
4903
  updateThread(threadId, { attachments: [] });
4866
4904
  }
@@ -4884,11 +4922,10 @@ var Orchestrator = class {
4884
4922
  });
4885
4923
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
4886
4924
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
4887
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4925
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
4888
4926
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
4889
4927
  customPrompt: settings?.prompts?.renameBranch
4890
4928
  }) : null;
4891
- const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
4892
4929
  let seed = null;
4893
4930
  if (!fresh.sessionId) {
4894
4931
  const prior = fresh.messages.slice(0, -1);
@@ -4909,12 +4946,12 @@ var Orchestrator = class {
4909
4946
  });
4910
4947
  }
4911
4948
  }
4912
- const cachedPrefix = [
4949
+ const cachedPrefix = fresh.sessionId ? "" : [
4913
4950
  coordinatorDirective,
4914
4951
  worktreeDirective,
4915
4952
  artifactDirective,
4916
4953
  renameBranchDirective,
4917
- ...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
4954
+ seed
4918
4955
  ].filter(Boolean).join("\n\n---\n\n");
4919
4956
  try {
4920
4957
  const stderrTail = [];
@@ -5001,15 +5038,11 @@ var Orchestrator = class {
5001
5038
  const retryThread = this.requireThread(threadId);
5002
5039
  const prior = retryThread.messages.slice(0, -1);
5003
5040
  const retrySeed = buildSessionSeed(prior);
5004
- const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
5005
- loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
5006
- );
5007
5041
  const retryPrefix = [
5008
5042
  coordinatorDirective,
5009
5043
  worktreeDirective,
5010
5044
  artifactDirective,
5011
5045
  renameBranchDirective,
5012
- retryInstructions,
5013
5046
  retrySeed
5014
5047
  ].filter(Boolean).join("\n\n---\n\n");
5015
5048
  const retryHandle = await spawnAgentTurn(
@@ -5520,10 +5553,19 @@ var Orchestrator = class {
5520
5553
  this.assertNotGlobal(thread, "Merge PR");
5521
5554
  if (!selector) throw new Error("No pull request linked to this thread");
5522
5555
  const result = await mergePr(cwd, selector);
5523
- if (result.url && result.url !== thread.prUrl) {
5524
- updateThread(thread.id, { prUrl: result.url });
5525
- }
5526
- return result;
5556
+ const state = normalizePrState(result.state) || "MERGED";
5557
+ const metaLike = {
5558
+ number: 0,
5559
+ title: thread.prTitle ?? thread.title,
5560
+ url: result.url || thread.prUrl || "",
5561
+ state,
5562
+ isDraft: false,
5563
+ reviewDecision: null,
5564
+ baseRefName: "",
5565
+ headRefName: ""
5566
+ };
5567
+ await this.persistPrMetaAndMaybeArchive(thread, metaLike);
5568
+ return { url: metaLike.url, state };
5527
5569
  }
5528
5570
  /** Resolve PR selector and optionally persist `prUrl` when found. */
5529
5571
  async withPrSelector(threadRef) {
@@ -5545,19 +5587,55 @@ var Orchestrator = class {
5545
5587
  if (!selector) return null;
5546
5588
  const meta = await getPrMeta(cwd, selector);
5547
5589
  if (meta) {
5548
- const patch = {};
5549
- if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
5550
- if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
5551
- if (Object.keys(patch).length > 0) {
5552
- updateThread(thread.id, patch);
5553
- const latest = this.requireThread(thread.id);
5554
- if (!latest.userSetTitle && meta.title && latest.title !== meta.title) {
5555
- updateThread(thread.id, { title: meta.title });
5556
- }
5557
- }
5590
+ await this.persistPrMetaAndMaybeArchive(thread, meta);
5558
5591
  }
5559
5592
  return meta;
5560
5593
  }
5594
+ /**
5595
+ * Persist PR URL/title/state and Conductor-style auto-archive when the PR
5596
+ * first becomes MERGED.
5597
+ */
5598
+ async persistPrMetaAndMaybeArchive(thread, meta) {
5599
+ const prevState = normalizePrState(thread.prState);
5600
+ const nextState = normalizePrState(meta.state);
5601
+ const patch = {};
5602
+ if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
5603
+ if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
5604
+ if (nextState && nextState !== prevState) patch.prState = nextState;
5605
+ if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
5606
+ patch.skipAutoArchiveOnMerge = false;
5607
+ }
5608
+ if (Object.keys(patch).length > 0) {
5609
+ updateThread(thread.id, patch);
5610
+ const latest2 = this.requireThread(thread.id);
5611
+ if (!latest2.userSetTitle && meta.title && latest2.title !== meta.title) {
5612
+ updateThread(thread.id, { title: meta.title });
5613
+ }
5614
+ }
5615
+ const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
5616
+ const latest = this.requireThread(thread.id);
5617
+ if (!shouldAutoArchiveOnPrMerge({
5618
+ previousPrState: prevState || null,
5619
+ nextPrState: nextState,
5620
+ threadStatus: latest.status,
5621
+ skipAutoArchiveOnMerge: latest.skipAutoArchiveOnMerge,
5622
+ autoArchiveEnabled: autoArchiveOnMergeEnabled2(),
5623
+ isGlobal: isGlobalThread(latest)
5624
+ })) {
5625
+ return;
5626
+ }
5627
+ const siblings = threadsSharingWorktree(latest.worktreePath);
5628
+ for (const t of siblings) {
5629
+ const sibPatch = { prState: "MERGED" };
5630
+ if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
5631
+ if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
5632
+ if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
5633
+ }
5634
+ for (const t of siblings) {
5635
+ if (this.requireThread(t.id).status === "archived") continue;
5636
+ await this.archive(t.id);
5637
+ }
5638
+ }
5561
5639
  async getPrStack(threadRef) {
5562
5640
  const thread = this.requireThread(threadRef);
5563
5641
  if (!thread.worktreePath?.trim()) return null;
@@ -5735,7 +5813,9 @@ var Orchestrator = class {
5735
5813
  const thread = this.requireThread(threadRef);
5736
5814
  this.stop(thread.id);
5737
5815
  if (isGlobalThread(thread)) {
5738
- return setStatus(thread.id, "archived");
5816
+ const archived2 = setStatus(thread.id, "archived");
5817
+ this.emit({ type: "status_changed", threadId: archived2.id, status: "archived" });
5818
+ return archived2;
5739
5819
  }
5740
5820
  const siblings = threadsSharingWorktree(thread.worktreePath).filter((t) => t.id !== thread.id);
5741
5821
  if (siblings.length === 0) {
@@ -5753,9 +5833,10 @@ var Orchestrator = class {
5753
5833
  await removeWorktree(thread.repoPath, thread.worktreePath);
5754
5834
  }
5755
5835
  const archived = setStatus(thread.id, "archived");
5836
+ this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
5756
5837
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
5757
5838
  try {
5758
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-3GO3EDQG.js");
5839
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-OVBCSS5Y.js");
5759
5840
  await ensureWorkspace2(thread.repoPath);
5760
5841
  } catch {
5761
5842
  }
@@ -5776,7 +5857,7 @@ var Orchestrator = class {
5776
5857
  await runArchiveScript(thread.repoPath, thread.worktreePath);
5777
5858
  } catch {
5778
5859
  }
5779
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
5860
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-ONKMFSEJ.js");
5780
5861
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
5781
5862
  await removeWorktree(thread.repoPath, thread.worktreePath, {
5782
5863
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -5792,10 +5873,12 @@ var Orchestrator = class {
5792
5873
  if (isGlobalThread(thread)) {
5793
5874
  const { globalAgentCwd: globalAgentCwd2 } = await import("./paths-VPH3ITBK.js");
5794
5875
  updateThread(thread.id, { worktreePath: globalAgentCwd2() });
5795
- return setStatus(thread.id, "idle");
5876
+ const restored2 = setStatus(thread.id, "idle");
5877
+ this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
5878
+ return restored2;
5796
5879
  }
5797
5880
  if (!existsSync13(thread.worktreePath)) {
5798
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GBLHEFHR.js");
5881
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-K2JB53R5.js");
5799
5882
  const { execa: execa6 } = await import("execa");
5800
5883
  const slug = thread.worktreePath.split("/").pop();
5801
5884
  const dest = thread.worktreePath;
@@ -5805,7 +5888,31 @@ var Orchestrator = class {
5805
5888
  void createThreadWorktree2;
5806
5889
  void slug;
5807
5890
  }
5808
- return setStatus(thread.id, "idle");
5891
+ const restorePatch = {};
5892
+ let alreadyMerged = normalizePrState(thread.prState) === "MERGED";
5893
+ if (!alreadyMerged && thread.prUrl?.trim() && thread.worktreePath?.trim()) {
5894
+ try {
5895
+ const selector = resolvePrSelector(thread);
5896
+ if (selector) {
5897
+ const meta = await getPrMeta(thread.worktreePath, selector);
5898
+ if (meta) {
5899
+ const state = normalizePrState(meta.state);
5900
+ if (meta.url && meta.url !== thread.prUrl) restorePatch.prUrl = meta.url;
5901
+ if (meta.title && meta.title !== thread.prTitle) restorePatch.prTitle = meta.title;
5902
+ if (state) restorePatch.prState = state;
5903
+ if (state === "MERGED") alreadyMerged = true;
5904
+ }
5905
+ }
5906
+ } catch {
5907
+ }
5908
+ }
5909
+ if (alreadyMerged) restorePatch.skipAutoArchiveOnMerge = true;
5910
+ if (Object.keys(restorePatch).length > 0) {
5911
+ updateThread(thread.id, restorePatch);
5912
+ }
5913
+ const restored = setStatus(thread.id, "idle");
5914
+ this.emit({ type: "status_changed", threadId: restored.id, status: restored.status });
5915
+ return restored;
5809
5916
  }
5810
5917
  async attachCommand(threadRef) {
5811
5918
  const thread = this.requireThread(threadRef);
@@ -5880,7 +5987,7 @@ async function startOrchestration(opts) {
5880
5987
  sourceRef: "default",
5881
5988
  ...createOpts
5882
5989
  }).catch(async () => {
5883
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GBLHEFHR.js");
5990
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-K2JB53R5.js");
5884
5991
  const repo = await resolveRepoRoot2(repoPath);
5885
5992
  const def = await resolveDefaultBranch2(repo);
5886
5993
  return createThread({
@@ -5890,7 +5997,7 @@ async function startOrchestration(opts) {
5890
5997
  repoPath: repo
5891
5998
  });
5892
5999
  });
5893
- const { updateThread: upd } = await import("./thread-store-UJIGMI5J.js");
6000
+ const { updateThread: upd } = await import("./thread-store-XTPFCJAI.js");
5894
6001
  const updated = upd(thread.id, {
5895
6002
  sourceType: "orchestration",
5896
6003
  sourceRef: goal
@@ -6036,7 +6143,7 @@ function withTimeout(promise, ms, label) {
6036
6143
  async function startMcpServer() {
6037
6144
  const orch = getOrchestrator();
6038
6145
  try {
6039
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-TBHTJWLQ.js");
6146
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-ONKMFSEJ.js");
6040
6147
  orch.setMaxConcurrent(maxConcurrentAgents2());
6041
6148
  } catch {
6042
6149
  }
@@ -6127,14 +6234,13 @@ async function startMcpServer() {
6127
6234
  ),
6128
6235
  artifact_id: z.string().optional().describe("Stable id when updating the same artifact across turns")
6129
6236
  },
6130
- async ({ title, type, content, artifact_id }) => {
6237
+ async ({ title, type, artifact_id }) => {
6131
6238
  const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
6132
6239
  const payload = {
6133
6240
  ok: true,
6134
6241
  artifact_id: id,
6135
6242
  title,
6136
6243
  type,
6137
- content,
6138
6244
  message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
6139
6245
  };
6140
6246
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6187,7 +6293,6 @@ async function startMcpServer() {
6187
6293
  ok: true,
6188
6294
  path,
6189
6295
  title: title?.trim() || "Plan",
6190
- content,
6191
6296
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
6192
6297
  };
6193
6298
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6217,9 +6322,6 @@ async function startMcpServer() {
6217
6322
  datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
6218
6323
  resource_id: args.resource_id,
6219
6324
  record_id: args.record_id,
6220
- resource: args.resource,
6221
- record: args.record,
6222
- records: args.records,
6223
6325
  message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
6224
6326
  };
6225
6327
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -6247,739 +6349,741 @@ async function startMcpServer() {
6247
6349
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6248
6350
  }
6249
6351
  );
6250
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-TBHTJWLQ.js");
6251
- const accountDefaults = resolveThreadDefaults2();
6252
- const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
6253
- server.tool(
6254
- "create_thread",
6255
- `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.`,
6256
- {
6257
- sourceType: z.enum(["branch", "pr", "ticket"]),
6258
- sourceRef: z.string(),
6259
- agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
6260
- model: z.string().nullable().optional().describe(
6261
- `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
6262
- ),
6263
- repoPath: z.string(),
6264
- title: z.string().optional(),
6265
- parentThreadId: z.string().optional().describe(
6266
- "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
6267
- )
6268
- },
6269
- async (args) => {
6270
- const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
6271
- let parentId = args.parentThreadId?.trim() || "";
6272
- let parent = parentId ? orch.getThread(parentId) : null;
6273
- let parentCorrectedFrom;
6274
- if (envParentId) {
6275
- const envParent = orch.getThread(envParentId);
6276
- if (envParent) {
6277
- if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
6278
- else if (!parentId) parentCorrectedFrom = void 0;
6279
- parentId = envParentId;
6280
- 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
+ }
6281
6385
  }
6282
- }
6283
- if (parentId && !parent) {
6284
- parentCorrectedFrom = parentId;
6285
- parentId = "";
6286
- parent = null;
6287
- }
6288
- if (parentId) {
6289
- const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
6290
- 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;
6434
+ return {
6435
+ content: [
6436
+ {
6437
+ type: "text",
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);
6291
6459
  return {
6292
6460
  content: [
6293
6461
  {
6294
6462
  type: "text",
6295
- text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
6463
+ text: `create_thread failed: ${message}`
6296
6464
  }
6297
6465
  ],
6298
6466
  isError: true
6299
6467
  };
6300
6468
  }
6301
6469
  }
6302
- let agentArg = args.agent;
6303
- let agentCoercedFrom;
6304
- const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
6305
- if (resolvedProbe === "codex") {
6306
- agentCoercedFrom = agentArg ?? "codex";
6307
- const accountAgent = resolveNewThreadOptions2({}).agent;
6308
- agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
6309
- }
6310
- const opts = resolveNewThreadOptions2({
6311
- agent: agentArg,
6312
- model: args.model
6313
- });
6314
- try {
6315
- const thread = await withTimeout(
6316
- orch.createThread({
6317
- sourceType: args.sourceType,
6318
- sourceRef: args.sourceRef,
6319
- agent: opts.agent,
6320
- model: opts.model,
6321
- effort: opts.effort,
6322
- fast: opts.fast,
6323
- repoPath: args.repoPath,
6324
- title: args.title,
6325
- parentThreadId: parentId || null
6326
- }),
6327
- CREATE_THREAD_TIMEOUT_MS,
6328
- "create_thread"
6329
- );
6330
- 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);
6331
6487
  return {
6332
6488
  content: [
6333
6489
  {
6334
6490
  type: "text",
6335
6491
  text: JSON.stringify({
6336
6492
  id: thread.id,
6337
- title: thread.title,
6338
- branchName: thread.branchName,
6339
- worktreePath: thread.worktreePath,
6340
- agent: thread.agent,
6341
- model: thread.model,
6342
6493
  status: thread.status,
6343
- link: `sideboard://thread/${thread.id}`,
6344
- parentThreadId: thread.parentThreadId,
6345
- ...agentCoercedFrom ? {
6346
- agentCoercedFrom,
6347
- note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
6348
- } : {},
6349
- ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
6494
+ queueLength: thread.queue.length,
6495
+ forceStopped: Boolean(force_stop)
6350
6496
  })
6351
6497
  }
6352
6498
  ]
6353
6499
  };
6354
- } catch (err) {
6355
- 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);
6356
6512
  return {
6357
6513
  content: [
6358
6514
  {
6359
6515
  type: "text",
6360
- text: `create_thread failed: ${message}`
6516
+ text: JSON.stringify({
6517
+ id: thread.id,
6518
+ status: result.status,
6519
+ text: result.text
6520
+ })
6361
6521
  }
6362
- ],
6363
- isError: true
6364
- };
6365
- }
6366
- }
6367
- );
6368
- server.tool(
6369
- "send_to_thread",
6370
- "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.",
6371
- {
6372
- ref: z.string(),
6373
- prompt: z.string(),
6374
- force_stop: z.boolean().optional()
6375
- },
6376
- async ({ ref, prompt, force_stop }) => {
6377
- if (force_stop) {
6378
- const existing = orch.getThread(ref);
6379
- if (existing) {
6380
- orch.stop(ref, { clearQueue: true });
6381
- }
6382
- }
6383
- const thread = await orch.send(ref, prompt);
6384
- return {
6385
- content: [
6386
- {
6387
- type: "text",
6388
- text: JSON.stringify({
6389
- id: thread.id,
6390
- status: thread.status,
6391
- queueLength: thread.queue.length,
6392
- forceStopped: Boolean(force_stop)
6393
- })
6394
- }
6395
- ]
6396
- };
6397
- }
6398
- );
6399
- server.tool(
6400
- "wait_for_turn",
6401
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
6402
- {
6403
- ref: z.string(),
6404
- timeoutMs: z.number().optional()
6405
- },
6406
- async ({ ref, timeoutMs }) => {
6407
- const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
6408
- const result = orch.getTurnResult(thread.id);
6409
- return {
6410
- content: [
6411
- {
6412
- type: "text",
6413
- text: JSON.stringify({
6414
- id: thread.id,
6415
- status: result.status,
6416
- text: result.text
6417
- })
6418
- }
6419
- ]
6420
- };
6421
- }
6422
- );
6423
- server.tool(
6424
- "get_turn_result",
6425
- "Final assistant message only (not full transcript)",
6426
- { ref: z.string() },
6427
- async ({ ref }) => {
6428
- const result = orch.getTurnResult(ref);
6429
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
6430
- }
6431
- );
6432
- server.tool(
6433
- "stop_thread",
6434
- "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.",
6435
- {
6436
- ref: z.string(),
6437
- force: z.boolean().optional()
6438
- },
6439
- async ({ ref, force }) => {
6440
- const t = orch.getThread(ref);
6441
- if (!t) {
6442
- return {
6443
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6444
- isError: true
6445
- };
6446
- }
6447
- const clearQueue = force !== false;
6448
- const hadQueued = t.queue.length > 0;
6449
- const stopped = orch.stop(ref, { clearQueue });
6450
- return {
6451
- content: [
6452
- {
6453
- type: "text",
6454
- text: JSON.stringify({
6455
- id: stopped.id,
6456
- status: stopped.status,
6457
- clearedQueue: clearQueue && hadQueued
6458
- })
6459
- }
6460
- ]
6461
- };
6462
- }
6463
- );
6464
- server.tool(
6465
- "archive_thread",
6466
- "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.",
6467
- { ref: z.string() },
6468
- async ({ ref }) => {
6469
- const t = orch.getThread(ref);
6470
- if (!t) {
6471
- return {
6472
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6473
- isError: true
6522
+ ]
6474
6523
  };
6475
6524
  }
6476
- const blocked = mcpArchiveBlockedReason(t);
6477
- if (blocked) {
6478
- return {
6479
- content: [{ type: "text", text: blocked }],
6480
- isError: true
6481
- };
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) }] };
6482
6533
  }
6483
- const archived = await orch.archive(ref);
6484
- return {
6485
- content: [
6486
- {
6487
- type: "text",
6488
- text: JSON.stringify({
6489
- id: archived.id,
6490
- status: archived.status
6491
- })
6492
- }
6493
- ]
6494
- };
6495
- }
6496
- );
6497
- server.tool(
6498
- "restore_thread",
6499
- "Restore an archived thread (recreates worktree from branch when needed)",
6500
- { ref: z.string() },
6501
- async ({ ref }) => {
6502
- try {
6503
- const restored = await orch.restore(ref);
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 });
6504
6553
  return {
6505
6554
  content: [
6506
6555
  {
6507
6556
  type: "text",
6508
6557
  text: JSON.stringify({
6509
- id: restored.id,
6510
- status: restored.status,
6511
- worktreePath: restored.worktreePath
6558
+ id: stopped.id,
6559
+ status: stopped.status,
6560
+ clearedQueue: clearQueue && hadQueued
6512
6561
  })
6513
6562
  }
6514
6563
  ]
6515
6564
  };
6516
- } catch (err) {
6517
- const message = err instanceof Error ? err.message : String(err);
6518
- return { content: [{ type: "text", text: message }], isError: true };
6519
6565
  }
6520
- }
6521
- );
6522
- server.tool(
6523
- "get_diff",
6524
- "Compact diff summary (capped hunks, paginated)",
6525
- {
6526
- ref: z.string(),
6527
- maxFiles: z.number().optional()
6528
- },
6529
- async ({ ref, maxFiles }) => {
6530
- const summary = await orch.diffSummary(ref);
6531
- return {
6532
- content: [
6533
- {
6534
- type: "text",
6535
- text: JSON.stringify({
6536
- ...summary,
6537
- files: summary.files.slice(0, maxFiles ?? 10)
6538
- })
6539
- }
6540
- ]
6541
- };
6542
- }
6543
- );
6544
- server.tool(
6545
- "request_review",
6546
- '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.',
6547
- { ref: z.string().describe("Worktree thread id/ref to review") },
6548
- async ({ ref }) => {
6549
- try {
6550
- const tab = await orch.requestReview(ref);
6551
- const from = orch.getThread(ref);
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);
6552
6587
  return {
6553
6588
  content: [
6554
6589
  {
6555
6590
  type: "text",
6556
6591
  text: JSON.stringify({
6557
- id: tab.id,
6558
- title: tab.title,
6559
- status: tab.status,
6560
- fromThreadId: from?.id ?? ref,
6561
- link: `sideboard://thread/${tab.id}`
6592
+ id: archived.id,
6593
+ status: archived.status
6562
6594
  })
6563
6595
  }
6564
6596
  ]
6565
6597
  };
6566
- } catch (err) {
6567
- const message = err instanceof Error ? err.message : String(err);
6568
- return { content: [{ type: "text", text: message }], isError: true };
6569
6598
  }
6570
- }
6571
- );
6572
- const agentEnum = z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6573
- server.tool(
6574
- "list_models",
6575
- "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.",
6576
- {
6577
- agent: agentEnum.optional().describe("Limit to one agent; omit for all")
6578
- },
6579
- async ({ agent }) => {
6580
- try {
6581
- const catalogs = await listModelsForAgent(agent);
6582
- return {
6583
- content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
6584
- };
6585
- } catch (err) {
6586
- const message = err instanceof Error ? err.message : String(err);
6587
- return { content: [{ type: "text", text: message }], isError: true };
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
+ }
6588
6623
  }
6589
- }
6590
- );
6591
- server.tool(
6592
- "fork_worktree",
6593
- "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.",
6594
- {
6595
- ref: z.string().describe("Worktree thread id/ref to fork"),
6596
- through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6597
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6598
- model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6599
- title: z.string().optional()
6600
- },
6601
- async ({ ref, through_index, agent, model, title }) => {
6602
- try {
6603
- const source = orch.getThread(ref);
6604
- if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
6605
- const thread = await orch.forkThreadWorktree({
6606
- threadId: ref,
6607
- throughIndex: through_index,
6608
- agent,
6609
- model,
6610
- title
6611
- });
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);
6612
6634
  return {
6613
6635
  content: [
6614
6636
  {
6615
6637
  type: "text",
6616
6638
  text: JSON.stringify({
6617
- id: thread.id,
6618
- title: thread.title,
6619
- status: thread.status,
6620
- agent: thread.agent,
6621
- model: thread.model,
6622
- branchName: thread.branchName,
6623
- worktreePath: thread.worktreePath,
6624
- fromThreadId: source?.id ?? ref,
6625
- link: `sideboard://thread/${thread.id}`
6639
+ ...summary,
6640
+ files: summary.files.slice(0, maxFiles ?? 10)
6626
6641
  })
6627
6642
  }
6628
6643
  ]
6629
6644
  };
6630
- } catch (err) {
6631
- const message = err instanceof Error ? err.message : String(err);
6632
- return { content: [{ type: "text", text: message }], isError: true };
6633
6645
  }
6634
- }
6635
- );
6636
- server.tool(
6637
- "fork_chat",
6638
- "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.",
6639
- {
6640
- ref: z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6641
- through_index: z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6642
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6643
- model: z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6644
- title: z.string().optional()
6645
- },
6646
- async ({ ref, through_index, agent, model, title }) => {
6647
- try {
6648
- const source = orch.getThread(ref);
6649
- if (!source) {
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);
6650
6655
  return {
6651
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
6652
- isError: true
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
+ ]
6653
6668
  };
6669
+ } catch (err) {
6670
+ const message = err instanceof Error ? err.message : String(err);
6671
+ return { content: [{ type: "text", text: message }], isError: true };
6654
6672
  }
6655
- const tab = orch.forkChatTab({
6656
- threadId: source.id,
6657
- throughIndex: through_index,
6658
- agent,
6659
- model,
6660
- title
6661
- });
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
+ };
6757
+ }
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);
6662
6798
  return {
6663
6799
  content: [
6664
6800
  {
6665
6801
  type: "text",
6666
6802
  text: JSON.stringify({
6667
- id: tab.id,
6668
- title: tab.title,
6669
- status: tab.status,
6670
- agent: tab.agent,
6671
- model: tab.model,
6672
- sourceType: tab.sourceType,
6673
- worktreePath: tab.worktreePath,
6674
- fromThreadId: source.id,
6675
- link: `sideboard://thread/${tab.id}`
6803
+ port: result.port,
6804
+ scriptName: result.scriptName,
6805
+ ports: result.ports,
6806
+ url: `http://localhost:${result.port}`
6676
6807
  })
6677
6808
  }
6678
6809
  ]
6679
6810
  };
6680
- } catch (err) {
6681
- const message = err instanceof Error ? err.message : String(err);
6682
- return { content: [{ type: "text", text: message }], isError: true };
6683
6811
  }
6684
- }
6685
- );
6686
- server.tool(
6687
- "run_dev_script",
6688
- "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
6689
- {
6690
- ref: z.string(),
6691
- name: z.string().optional()
6692
- },
6693
- async ({ ref, name }) => {
6694
- const result = await orch.startDev(ref, name);
6695
- return {
6696
- content: [
6697
- {
6698
- type: "text",
6699
- text: JSON.stringify({
6700
- port: result.port,
6701
- scriptName: result.scriptName,
6702
- ports: result.ports,
6703
- url: `http://localhost:${result.port}`
6704
- })
6705
- }
6706
- ]
6707
- };
6708
- }
6709
- );
6710
- server.tool(
6711
- "list_run_scripts",
6712
- "List named run scripts available for a thread",
6713
- { ref: z.string() },
6714
- async ({ ref }) => {
6715
- const scripts = orch.listThreadRunScripts(ref);
6716
- const active = orch.getActiveRuns(ref);
6717
- return {
6718
- content: [
6719
- {
6720
- type: "text",
6721
- text: JSON.stringify({ scripts, active }, null, 2)
6722
- }
6723
- ]
6724
- };
6725
- }
6726
- );
6727
- server.tool(
6728
- "stop_dev_script",
6729
- "Stop a running script for a thread (all scripts if name omitted)",
6730
- {
6731
- ref: z.string(),
6732
- name: z.string().optional()
6733
- },
6734
- async ({ ref, name }) => {
6735
- orch.stopDev(ref, name);
6736
- return { content: [{ type: "text", text: "ok" }] };
6737
- }
6738
- );
6739
- server.tool(
6740
- "run_setup",
6741
- "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
6742
- { ref: z.string() },
6743
- async ({ ref }) => {
6744
- const result = await orch.runSetup(ref);
6745
- return {
6746
- content: [{ type: "text", text: JSON.stringify(result) }]
6747
- };
6748
- }
6749
- );
6750
- server.tool(
6751
- "add_workspace",
6752
- "Register a git repo as a Sideboard workspace",
6753
- { repoPath: z.string() },
6754
- async ({ repoPath }) => {
6755
- const ws = await orch.addWorkspace(repoPath);
6756
- return { content: [{ type: "text", text: JSON.stringify(ws) }] };
6757
- }
6758
- );
6759
- server.tool(
6760
- "remove_workspace",
6761
- "Unregister a Sideboard workspace (does not archive threads)",
6762
- { repoPath: z.string() },
6763
- async ({ repoPath }) => {
6764
- orch.removeWorkspace(repoPath);
6765
- return { content: [{ type: "text", text: "ok" }] };
6766
- }
6767
- );
6768
- server.tool(
6769
- "fanout",
6770
- "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
6771
- {
6772
- prompt: z.string(),
6773
- agents: z.array(z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6774
- repoPath: z.string(),
6775
- sourceType: z.enum(["branch", "pr", "ticket"]).optional(),
6776
- sourceRef: z.string().optional(),
6777
- title: z.string().optional()
6778
- },
6779
- async (args) => {
6780
- const threads = await orch.bestOfN(args);
6781
- return {
6782
- content: [
6783
- {
6784
- type: "text",
6785
- text: JSON.stringify(
6786
- threads.map((t) => ({
6787
- id: t.id,
6788
- agent: t.agent,
6789
- branchName: t.branchName,
6790
- worktreePath: t.worktreePath
6791
- })),
6792
- null,
6793
- 2
6794
- )
6795
- }
6796
- ]
6797
- };
6798
- }
6799
- );
6800
- server.tool(
6801
- "list_branches",
6802
- "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).",
6803
- {
6804
- repoPath: z.string(),
6805
- unmergedOnly: z.boolean().optional()
6806
- },
6807
- async ({ repoPath, unmergedOnly }) => {
6808
- const root = await resolveRepoRoot(repoPath);
6809
- const branches = await listBranches(root, {
6810
- unmergedOnly: unmergedOnly !== false
6811
- });
6812
- return {
6813
- content: [
6814
- {
6815
- type: "text",
6816
- text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
6817
- }
6818
- ]
6819
- };
6820
- }
6821
- );
6822
- server.tool(
6823
- "list_prs",
6824
- "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.",
6825
- { repoPath: z.string() },
6826
- async ({ repoPath }) => {
6827
- const root = await resolveRepoRoot(repoPath);
6828
- const prs = await listPrs(root);
6829
- return {
6830
- content: [
6831
- {
6832
- type: "text",
6833
- text: prs.map(
6834
- (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
6835
- ).join("\n")
6836
- }
6837
- ]
6838
- };
6839
- }
6840
- );
6841
- server.tool(
6842
- "get_pr_stack",
6843
- "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.",
6844
- { ref: z.string() },
6845
- async ({ ref }) => {
6846
- const stack = await orch.getPrStack(ref);
6847
- return {
6848
- content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
6849
- };
6850
- }
6851
- );
6852
- server.tool(
6853
- "open_pr_stack_layers",
6854
- "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6855
- {
6856
- ref: z.string(),
6857
- layer: z.number().int().positive().optional()
6858
- },
6859
- async ({ ref, layer }) => {
6860
- const result = await orch.openPrStackLayers(ref, { layer });
6861
- return {
6862
- content: [
6863
- {
6864
- type: "text",
6865
- text: JSON.stringify(
6866
- {
6867
- stackNumber: result.stack.stackNumber,
6868
- trunk: result.stack.trunk,
6869
- threads: result.threads.map((t) => ({
6870
- id: t.id,
6871
- title: t.title,
6872
- branchName: t.branchName,
6873
- stackLayer: t.stackLayer,
6874
- worktreePath: t.worktreePath,
6875
- prUrl: t.prUrl,
6876
- link: `sideboard://thread/${t.id}`
6877
- }))
6878
- },
6879
- null,
6880
- 2
6881
- )
6882
- }
6883
- ]
6884
- };
6885
- }
6886
- );
6887
- server.tool(
6888
- "add_stack_layer",
6889
- "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6890
- {
6891
- ref: z.string(),
6892
- branchName: z.string(),
6893
- title: z.string().optional()
6894
- },
6895
- async ({ ref, branchName, title }) => {
6896
- const result = await orch.addStackLayer(ref, branchName, { title });
6897
- return {
6898
- content: [
6899
- {
6900
- type: "text",
6901
- text: JSON.stringify(
6902
- {
6903
- id: result.thread.id,
6904
- title: result.thread.title,
6905
- branchName: result.thread.branchName,
6906
- stackLayer: result.thread.stackLayer,
6907
- worktreePath: result.thread.worktreePath,
6908
- link: `sideboard://thread/${result.thread.id}`
6909
- },
6910
- null,
6911
- 2
6912
- )
6913
- }
6914
- ]
6915
- };
6916
- }
6917
- );
6918
- server.tool(
6919
- "create_pr_stack",
6920
- "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
6921
- {
6922
- repoPath: z.string(),
6923
- branches: z.array(z.string()).min(1),
6924
- agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6925
- base: z.string().optional(),
6926
- title: z.string().optional()
6927
- },
6928
- async (args) => {
6929
- const result = await orch.createPrStack({
6930
- repoPath: args.repoPath,
6931
- branches: args.branches,
6932
- agent: args.agent,
6933
- base: args.base,
6934
- title: args.title
6935
- });
6936
- return {
6937
- content: [
6938
- {
6939
- type: "text",
6940
- text: JSON.stringify(
6941
- {
6942
- stackNumber: result.stack.stackNumber,
6943
- trunk: result.stack.trunk,
6944
- threads: result.threads.map((t) => ({
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);
6820
+ return {
6821
+ content: [
6822
+ {
6823
+ type: "text",
6824
+ text: JSON.stringify({ scripts, active }, null, 2)
6825
+ }
6826
+ ]
6827
+ };
6828
+ }
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) => ({
6945
6890
  id: t.id,
6946
- title: t.title,
6891
+ agent: t.agent,
6947
6892
  branchName: t.branchName,
6948
- stackLayer: t.stackLayer,
6949
- worktreePath: t.worktreePath,
6950
- link: `sideboard://thread/${t.id}`
6951
- }))
6952
- },
6953
- null,
6954
- 2
6955
- )
6956
- }
6957
- ]
6958
- };
6959
- }
6960
- );
6961
- server.tool(
6962
- "list_issues",
6963
- "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.",
6964
- { repoPath: z.string() },
6965
- async ({ repoPath }) => {
6966
- const root = await resolveRepoRoot(repoPath);
6967
- const result = await listIssues(root);
6968
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
6969
- }
6970
- );
6971
- server.tool(
6972
- "list_linear_issues",
6973
- "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
6974
- {
6975
- agent: z.enum(["claude", "codex", "opencode"]),
6976
- repoPath: z.string()
6977
- },
6978
- async ({ agent, repoPath }) => {
6979
- const issues = await listLinearIssues(agent, repoPath);
6980
- return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
6981
- }
6982
- );
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
6914
+ });
6915
+ return {
6916
+ content: [
6917
+ {
6918
+ type: "text",
6919
+ text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
6920
+ }
6921
+ ]
6922
+ };
6923
+ }
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
7038
+ });
7039
+ return {
7040
+ content: [
7041
+ {
7042
+ type: "text",
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
+ )
7059
+ }
7060
+ ]
7061
+ };
7062
+ }
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
+ }
6983
7087
  const transport = new StdioServerTransport();
6984
7088
  await server.connect(transport);
6985
7089
  }
@@ -7345,6 +7449,7 @@ export {
7345
7449
  REVIEW_REQUEST_PREFILL,
7346
7450
  SIDEBOARD_FORCE_STOP,
7347
7451
  SIDEBOARD_MCP_ALLOWED_TOOLS,
7452
+ SIDEBOARD_MCP_PROFILE_ENV,
7348
7453
  THINKING_EFFORTS,
7349
7454
  addPrStackLayer,
7350
7455
  addStackLayerFromThread,
@@ -7367,6 +7472,7 @@ export {
7367
7472
  attachmentsFromBuffers,
7368
7473
  attachmentsFromWorktreePaths,
7369
7474
  attachmentsGitignoreBody,
7475
+ autoArchiveOnMergeEnabled,
7370
7476
  autoCleanupOrphansEnabled,
7371
7477
  autoRenameBranchEnabled,
7372
7478
  autoRunAfterSetupEnabled,
@@ -7375,6 +7481,7 @@ export {
7375
7481
  brightsyCloudConnectAgent,
7376
7482
  brightsyCloudConnectEnabled,
7377
7483
  brightsyConfigPath,
7484
+ brightsyInjectWorktreeMcpEnabled,
7378
7485
  brightsyMcpAllowedTools,
7379
7486
  brightsyMcpServerName,
7380
7487
  buildCachedUserContent,
@@ -7426,7 +7533,9 @@ export {
7426
7533
  detectLocalMergeConflicts,
7427
7534
  disconnectBrightsyTeam,
7428
7535
  discoverSkills,
7536
+ dropCachedPrefixOnResume,
7429
7537
  encodeBrightsyTarget,
7538
+ enrichPathWithNpmGlobalBin,
7430
7539
  enrichWorkspacesWithGithub,
7431
7540
  ensureAgentPath,
7432
7541
  ensureCloudCoordinator,
@@ -7464,6 +7573,7 @@ export {
7464
7573
  formatTranscriptMarkdown,
7465
7574
  formatWorkspaceInventory,
7466
7575
  formatWorktreeDirective,
7576
+ formatWorktreeReminder,
7467
7577
  getAdapter,
7468
7578
  getAgentSetupInfo,
7469
7579
  getBrightsySession,
@@ -7590,7 +7700,10 @@ export {
7590
7700
  pastedTextStats,
7591
7701
  permissionMode,
7592
7702
  planFileAbs,
7703
+ posixShellSingleQuote,
7704
+ prepareTerminalCommand,
7593
7705
  previewLand,
7706
+ promptMentionsBrightsy,
7594
7707
  pushBranch,
7595
7708
  readExistingReviewRequestFile,
7596
7709
  readPlanFile,
@@ -7606,7 +7719,9 @@ export {
7606
7719
  requestReview,
7607
7720
  requireAgent,
7608
7721
  resetGhStackDetectCache,
7722
+ resolveAgentExecutable,
7609
7723
  resolveClaudeExecutable,
7724
+ resolveCommandBinarySync,
7610
7725
  resolveConductorCursorAgentId,
7611
7726
  resolveCursorModelId,
7612
7727
  resolveDefaultBranch,
@@ -7615,6 +7730,7 @@ export {
7615
7730
  resolveFilesToCopy,
7616
7731
  resolveGhAuthToken,
7617
7732
  resolveGithubRepoSlug,
7733
+ resolveLoginCommand,
7618
7734
  resolveNewThreadOptions,
7619
7735
  resolvePlanMarkdown,
7620
7736
  resolvePrSelector,
@@ -7636,9 +7752,11 @@ export {
7636
7752
  settingsSourceLabel,
7637
7753
  shouldAttachPastedText,
7638
7754
  shouldCompactContext,
7755
+ shouldInjectBrightsyMcp,
7639
7756
  shouldRefreshReviewRequestTemplate,
7640
7757
  shouldRunWorktreeCleanup,
7641
7758
  sideboardHomeDir,
7759
+ sideboardMcpProfile,
7642
7760
  sideboardReposDir,
7643
7761
  sideboardWorkspacesDir,
7644
7762
  slugify,
@@ -7666,6 +7784,7 @@ export {
7666
7784
  threadDisplayLabel,
7667
7785
  threadFilePath,
7668
7786
  threadLockPath,
7787
+ threadRequestsBrightsyMcp,
7669
7788
  threadsDir,
7670
7789
  threadsSharingWorktree,
7671
7790
  toolDescription,
@@ -7673,14 +7792,18 @@ export {
7673
7792
  toolFilePath,
7674
7793
  totalTokens,
7675
7794
  updateAdvancedSettings,
7795
+ updateAgentExecutable,
7676
7796
  updateAppEnvironment,
7677
7797
  updateBrightsySettings,
7678
7798
  updateClaudeSettings,
7799
+ updateCodexSettings,
7679
7800
  updateDefaultsSettings,
7680
7801
  updateIntegrationsSettings,
7802
+ updateOpencodeSettings,
7681
7803
  updateThread,
7682
7804
  validateLinearApiKey,
7683
7805
  withAgentInstructions,
7806
+ withExportedPath,
7684
7807
  withThreadLock,
7685
7808
  workspaceSettingsSourceLabel,
7686
7809
  worktreeCleanupSettings,