@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.cjs CHANGED
@@ -391,11 +391,13 @@ __export(app_settings_exports, {
391
391
  HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
392
392
  appSettingsPath: () => appSettingsPath,
393
393
  applyAppEnvironment: () => applyAppEnvironment,
394
+ autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
394
395
  autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
395
396
  autoRenameBranchEnabled: () => autoRenameBranchEnabled,
396
397
  autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
397
398
  brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
398
399
  brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
400
+ brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
399
401
  caffeinateWhileCloudConnectEnabled: () => caffeinateWhileCloudConnectEnabled,
400
402
  caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
401
403
  childEnvWithAppSettings: () => childEnvWithAppSettings,
@@ -414,17 +416,21 @@ __export(app_settings_exports, {
414
416
  maxConcurrentAgents: () => maxConcurrentAgents,
415
417
  orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
416
418
  orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
419
+ resolveAgentExecutable: () => resolveAgentExecutable,
417
420
  resolveClaudeExecutable: () => resolveClaudeExecutable,
418
421
  resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
419
422
  resolveNewThreadOptions: () => resolveNewThreadOptions,
420
423
  resolveThreadDefaults: () => resolveThreadDefaults,
421
424
  saveAppSettings: () => saveAppSettings,
422
425
  updateAdvancedSettings: () => updateAdvancedSettings,
426
+ updateAgentExecutable: () => updateAgentExecutable,
423
427
  updateAppEnvironment: () => updateAppEnvironment,
424
428
  updateBrightsySettings: () => updateBrightsySettings,
425
429
  updateClaudeSettings: () => updateClaudeSettings,
430
+ updateCodexSettings: () => updateCodexSettings,
426
431
  updateDefaultsSettings: () => updateDefaultsSettings,
427
- updateIntegrationsSettings: () => updateIntegrationsSettings
432
+ updateIntegrationsSettings: () => updateIntegrationsSettings,
433
+ updateOpencodeSettings: () => updateOpencodeSettings
428
434
  });
429
435
  function appSettingsPath() {
430
436
  return (0, import_node_path4.join)(appDataDir(), "settings.json");
@@ -445,16 +451,33 @@ function normalizeClaude(raw) {
445
451
  }
446
452
  return out;
447
453
  }
454
+ function normalizeCliExecutable(raw) {
455
+ if (!raw || typeof raw !== "object") return {};
456
+ const source = raw;
457
+ const out = {};
458
+ if (typeof source.executablePath === "string") {
459
+ const path = source.executablePath.trim();
460
+ if (path) out.executablePath = path;
461
+ }
462
+ return out;
463
+ }
448
464
  function normalizeBrightsy(raw) {
449
465
  if (!raw || typeof raw !== "object") return {};
450
466
  const source = raw;
451
467
  const out = {};
468
+ if (typeof source.executablePath === "string") {
469
+ const path = source.executablePath.trim();
470
+ if (path) out.executablePath = path;
471
+ }
452
472
  if (typeof source.cloudConnectEnabled === "boolean") {
453
473
  out.cloudConnectEnabled = source.cloudConnectEnabled;
454
474
  }
455
475
  if (typeof source.cloudConnectAgent === "string" && CLOUD_CONNECT_AGENTS.has(source.cloudConnectAgent)) {
456
476
  out.cloudConnectAgent = source.cloudConnectAgent;
457
477
  }
478
+ if (typeof source.injectWorktreeMcp === "boolean") {
479
+ out.injectWorktreeMcp = source.injectWorktreeMcp;
480
+ }
458
481
  return out;
459
482
  }
460
483
  function normalizeIntegrations(raw) {
@@ -508,6 +531,9 @@ function normalizeAdvanced(raw) {
508
531
  if (typeof source.deleteBranchOnPurge === "boolean") {
509
532
  out.deleteBranchOnPurge = source.deleteBranchOnPurge;
510
533
  }
534
+ if (typeof source.autoArchiveOnMerge === "boolean") {
535
+ out.autoArchiveOnMerge = source.autoArchiveOnMerge;
536
+ }
511
537
  if (typeof source.maxConcurrent === "number" && Number.isFinite(source.maxConcurrent)) {
512
538
  out.maxConcurrent = Math.max(1, Math.min(32, Math.floor(source.maxConcurrent)));
513
539
  }
@@ -537,6 +563,8 @@ function normalizeAdvanced(raw) {
537
563
  function normalizeSettings(raw) {
538
564
  const env = {};
539
565
  let claude = {};
566
+ let codex = {};
567
+ let opencode = {};
540
568
  let brightsy = {};
541
569
  let integrations = {};
542
570
  let defaults = {};
@@ -555,6 +583,12 @@ function normalizeSettings(raw) {
555
583
  if ("claude" in raw) {
556
584
  claude = normalizeClaude(raw.claude);
557
585
  }
586
+ if ("codex" in raw) {
587
+ codex = normalizeCliExecutable(raw.codex);
588
+ }
589
+ if ("opencode" in raw) {
590
+ opencode = normalizeCliExecutable(raw.opencode);
591
+ }
558
592
  if ("brightsy" in raw) {
559
593
  brightsy = normalizeBrightsy(raw.brightsy);
560
594
  }
@@ -570,7 +604,16 @@ function normalizeSettings(raw) {
570
604
  advanced = normalizeAdvanced(raw.advanced);
571
605
  }
572
606
  }
573
- return { environment: env, claude, brightsy, integrations, defaults, advanced };
607
+ return {
608
+ environment: env,
609
+ claude,
610
+ codex,
611
+ opencode,
612
+ brightsy,
613
+ integrations,
614
+ defaults,
615
+ advanced
616
+ };
574
617
  }
575
618
  function loadAppSettings() {
576
619
  const path = appSettingsPath();
@@ -623,6 +666,13 @@ function updateClaudeSettings(patch) {
623
666
  function updateBrightsySettings(patch) {
624
667
  const current = loadAppSettings();
625
668
  const brightsy = { ...current.brightsy };
669
+ if ("executablePath" in patch) {
670
+ if (patch.executablePath == null || patch.executablePath.trim() === "") {
671
+ delete brightsy.executablePath;
672
+ } else {
673
+ brightsy.executablePath = patch.executablePath.trim();
674
+ }
675
+ }
626
676
  if (typeof patch.cloudConnectEnabled === "boolean") {
627
677
  brightsy.cloudConnectEnabled = patch.cloudConnectEnabled;
628
678
  }
@@ -633,8 +683,35 @@ function updateBrightsySettings(patch) {
633
683
  brightsy.cloudConnectAgent = patch.cloudConnectAgent;
634
684
  }
635
685
  }
686
+ if (typeof patch.injectWorktreeMcp === "boolean") {
687
+ brightsy.injectWorktreeMcp = patch.injectWorktreeMcp;
688
+ }
636
689
  return saveAppSettings({ ...current, brightsy });
637
690
  }
691
+ function updateCodexSettings(patch) {
692
+ const current = loadAppSettings();
693
+ const codex = { ...current.codex };
694
+ if ("executablePath" in patch) {
695
+ if (patch.executablePath == null || patch.executablePath.trim() === "") {
696
+ delete codex.executablePath;
697
+ } else {
698
+ codex.executablePath = patch.executablePath.trim();
699
+ }
700
+ }
701
+ return saveAppSettings({ ...current, codex });
702
+ }
703
+ function updateOpencodeSettings(patch) {
704
+ const current = loadAppSettings();
705
+ const opencode = { ...current.opencode };
706
+ if ("executablePath" in patch) {
707
+ if (patch.executablePath == null || patch.executablePath.trim() === "") {
708
+ delete opencode.executablePath;
709
+ } else {
710
+ opencode.executablePath = patch.executablePath.trim();
711
+ }
712
+ }
713
+ return saveAppSettings({ ...current, opencode });
714
+ }
638
715
  function updateIntegrationsSettings(patch) {
639
716
  const current = loadAppSettings();
640
717
  const integrations = { ...current.integrations };
@@ -742,6 +819,9 @@ function getLinearApiKey(settings = loadAppSettings()) {
742
819
  function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
743
820
  return Boolean(settings.brightsy.cloudConnectEnabled);
744
821
  }
822
+ function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
823
+ return Boolean(settings.brightsy.injectWorktreeMcp);
824
+ }
745
825
  function brightsyCloudConnectAgent(settings = loadAppSettings()) {
746
826
  const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
747
827
  const preferred = getDefaultAgent(settings);
@@ -768,6 +848,9 @@ function updateAdvancedSettings(patch) {
768
848
  if (typeof patch.deleteBranchOnPurge === "boolean") {
769
849
  advanced.deleteBranchOnPurge = patch.deleteBranchOnPurge;
770
850
  }
851
+ if (typeof patch.autoArchiveOnMerge === "boolean") {
852
+ advanced.autoArchiveOnMerge = patch.autoArchiveOnMerge;
853
+ }
771
854
  if (typeof patch.maxConcurrent === "number" && Number.isFinite(patch.maxConcurrent)) {
772
855
  advanced.maxConcurrent = Math.max(1, Math.min(32, Math.floor(patch.maxConcurrent)));
773
856
  }
@@ -809,6 +892,9 @@ function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
809
892
  function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
810
893
  return Boolean(settings.advanced.deleteBranchOnPurge);
811
894
  }
895
+ function autoArchiveOnMergeEnabled(settings = loadAppSettings()) {
896
+ return Boolean(settings.advanced.autoArchiveOnMerge);
897
+ }
812
898
  function autoCleanupOrphansEnabled(settings = loadAppSettings()) {
813
899
  return Boolean(settings.advanced.autoCleanupOrphans);
814
900
  }
@@ -830,8 +916,36 @@ function maxConcurrentAgents(settings = loadAppSettings()) {
830
916
  return 3;
831
917
  }
832
918
  function resolveClaudeExecutable(settings = loadAppSettings()) {
833
- const override = settings.claude.executablePath?.trim();
834
- return override || "claude";
919
+ return resolveAgentExecutable("claude", settings);
920
+ }
921
+ function resolveAgentExecutable(agent, settings = loadAppSettings()) {
922
+ const fallback = DEFAULT_CLI_BIN[agent];
923
+ if (agent === "claude") {
924
+ const override2 = settings.claude.executablePath?.trim();
925
+ return override2 || fallback;
926
+ }
927
+ if (agent === "codex") {
928
+ const override2 = settings.codex.executablePath?.trim();
929
+ return override2 || fallback;
930
+ }
931
+ if (agent === "opencode") {
932
+ const override2 = settings.opencode.executablePath?.trim();
933
+ return override2 || fallback;
934
+ }
935
+ const override = settings.brightsy.executablePath?.trim();
936
+ return override || fallback;
937
+ }
938
+ function updateAgentExecutable(agent, executablePath) {
939
+ if (agent === "claude") {
940
+ return updateClaudeSettings({ executablePath });
941
+ }
942
+ if (agent === "codex") {
943
+ return updateCodexSettings({ executablePath });
944
+ }
945
+ if (agent === "opencode") {
946
+ return updateOpencodeSettings({ executablePath });
947
+ }
948
+ return updateBrightsySettings({ executablePath });
835
949
  }
836
950
  function claudeChromeEnabled(settings = loadAppSettings()) {
837
951
  return Boolean(settings.claude.chromeEnabled);
@@ -859,7 +973,7 @@ function childEnvWithAppSettings(extra) {
859
973
  function harnessEnvKey(harness) {
860
974
  return HARNESS_ENV_KEYS[harness];
861
975
  }
862
- var import_node_fs3, import_node_os3, import_node_path4, HARNESS_ENV_KEYS, DEFAULT_AGENTS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, EMPTY_SETTINGS;
976
+ var import_node_fs3, import_node_os3, import_node_path4, HARNESS_ENV_KEYS, DEFAULT_AGENTS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
863
977
  var init_app_settings = __esm({
864
978
  "src/store/app-settings.ts"() {
865
979
  "use strict";
@@ -892,11 +1006,19 @@ var init_app_settings = __esm({
892
1006
  EMPTY_SETTINGS = {
893
1007
  environment: {},
894
1008
  claude: {},
1009
+ codex: {},
1010
+ opencode: {},
895
1011
  brightsy: {},
896
1012
  integrations: {},
897
1013
  defaults: {},
898
1014
  advanced: {}
899
1015
  };
1016
+ DEFAULT_CLI_BIN = {
1017
+ claude: "claude",
1018
+ codex: "codex",
1019
+ opencode: "opencode",
1020
+ brightsy: "brightsy"
1021
+ };
900
1022
  }
901
1023
  });
902
1024
 
@@ -937,6 +1059,8 @@ function normalizeThread(raw) {
937
1059
  agentPid: raw.agentPid ?? null,
938
1060
  attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
939
1061
  prTitle: raw.prTitle ?? null,
1062
+ prState: raw.prState ?? null,
1063
+ skipAutoArchiveOnMerge: Boolean(raw.skipAutoArchiveOnMerge),
940
1064
  stackId: raw.stackId ?? null,
941
1065
  stackLayer: raw.stackLayer ?? null,
942
1066
  userSetTitle: Boolean(raw.userSetTitle),
@@ -963,6 +1087,8 @@ function createEmptyThread(partial) {
963
1087
  activeRuns: partial.activeRuns ?? [],
964
1088
  prUrl: partial.prUrl ?? null,
965
1089
  prTitle: partial.prTitle ?? null,
1090
+ prState: partial.prState ?? null,
1091
+ skipAutoArchiveOnMerge: partial.skipAutoArchiveOnMerge ?? false,
966
1092
  stackId: partial.stackId ?? null,
967
1093
  stackLayer: partial.stackLayer ?? null,
968
1094
  userSetTitle: partial.userSetTitle ?? false,
@@ -2027,18 +2153,28 @@ var init_gh_errors = __esm({
2027
2153
  });
2028
2154
 
2029
2155
  // src/agents/path.ts
2156
+ function prependPathDir(env, dir) {
2157
+ if (!dir || !(0, import_node_fs5.existsSync)(dir)) return;
2158
+ const current = env.PATH ?? "";
2159
+ const parts = current.split(import_node_path5.delimiter).filter(Boolean);
2160
+ if (parts.includes(dir)) {
2161
+ env.PATH = current;
2162
+ return;
2163
+ }
2164
+ env.PATH = [dir, ...parts].join(import_node_path5.delimiter);
2165
+ }
2030
2166
  function ensureAgentPath(env = process.env) {
2031
2167
  const home = env.HOME || env.USERPROFILE || (0, import_node_os4.homedir)();
2032
2168
  const current = env.PATH ?? "";
2033
2169
  const parts = current.split(import_node_path5.delimiter).filter(Boolean);
2034
2170
  const seen = new Set(parts);
2035
2171
  const extras = [
2036
- ...EXTRA_BIN_DIRS.map((rel) => `${home}/${rel}`),
2172
+ ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path5.join)(home, rel)),
2037
2173
  "/opt/homebrew/bin",
2038
2174
  "/usr/local/bin"
2039
2175
  ];
2040
2176
  for (const dir of extras.reverse()) {
2041
- if (!dir || seen.has(dir)) continue;
2177
+ if (!dir || seen.has(dir) || !(0, import_node_fs5.existsSync)(dir)) continue;
2042
2178
  parts.unshift(dir);
2043
2179
  seen.add(dir);
2044
2180
  }
@@ -2046,10 +2182,50 @@ function ensureAgentPath(env = process.env) {
2046
2182
  env.PATH = next;
2047
2183
  return next;
2048
2184
  }
2049
- var import_node_os4, import_node_path5, EXTRA_BIN_DIRS;
2185
+ function enrichPathWithNpmGlobalBin(env = process.env) {
2186
+ ensureAgentPath(env);
2187
+ try {
2188
+ const prefix = (0, import_node_child_process.execFileSync)("npm", ["prefix", "-g"], {
2189
+ encoding: "utf8",
2190
+ env: { ...process.env, ...env },
2191
+ timeout: 8e3,
2192
+ stdio: ["ignore", "pipe", "ignore"]
2193
+ }).trim().split(/\r?\n/).find(Boolean);
2194
+ if (prefix) {
2195
+ const binDir = process.platform === "win32" ? prefix : (0, import_node_path5.join)(prefix, "bin");
2196
+ prependPathDir(env, binDir);
2197
+ }
2198
+ } catch {
2199
+ }
2200
+ return env.PATH ?? "";
2201
+ }
2202
+ function posixShellSingleQuote(value) {
2203
+ return `'${value.replace(/'/g, `'\\''`)}'`;
2204
+ }
2205
+ function resolveCommandBinarySync(command, whichPath) {
2206
+ const trimmed = command.trim();
2207
+ if (!trimmed) return trimmed;
2208
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
2209
+ if (!match) return trimmed;
2210
+ const bin = match[1];
2211
+ const rest = match[2] ?? "";
2212
+ if (bin.includes("/") || bin.includes("\\")) return trimmed;
2213
+ const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
2214
+ if (!abs) return trimmed;
2215
+ return `${abs}${rest}`;
2216
+ }
2217
+ function withExportedPath(command, pathValue) {
2218
+ const trimmed = command.trim();
2219
+ if (!trimmed) return trimmed;
2220
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
2221
+ return `export PATH=${posixShellSingleQuote(pathValue)}; ${trimmed}`;
2222
+ }
2223
+ var import_node_fs5, import_node_child_process, import_node_os4, import_node_path5, EXTRA_BIN_DIRS;
2050
2224
  var init_path = __esm({
2051
2225
  "src/agents/path.ts"() {
2052
2226
  "use strict";
2227
+ import_node_fs5 = require("fs");
2228
+ import_node_child_process = require("child_process");
2053
2229
  import_node_os4 = require("os");
2054
2230
  import_node_path5 = require("path");
2055
2231
  EXTRA_BIN_DIRS = [
@@ -2057,6 +2233,10 @@ var init_path = __esm({
2057
2233
  ".cargo/bin",
2058
2234
  ".nvm/current/bin",
2059
2235
  ".asdf/shims",
2236
+ ".volta/bin",
2237
+ ".npm-global/bin",
2238
+ // fnm default alias (common when shell init is skipped)
2239
+ ".local/share/fnm/aliases/default/bin",
2060
2240
  // pnpm / npm global bins (where `@brightsy/cli` typically lands)
2061
2241
  "Library/pnpm",
2062
2242
  ".pnpm"
@@ -3144,7 +3324,7 @@ function isLocalPrFetchBranch(ref) {
3144
3324
  async function createThreadWorktree(opts) {
3145
3325
  let branchName = `thread/${opts.slug}`;
3146
3326
  const worktreePath = (0, import_node_path6.join)(worktreesRoot(opts.repoPath), opts.slug);
3147
- if ((0, import_node_fs5.existsSync)(worktreePath)) {
3327
+ if ((0, import_node_fs6.existsSync)(worktreePath)) {
3148
3328
  throw new Error(`Worktree already exists at ${worktreePath}`);
3149
3329
  }
3150
3330
  await ensureGhPreferOrigin(opts.repoPath);
@@ -3214,7 +3394,7 @@ async function createExistingBranchWorktree(opts) {
3214
3394
  const branchName = opts.branchName.trim();
3215
3395
  if (!branchName) throw new Error("branch name required");
3216
3396
  const worktreePath = (0, import_node_path6.join)(worktreesRoot(opts.repoPath), opts.slug);
3217
- if ((0, import_node_fs5.existsSync)(worktreePath)) {
3397
+ if ((0, import_node_fs6.existsSync)(worktreePath)) {
3218
3398
  throw new Error(`Worktree already exists at ${worktreePath}`);
3219
3399
  }
3220
3400
  await ensureGhPreferOrigin(opts.repoPath);
@@ -3475,9 +3655,9 @@ function sameRepoPath(a, b) {
3475
3655
  }
3476
3656
  function listLocalThreadBranchSlugs(repoPath) {
3477
3657
  const refsDir = (0, import_node_path6.join)(repoPath, ".git", "refs", "heads", "thread");
3478
- if (!(0, import_node_fs5.existsSync)(refsDir)) return [];
3658
+ if (!(0, import_node_fs6.existsSync)(refsDir)) return [];
3479
3659
  try {
3480
- return (0, import_node_fs5.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
3660
+ return (0, import_node_fs6.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
3481
3661
  } catch {
3482
3662
  return [];
3483
3663
  }
@@ -3485,8 +3665,8 @@ function listLocalThreadBranchSlugs(repoPath) {
3485
3665
  function collectTakenTeamSlugs(repoPath) {
3486
3666
  const taken = /* @__PURE__ */ new Set();
3487
3667
  const root = worktreesRoot(repoPath);
3488
- if ((0, import_node_fs5.existsSync)(root)) {
3489
- for (const entry of (0, import_node_fs5.readdirSync)(root, { withFileTypes: true })) {
3668
+ if ((0, import_node_fs6.existsSync)(root)) {
3669
+ for (const entry of (0, import_node_fs6.readdirSync)(root, { withFileTypes: true })) {
3490
3670
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
3491
3671
  taken.add(normalizeTakenSlug(entry.name));
3492
3672
  }
@@ -3508,16 +3688,16 @@ function allocateTeamSlug(repoPath) {
3508
3688
  for (let attempt = 0; attempt < 32; attempt++) {
3509
3689
  const team = allocateTeamName(taken);
3510
3690
  const path = (0, import_node_path6.join)(worktreesRoot(repoPath), team.slug);
3511
- if (!(0, import_node_fs5.existsSync)(path)) return team;
3691
+ if (!(0, import_node_fs6.existsSync)(path)) return team;
3512
3692
  taken.add(team.slug);
3513
3693
  }
3514
3694
  throw new Error("No available soccer team worktree directories left");
3515
3695
  }
3516
- var import_node_fs5, import_node_path6;
3696
+ var import_node_fs6, import_node_path6;
3517
3697
  var init_worktree = __esm({
3518
3698
  "src/git/worktree.ts"() {
3519
3699
  "use strict";
3520
- import_node_fs5 = require("fs");
3700
+ import_node_fs6 = require("fs");
3521
3701
  import_node_path6 = require("path");
3522
3702
  init_paths();
3523
3703
  init_thread_store();
@@ -3597,7 +3777,7 @@ function coordinatorTurnReminder(opts) {
3597
3777
  function ensureGlobalCoordinatorCwd(opts) {
3598
3778
  const dir = globalAgentCwd();
3599
3779
  try {
3600
- (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
3780
+ (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
3601
3781
  } catch {
3602
3782
  return dir;
3603
3783
  }
@@ -3605,7 +3785,7 @@ function ensureGlobalCoordinatorCwd(opts) {
3605
3785
  let orchId = opts?.orchestratorThreadId?.trim() || "";
3606
3786
  if (!orchId) {
3607
3787
  try {
3608
- const existing = (0, import_node_fs6.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
3788
+ const existing = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
3609
3789
  const m = existing.match(
3610
3790
  /YOUR orchestration thread id is `([0-9a-f-]{36})`/i
3611
3791
  );
@@ -3647,9 +3827,9 @@ function ensureGlobalCoordinatorCwd(opts) {
3647
3827
  "Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
3648
3828
  ].join("\n");
3649
3829
  try {
3650
- (0, import_node_fs6.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
3830
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
3651
3831
  `, "utf8");
3652
- (0, import_node_fs6.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
3832
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
3653
3833
  `, "utf8");
3654
3834
  } catch {
3655
3835
  }
@@ -3682,11 +3862,11 @@ function coordinatorSystemPrompt(opts) {
3682
3862
  formatWorkspaceInventory(opts.workspaces)
3683
3863
  ].join("\n");
3684
3864
  }
3685
- var import_node_fs6, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
3865
+ var import_node_fs7, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
3686
3866
  var init_coordinator_prompt = __esm({
3687
3867
  "src/orchestrator/coordinator-prompt.ts"() {
3688
3868
  "use strict";
3689
- import_node_fs6 = require("fs");
3869
+ import_node_fs7 = require("fs");
3690
3870
  import_node_path7 = require("path");
3691
3871
  init_worktree();
3692
3872
  init_app_settings();
@@ -3923,31 +4103,31 @@ function removedWorkspacesFile() {
3923
4103
  }
3924
4104
  function readAll() {
3925
4105
  const path = workspacesFile();
3926
- if (!(0, import_node_fs7.existsSync)(path)) return [];
4106
+ if (!(0, import_node_fs8.existsSync)(path)) return [];
3927
4107
  try {
3928
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
4108
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
3929
4109
  return Array.isArray(raw) ? raw : [];
3930
4110
  } catch {
3931
4111
  return [];
3932
4112
  }
3933
4113
  }
3934
4114
  function writeAll(list) {
3935
- (0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
3936
- (0, import_node_fs7.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
4115
+ (0, import_node_fs8.mkdirSync)(appDataDir(), { recursive: true });
4116
+ (0, import_node_fs8.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
3937
4117
  }
3938
4118
  function readRemoved() {
3939
4119
  const path = removedWorkspacesFile();
3940
- if (!(0, import_node_fs7.existsSync)(path)) return /* @__PURE__ */ new Set();
4120
+ if (!(0, import_node_fs8.existsSync)(path)) return /* @__PURE__ */ new Set();
3941
4121
  try {
3942
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
4122
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
3943
4123
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
3944
4124
  } catch {
3945
4125
  return /* @__PURE__ */ new Set();
3946
4126
  }
3947
4127
  }
3948
4128
  function writeRemoved(paths) {
3949
- (0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
3950
- (0, import_node_fs7.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
4129
+ (0, import_node_fs8.mkdirSync)(appDataDir(), { recursive: true });
4130
+ (0, import_node_fs8.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
3951
4131
  }
3952
4132
  function rememberRemoved(repoPath) {
3953
4133
  const next = readRemoved();
@@ -3970,7 +4150,7 @@ function listWorkspaces() {
3970
4150
  async function addWorkspace(repoPath) {
3971
4151
  const root = await resolveRepoRoot(repoPath);
3972
4152
  if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
3973
- if (!(0, import_node_fs7.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
4153
+ if (!(0, import_node_fs8.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
3974
4154
  forgetRemoved(root);
3975
4155
  await ensureGhPreferOrigin(root);
3976
4156
  const current = readAll();
@@ -4000,7 +4180,7 @@ function syncWorkspacesFromThreads(repoPaths) {
4000
4180
  if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
4001
4181
  continue;
4002
4182
  }
4003
- if (!(0, import_node_fs7.existsSync)(path)) continue;
4183
+ if (!(0, import_node_fs8.existsSync)(path)) continue;
4004
4184
  const ws = {
4005
4185
  path,
4006
4186
  name: (0, import_node_path8.basename)(path),
@@ -4013,11 +4193,11 @@ function syncWorkspacesFromThreads(repoPaths) {
4013
4193
  if (dirty) writeAll(next);
4014
4194
  return next.sort((a, b) => a.name.localeCompare(b.name));
4015
4195
  }
4016
- var import_node_fs7, import_node_path8;
4196
+ var import_node_fs8, import_node_path8;
4017
4197
  var init_workspaces = __esm({
4018
4198
  "src/store/workspaces.ts"() {
4019
4199
  "use strict";
4020
- import_node_fs7 = require("fs");
4200
+ import_node_fs8 = require("fs");
4021
4201
  import_node_path8 = require("path");
4022
4202
  init_paths();
4023
4203
  init_global_workspace();
@@ -4031,26 +4211,26 @@ function brightsyConfigPath() {
4031
4211
  }
4032
4212
  function loadBrightsyConfig() {
4033
4213
  const path = brightsyConfigPath();
4034
- if (!(0, import_node_fs8.existsSync)(path)) {
4214
+ if (!(0, import_node_fs9.existsSync)(path)) {
4035
4215
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
4036
4216
  }
4037
- const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
4217
+ const raw = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
4038
4218
  if (!raw.access_token || !raw.account_id) {
4039
4219
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
4040
4220
  }
4041
4221
  return raw;
4042
4222
  }
4043
4223
  function saveBrightsyConfig(cfg) {
4044
- (0, import_node_fs8.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
4224
+ (0, import_node_fs9.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
4045
4225
  `, {
4046
4226
  mode: 384
4047
4227
  });
4048
4228
  }
4049
- var import_node_fs8, import_node_os5, import_node_path9;
4229
+ var import_node_fs9, import_node_os5, import_node_path9;
4050
4230
  var init_config = __esm({
4051
4231
  "src/brightsy/config.ts"() {
4052
4232
  "use strict";
4053
- import_node_fs8 = require("fs");
4233
+ import_node_fs9 = require("fs");
4054
4234
  import_node_os5 = require("os");
4055
4235
  import_node_path9 = require("path");
4056
4236
  }
@@ -4159,18 +4339,18 @@ function storePath() {
4159
4339
  }
4160
4340
  function readStore() {
4161
4341
  const path = storePath();
4162
- if (!(0, import_node_fs9.existsSync)(path)) return [];
4342
+ if (!(0, import_node_fs10.existsSync)(path)) return [];
4163
4343
  try {
4164
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
4344
+ const parsed = JSON.parse((0, import_node_fs10.readFileSync)(path, "utf8"));
4165
4345
  return Array.isArray(parsed.teams) ? parsed.teams : [];
4166
4346
  } catch {
4167
4347
  return [];
4168
4348
  }
4169
4349
  }
4170
4350
  function writeStore(teams) {
4171
- (0, import_node_fs9.mkdirSync)(appDataDir(), { recursive: true });
4351
+ (0, import_node_fs10.mkdirSync)(appDataDir(), { recursive: true });
4172
4352
  const path = storePath();
4173
- (0, import_node_fs9.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
4353
+ (0, import_node_fs10.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
4174
4354
  `, {
4175
4355
  mode: 384
4176
4356
  });
@@ -4332,11 +4512,11 @@ function brightsyMcpServerName(slug) {
4332
4512
  const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
4333
4513
  return `brightsy_${cleaned || "team"}`;
4334
4514
  }
4335
- var import_node_fs9, import_node_path10;
4515
+ var import_node_fs10, import_node_path10;
4336
4516
  var init_connected_teams = __esm({
4337
4517
  "src/brightsy/connected-teams.ts"() {
4338
4518
  "use strict";
4339
- import_node_fs9 = require("fs");
4519
+ import_node_fs10 = require("fs");
4340
4520
  import_node_path10 = require("path");
4341
4521
  init_paths();
4342
4522
  init_accounts();
@@ -4511,6 +4691,11 @@ function normalizeTurnInput(input) {
4511
4691
  if (typeof input === "string") return { prompt: input };
4512
4692
  return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
4513
4693
  }
4694
+ function dropCachedPrefixOnResume(input, sessionId) {
4695
+ const turn = normalizeTurnInput(input);
4696
+ if (sessionId) return { prompt: turn.prompt };
4697
+ return turn;
4698
+ }
4514
4699
  function flattenTurnInput(input) {
4515
4700
  const { cachedPrefix, prompt } = normalizeTurnInput(input);
4516
4701
  if (!cachedPrefix) return prompt;
@@ -4699,9 +4884,13 @@ async function fetchTeamChatTargets(team) {
4699
4884
  };
4700
4885
  }
4701
4886
  async function listBrightsyChatTargetsViaCli() {
4702
- const listed = await run("brightsy", ["chat", "--list-targets", "--json"], {
4703
- reject: false
4704
- });
4887
+ const listed = await run(
4888
+ resolveAgentExecutable("brightsy"),
4889
+ ["chat", "--list-targets", "--json"],
4890
+ {
4891
+ reject: false
4892
+ }
4893
+ );
4705
4894
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
4706
4895
  throw new Error(
4707
4896
  listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
@@ -4785,13 +4974,15 @@ async function syncCliForTarget(accountId) {
4785
4974
  }
4786
4975
  applyConnectedTeamToCli(team);
4787
4976
  }
4788
- var brightsyAdapter;
4977
+ var import_node_fs11, brightsyAdapter;
4789
4978
  var init_brightsy = __esm({
4790
4979
  "src/agents/brightsy.ts"() {
4791
4980
  "use strict";
4981
+ import_node_fs11 = require("fs");
4792
4982
  init_run();
4793
4983
  init_connected_teams();
4794
4984
  init_config();
4985
+ init_app_settings();
4795
4986
  init_brightsy_targets();
4796
4987
  init_error_detail();
4797
4988
  init_turn_input();
@@ -4799,18 +4990,32 @@ var init_brightsy = __esm({
4799
4990
  brightsyAdapter = {
4800
4991
  kind: "brightsy",
4801
4992
  async detect() {
4802
- const which = await run("which", ["brightsy"], { reject: false });
4803
- if (which.exitCode !== 0) {
4804
- return {
4805
- agent: "brightsy",
4806
- installed: false,
4807
- authenticated: false,
4808
- linearMcp: false,
4809
- warnings: [],
4810
- reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
4811
- };
4993
+ const brightsy = resolveAgentExecutable("brightsy");
4994
+ if (brightsy !== "brightsy") {
4995
+ if (!(0, import_node_fs11.existsSync)(brightsy)) {
4996
+ return {
4997
+ agent: "brightsy",
4998
+ installed: false,
4999
+ authenticated: false,
5000
+ linearMcp: false,
5001
+ warnings: [],
5002
+ reason: `Brightsy executable not found: ${brightsy}`
5003
+ };
5004
+ }
5005
+ } else {
5006
+ const which = await run("which", ["brightsy"], { reject: false });
5007
+ if (which.exitCode !== 0) {
5008
+ return {
5009
+ agent: "brightsy",
5010
+ installed: false,
5011
+ authenticated: false,
5012
+ linearMcp: false,
5013
+ warnings: [],
5014
+ reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
5015
+ };
5016
+ }
4812
5017
  }
4813
- const who = await run("brightsy", ["whoami"], { reject: false });
5018
+ const who = await run(brightsy, ["whoami"], { reject: false });
4814
5019
  const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
4815
5020
  return {
4816
5021
  agent: "brightsy",
@@ -4835,7 +5040,7 @@ var init_brightsy = __esm({
4835
5040
  target.id
4836
5041
  ];
4837
5042
  return {
4838
- file: "brightsy",
5043
+ file: resolveAgentExecutable("brightsy"),
4839
5044
  args,
4840
5045
  cwd: thread.worktreePath,
4841
5046
  // Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
@@ -4852,7 +5057,11 @@ var init_brightsy = __esm({
4852
5057
  const target = decodeBrightsyTarget(thread.model);
4853
5058
  await syncCliForTarget(target.accountId);
4854
5059
  const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
4855
- return { file: "brightsy", args, cwd: thread.worktreePath };
5060
+ return {
5061
+ file: resolveAgentExecutable("brightsy"),
5062
+ args,
5063
+ cwd: thread.worktreePath
5064
+ };
4856
5065
  }
4857
5066
  };
4858
5067
  }
@@ -4904,6 +5113,18 @@ var init_claude_mcp = __esm({
4904
5113
  }
4905
5114
  });
4906
5115
 
5116
+ // src/mcp/profile.ts
5117
+ function sideboardMcpProfile(env = process.env) {
5118
+ return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
5119
+ }
5120
+ var SIDEBOARD_MCP_PROFILE_ENV;
5121
+ var init_profile = __esm({
5122
+ "src/mcp/profile.ts"() {
5123
+ "use strict";
5124
+ SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
5125
+ }
5126
+ });
5127
+
4907
5128
  // src/agents/node-launch.ts
4908
5129
  function isAsarPath(filePath) {
4909
5130
  return /\.asar([/\\]|$)/.test(filePath);
@@ -4951,6 +5172,38 @@ function isBrightsyConnected() {
4951
5172
  return false;
4952
5173
  }
4953
5174
  }
5175
+ function promptMentionsBrightsy(text) {
5176
+ return BRIGHTSY_WORD.test(text ?? "");
5177
+ }
5178
+ function isBrightsyMcpToolName(name) {
5179
+ const n = name.toLowerCase();
5180
+ return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
5181
+ }
5182
+ function toolInputUsesBrightsyDatasource(input) {
5183
+ const ds = input?.datasource;
5184
+ return typeof ds === "string" && ds.toLowerCase() === "brightsy";
5185
+ }
5186
+ function messageUsedBrightsyMcp(msg) {
5187
+ if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
5188
+ for (const part of msg.parts ?? []) {
5189
+ if (part.type !== "tool") continue;
5190
+ if (isBrightsyMcpToolName(part.name)) return true;
5191
+ if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
5192
+ return true;
5193
+ }
5194
+ }
5195
+ return false;
5196
+ }
5197
+ function threadRequestsBrightsyMcp(thread) {
5198
+ return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
5199
+ }
5200
+ function shouldInjectBrightsyMcp(thread, opts) {
5201
+ if (!(opts?.connected ?? isBrightsyConnected())) return false;
5202
+ if (opts?.orchestrator) return true;
5203
+ const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
5204
+ if (alwaysOn) return true;
5205
+ return threadRequestsBrightsyMcp(thread);
5206
+ }
4954
5207
  function mcpLaunch(cmd, name, env) {
4955
5208
  if (cmd === "brightsy-mcp") {
4956
5209
  return { name, command: "brightsy-mcp", ...env ? { env } : {} };
@@ -4996,7 +5249,7 @@ function corePackageDir() {
4996
5249
  }
4997
5250
  function findSideboardMcpJsEntry() {
4998
5251
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
4999
- if (override && (0, import_node_fs10.existsSync)(override)) return override;
5252
+ if (override && (0, import_node_fs12.existsSync)(override)) return override;
5000
5253
  let dir = corePackageDir();
5001
5254
  for (let i = 0; i < 10; i++) {
5002
5255
  const candidates = [
@@ -5009,7 +5262,7 @@ function findSideboardMcpJsEntry() {
5009
5262
  (0, import_node_path11.join)(dir, "cli/dist/index.js")
5010
5263
  ];
5011
5264
  for (const p of candidates) {
5012
- if ((0, import_node_fs10.existsSync)(p)) return p;
5265
+ if ((0, import_node_fs12.existsSync)(p)) return p;
5013
5266
  }
5014
5267
  const parent = (0, import_node_path11.dirname)(dir);
5015
5268
  if (parent === dir) break;
@@ -5040,11 +5293,13 @@ async function buildInjectedMcpServers(opts) {
5040
5293
  const servers = [];
5041
5294
  if (opts.includeSideboard) {
5042
5295
  const sideboard = await resolveSideboardMcpServer();
5296
+ const orchId = opts.orchestratorThreadId?.trim();
5297
+ const profile = orchId ? "orchestration" : "worktree";
5043
5298
  sideboard.env = {
5044
5299
  ...sideboard.env ?? {},
5045
- SIDEBOARD_APP_DATA: appDataDir()
5300
+ SIDEBOARD_APP_DATA: appDataDir(),
5301
+ [SIDEBOARD_MCP_PROFILE_ENV]: profile
5046
5302
  };
5047
- const orchId = opts.orchestratorThreadId?.trim();
5048
5303
  if (orchId) {
5049
5304
  sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
5050
5305
  }
@@ -5119,19 +5374,19 @@ function writeMcpServersConfig(servers) {
5119
5374
  ...s.env ? { env: s.env } : {}
5120
5375
  };
5121
5376
  }
5122
- const dir = (0, import_node_fs10.mkdtempSync)((0, import_node_path11.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
5377
+ const dir = (0, import_node_fs12.mkdtempSync)((0, import_node_path11.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
5123
5378
  const cfgPath = (0, import_node_path11.join)(dir, "mcp.json");
5124
- (0, import_node_fs10.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
5379
+ (0, import_node_fs12.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
5125
5380
  return cfgPath;
5126
5381
  }
5127
5382
  async function writeInjectedMcpConfig(opts) {
5128
5383
  return writeMcpServersConfig(await buildInjectedMcpServers(opts));
5129
5384
  }
5130
- var import_node_fs10, import_node_module, import_node_os6, import_node_path11, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache;
5385
+ var import_node_fs12, import_node_module, import_node_os6, import_node_path11, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
5131
5386
  var init_injected_mcp = __esm({
5132
5387
  "src/agents/injected-mcp.ts"() {
5133
5388
  "use strict";
5134
- import_node_fs10 = require("fs");
5389
+ import_node_fs12 = require("fs");
5135
5390
  import_node_module = require("module");
5136
5391
  import_node_os6 = require("os");
5137
5392
  import_node_path11 = require("path");
@@ -5139,6 +5394,8 @@ var init_injected_mcp = __esm({
5139
5394
  init_run();
5140
5395
  init_config();
5141
5396
  init_connected_teams();
5397
+ init_profile();
5398
+ init_app_settings();
5142
5399
  init_paths();
5143
5400
  init_node_launch();
5144
5401
  import_meta = {};
@@ -5158,6 +5415,7 @@ var init_injected_mcp = __esm({
5158
5415
  "mcp__brightsy__*"
5159
5416
  ];
5160
5417
  brightsyMcpCommandCache = null;
5418
+ BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
5161
5419
  }
5162
5420
  });
5163
5421
 
@@ -5312,11 +5570,11 @@ function parseIssuesJson(raw) {
5312
5570
  }
5313
5571
  return [];
5314
5572
  }
5315
- var import_node_fs11, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
5573
+ var import_node_fs13, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
5316
5574
  var init_claude = __esm({
5317
5575
  "src/agents/claude.ts"() {
5318
5576
  "use strict";
5319
- import_node_fs11 = require("fs");
5577
+ import_node_fs13 = require("fs");
5320
5578
  init_run();
5321
5579
  init_app_settings();
5322
5580
  init_claude_mcp();
@@ -5345,7 +5603,7 @@ var init_claude = __esm({
5345
5603
  async detect() {
5346
5604
  const claude = resolveClaudeExecutable();
5347
5605
  if (claude !== "claude") {
5348
- if (!(0, import_node_fs11.existsSync)(claude)) {
5606
+ if (!(0, import_node_fs13.existsSync)(claude)) {
5349
5607
  return {
5350
5608
  agent: "claude",
5351
5609
  installed: false,
@@ -5384,9 +5642,8 @@ var init_claude = __esm({
5384
5642
  },
5385
5643
  async buildTurn(thread, input) {
5386
5644
  const claude = resolveClaudeExecutable();
5387
- const turn = normalizeTurnInput(input);
5388
5645
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
5389
- const effective = sessionId ? { prompt: turn.prompt } : turn;
5646
+ const effective = dropCachedPrefixOnResume(input, sessionId);
5390
5647
  const promptText = flattenTurnInput(effective);
5391
5648
  const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
5392
5649
  if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
@@ -5399,7 +5656,9 @@ var init_claude = __esm({
5399
5656
  const isOrchestrator = isOrchestratorThread2(thread);
5400
5657
  const injectedServers = await buildInjectedMcpServers({
5401
5658
  includeSideboard: true,
5402
- includeBrightsy: isBrightsyConnected(),
5659
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
5660
+ orchestrator: isOrchestrator
5661
+ }),
5403
5662
  orchestratorThreadId: isOrchestrator ? thread.id : null
5404
5663
  });
5405
5664
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
@@ -5573,9 +5832,14 @@ async function listCodexModels() {
5573
5832
  if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
5574
5833
  return cachedCodexModels.models;
5575
5834
  }
5576
- const which = await run("which", ["codex"], { reject: false });
5577
- if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
5578
- const listed = await run("codex", ["debug", "models"], { reject: false });
5835
+ const codex = resolveAgentExecutable("codex");
5836
+ if (codex === "codex") {
5837
+ const which = await run("which", ["codex"], { reject: false });
5838
+ if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
5839
+ } else if (!(0, import_node_fs14.existsSync)(codex)) {
5840
+ return FALLBACK_CODEX_MODELS;
5841
+ }
5842
+ const listed = await run(codex, ["debug", "models"], { reject: false });
5579
5843
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
5580
5844
  return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
5581
5845
  }
@@ -5614,8 +5878,8 @@ function codexConfigHasNetworkAccess() {
5614
5878
  (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
5615
5879
  ];
5616
5880
  for (const path of candidates) {
5617
- if (!(0, import_node_fs12.existsSync)(path)) continue;
5618
- const text = (0, import_node_fs12.readFileSync)(path, "utf8");
5881
+ if (!(0, import_node_fs14.existsSync)(path)) continue;
5882
+ const text = (0, import_node_fs14.readFileSync)(path, "utf8");
5619
5883
  if (/network_access\s*=\s*true/.test(text)) return true;
5620
5884
  }
5621
5885
  return false;
@@ -5648,21 +5912,23 @@ function asRecord(value) {
5648
5912
  }
5649
5913
  function codexLooksAuthenticated() {
5650
5914
  const authPath = (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
5651
- if (!(0, import_node_fs12.existsSync)(authPath)) return false;
5915
+ if (!(0, import_node_fs14.existsSync)(authPath)) return false;
5652
5916
  try {
5653
- return (0, import_node_fs12.statSync)(authPath).size > 2;
5917
+ return (0, import_node_fs14.statSync)(authPath).size > 2;
5654
5918
  } catch {
5655
5919
  return false;
5656
5920
  }
5657
5921
  }
5658
- var import_node_fs12, import_node_os7, import_node_path12, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
5922
+ var import_node_fs14, import_node_os7, import_node_path12, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
5659
5923
  var init_codex = __esm({
5660
5924
  "src/agents/codex.ts"() {
5661
5925
  "use strict";
5662
- import_node_fs12 = require("fs");
5926
+ import_node_fs14 = require("fs");
5663
5927
  import_node_os7 = require("os");
5664
5928
  import_node_path12 = require("path");
5665
5929
  init_run();
5930
+ init_app_settings();
5931
+ init_global_workspace();
5666
5932
  init_error_detail();
5667
5933
  init_injected_mcp();
5668
5934
  init_turn_input();
@@ -5680,16 +5946,30 @@ var init_codex = __esm({
5680
5946
  codexAdapter = {
5681
5947
  kind: "codex",
5682
5948
  async detect() {
5683
- const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
5684
- if (which.exitCode !== 0) {
5685
- return {
5686
- agent: "codex",
5687
- installed: false,
5688
- authenticated: false,
5689
- linearMcp: false,
5690
- warnings: [],
5691
- reason: "codex CLI not found on PATH"
5692
- };
5949
+ const codex = resolveAgentExecutable("codex");
5950
+ if (codex !== "codex") {
5951
+ if (!(0, import_node_fs14.existsSync)(codex)) {
5952
+ return {
5953
+ agent: "codex",
5954
+ installed: false,
5955
+ authenticated: false,
5956
+ linearMcp: false,
5957
+ warnings: [],
5958
+ reason: `Codex executable not found: ${codex}`
5959
+ };
5960
+ }
5961
+ } else {
5962
+ const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
5963
+ if (which.exitCode !== 0) {
5964
+ return {
5965
+ agent: "codex",
5966
+ installed: false,
5967
+ authenticated: false,
5968
+ linearMcp: false,
5969
+ warnings: [],
5970
+ reason: "codex CLI not found on PATH"
5971
+ };
5972
+ }
5693
5973
  }
5694
5974
  const authenticated = codexLooksAuthenticated();
5695
5975
  const linearMcp = false;
@@ -5709,22 +5989,24 @@ var init_codex = __esm({
5709
5989
  };
5710
5990
  },
5711
5991
  async buildTurn(thread, input) {
5712
- const prompt = flattenTurnInput(input);
5713
5992
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
5993
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
5714
5994
  const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
5715
5995
  const promptArg = useStdin ? "-" : prompt;
5716
5996
  if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
5717
- const { cachedPrefix } = normalizeTurnInput(input);
5718
5997
  console.error(
5719
- `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${Boolean(cachedPrefix)}`
5998
+ `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${prompt.includes("Current request:")}`
5720
5999
  );
5721
6000
  }
5722
6001
  const mode = permissionMode(thread);
5723
6002
  const model = thread.model?.trim();
6003
+ const isOrchestrator = isOrchestratorThread(thread);
5724
6004
  const injected = await buildInjectedMcpServers({
5725
6005
  includeSideboard: true,
5726
- includeBrightsy: isBrightsyConnected(),
5727
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6006
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6007
+ orchestrator: isOrchestrator
6008
+ }),
6009
+ orchestratorThreadId: isOrchestrator ? thread.id : null
5728
6010
  });
5729
6011
  const mcpOverrides = toCodexMcpConfigArgs(injected);
5730
6012
  const execOpts = [
@@ -5744,7 +6026,7 @@ var init_codex = __esm({
5744
6026
  ];
5745
6027
  const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
5746
6028
  return {
5747
- file: "codex",
6029
+ file: resolveAgentExecutable("codex"),
5748
6030
  args,
5749
6031
  cwd: thread.worktreePath,
5750
6032
  stdin: useStdin ? `${prompt}
@@ -5840,16 +6122,17 @@ var init_codex = __esm({
5840
6122
  return cached;
5841
6123
  },
5842
6124
  async buildAttach(thread) {
6125
+ const codex = resolveAgentExecutable("codex");
5843
6126
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
5844
6127
  if (sessionId) {
5845
6128
  return {
5846
- file: "codex",
6129
+ file: codex,
5847
6130
  args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
5848
6131
  cwd: thread.worktreePath
5849
6132
  };
5850
6133
  }
5851
6134
  return {
5852
- file: "codex",
6135
+ file: codex,
5853
6136
  args: ["--cd", thread.worktreePath],
5854
6137
  cwd: thread.worktreePath
5855
6138
  };
@@ -5857,7 +6140,7 @@ var init_codex = __esm({
5857
6140
  async listLinearIssues(_repoPath) {
5858
6141
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
5859
6142
  const { stdout, exitCode } = await run(
5860
- "codex",
6143
+ resolveAgentExecutable("codex"),
5861
6144
  [
5862
6145
  "exec",
5863
6146
  "--json",
@@ -6120,21 +6403,22 @@ function cursorRunnerPath() {
6120
6403
  (0, import_node_path13.join)(root, "src", "agents", "cursor-runner.ts")
6121
6404
  ];
6122
6405
  for (const candidate of candidates) {
6123
- if ((0, import_node_fs13.existsSync)(candidate)) return candidate;
6406
+ if ((0, import_node_fs15.existsSync)(candidate)) return candidate;
6124
6407
  }
6125
6408
  return candidates[0];
6126
6409
  }
6127
- var import_node_fs13, import_node_module2, import_node_path13, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
6410
+ var import_node_fs15, import_node_module2, import_node_path13, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
6128
6411
  var init_cursor = __esm({
6129
6412
  "src/agents/cursor.ts"() {
6130
6413
  "use strict";
6131
- import_node_fs13 = require("fs");
6414
+ import_node_fs15 = require("fs");
6132
6415
  import_node_module2 = require("module");
6133
6416
  import_node_path13 = require("path");
6134
6417
  import_node_url2 = require("url");
6135
6418
  import_sdk = require("@cursor/sdk");
6136
6419
  init_run();
6137
6420
  init_app_settings();
6421
+ init_global_workspace();
6138
6422
  init_cursor_events();
6139
6423
  init_injected_mcp();
6140
6424
  init_node_launch();
@@ -6171,13 +6455,16 @@ var init_cursor = __esm({
6171
6455
  };
6172
6456
  },
6173
6457
  async buildTurn(thread, input) {
6174
- const prompt = flattenTurnInput(input);
6175
6458
  const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
6459
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
6176
6460
  const apiKey = resolveCursorApiKey() || void 0;
6461
+ const isOrchestrator = isOrchestratorThread(thread);
6177
6462
  const injected = await buildInjectedMcpServers({
6178
6463
  includeSideboard: true,
6179
- includeBrightsy: isBrightsyConnected(),
6180
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6464
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6465
+ orchestrator: isOrchestrator
6466
+ }),
6467
+ orchestratorThreadId: isOrchestrator ? thread.id : null
6181
6468
  });
6182
6469
  const mcpServers = toCursorMcpServers(injected);
6183
6470
  const req = {
@@ -6248,9 +6535,14 @@ async function listOpencodeModels() {
6248
6535
  if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
6249
6536
  return cachedOpencodeModels.models;
6250
6537
  }
6251
- const which = await run("which", ["opencode"], { reject: false });
6252
- if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
6253
- const listed = await run("opencode", ["models"], { reject: false });
6538
+ const opencode = resolveAgentExecutable("opencode");
6539
+ if (opencode === "opencode") {
6540
+ const which = await run("which", ["opencode"], { reject: false });
6541
+ if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
6542
+ } else if (!(0, import_node_fs16.existsSync)(opencode)) {
6543
+ return FALLBACK_OPENCODE_MODELS;
6544
+ }
6545
+ const listed = await run(opencode, ["models"], { reject: false });
6254
6546
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
6255
6547
  return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
6256
6548
  }
@@ -6277,11 +6569,14 @@ function usageFromOpencode(tokens) {
6277
6569
  cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
6278
6570
  };
6279
6571
  }
6280
- var FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
6572
+ var import_node_fs16, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
6281
6573
  var init_opencode = __esm({
6282
6574
  "src/agents/opencode.ts"() {
6283
6575
  "use strict";
6576
+ import_node_fs16 = require("fs");
6284
6577
  init_run();
6578
+ init_app_settings();
6579
+ init_global_workspace();
6285
6580
  init_error_detail();
6286
6581
  init_injected_mcp();
6287
6582
  init_turn_input();
@@ -6302,20 +6597,34 @@ var init_opencode = __esm({
6302
6597
  opencodeAdapter = {
6303
6598
  kind: "opencode",
6304
6599
  async detect() {
6305
- const which = await run("which", ["opencode"], { reject: false });
6306
- if (which.exitCode !== 0) {
6307
- return {
6308
- agent: "opencode",
6309
- installed: false,
6310
- authenticated: false,
6311
- linearMcp: false,
6312
- warnings: [],
6313
- reason: "opencode CLI not found on PATH"
6314
- };
6600
+ const opencode = resolveAgentExecutable("opencode");
6601
+ if (opencode !== "opencode") {
6602
+ if (!(0, import_node_fs16.existsSync)(opencode)) {
6603
+ return {
6604
+ agent: "opencode",
6605
+ installed: false,
6606
+ authenticated: false,
6607
+ linearMcp: false,
6608
+ warnings: [],
6609
+ reason: `OpenCode executable not found: ${opencode}`
6610
+ };
6611
+ }
6612
+ } else {
6613
+ const which = await run("which", ["opencode"], { reject: false });
6614
+ if (which.exitCode !== 0) {
6615
+ return {
6616
+ agent: "opencode",
6617
+ installed: false,
6618
+ authenticated: false,
6619
+ linearMcp: false,
6620
+ warnings: [],
6621
+ reason: "opencode CLI not found on PATH"
6622
+ };
6623
+ }
6315
6624
  }
6316
- const auth = await run("opencode", ["auth", "list"], { reject: false });
6625
+ const auth = await run(opencode, ["auth", "list"], { reject: false });
6317
6626
  const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
6318
- const mcp = await run("opencode", ["mcp", "list"], { reject: false });
6627
+ const mcp = await run(opencode, ["mcp", "list"], { reject: false });
6319
6628
  const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
6320
6629
  return {
6321
6630
  agent: "opencode",
@@ -6327,8 +6636,8 @@ var init_opencode = __esm({
6327
6636
  };
6328
6637
  },
6329
6638
  async buildTurn(thread, input) {
6330
- const prompt = flattenTurnInput(input);
6331
6639
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
6640
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
6332
6641
  const mode = permissionMode(thread);
6333
6642
  const model = thread.model?.trim();
6334
6643
  const args = [
@@ -6344,14 +6653,17 @@ var init_opencode = __esm({
6344
6653
  if (model) {
6345
6654
  args.push("--model", model);
6346
6655
  }
6656
+ const isOrchestrator = isOrchestratorThread(thread);
6347
6657
  const injected = await buildInjectedMcpServers({
6348
6658
  includeSideboard: true,
6349
- includeBrightsy: isBrightsyConnected(),
6350
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6659
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6660
+ orchestrator: isOrchestrator
6661
+ }),
6662
+ orchestratorThreadId: isOrchestrator ? thread.id : null
6351
6663
  });
6352
6664
  const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
6353
6665
  return {
6354
- file: "opencode",
6666
+ file: resolveAgentExecutable("opencode"),
6355
6667
  args,
6356
6668
  cwd: thread.worktreePath,
6357
6669
  // `opencode run` treats non-TTY stdin as the message body when no positional
@@ -6427,7 +6739,7 @@ var init_opencode = __esm({
6427
6739
  async resolveSessionId(worktreePath, cached) {
6428
6740
  const cachedId = cached?.trim() || null;
6429
6741
  const listed = await run(
6430
- "opencode",
6742
+ resolveAgentExecutable("opencode"),
6431
6743
  ["session", "list", "--format", "json"],
6432
6744
  { cwd: worktreePath, reject: false }
6433
6745
  );
@@ -6458,7 +6770,7 @@ var init_opencode = __esm({
6458
6770
  const args = ["--dir", thread.worktreePath];
6459
6771
  if (sessionId) args.push("--session", sessionId);
6460
6772
  return {
6461
- file: "opencode",
6773
+ file: resolveAgentExecutable("opencode"),
6462
6774
  args,
6463
6775
  cwd: thread.worktreePath,
6464
6776
  env: {
@@ -6469,7 +6781,7 @@ var init_opencode = __esm({
6469
6781
  async listLinearIssues(_repoPath) {
6470
6782
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
6471
6783
  const { stdout, exitCode } = await run(
6472
- "opencode",
6784
+ resolveAgentExecutable("opencode"),
6473
6785
  ["run", prompt, "--format", "json"],
6474
6786
  {
6475
6787
  reject: false,
@@ -6702,9 +7014,30 @@ function getAgentSetupInfo(agent) {
6702
7014
  function listAgentSetupInfo() {
6703
7015
  return Object.values(SETUP);
6704
7016
  }
6705
- async function openInSystemTerminal(command) {
6706
- ensureAgentPath();
7017
+ function resolveLoginCommand(agent) {
7018
+ const info = getAgentSetupInfo(agent);
7019
+ if (!info.loginCommand) return null;
7020
+ if (!CLI_LOGIN_AGENTS.has(agent)) return info.loginCommand;
7021
+ const exe = resolveAgentExecutable(agent);
7022
+ return info.loginCommand.replace(/^\S+/, exe);
7023
+ }
7024
+ async function prepareTerminalCommand(command) {
7025
+ enrichPathWithNpmGlobalBin();
6707
7026
  const trimmed = command.trim();
7027
+ if (!trimmed) return trimmed;
7028
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
7029
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
7030
+ const bin = match?.[1];
7031
+ let whichPath = null;
7032
+ if (bin && !bin.includes("/") && !bin.includes("\\")) {
7033
+ const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
7034
+ whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
7035
+ }
7036
+ const resolved = resolveCommandBinarySync(trimmed, whichPath);
7037
+ return withExportedPath(resolved, process.env.PATH ?? "");
7038
+ }
7039
+ async function openInSystemTerminal(command) {
7040
+ const trimmed = (await prepareTerminalCommand(command)).trim();
6708
7041
  if (!trimmed) throw new Error("Command is empty");
6709
7042
  if (process.platform === "darwin") {
6710
7043
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
@@ -6765,7 +7098,7 @@ async function installAgent(agent) {
6765
7098
  message: `Opened Terminal to run: ${info.installCommand}`
6766
7099
  };
6767
7100
  }
6768
- ensureAgentPath();
7101
+ enrichPathWithNpmGlobalBin();
6769
7102
  const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
6770
7103
  const ok = result.exitCode === 0;
6771
7104
  if (!ok) {
@@ -6791,36 +7124,46 @@ async function installAgent(agent) {
6791
7124
  };
6792
7125
  }
6793
7126
  }
7127
+ enrichPathWithNpmGlobalBin();
7128
+ const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
7129
+ let binPath = null;
7130
+ if (cliBin) {
7131
+ const which = await run("which", [cliBin], { reject: false, timeoutMs: 3e3 });
7132
+ binPath = which.exitCode === 0 ? which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null : null;
7133
+ }
6794
7134
  return {
6795
7135
  ok: true,
6796
7136
  command: info.installCommand,
6797
7137
  exitCode: 0,
6798
7138
  stdout: result.stdout,
6799
7139
  stderr: result.stderr,
6800
- message: `Installed ${info.npmPackage}`
7140
+ message: binPath ? `Installed ${info.npmPackage} \u2192 ${binPath}` : `Installed ${info.npmPackage} (CLI not yet on PATH \u2014 Log in still exports Electron\u2019s PATH into Terminal)`
6801
7141
  };
6802
7142
  }
6803
7143
  async function loginAgent(agent) {
6804
7144
  const info = getAgentSetupInfo(agent);
6805
- if (!info.loginCommand) {
7145
+ const loginCommand = resolveLoginCommand(agent);
7146
+ if (!loginCommand) {
6806
7147
  return {
6807
7148
  ok: true,
6808
7149
  message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
6809
7150
  };
6810
7151
  }
6811
- await openInSystemTerminal(info.loginCommand);
7152
+ const prepared = await prepareTerminalCommand(loginCommand);
7153
+ await openInSystemTerminal(prepared);
6812
7154
  return {
6813
7155
  ok: true,
6814
7156
  openedTerminal: true,
6815
- command: info.loginCommand,
6816
- message: `Opened Terminal to run: ${info.loginCommand}`
7157
+ command: prepared,
7158
+ message: `Opened Terminal to run: ${prepared}`
6817
7159
  };
6818
7160
  }
6819
- var SETUP;
7161
+ var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
6820
7162
  var init_install = __esm({
6821
7163
  "src/agents/install.ts"() {
6822
7164
  "use strict";
6823
7165
  init_run();
7166
+ init_app_settings();
6824
7167
  init_path();
6825
7168
  SETUP = {
6826
7169
  claude: {
@@ -6869,6 +7212,18 @@ var init_install = __esm({
6869
7212
  npmPackage: "@brightsy/cli"
6870
7213
  }
6871
7214
  };
7215
+ CLI_BIN = {
7216
+ claude: "claude",
7217
+ codex: "codex",
7218
+ opencode: "opencode",
7219
+ brightsy: "brightsy"
7220
+ };
7221
+ CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
7222
+ "claude",
7223
+ "codex",
7224
+ "opencode",
7225
+ "brightsy"
7226
+ ]);
6872
7227
  }
6873
7228
  });
6874
7229
 
@@ -6888,6 +7243,7 @@ __export(agents_exports, {
6888
7243
  cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
6889
7244
  decodeBrightsyTarget: () => decodeBrightsyTarget,
6890
7245
  encodeBrightsyTarget: () => encodeBrightsyTarget,
7246
+ enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
6891
7247
  ensureAgentPath: () => ensureAgentPath,
6892
7248
  getAdapter: () => getAdapter,
6893
7249
  getAgentSetupInfo: () => getAgentSetupInfo,
@@ -6907,8 +7263,13 @@ __export(agents_exports, {
6907
7263
  parseCursorRunnerLine: () => parseCursorRunnerLine,
6908
7264
  parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
6909
7265
  permissionMode: () => permissionMode,
7266
+ posixShellSingleQuote: () => posixShellSingleQuote,
7267
+ prepareTerminalCommand: () => prepareTerminalCommand,
7268
+ resolveCommandBinarySync: () => resolveCommandBinarySync,
6910
7269
  resolveCursorModelId: () => resolveCursorModelId,
6911
- resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent
7270
+ resolveLoginCommand: () => resolveLoginCommand,
7271
+ resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
7272
+ withExportedPath: () => withExportedPath
6912
7273
  });
6913
7274
  function getAdapter(kind) {
6914
7275
  return adapters[kind];
@@ -7020,17 +7381,17 @@ __export(plan_file_exports, {
7020
7381
  });
7021
7382
  function ensureAttachmentsGitignore2(worktreePath) {
7022
7383
  const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
7023
- if ((0, import_node_fs26.existsSync)(gitignoreAbs)) return;
7024
- (0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
7025
- (0, import_node_fs26.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
7384
+ if ((0, import_node_fs29.existsSync)(gitignoreAbs)) return;
7385
+ (0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
7386
+ (0, import_node_fs29.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
7026
7387
  }
7027
7388
  function planFileAbs(worktreePath) {
7028
7389
  return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
7029
7390
  }
7030
7391
  function readTextIfPresent2(abs) {
7031
- if (!(0, import_node_fs26.existsSync)(abs)) return null;
7392
+ if (!(0, import_node_fs29.existsSync)(abs)) return null;
7032
7393
  try {
7033
- const content = (0, import_node_fs26.readFileSync)(abs, "utf8");
7394
+ const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
7034
7395
  return content.trim() ? content : null;
7035
7396
  } catch {
7036
7397
  return null;
@@ -7042,16 +7403,16 @@ function readPlanFile(worktreePath) {
7042
7403
  function writePlanFile(worktreePath, content) {
7043
7404
  ensureAttachmentsGitignore2(worktreePath);
7044
7405
  const abs = planFileAbs(worktreePath);
7045
- (0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
7406
+ (0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
7046
7407
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
7047
- (0, import_node_fs26.writeFileSync)(abs, body, "utf8");
7408
+ (0, import_node_fs29.writeFileSync)(abs, body, "utf8");
7048
7409
  return PLAN_FILE_REL;
7049
7410
  }
7050
- var import_node_fs26, import_node_path24;
7411
+ var import_node_fs29, import_node_path24;
7051
7412
  var init_plan_file = __esm({
7052
7413
  "src/plan/plan-file.ts"() {
7053
7414
  "use strict";
7054
- import_node_fs26 = require("fs");
7415
+ import_node_fs29 = require("fs");
7055
7416
  import_node_path24 = require("path");
7056
7417
  init_workspace_scratch();
7057
7418
  init_plan_present();
@@ -7068,9 +7429,9 @@ function recoverFinishedCursorRun(opts) {
7068
7429
  const agentId = opts.agentId.trim();
7069
7430
  if (!agentId) return null;
7070
7431
  const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
7071
- if (!(0, import_node_fs27.existsSync)(runsPath)) return null;
7432
+ if (!(0, import_node_fs30.existsSync)(runsPath)) return null;
7072
7433
  try {
7073
- const lines = (0, import_node_fs27.readFileSync)(runsPath, "utf8").split("\n");
7434
+ const lines = (0, import_node_fs30.readFileSync)(runsPath, "utf8").split("\n");
7074
7435
  let best = null;
7075
7436
  for (const line of lines) {
7076
7437
  const trimmed = line.trim();
@@ -7096,11 +7457,11 @@ function recoverFinishedCursorRun(opts) {
7096
7457
  return null;
7097
7458
  }
7098
7459
  }
7099
- var import_node_fs27, import_node_path25;
7460
+ var import_node_fs30, import_node_path25;
7100
7461
  var init_cursor_recover = __esm({
7101
7462
  "src/agents/cursor-recover.ts"() {
7102
7463
  "use strict";
7103
- import_node_fs27 = require("fs");
7464
+ import_node_fs30 = require("fs");
7104
7465
  import_node_path25 = require("path");
7105
7466
  init_paths();
7106
7467
  }
@@ -7174,6 +7535,7 @@ __export(index_exports, {
7174
7535
  REVIEW_REQUEST_PREFILL: () => REVIEW_REQUEST_PREFILL,
7175
7536
  SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
7176
7537
  SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
7538
+ SIDEBOARD_MCP_PROFILE_ENV: () => SIDEBOARD_MCP_PROFILE_ENV,
7177
7539
  THINKING_EFFORTS: () => THINKING_EFFORTS,
7178
7540
  addPrStackLayer: () => addPrStackLayer,
7179
7541
  addStackLayerFromThread: () => addStackLayerFromThread,
@@ -7196,6 +7558,7 @@ __export(index_exports, {
7196
7558
  attachmentsFromBuffers: () => attachmentsFromBuffers,
7197
7559
  attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
7198
7560
  attachmentsGitignoreBody: () => attachmentsGitignoreBody,
7561
+ autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
7199
7562
  autoCleanupOrphansEnabled: () => autoCleanupOrphansEnabled,
7200
7563
  autoRenameBranchEnabled: () => autoRenameBranchEnabled,
7201
7564
  autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
@@ -7204,6 +7567,7 @@ __export(index_exports, {
7204
7567
  brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
7205
7568
  brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
7206
7569
  brightsyConfigPath: () => brightsyConfigPath,
7570
+ brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
7207
7571
  brightsyMcpAllowedTools: () => brightsyMcpAllowedTools,
7208
7572
  brightsyMcpServerName: () => brightsyMcpServerName,
7209
7573
  buildCachedUserContent: () => buildCachedUserContent,
@@ -7255,7 +7619,9 @@ __export(index_exports, {
7255
7619
  detectLocalMergeConflicts: () => detectLocalMergeConflicts,
7256
7620
  disconnectBrightsyTeam: () => disconnectBrightsyTeam,
7257
7621
  discoverSkills: () => discoverSkills,
7622
+ dropCachedPrefixOnResume: () => dropCachedPrefixOnResume,
7258
7623
  encodeBrightsyTarget: () => encodeBrightsyTarget,
7624
+ enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
7259
7625
  enrichWorkspacesWithGithub: () => enrichWorkspacesWithGithub,
7260
7626
  ensureAgentPath: () => ensureAgentPath,
7261
7627
  ensureCloudCoordinator: () => ensureCloudCoordinator,
@@ -7293,6 +7659,7 @@ __export(index_exports, {
7293
7659
  formatTranscriptMarkdown: () => formatTranscriptMarkdown,
7294
7660
  formatWorkspaceInventory: () => formatWorkspaceInventory,
7295
7661
  formatWorktreeDirective: () => formatWorktreeDirective,
7662
+ formatWorktreeReminder: () => formatWorktreeReminder,
7296
7663
  getAdapter: () => getAdapter,
7297
7664
  getAgentSetupInfo: () => getAgentSetupInfo,
7298
7665
  getBrightsySession: () => getBrightsySession,
@@ -7419,7 +7786,10 @@ __export(index_exports, {
7419
7786
  pastedTextStats: () => pastedTextStats,
7420
7787
  permissionMode: () => permissionMode,
7421
7788
  planFileAbs: () => planFileAbs,
7789
+ posixShellSingleQuote: () => posixShellSingleQuote,
7790
+ prepareTerminalCommand: () => prepareTerminalCommand,
7422
7791
  previewLand: () => previewLand,
7792
+ promptMentionsBrightsy: () => promptMentionsBrightsy,
7423
7793
  pushBranch: () => pushBranch,
7424
7794
  readExistingReviewRequestFile: () => readExistingReviewRequestFile,
7425
7795
  readPlanFile: () => readPlanFile,
@@ -7435,7 +7805,9 @@ __export(index_exports, {
7435
7805
  requestReview: () => requestReview,
7436
7806
  requireAgent: () => requireAgent,
7437
7807
  resetGhStackDetectCache: () => resetGhStackDetectCache,
7808
+ resolveAgentExecutable: () => resolveAgentExecutable,
7438
7809
  resolveClaudeExecutable: () => resolveClaudeExecutable,
7810
+ resolveCommandBinarySync: () => resolveCommandBinarySync,
7439
7811
  resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
7440
7812
  resolveCursorModelId: () => resolveCursorModelId,
7441
7813
  resolveDefaultBranch: () => resolveDefaultBranch,
@@ -7444,6 +7816,7 @@ __export(index_exports, {
7444
7816
  resolveFilesToCopy: () => resolveFilesToCopy,
7445
7817
  resolveGhAuthToken: () => resolveGhAuthToken,
7446
7818
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
7819
+ resolveLoginCommand: () => resolveLoginCommand,
7447
7820
  resolveNewThreadOptions: () => resolveNewThreadOptions,
7448
7821
  resolvePlanMarkdown: () => resolvePlanMarkdown,
7449
7822
  resolvePrSelector: () => resolvePrSelector,
@@ -7465,9 +7838,11 @@ __export(index_exports, {
7465
7838
  settingsSourceLabel: () => settingsSourceLabel,
7466
7839
  shouldAttachPastedText: () => shouldAttachPastedText,
7467
7840
  shouldCompactContext: () => shouldCompactContext,
7841
+ shouldInjectBrightsyMcp: () => shouldInjectBrightsyMcp,
7468
7842
  shouldRefreshReviewRequestTemplate: () => shouldRefreshReviewRequestTemplate,
7469
7843
  shouldRunWorktreeCleanup: () => shouldRunWorktreeCleanup,
7470
7844
  sideboardHomeDir: () => sideboardHomeDir,
7845
+ sideboardMcpProfile: () => sideboardMcpProfile,
7471
7846
  sideboardReposDir: () => sideboardReposDir,
7472
7847
  sideboardWorkspacesDir: () => sideboardWorkspacesDir,
7473
7848
  slugify: () => slugify,
@@ -7495,6 +7870,7 @@ __export(index_exports, {
7495
7870
  threadDisplayLabel: () => threadDisplayLabel,
7496
7871
  threadFilePath: () => threadFilePath,
7497
7872
  threadLockPath: () => threadLockPath,
7873
+ threadRequestsBrightsyMcp: () => threadRequestsBrightsyMcp,
7498
7874
  threadsDir: () => threadsDir,
7499
7875
  threadsSharingWorktree: () => threadsSharingWorktree,
7500
7876
  toolDescription: () => toolDescription,
@@ -7502,14 +7878,18 @@ __export(index_exports, {
7502
7878
  toolFilePath: () => toolFilePath,
7503
7879
  totalTokens: () => totalTokens,
7504
7880
  updateAdvancedSettings: () => updateAdvancedSettings,
7881
+ updateAgentExecutable: () => updateAgentExecutable,
7505
7882
  updateAppEnvironment: () => updateAppEnvironment,
7506
7883
  updateBrightsySettings: () => updateBrightsySettings,
7507
7884
  updateClaudeSettings: () => updateClaudeSettings,
7885
+ updateCodexSettings: () => updateCodexSettings,
7508
7886
  updateDefaultsSettings: () => updateDefaultsSettings,
7509
7887
  updateIntegrationsSettings: () => updateIntegrationsSettings,
7888
+ updateOpencodeSettings: () => updateOpencodeSettings,
7510
7889
  updateThread: () => updateThread,
7511
7890
  validateLinearApiKey: () => validateLinearApiKey,
7512
7891
  withAgentInstructions: () => withAgentInstructions,
7892
+ withExportedPath: () => withExportedPath,
7513
7893
  withThreadLock: () => withThreadLock,
7514
7894
  workspaceSettingsSourceLabel: () => workspaceSettingsSourceLabel,
7515
7895
  worktreeCleanupSettings: () => worktreeCleanupSettings,
@@ -8086,7 +8466,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
8086
8466
  init_claude_mcp();
8087
8467
 
8088
8468
  // src/agents/instructions.ts
8089
- var import_node_fs14 = require("fs");
8469
+ var import_node_fs17 = require("fs");
8090
8470
  var import_node_path14 = require("path");
8091
8471
  init_worktree_labels();
8092
8472
  function normPath2(p) {
@@ -8186,6 +8566,9 @@ function formatWorktreeDirective(thread, opts) {
8186
8566
  );
8187
8567
  return lines.join("\n");
8188
8568
  }
8569
+ function formatWorktreeReminder() {
8570
+ 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.";
8571
+ }
8189
8572
  function formatArtifactDirective() {
8190
8573
  return [
8191
8574
  "Sideboard side column (desktop UI):",
@@ -8227,10 +8610,10 @@ function loadAgentInstructions(worktreePath, agent) {
8227
8610
  for (const rel of candidates) {
8228
8611
  if (seen.has(rel)) continue;
8229
8612
  const abs = (0, import_node_path14.join)(worktreePath, rel);
8230
- if (!(0, import_node_fs14.existsSync)(abs)) continue;
8613
+ if (!(0, import_node_fs17.existsSync)(abs)) continue;
8231
8614
  try {
8232
- if (!(0, import_node_fs14.statSync)(abs).isFile()) continue;
8233
- let content = (0, import_node_fs14.readFileSync)(abs, "utf8");
8615
+ if (!(0, import_node_fs17.statSync)(abs).isFile()) continue;
8616
+ let content = (0, import_node_fs17.readFileSync)(abs, "utf8");
8234
8617
  if (!content.trim()) continue;
8235
8618
  if (content.length > MAX_CHARS_PER_FILE) {
8236
8619
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -8275,11 +8658,13 @@ init_turn_input();
8275
8658
  init_agents();
8276
8659
  async function detectAgents() {
8277
8660
  ensureAgentPath();
8661
+ enrichPathWithNpmGlobalBin();
8278
8662
  return Promise.all(allAdapters().map((a) => a.detect()));
8279
8663
  }
8280
8664
  var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
8281
8665
  async function requireAgent(agent, opts) {
8282
8666
  ensureAgentPath();
8667
+ enrichPathWithNpmGlobalBin();
8283
8668
  const status = await Promise.race([
8284
8669
  getAdapter(agent).detect(),
8285
8670
  new Promise((_, reject) => {
@@ -8307,7 +8692,7 @@ async function requireAgent(agent, opts) {
8307
8692
  init_settings();
8308
8693
 
8309
8694
  // src/hook/conductor.ts
8310
- var import_node_fs15 = require("fs");
8695
+ var import_node_fs18 = require("fs");
8311
8696
  var import_node_net = require("net");
8312
8697
  var import_node_path15 = require("path");
8313
8698
  var import_execa3 = require("execa");
@@ -8320,8 +8705,8 @@ function matchSimpleGlob(pattern, name) {
8320
8705
  }
8321
8706
  function readWorktreeInclude(repoPath) {
8322
8707
  const path = (0, import_node_path15.join)(repoPath, ".worktreeinclude");
8323
- if (!(0, import_node_fs15.existsSync)(path)) return [];
8324
- return (0, import_node_fs15.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
8708
+ if (!(0, import_node_fs18.existsSync)(path)) return [];
8709
+ return (0, import_node_fs18.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
8325
8710
  }
8326
8711
  function resolveFilesToCopy(repoPath) {
8327
8712
  const fromInclude = readWorktreeInclude(repoPath);
@@ -8331,7 +8716,7 @@ function resolveFilesToCopy(repoPath) {
8331
8716
  if (settings?.fileIncludeGlobs?.length) {
8332
8717
  const matched = [];
8333
8718
  try {
8334
- for (const entry of (0, import_node_fs15.readdirSync)(repoPath, { withFileTypes: true })) {
8719
+ for (const entry of (0, import_node_fs18.readdirSync)(repoPath, { withFileTypes: true })) {
8335
8720
  if (!entry.isFile()) continue;
8336
8721
  for (const glob of settings.fileIncludeGlobs) {
8337
8722
  if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path15.basename)(glob), entry.name)) {
@@ -8346,7 +8731,7 @@ function resolveFilesToCopy(repoPath) {
8346
8731
  }
8347
8732
  const defaults = [];
8348
8733
  try {
8349
- for (const entry of (0, import_node_fs15.readdirSync)(repoPath, { withFileTypes: true })) {
8734
+ for (const entry of (0, import_node_fs18.readdirSync)(repoPath, { withFileTypes: true })) {
8350
8735
  if (entry.isFile() && entry.name.startsWith(".env")) {
8351
8736
  defaults.push(entry.name);
8352
8737
  }
@@ -8361,10 +8746,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
8361
8746
  const copied = [];
8362
8747
  for (const rel of patterns) {
8363
8748
  const src = (0, import_node_path15.join)(repoPath, rel);
8364
- if (!(0, import_node_fs15.existsSync)(src)) continue;
8749
+ if (!(0, import_node_fs18.existsSync)(src)) continue;
8365
8750
  const dest = (0, import_node_path15.join)(worktreePath, rel);
8366
- (0, import_node_fs15.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
8367
- (0, import_node_fs15.copyFileSync)(src, dest);
8751
+ (0, import_node_fs18.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
8752
+ (0, import_node_fs18.copyFileSync)(src, dest);
8368
8753
  copied.push(rel);
8369
8754
  }
8370
8755
  return copied;
@@ -8617,15 +9002,15 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
8617
9002
  }
8618
9003
 
8619
9004
  // src/hook/cursor-worktrees.ts
8620
- var import_node_fs16 = require("fs");
9005
+ var import_node_fs19 = require("fs");
8621
9006
  var import_node_path16 = require("path");
8622
9007
  var import_execa4 = require("execa");
8623
9008
  var import_node_readline3 = require("readline");
8624
9009
  function loadCursorWorktreesJson(rootPath) {
8625
9010
  const path = (0, import_node_path16.join)(rootPath, ".cursor", "worktrees.json");
8626
- if (!(0, import_node_fs16.existsSync)(path)) return null;
9011
+ if (!(0, import_node_fs19.existsSync)(path)) return null;
8627
9012
  try {
8628
- return JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
9013
+ return JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
8629
9014
  } catch {
8630
9015
  return null;
8631
9016
  }
@@ -8690,12 +9075,12 @@ function hasCursorWorktreeSetup(worktreePath, repoPath) {
8690
9075
  }
8691
9076
 
8692
9077
  // src/diff/diff.ts
8693
- var import_node_fs17 = require("fs");
9078
+ var import_node_fs20 = require("fs");
8694
9079
  var import_node_path17 = require("path");
8695
9080
  init_run();
8696
9081
  init_worktree();
8697
9082
  async function inspectGitWorktree(worktreePath) {
8698
- if (!worktreePath || !(0, import_node_fs17.existsSync)(worktreePath)) return "missing_worktree";
9083
+ if (!worktreePath || !(0, import_node_fs20.existsSync)(worktreePath)) return "missing_worktree";
8699
9084
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
8700
9085
  reject: false
8701
9086
  });
@@ -8703,7 +9088,7 @@ async function inspectGitWorktree(worktreePath) {
8703
9088
  return "ok";
8704
9089
  }
8705
9090
  async function initializeGitRepository(worktreePath) {
8706
- if (!worktreePath || !(0, import_node_fs17.existsSync)(worktreePath)) {
9091
+ if (!worktreePath || !(0, import_node_fs20.existsSync)(worktreePath)) {
8707
9092
  throw new Error("Worktree not found");
8708
9093
  }
8709
9094
  const status = await inspectGitWorktree(worktreePath);
@@ -9141,7 +9526,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
9141
9526
  assertSafeRelativePath(relativePath);
9142
9527
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
9143
9528
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9144
- const st = (0, import_node_fs17.statSync)(abs);
9529
+ const st = (0, import_node_fs20.statSync)(abs);
9145
9530
  if (!st.isFile()) {
9146
9531
  throw new Error(`Not a file: ${relativePath}`);
9147
9532
  }
@@ -9150,7 +9535,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
9150
9535
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
9151
9536
  );
9152
9537
  }
9153
- const buf = (0, import_node_fs17.readFileSync)(abs);
9538
+ const buf = (0, import_node_fs20.readFileSync)(abs);
9154
9539
  return {
9155
9540
  path: relativePath,
9156
9541
  contentBase64: buf.toString("base64"),
@@ -9161,11 +9546,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
9161
9546
  assertSafeRelativePath(relativePath);
9162
9547
  const maxBytes = opts?.maxBytes ?? 2e5;
9163
9548
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9164
- const st = (0, import_node_fs17.statSync)(abs);
9549
+ const st = (0, import_node_fs20.statSync)(abs);
9165
9550
  if (!st.isFile()) {
9166
9551
  throw new Error(`Not a file: ${relativePath}`);
9167
9552
  }
9168
- const buf = (0, import_node_fs17.readFileSync)(abs);
9553
+ const buf = (0, import_node_fs20.readFileSync)(abs);
9169
9554
  if (isImageRelativePath(relativePath)) {
9170
9555
  const maxImageBytes = Math.max(maxBytes, 15e6);
9171
9556
  const truncated2 = buf.length > maxImageBytes;
@@ -9209,8 +9594,8 @@ function assertSafeRelativePath(relativePath) {
9209
9594
  function writeWorktreeFile(worktreePath, relativePath, content) {
9210
9595
  assertSafeRelativePath(relativePath);
9211
9596
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9212
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(abs), { recursive: true });
9213
- (0, import_node_fs17.writeFileSync)(abs, content, "utf8");
9597
+ (0, import_node_fs20.mkdirSync)((0, import_node_path17.dirname)(abs), { recursive: true });
9598
+ (0, import_node_fs20.writeFileSync)(abs, content, "utf8");
9214
9599
  return { path: relativePath };
9215
9600
  }
9216
9601
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -9229,7 +9614,7 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
9229
9614
  }
9230
9615
 
9231
9616
  // src/skills/discover.ts
9232
- var import_node_fs18 = require("fs");
9617
+ var import_node_fs21 = require("fs");
9233
9618
  var import_node_os8 = require("os");
9234
9619
  var import_node_path18 = require("path");
9235
9620
  function toCommand(name) {
@@ -9263,7 +9648,7 @@ function parseFrontmatter(content) {
9263
9648
  }
9264
9649
  function readSkill(skillMd, source) {
9265
9650
  try {
9266
- const content = (0, import_node_fs18.readFileSync)(skillMd, "utf8");
9651
+ const content = (0, import_node_fs21.readFileSync)(skillMd, "utf8");
9267
9652
  const { name: fmName, description } = parseFrontmatter(content);
9268
9653
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
9269
9654
  const name = fmName || dirName;
@@ -9282,19 +9667,19 @@ function readSkill(skillMd, source) {
9282
9667
  }
9283
9668
  }
9284
9669
  function scanSkillsDir(dir, source, out) {
9285
- if (!(0, import_node_fs18.existsSync)(dir)) return;
9670
+ if (!(0, import_node_fs21.existsSync)(dir)) return;
9286
9671
  let entries;
9287
9672
  try {
9288
- entries = (0, import_node_fs18.readdirSync)(dir);
9673
+ entries = (0, import_node_fs21.readdirSync)(dir);
9289
9674
  } catch {
9290
9675
  return;
9291
9676
  }
9292
9677
  for (const entry of entries) {
9293
9678
  if (entry.startsWith(".")) continue;
9294
9679
  const skillMd = (0, import_node_path18.join)(dir, entry, "SKILL.md");
9295
- if (!(0, import_node_fs18.existsSync)(skillMd)) continue;
9680
+ if (!(0, import_node_fs21.existsSync)(skillMd)) continue;
9296
9681
  try {
9297
- if (!(0, import_node_fs18.statSync)(skillMd).isFile()) continue;
9682
+ if (!(0, import_node_fs21.statSync)(skillMd).isFile()) continue;
9298
9683
  } catch {
9299
9684
  continue;
9300
9685
  }
@@ -9303,12 +9688,12 @@ function scanSkillsDir(dir, source, out) {
9303
9688
  }
9304
9689
  }
9305
9690
  function scanClaudePluginSkills(pluginsRoot, out) {
9306
- if (!(0, import_node_fs18.existsSync)(pluginsRoot)) return;
9691
+ if (!(0, import_node_fs21.existsSync)(pluginsRoot)) return;
9307
9692
  const walk = (dir, depth, lookingForSkillsDir) => {
9308
9693
  if (depth > 7) return;
9309
9694
  let entries;
9310
9695
  try {
9311
- entries = (0, import_node_fs18.readdirSync)(dir);
9696
+ entries = (0, import_node_fs21.readdirSync)(dir);
9312
9697
  } catch {
9313
9698
  return;
9314
9699
  }
@@ -9320,7 +9705,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
9320
9705
  if (entry === "node_modules" || entry === ".git") continue;
9321
9706
  const full = (0, import_node_path18.join)(dir, entry);
9322
9707
  try {
9323
- if (!(0, import_node_fs18.statSync)(full).isDirectory()) continue;
9708
+ if (!(0, import_node_fs21.statSync)(full).isDirectory()) continue;
9324
9709
  } catch {
9325
9710
  continue;
9326
9711
  }
@@ -9360,7 +9745,7 @@ function discoverSkills(worktreePath) {
9360
9745
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
9361
9746
  }
9362
9747
  function readSkillBody(skillPath, maxChars = 12e3) {
9363
- const raw = (0, import_node_fs18.readFileSync)(skillPath, "utf8");
9748
+ const raw = (0, import_node_fs21.readFileSync)(skillPath, "utf8");
9364
9749
  if (raw.startsWith("---")) {
9365
9750
  const end = raw.indexOf("\n---", 3);
9366
9751
  if (end >= 0) {
@@ -9501,7 +9886,7 @@ function buildDiffCommentAttachment(input) {
9501
9886
  }
9502
9887
 
9503
9888
  // src/composer/stage-files.ts
9504
- var import_node_fs19 = require("fs");
9889
+ var import_node_fs22 = require("fs");
9505
9890
  var import_node_path19 = require("path");
9506
9891
  var import_node_crypto2 = require("crypto");
9507
9892
  init_workspace_scratch();
@@ -9539,21 +9924,21 @@ function imageMimeType(filePath) {
9539
9924
  }
9540
9925
  function ensureAttachmentsDir(worktreePath) {
9541
9926
  const dir = (0, import_node_path19.join)(worktreePath, ATTACHMENTS_DIR);
9542
- (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
9927
+ (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
9543
9928
  const gi = (0, import_node_path19.join)(dir, ".gitignore");
9544
- if (!(0, import_node_fs19.existsSync)(gi)) {
9545
- (0, import_node_fs19.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
9929
+ if (!(0, import_node_fs22.existsSync)(gi)) {
9930
+ (0, import_node_fs22.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
9546
9931
  }
9547
9932
  return dir;
9548
9933
  }
9549
9934
  function uniqueAttachmentName(dir, originalName) {
9550
9935
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
9551
- if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
9936
+ if (!(0, import_node_fs22.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
9552
9937
  const ext = (0, import_node_path19.extname)(safe);
9553
9938
  const stem = ext ? safe.slice(0, -ext.length) : safe;
9554
9939
  for (let i = 1; i < 1e4; i++) {
9555
9940
  const candidate = `${stem}-${i}${ext}`;
9556
- if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
9941
+ if (!(0, import_node_fs22.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
9557
9942
  }
9558
9943
  return `${stem}-${(0, import_node_crypto2.randomUUID)()}${ext}`;
9559
9944
  }
@@ -9607,7 +9992,7 @@ function attachmentFromBuffer(name, buf, opts) {
9607
9992
  function attachmentFromAbsolutePath(absolutePath) {
9608
9993
  const name = (0, import_node_path19.basename)(absolutePath);
9609
9994
  try {
9610
- const st = (0, import_node_fs19.statSync)(absolutePath);
9995
+ const st = (0, import_node_fs22.statSync)(absolutePath);
9611
9996
  if (!st.isFile()) {
9612
9997
  return {
9613
9998
  id: (0, import_node_crypto2.randomUUID)(),
@@ -9616,7 +10001,7 @@ function attachmentFromAbsolutePath(absolutePath) {
9616
10001
  content: `(not a file: ${absolutePath})`
9617
10002
  };
9618
10003
  }
9619
- const buf = (0, import_node_fs19.readFileSync)(absolutePath);
10004
+ const buf = (0, import_node_fs22.readFileSync)(absolutePath);
9620
10005
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
9621
10006
  } catch (err) {
9622
10007
  return {
@@ -9634,13 +10019,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
9634
10019
  for (const abs of absolutePaths) {
9635
10020
  const originalName = (0, import_node_path19.basename)(abs);
9636
10021
  try {
9637
- const st = (0, import_node_fs19.statSync)(abs);
10022
+ const st = (0, import_node_fs22.statSync)(abs);
9638
10023
  if (!st.isFile()) continue;
9639
10024
  const name = uniqueAttachmentName(dir, originalName);
9640
10025
  const destAbs = (0, import_node_path19.join)(dir, name);
9641
- (0, import_node_fs19.copyFileSync)(abs, destAbs);
10026
+ (0, import_node_fs22.copyFileSync)(abs, destAbs);
9642
10027
  const rel = `${ATTACHMENTS_DIR}/${name}`;
9643
- const buf = (0, import_node_fs19.readFileSync)(destAbs);
10028
+ const buf = (0, import_node_fs22.readFileSync)(destAbs);
9644
10029
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9645
10030
  } catch (err) {
9646
10031
  out.push({
@@ -9663,7 +10048,7 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
9663
10048
  const buf = Buffer.from(item.dataBase64, "base64");
9664
10049
  const name = uniqueAttachmentName(dir, originalName);
9665
10050
  const destAbs = (0, import_node_path19.join)(dir, name);
9666
- (0, import_node_fs19.writeFileSync)(destAbs, buf);
10051
+ (0, import_node_fs22.writeFileSync)(destAbs, buf);
9667
10052
  const rel = `${ATTACHMENTS_DIR}/${name}`;
9668
10053
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
9669
10054
  } catch (err) {
@@ -9708,9 +10093,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
9708
10093
  const name = (0, import_node_path19.basename)(rel);
9709
10094
  try {
9710
10095
  const abs = (0, import_node_path19.join)(worktreePath, rel);
9711
- const st = (0, import_node_fs19.statSync)(abs);
10096
+ const st = (0, import_node_fs22.statSync)(abs);
9712
10097
  if (!st.isFile()) continue;
9713
- const buf = (0, import_node_fs19.readFileSync)(abs);
10098
+ const buf = (0, import_node_fs22.readFileSync)(abs);
9714
10099
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9715
10100
  } catch (err) {
9716
10101
  out.push({
@@ -10198,7 +10583,7 @@ async function confirmLand(thread, opts) {
10198
10583
  }
10199
10584
 
10200
10585
  // src/threads/create.ts
10201
- var import_node_fs20 = require("fs");
10586
+ var import_node_fs23 = require("fs");
10202
10587
  init_worktree();
10203
10588
  init_app_settings();
10204
10589
  init_thread_store();
@@ -10212,7 +10597,7 @@ async function createThread(input, _onSetupLine) {
10212
10597
  });
10213
10598
  await requireAgent(resolved.agent);
10214
10599
  const repoPath = await resolveRepoRoot(input.repoPath);
10215
- if (!(0, import_node_fs20.existsSync)(repoPath)) {
10600
+ if (!(0, import_node_fs23.existsSync)(repoPath)) {
10216
10601
  throw new Error(`Repo not found: ${repoPath}`);
10217
10602
  }
10218
10603
  let sourceRef = input.sourceRef;
@@ -10320,6 +10705,8 @@ function worktreeBindingFrom(from) {
10320
10705
  parentThreadId: from.parentThreadId,
10321
10706
  prUrl: from.prUrl,
10322
10707
  prTitle: from.prTitle,
10708
+ prState: from.prState,
10709
+ skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
10323
10710
  stackId: from.stackId,
10324
10711
  stackLayer: from.stackLayer
10325
10712
  };
@@ -10437,7 +10824,7 @@ async function forkThreadWorktree(input, onSetupLine) {
10437
10824
  }
10438
10825
 
10439
10826
  // src/threads/stack-layers.ts
10440
- var import_node_fs21 = require("fs");
10827
+ var import_node_fs24 = require("fs");
10441
10828
  init_run();
10442
10829
  init_stack();
10443
10830
  init_worktree();
@@ -10505,7 +10892,7 @@ async function openStackLayer(input, _onSetupLine) {
10505
10892
  let createdWorktree = false;
10506
10893
  const trees = await listWorktrees(repoPath);
10507
10894
  const checkedOut = trees.find((w) => w.branch === branchName);
10508
- if (checkedOut?.path && (0, import_node_fs21.existsSync)(checkedOut.path)) {
10895
+ if (checkedOut?.path && (0, import_node_fs24.existsSync)(checkedOut.path)) {
10509
10896
  if (input.reuseExistingWorktree !== false) {
10510
10897
  worktreePath = checkedOut.path;
10511
10898
  } else {
@@ -10647,7 +11034,7 @@ async function initStackFromThread(input, onSetupLine) {
10647
11034
  async function createPrStack(input, onSetupLine) {
10648
11035
  await requireAgent(input.agent);
10649
11036
  const repoPath = await resolveRepoRoot(input.repoPath);
10650
- if (!(0, import_node_fs21.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
11037
+ if (!(0, import_node_fs24.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
10651
11038
  if (!input.branches.length) throw new Error("At least one branch name required");
10652
11039
  const status = await detectGhStack(repoPath);
10653
11040
  if (!status.available) throw new Error(status.reason);
@@ -10714,7 +11101,7 @@ async function createPrStack(input, onSetupLine) {
10714
11101
  }
10715
11102
  }
10716
11103
  const claimed = new Set(threads.map((t) => t.worktreePath));
10717
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs21.existsSync)(bootstrap.worktreePath)) {
11104
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs24.existsSync)(bootstrap.worktreePath)) {
10718
11105
  try {
10719
11106
  await removeWorktree(repoPath, bootstrap.worktreePath, {
10720
11107
  deleteBranch: bootstrap.branchName
@@ -10736,8 +11123,8 @@ function stackAgentDefaultsFrom(input) {
10736
11123
  }
10737
11124
 
10738
11125
  // src/threads/adopt.ts
10739
- var import_node_child_process = require("child_process");
10740
- var import_node_fs22 = require("fs");
11126
+ var import_node_child_process2 = require("child_process");
11127
+ var import_node_fs25 = require("fs");
10741
11128
  var import_node_os9 = require("os");
10742
11129
  var import_node_path20 = require("path");
10743
11130
  var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
@@ -10762,21 +11149,21 @@ function mapAgentType(raw) {
10762
11149
  return null;
10763
11150
  }
10764
11151
  function resolveConductorCursorAgentId(workspacePath) {
10765
- if (!workspacePath || !(0, import_node_fs22.existsSync)(CURSOR_SDK_STORE)) return null;
11152
+ if (!workspacePath || !(0, import_node_fs25.existsSync)(CURSOR_SDK_STORE)) return null;
10766
11153
  const normalized = workspacePath.replace(/\/$/, "");
10767
11154
  let best = null;
10768
11155
  let hashes;
10769
11156
  try {
10770
- hashes = (0, import_node_fs22.readdirSync)(CURSOR_SDK_STORE);
11157
+ hashes = (0, import_node_fs25.readdirSync)(CURSOR_SDK_STORE);
10771
11158
  } catch {
10772
11159
  return null;
10773
11160
  }
10774
11161
  for (const hash of hashes) {
10775
11162
  const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
10776
- if (!(0, import_node_fs22.existsSync)(agentsFile)) continue;
11163
+ if (!(0, import_node_fs25.existsSync)(agentsFile)) continue;
10777
11164
  let text;
10778
11165
  try {
10779
- text = (0, import_node_fs22.readFileSync)(agentsFile, "utf8");
11166
+ text = (0, import_node_fs25.readFileSync)(agentsFile, "utf8");
10780
11167
  } catch {
10781
11168
  continue;
10782
11169
  }
@@ -10800,7 +11187,7 @@ function resolveConductorCursorAgentId(workspacePath) {
10800
11187
  return best?.agentId ?? null;
10801
11188
  }
10802
11189
  async function adoptThread(input) {
10803
- if (!(0, import_node_fs22.existsSync)(input.worktreePath)) {
11190
+ if (!(0, import_node_fs25.existsSync)(input.worktreePath)) {
10804
11191
  throw new Error(`Worktree not found: ${input.worktreePath}`);
10805
11192
  }
10806
11193
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -10827,18 +11214,18 @@ function conductorDbPath() {
10827
11214
  return CONDUCTOR_DB;
10828
11215
  }
10829
11216
  function listConductorWorkspaces() {
10830
- if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
11217
+ if (!(0, import_node_fs25.existsSync)(CONDUCTOR_DB)) {
10831
11218
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
10832
11219
  }
10833
- const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
11220
+ const tmp = (0, import_node_fs25.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
10834
11221
  const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
10835
11222
  try {
10836
- (0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
11223
+ (0, import_node_fs25.copyFileSync)(CONDUCTOR_DB, snapshot);
10837
11224
  for (const suffix of ["-wal", "-shm"]) {
10838
11225
  const src = `${CONDUCTOR_DB}${suffix}`;
10839
- if ((0, import_node_fs22.existsSync)(src)) {
11226
+ if ((0, import_node_fs25.existsSync)(src)) {
10840
11227
  try {
10841
- (0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
11228
+ (0, import_node_fs25.copyFileSync)(src, `${snapshot}${suffix}`);
10842
11229
  } catch {
10843
11230
  }
10844
11231
  }
@@ -10914,22 +11301,22 @@ function listConductorWorkspaces() {
10914
11301
  db.close();
10915
11302
  }
10916
11303
  } finally {
10917
- (0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
11304
+ (0, import_node_fs25.rmSync)(tmp, { recursive: true, force: true });
10918
11305
  }
10919
11306
  }
10920
11307
  function importConductorWorkspace(workspaceId) {
10921
- if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
11308
+ if (!(0, import_node_fs25.existsSync)(CONDUCTOR_DB)) {
10922
11309
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
10923
11310
  }
10924
- const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
11311
+ const tmp = (0, import_node_fs25.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
10925
11312
  const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
10926
11313
  try {
10927
- (0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
11314
+ (0, import_node_fs25.copyFileSync)(CONDUCTOR_DB, snapshot);
10928
11315
  for (const suffix of ["-wal", "-shm"]) {
10929
11316
  const src = `${CONDUCTOR_DB}${suffix}`;
10930
- if ((0, import_node_fs22.existsSync)(src)) {
11317
+ if ((0, import_node_fs25.existsSync)(src)) {
10931
11318
  try {
10932
- (0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
11319
+ (0, import_node_fs25.copyFileSync)(src, `${snapshot}${suffix}`);
10933
11320
  } catch {
10934
11321
  }
10935
11322
  }
@@ -10947,7 +11334,7 @@ function importConductorWorkspace(workspaceId) {
10947
11334
  ).get(workspaceId);
10948
11335
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
10949
11336
  const worktreePath = String(row.workspacePath);
10950
- if (!(0, import_node_fs22.existsSync)(worktreePath)) {
11337
+ if (!(0, import_node_fs25.existsSync)(worktreePath)) {
10951
11338
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
10952
11339
  }
10953
11340
  let sessionId = null;
@@ -10987,7 +11374,7 @@ function importConductorWorkspace(workspaceId) {
10987
11374
  }
10988
11375
  let repoPath = worktreePath;
10989
11376
  try {
10990
- repoPath = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
11377
+ repoPath = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
10991
11378
  cwd: worktreePath,
10992
11379
  encoding: "utf8"
10993
11380
  }).trim();
@@ -11010,7 +11397,7 @@ function importConductorWorkspace(workspaceId) {
11010
11397
  db.close();
11011
11398
  }
11012
11399
  } finally {
11013
- (0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
11400
+ (0, import_node_fs25.rmSync)(tmp, { recursive: true, force: true });
11014
11401
  }
11015
11402
  }
11016
11403
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -11019,14 +11406,28 @@ async function importConductorWorkspaceAsync(workspaceId) {
11019
11406
 
11020
11407
  // src/orchestrator/orchestrator.ts
11021
11408
  var import_node_events = require("events");
11022
- var import_node_fs28 = require("fs");
11409
+ var import_node_fs31 = require("fs");
11023
11410
  init_error_detail();
11024
11411
  init_agents();
11025
11412
  init_worktree();
11026
11413
  init_stack();
11027
11414
 
11415
+ // src/git/pr-merge-archive.ts
11416
+ function normalizePrState(state) {
11417
+ return (state ?? "").trim().toUpperCase();
11418
+ }
11419
+ function shouldAutoArchiveOnPrMerge(opts) {
11420
+ if (!opts.autoArchiveEnabled) return false;
11421
+ if (opts.isGlobal) return false;
11422
+ if (opts.skipAutoArchiveOnMerge) return false;
11423
+ if (opts.threadStatus === "archived") return false;
11424
+ const next = normalizePrState(opts.nextPrState);
11425
+ if (next !== "MERGED") return false;
11426
+ return normalizePrState(opts.previousPrState) !== "MERGED";
11427
+ }
11428
+
11028
11429
  // src/git/orphan-cleanup.ts
11029
- var import_node_fs23 = require("fs");
11430
+ var import_node_fs26 = require("fs");
11030
11431
  var import_node_path21 = require("path");
11031
11432
  init_worktree();
11032
11433
  init_thread_store();
@@ -11042,9 +11443,9 @@ async function findOrphanWorktrees(repoPaths) {
11042
11443
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
11043
11444
  );
11044
11445
  const homeRoot = sideboardWorkspacesDir();
11045
- if ((0, import_node_fs23.existsSync)(homeRoot)) {
11446
+ if ((0, import_node_fs26.existsSync)(homeRoot)) {
11046
11447
  try {
11047
- for (const entry of (0, import_node_fs23.readdirSync)(homeRoot, { withFileTypes: true })) {
11448
+ for (const entry of (0, import_node_fs26.readdirSync)(homeRoot, { withFileTypes: true })) {
11048
11449
  if (!entry.isDirectory()) continue;
11049
11450
  void entry;
11050
11451
  }
@@ -11054,7 +11455,7 @@ async function findOrphanWorktrees(repoPaths) {
11054
11455
  const orphans = [];
11055
11456
  const seen = /* @__PURE__ */ new Set();
11056
11457
  for (const repoPath of repos) {
11057
- if (!repoPath || !(0, import_node_fs23.existsSync)(repoPath)) continue;
11458
+ if (!repoPath || !(0, import_node_fs26.existsSync)(repoPath)) continue;
11058
11459
  try {
11059
11460
  const wts = await listWorktrees(repoPath);
11060
11461
  for (const wt of wts) {
@@ -11065,7 +11466,7 @@ async function findOrphanWorktrees(repoPaths) {
11065
11466
  seen.add(path);
11066
11467
  let mtimeMs = 0;
11067
11468
  try {
11068
- mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
11469
+ mtimeMs = (0, import_node_fs26.statSync)(path).mtimeMs;
11069
11470
  } catch {
11070
11471
  mtimeMs = 0;
11071
11472
  }
@@ -11075,16 +11476,16 @@ async function findOrphanWorktrees(repoPaths) {
11075
11476
  }
11076
11477
  try {
11077
11478
  const root = worktreesRoot(repoPath);
11078
- if ((0, import_node_fs23.existsSync)(root)) {
11079
- for (const entry of (0, import_node_fs23.readdirSync)(root, { withFileTypes: true })) {
11479
+ if ((0, import_node_fs26.existsSync)(root)) {
11480
+ for (const entry of (0, import_node_fs26.readdirSync)(root, { withFileTypes: true })) {
11080
11481
  if (!entry.isDirectory()) continue;
11081
11482
  const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
11082
11483
  if (known.has(path) || seen.has(path)) continue;
11083
- if (!(0, import_node_fs23.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
11484
+ if (!(0, import_node_fs26.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
11084
11485
  seen.add(path);
11085
11486
  let mtimeMs = 0;
11086
11487
  try {
11087
- mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
11488
+ mtimeMs = (0, import_node_fs26.statSync)(path).mtimeMs;
11088
11489
  } catch {
11089
11490
  mtimeMs = Date.now();
11090
11491
  }
@@ -11230,7 +11631,7 @@ async function applyThreadIntoMain(thread, opts) {
11230
11631
  }
11231
11632
 
11232
11633
  // src/git/clone-repo.ts
11233
- var import_node_fs24 = require("fs");
11634
+ var import_node_fs27 = require("fs");
11234
11635
  var import_node_path22 = require("path");
11235
11636
  var import_execa6 = require("execa");
11236
11637
  init_paths();
@@ -11246,7 +11647,7 @@ async function cloneRepoIntoSideboard(opts) {
11246
11647
  }
11247
11648
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
11248
11649
  const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
11249
- if ((0, import_node_fs24.existsSync)(dest)) {
11650
+ if ((0, import_node_fs27.existsSync)(dest)) {
11250
11651
  const repoPath2 = await resolveRepoRoot(dest);
11251
11652
  const workspace2 = await ensureWorkspace(repoPath2);
11252
11653
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -11268,7 +11669,7 @@ init_orchestrator_capable();
11268
11669
 
11269
11670
  // src/review/request-review.ts
11270
11671
  var import_node_crypto5 = require("crypto");
11271
- var import_node_fs25 = require("fs");
11672
+ var import_node_fs28 = require("fs");
11272
11673
  var import_node_path23 = require("path");
11273
11674
  init_global_workspace();
11274
11675
  init_thread_store();
@@ -11413,9 +11814,9 @@ function shouldRefreshReviewRequestTemplate(content) {
11413
11814
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
11414
11815
  }
11415
11816
  function readTextIfPresent(abs) {
11416
- if (!(0, import_node_fs25.existsSync)(abs)) return null;
11817
+ if (!(0, import_node_fs28.existsSync)(abs)) return null;
11417
11818
  try {
11418
- const content = (0, import_node_fs25.readFileSync)(abs, "utf8");
11819
+ const content = (0, import_node_fs28.readFileSync)(abs, "utf8");
11419
11820
  return content.trim() ? content : null;
11420
11821
  } catch {
11421
11822
  return null;
@@ -11423,9 +11824,9 @@ function readTextIfPresent(abs) {
11423
11824
  }
11424
11825
  function ensureAttachmentsGitignore(worktreePath) {
11425
11826
  const gitignoreAbs = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
11426
- if ((0, import_node_fs25.existsSync)(gitignoreAbs)) return;
11427
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
11428
- (0, import_node_fs25.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11827
+ if ((0, import_node_fs28.existsSync)(gitignoreAbs)) return;
11828
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
11829
+ (0, import_node_fs28.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11429
11830
  }
11430
11831
  function resolveReviewGuidelines(worktreePath) {
11431
11832
  const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
@@ -11459,8 +11860,8 @@ function resolveReviewGuidelines(worktreePath) {
11459
11860
  };
11460
11861
  }
11461
11862
  ensureAttachmentsGitignore(worktreePath);
11462
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
11463
- (0, import_node_fs25.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11863
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
11864
+ (0, import_node_fs28.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11464
11865
  return {
11465
11866
  path: REVIEW_REQUEST_PATH,
11466
11867
  name: REVIEW_REQUEST_NAME,
@@ -11482,7 +11883,7 @@ function ensureReviewRequestFile(worktreePath) {
11482
11883
  const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
11483
11884
  const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
11484
11885
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
11485
- const path = (0, import_node_fs25.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
11886
+ const path = (0, import_node_fs28.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
11486
11887
  return {
11487
11888
  path,
11488
11889
  name: REVIEW_REQUEST_NAME,
@@ -11490,8 +11891,8 @@ function ensureReviewRequestFile(worktreePath) {
11490
11891
  source: "local"
11491
11892
  };
11492
11893
  }
11493
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
11494
- (0, import_node_fs25.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11894
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
11895
+ (0, import_node_fs28.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11495
11896
  return {
11496
11897
  path: REPO_REVIEW_PATH,
11497
11898
  name: REPO_REVIEW_NAME,
@@ -11771,7 +12172,7 @@ var Orchestrator = class {
11771
12172
  }
11772
12173
  continue;
11773
12174
  }
11774
- if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
12175
+ if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
11775
12176
  setStatus(thread.id, "broken", "Worktree missing on disk");
11776
12177
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
11777
12178
  continue;
@@ -12188,13 +12589,14 @@ var Orchestrator = class {
12188
12589
  "Files column: MCP present_files (brightsy account storage or memory).",
12189
12590
  "Do not say artifacts/CMS UI are unavailable."
12190
12591
  ].join(" ") : null;
12592
+ const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
12191
12593
  const agentPrompt = [
12192
12594
  thread.planMode ? PLAN_MODE_INSTRUCTION : null,
12193
12595
  orchestrationReminder,
12596
+ worktreeReminder,
12194
12597
  artifactReminder,
12195
12598
  expandedPrompt
12196
12599
  ].filter(Boolean).join("\n\n");
12197
- const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
12198
12600
  if (thread.attachments.length > 0) {
12199
12601
  updateThread(threadId, { attachments: [] });
12200
12602
  }
@@ -12222,7 +12624,6 @@ var Orchestrator = class {
12222
12624
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
12223
12625
  customPrompt: settings?.prompts?.renameBranch
12224
12626
  }) : null;
12225
- const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
12226
12627
  let seed = null;
12227
12628
  if (!fresh.sessionId) {
12228
12629
  const prior = fresh.messages.slice(0, -1);
@@ -12243,12 +12644,12 @@ var Orchestrator = class {
12243
12644
  });
12244
12645
  }
12245
12646
  }
12246
- const cachedPrefix = [
12647
+ const cachedPrefix = fresh.sessionId ? "" : [
12247
12648
  coordinatorDirective,
12248
12649
  worktreeDirective,
12249
12650
  artifactDirective,
12250
12651
  renameBranchDirective,
12251
- ...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
12652
+ seed
12252
12653
  ].filter(Boolean).join("\n\n---\n\n");
12253
12654
  try {
12254
12655
  const stderrTail = [];
@@ -12335,15 +12736,11 @@ var Orchestrator = class {
12335
12736
  const retryThread = this.requireThread(threadId);
12336
12737
  const prior = retryThread.messages.slice(0, -1);
12337
12738
  const retrySeed = buildSessionSeed(prior);
12338
- const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
12339
- loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
12340
- );
12341
12739
  const retryPrefix = [
12342
12740
  coordinatorDirective,
12343
12741
  worktreeDirective,
12344
12742
  artifactDirective,
12345
12743
  renameBranchDirective,
12346
- retryInstructions,
12347
12744
  retrySeed
12348
12745
  ].filter(Boolean).join("\n\n---\n\n");
12349
12746
  const retryHandle = await spawnAgentTurn(
@@ -12854,10 +13251,19 @@ var Orchestrator = class {
12854
13251
  this.assertNotGlobal(thread, "Merge PR");
12855
13252
  if (!selector) throw new Error("No pull request linked to this thread");
12856
13253
  const result = await mergePr(cwd, selector);
12857
- if (result.url && result.url !== thread.prUrl) {
12858
- updateThread(thread.id, { prUrl: result.url });
12859
- }
12860
- return result;
13254
+ const state = normalizePrState(result.state) || "MERGED";
13255
+ const metaLike = {
13256
+ number: 0,
13257
+ title: thread.prTitle ?? thread.title,
13258
+ url: result.url || thread.prUrl || "",
13259
+ state,
13260
+ isDraft: false,
13261
+ reviewDecision: null,
13262
+ baseRefName: "",
13263
+ headRefName: ""
13264
+ };
13265
+ await this.persistPrMetaAndMaybeArchive(thread, metaLike);
13266
+ return { url: metaLike.url, state };
12861
13267
  }
12862
13268
  /** Resolve PR selector and optionally persist `prUrl` when found. */
12863
13269
  async withPrSelector(threadRef) {
@@ -12879,19 +13285,55 @@ var Orchestrator = class {
12879
13285
  if (!selector) return null;
12880
13286
  const meta = await getPrMeta(cwd, selector);
12881
13287
  if (meta) {
12882
- const patch = {};
12883
- if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
12884
- if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
12885
- if (Object.keys(patch).length > 0) {
12886
- updateThread(thread.id, patch);
12887
- const latest = this.requireThread(thread.id);
12888
- if (!latest.userSetTitle && meta.title && latest.title !== meta.title) {
12889
- updateThread(thread.id, { title: meta.title });
12890
- }
12891
- }
13288
+ await this.persistPrMetaAndMaybeArchive(thread, meta);
12892
13289
  }
12893
13290
  return meta;
12894
13291
  }
13292
+ /**
13293
+ * Persist PR URL/title/state and Conductor-style auto-archive when the PR
13294
+ * first becomes MERGED.
13295
+ */
13296
+ async persistPrMetaAndMaybeArchive(thread, meta) {
13297
+ const prevState = normalizePrState(thread.prState);
13298
+ const nextState = normalizePrState(meta.state);
13299
+ const patch = {};
13300
+ if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
13301
+ if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
13302
+ if (nextState && nextState !== prevState) patch.prState = nextState;
13303
+ if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
13304
+ patch.skipAutoArchiveOnMerge = false;
13305
+ }
13306
+ if (Object.keys(patch).length > 0) {
13307
+ updateThread(thread.id, patch);
13308
+ const latest2 = this.requireThread(thread.id);
13309
+ if (!latest2.userSetTitle && meta.title && latest2.title !== meta.title) {
13310
+ updateThread(thread.id, { title: meta.title });
13311
+ }
13312
+ }
13313
+ const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
13314
+ const latest = this.requireThread(thread.id);
13315
+ if (!shouldAutoArchiveOnPrMerge({
13316
+ previousPrState: prevState || null,
13317
+ nextPrState: nextState,
13318
+ threadStatus: latest.status,
13319
+ skipAutoArchiveOnMerge: latest.skipAutoArchiveOnMerge,
13320
+ autoArchiveEnabled: autoArchiveOnMergeEnabled2(),
13321
+ isGlobal: isGlobalThread(latest)
13322
+ })) {
13323
+ return;
13324
+ }
13325
+ const siblings = threadsSharingWorktree(latest.worktreePath);
13326
+ for (const t of siblings) {
13327
+ const sibPatch = { prState: "MERGED" };
13328
+ if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
13329
+ if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
13330
+ if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
13331
+ }
13332
+ for (const t of siblings) {
13333
+ if (this.requireThread(t.id).status === "archived") continue;
13334
+ await this.archive(t.id);
13335
+ }
13336
+ }
12895
13337
  async getPrStack(threadRef) {
12896
13338
  const thread = this.requireThread(threadRef);
12897
13339
  if (!thread.worktreePath?.trim()) return null;
@@ -13069,7 +13511,9 @@ var Orchestrator = class {
13069
13511
  const thread = this.requireThread(threadRef);
13070
13512
  this.stop(thread.id);
13071
13513
  if (isGlobalThread(thread)) {
13072
- return setStatus(thread.id, "archived");
13514
+ const archived2 = setStatus(thread.id, "archived");
13515
+ this.emit({ type: "status_changed", threadId: archived2.id, status: "archived" });
13516
+ return archived2;
13073
13517
  }
13074
13518
  const siblings = threadsSharingWorktree(thread.worktreePath).filter((t) => t.id !== thread.id);
13075
13519
  if (siblings.length === 0) {
@@ -13087,6 +13531,7 @@ var Orchestrator = class {
13087
13531
  await removeWorktree(thread.repoPath, thread.worktreePath);
13088
13532
  }
13089
13533
  const archived = setStatus(thread.id, "archived");
13534
+ this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
13090
13535
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
13091
13536
  try {
13092
13537
  const { ensureWorkspace: ensureWorkspace2 } = await Promise.resolve().then(() => (init_workspaces(), workspaces_exports));
@@ -13126,9 +13571,11 @@ var Orchestrator = class {
13126
13571
  if (isGlobalThread(thread)) {
13127
13572
  const { globalAgentCwd: globalAgentCwd2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
13128
13573
  updateThread(thread.id, { worktreePath: globalAgentCwd2() });
13129
- return setStatus(thread.id, "idle");
13574
+ const restored2 = setStatus(thread.id, "idle");
13575
+ this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
13576
+ return restored2;
13130
13577
  }
13131
- if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
13578
+ if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
13132
13579
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
13133
13580
  const { execa: execa7 } = await import("execa");
13134
13581
  const slug = thread.worktreePath.split("/").pop();
@@ -13139,9 +13586,33 @@ var Orchestrator = class {
13139
13586
  void createThreadWorktree2;
13140
13587
  void slug;
13141
13588
  }
13142
- return setStatus(thread.id, "idle");
13143
- }
13144
- async attachCommand(threadRef) {
13589
+ const restorePatch = {};
13590
+ let alreadyMerged = normalizePrState(thread.prState) === "MERGED";
13591
+ if (!alreadyMerged && thread.prUrl?.trim() && thread.worktreePath?.trim()) {
13592
+ try {
13593
+ const selector = resolvePrSelector(thread);
13594
+ if (selector) {
13595
+ const meta = await getPrMeta(thread.worktreePath, selector);
13596
+ if (meta) {
13597
+ const state = normalizePrState(meta.state);
13598
+ if (meta.url && meta.url !== thread.prUrl) restorePatch.prUrl = meta.url;
13599
+ if (meta.title && meta.title !== thread.prTitle) restorePatch.prTitle = meta.title;
13600
+ if (state) restorePatch.prState = state;
13601
+ if (state === "MERGED") alreadyMerged = true;
13602
+ }
13603
+ }
13604
+ } catch {
13605
+ }
13606
+ }
13607
+ if (alreadyMerged) restorePatch.skipAutoArchiveOnMerge = true;
13608
+ if (Object.keys(restorePatch).length > 0) {
13609
+ updateThread(thread.id, restorePatch);
13610
+ }
13611
+ const restored = setStatus(thread.id, "idle");
13612
+ this.emit({ type: "status_changed", threadId: restored.id, status: restored.status });
13613
+ return restored;
13614
+ }
13615
+ async attachCommand(threadRef) {
13145
13616
  const thread = this.requireThread(threadRef);
13146
13617
  const adapter = getAdapter(thread.agent);
13147
13618
  return adapter.buildAttach(thread);
@@ -13360,6 +13831,7 @@ function mcpArchiveBlockedReason(thread) {
13360
13831
  }
13361
13832
 
13362
13833
  // src/mcp/server.ts
13834
+ init_profile();
13363
13835
  var MAX_ORCH_THREADS = 5;
13364
13836
  var CREATE_THREAD_TIMEOUT_MS = 9e4;
13365
13837
  function withTimeout(promise, ms, label) {
@@ -13473,14 +13945,13 @@ async function startMcpServer() {
13473
13945
  ),
13474
13946
  artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
13475
13947
  },
13476
- async ({ title, type, content, artifact_id }) => {
13948
+ async ({ title, type, artifact_id }) => {
13477
13949
  const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
13478
13950
  const payload = {
13479
13951
  ok: true,
13480
13952
  artifact_id: id,
13481
13953
  title,
13482
13954
  type,
13483
- content,
13484
13955
  message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
13485
13956
  };
13486
13957
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13533,7 +14004,6 @@ async function startMcpServer() {
13533
14004
  ok: true,
13534
14005
  path,
13535
14006
  title: title?.trim() || "Plan",
13536
- content,
13537
14007
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
13538
14008
  };
13539
14009
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13563,9 +14033,6 @@ async function startMcpServer() {
13563
14033
  datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
13564
14034
  resource_id: args.resource_id,
13565
14035
  record_id: args.record_id,
13566
- resource: args.resource,
13567
- record: args.record,
13568
- records: args.records,
13569
14036
  message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
13570
14037
  };
13571
14038
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13593,744 +14060,747 @@ async function startMcpServer() {
13593
14060
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
13594
14061
  }
13595
14062
  );
13596
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
13597
- const accountDefaults = resolveThreadDefaults2();
13598
- const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
13599
- server.tool(
13600
- "create_thread",
13601
- `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.`,
13602
- {
13603
- sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
13604
- sourceRef: import_zod.z.string(),
13605
- agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
13606
- model: import_zod.z.string().nullable().optional().describe(
13607
- `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
13608
- ),
13609
- repoPath: import_zod.z.string(),
13610
- title: import_zod.z.string().optional(),
13611
- parentThreadId: import_zod.z.string().optional().describe(
13612
- "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
13613
- )
13614
- },
13615
- async (args) => {
13616
- const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
13617
- let parentId = args.parentThreadId?.trim() || "";
13618
- let parent = parentId ? orch.getThread(parentId) : null;
13619
- let parentCorrectedFrom;
13620
- if (envParentId) {
13621
- const envParent = orch.getThread(envParentId);
13622
- if (envParent) {
13623
- if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
13624
- else if (!parentId) parentCorrectedFrom = void 0;
13625
- parentId = envParentId;
13626
- parent = envParent;
14063
+ if (sideboardMcpProfile() !== "worktree") {
14064
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
14065
+ const accountDefaults = resolveThreadDefaults2();
14066
+ const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
14067
+ server.tool(
14068
+ "create_thread",
14069
+ `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.`,
14070
+ {
14071
+ sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
14072
+ sourceRef: import_zod.z.string(),
14073
+ agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
14074
+ model: import_zod.z.string().nullable().optional().describe(
14075
+ `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
14076
+ ),
14077
+ repoPath: import_zod.z.string(),
14078
+ title: import_zod.z.string().optional(),
14079
+ parentThreadId: import_zod.z.string().optional().describe(
14080
+ "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
14081
+ )
14082
+ },
14083
+ async (args) => {
14084
+ const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
14085
+ let parentId = args.parentThreadId?.trim() || "";
14086
+ let parent = parentId ? orch.getThread(parentId) : null;
14087
+ let parentCorrectedFrom;
14088
+ if (envParentId) {
14089
+ const envParent = orch.getThread(envParentId);
14090
+ if (envParent) {
14091
+ if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
14092
+ else if (!parentId) parentCorrectedFrom = void 0;
14093
+ parentId = envParentId;
14094
+ parent = envParent;
14095
+ }
13627
14096
  }
13628
- }
13629
- if (parentId && !parent) {
13630
- parentCorrectedFrom = parentId;
13631
- parentId = "";
13632
- parent = null;
13633
- }
13634
- if (parentId) {
13635
- const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
13636
- if (children.length >= MAX_ORCH_THREADS) {
14097
+ if (parentId && !parent) {
14098
+ parentCorrectedFrom = parentId;
14099
+ parentId = "";
14100
+ parent = null;
14101
+ }
14102
+ if (parentId) {
14103
+ const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
14104
+ if (children.length >= MAX_ORCH_THREADS) {
14105
+ return {
14106
+ content: [
14107
+ {
14108
+ type: "text",
14109
+ text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
14110
+ }
14111
+ ],
14112
+ isError: true
14113
+ };
14114
+ }
14115
+ }
14116
+ let agentArg = args.agent;
14117
+ let agentCoercedFrom;
14118
+ const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
14119
+ if (resolvedProbe === "codex") {
14120
+ agentCoercedFrom = agentArg ?? "codex";
14121
+ const accountAgent = resolveNewThreadOptions2({}).agent;
14122
+ agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
14123
+ }
14124
+ const opts = resolveNewThreadOptions2({
14125
+ agent: agentArg,
14126
+ model: args.model
14127
+ });
14128
+ try {
14129
+ const thread = await withTimeout(
14130
+ orch.createThread({
14131
+ sourceType: args.sourceType,
14132
+ sourceRef: args.sourceRef,
14133
+ agent: opts.agent,
14134
+ model: opts.model,
14135
+ effort: opts.effort,
14136
+ fast: opts.fast,
14137
+ repoPath: args.repoPath,
14138
+ title: args.title,
14139
+ parentThreadId: parentId || null
14140
+ }),
14141
+ CREATE_THREAD_TIMEOUT_MS,
14142
+ "create_thread"
14143
+ );
14144
+ const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
13637
14145
  return {
13638
14146
  content: [
13639
14147
  {
13640
14148
  type: "text",
13641
- text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
14149
+ text: JSON.stringify({
14150
+ id: thread.id,
14151
+ title: thread.title,
14152
+ branchName: thread.branchName,
14153
+ worktreePath: thread.worktreePath,
14154
+ agent: thread.agent,
14155
+ model: thread.model,
14156
+ status: thread.status,
14157
+ link: `sideboard://thread/${thread.id}`,
14158
+ parentThreadId: thread.parentThreadId,
14159
+ ...agentCoercedFrom ? {
14160
+ agentCoercedFrom,
14161
+ note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
14162
+ } : {},
14163
+ ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
14164
+ })
14165
+ }
14166
+ ]
14167
+ };
14168
+ } catch (err) {
14169
+ const message = err instanceof Error ? err.message : String(err);
14170
+ return {
14171
+ content: [
14172
+ {
14173
+ type: "text",
14174
+ text: `create_thread failed: ${message}`
13642
14175
  }
13643
14176
  ],
13644
14177
  isError: true
13645
14178
  };
13646
14179
  }
13647
14180
  }
13648
- let agentArg = args.agent;
13649
- let agentCoercedFrom;
13650
- const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
13651
- if (resolvedProbe === "codex") {
13652
- agentCoercedFrom = agentArg ?? "codex";
13653
- const accountAgent = resolveNewThreadOptions2({}).agent;
13654
- agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
13655
- }
13656
- const opts = resolveNewThreadOptions2({
13657
- agent: agentArg,
13658
- model: args.model
13659
- });
13660
- try {
13661
- const thread = await withTimeout(
13662
- orch.createThread({
13663
- sourceType: args.sourceType,
13664
- sourceRef: args.sourceRef,
13665
- agent: opts.agent,
13666
- model: opts.model,
13667
- effort: opts.effort,
13668
- fast: opts.fast,
13669
- repoPath: args.repoPath,
13670
- title: args.title,
13671
- parentThreadId: parentId || null
13672
- }),
13673
- CREATE_THREAD_TIMEOUT_MS,
13674
- "create_thread"
13675
- );
13676
- const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
14181
+ );
14182
+ server.tool(
14183
+ "send_to_thread",
14184
+ "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.",
14185
+ {
14186
+ ref: import_zod.z.string(),
14187
+ prompt: import_zod.z.string(),
14188
+ force_stop: import_zod.z.boolean().optional()
14189
+ },
14190
+ async ({ ref, prompt, force_stop }) => {
14191
+ if (force_stop) {
14192
+ const existing = orch.getThread(ref);
14193
+ if (existing) {
14194
+ orch.stop(ref, { clearQueue: true });
14195
+ }
14196
+ }
14197
+ const thread = await orch.send(ref, prompt);
13677
14198
  return {
13678
14199
  content: [
13679
14200
  {
13680
14201
  type: "text",
13681
14202
  text: JSON.stringify({
13682
14203
  id: thread.id,
13683
- title: thread.title,
13684
- branchName: thread.branchName,
13685
- worktreePath: thread.worktreePath,
13686
- agent: thread.agent,
13687
- model: thread.model,
13688
14204
  status: thread.status,
13689
- link: `sideboard://thread/${thread.id}`,
13690
- parentThreadId: thread.parentThreadId,
13691
- ...agentCoercedFrom ? {
13692
- agentCoercedFrom,
13693
- note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
13694
- } : {},
13695
- ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
14205
+ queueLength: thread.queue.length,
14206
+ forceStopped: Boolean(force_stop)
13696
14207
  })
13697
14208
  }
13698
14209
  ]
13699
14210
  };
13700
- } catch (err) {
13701
- const message = err instanceof Error ? err.message : String(err);
14211
+ }
14212
+ );
14213
+ server.tool(
14214
+ "wait_for_turn",
14215
+ "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
14216
+ {
14217
+ ref: import_zod.z.string(),
14218
+ timeoutMs: import_zod.z.number().optional()
14219
+ },
14220
+ async ({ ref, timeoutMs }) => {
14221
+ const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
14222
+ const result = orch.getTurnResult(thread.id);
13702
14223
  return {
13703
14224
  content: [
13704
14225
  {
13705
14226
  type: "text",
13706
- text: `create_thread failed: ${message}`
14227
+ text: JSON.stringify({
14228
+ id: thread.id,
14229
+ status: result.status,
14230
+ text: result.text
14231
+ })
13707
14232
  }
13708
- ],
13709
- isError: true
14233
+ ]
13710
14234
  };
13711
14235
  }
13712
- }
13713
- );
13714
- server.tool(
13715
- "send_to_thread",
13716
- "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.",
13717
- {
13718
- ref: import_zod.z.string(),
13719
- prompt: import_zod.z.string(),
13720
- force_stop: import_zod.z.boolean().optional()
13721
- },
13722
- async ({ ref, prompt, force_stop }) => {
13723
- if (force_stop) {
13724
- const existing = orch.getThread(ref);
13725
- if (existing) {
13726
- orch.stop(ref, { clearQueue: true });
13727
- }
14236
+ );
14237
+ server.tool(
14238
+ "get_turn_result",
14239
+ "Final assistant message only (not full transcript)",
14240
+ { ref: import_zod.z.string() },
14241
+ async ({ ref }) => {
14242
+ const result = orch.getTurnResult(ref);
14243
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
13728
14244
  }
13729
- const thread = await orch.send(ref, prompt);
13730
- return {
13731
- content: [
13732
- {
13733
- type: "text",
13734
- text: JSON.stringify({
13735
- id: thread.id,
13736
- status: thread.status,
13737
- queueLength: thread.queue.length,
13738
- forceStopped: Boolean(force_stop)
13739
- })
13740
- }
13741
- ]
13742
- };
13743
- }
13744
- );
13745
- server.tool(
13746
- "wait_for_turn",
13747
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
13748
- {
13749
- ref: import_zod.z.string(),
13750
- timeoutMs: import_zod.z.number().optional()
13751
- },
13752
- async ({ ref, timeoutMs }) => {
13753
- const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
13754
- const result = orch.getTurnResult(thread.id);
13755
- return {
13756
- content: [
13757
- {
13758
- type: "text",
13759
- text: JSON.stringify({
13760
- id: thread.id,
13761
- status: result.status,
13762
- text: result.text
13763
- })
13764
- }
13765
- ]
13766
- };
13767
- }
13768
- );
13769
- server.tool(
13770
- "get_turn_result",
13771
- "Final assistant message only (not full transcript)",
13772
- { ref: import_zod.z.string() },
13773
- async ({ ref }) => {
13774
- const result = orch.getTurnResult(ref);
13775
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
13776
- }
13777
- );
13778
- server.tool(
13779
- "stop_thread",
13780
- "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.",
13781
- {
13782
- ref: import_zod.z.string(),
13783
- force: import_zod.z.boolean().optional()
13784
- },
13785
- async ({ ref, force }) => {
13786
- const t = orch.getThread(ref);
13787
- if (!t) {
14245
+ );
14246
+ server.tool(
14247
+ "stop_thread",
14248
+ "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.",
14249
+ {
14250
+ ref: import_zod.z.string(),
14251
+ force: import_zod.z.boolean().optional()
14252
+ },
14253
+ async ({ ref, force }) => {
14254
+ const t = orch.getThread(ref);
14255
+ if (!t) {
14256
+ return {
14257
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14258
+ isError: true
14259
+ };
14260
+ }
14261
+ const clearQueue = force !== false;
14262
+ const hadQueued = t.queue.length > 0;
14263
+ const stopped = orch.stop(ref, { clearQueue });
13788
14264
  return {
13789
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
13790
- isError: true
14265
+ content: [
14266
+ {
14267
+ type: "text",
14268
+ text: JSON.stringify({
14269
+ id: stopped.id,
14270
+ status: stopped.status,
14271
+ clearedQueue: clearQueue && hadQueued
14272
+ })
14273
+ }
14274
+ ]
13791
14275
  };
13792
14276
  }
13793
- const clearQueue = force !== false;
13794
- const hadQueued = t.queue.length > 0;
13795
- const stopped = orch.stop(ref, { clearQueue });
13796
- return {
13797
- content: [
13798
- {
13799
- type: "text",
13800
- text: JSON.stringify({
13801
- id: stopped.id,
13802
- status: stopped.status,
13803
- clearedQueue: clearQueue && hadQueued
13804
- })
13805
- }
13806
- ]
13807
- };
13808
- }
13809
- );
13810
- server.tool(
13811
- "archive_thread",
13812
- "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.",
13813
- { ref: import_zod.z.string() },
13814
- async ({ ref }) => {
13815
- const t = orch.getThread(ref);
13816
- if (!t) {
14277
+ );
14278
+ server.tool(
14279
+ "archive_thread",
14280
+ "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.",
14281
+ { ref: import_zod.z.string() },
14282
+ async ({ ref }) => {
14283
+ const t = orch.getThread(ref);
14284
+ if (!t) {
14285
+ return {
14286
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14287
+ isError: true
14288
+ };
14289
+ }
14290
+ const blocked = mcpArchiveBlockedReason(t);
14291
+ if (blocked) {
14292
+ return {
14293
+ content: [{ type: "text", text: blocked }],
14294
+ isError: true
14295
+ };
14296
+ }
14297
+ const archived = await orch.archive(ref);
13817
14298
  return {
13818
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
13819
- isError: true
14299
+ content: [
14300
+ {
14301
+ type: "text",
14302
+ text: JSON.stringify({
14303
+ id: archived.id,
14304
+ status: archived.status
14305
+ })
14306
+ }
14307
+ ]
13820
14308
  };
13821
14309
  }
13822
- const blocked = mcpArchiveBlockedReason(t);
13823
- if (blocked) {
13824
- return {
13825
- content: [{ type: "text", text: blocked }],
13826
- isError: true
13827
- };
14310
+ );
14311
+ server.tool(
14312
+ "restore_thread",
14313
+ "Restore an archived thread (recreates worktree from branch when needed)",
14314
+ { ref: import_zod.z.string() },
14315
+ async ({ ref }) => {
14316
+ try {
14317
+ const restored = await orch.restore(ref);
14318
+ return {
14319
+ content: [
14320
+ {
14321
+ type: "text",
14322
+ text: JSON.stringify({
14323
+ id: restored.id,
14324
+ status: restored.status,
14325
+ worktreePath: restored.worktreePath
14326
+ })
14327
+ }
14328
+ ]
14329
+ };
14330
+ } catch (err) {
14331
+ const message = err instanceof Error ? err.message : String(err);
14332
+ return { content: [{ type: "text", text: message }], isError: true };
14333
+ }
13828
14334
  }
13829
- const archived = await orch.archive(ref);
13830
- return {
13831
- content: [
13832
- {
13833
- type: "text",
13834
- text: JSON.stringify({
13835
- id: archived.id,
13836
- status: archived.status
13837
- })
13838
- }
13839
- ]
13840
- };
13841
- }
13842
- );
13843
- server.tool(
13844
- "restore_thread",
13845
- "Restore an archived thread (recreates worktree from branch when needed)",
13846
- { ref: import_zod.z.string() },
13847
- async ({ ref }) => {
13848
- try {
13849
- const restored = await orch.restore(ref);
14335
+ );
14336
+ server.tool(
14337
+ "get_diff",
14338
+ "Compact diff summary (capped hunks, paginated)",
14339
+ {
14340
+ ref: import_zod.z.string(),
14341
+ maxFiles: import_zod.z.number().optional()
14342
+ },
14343
+ async ({ ref, maxFiles }) => {
14344
+ const summary = await orch.diffSummary(ref);
13850
14345
  return {
13851
14346
  content: [
13852
14347
  {
13853
14348
  type: "text",
13854
14349
  text: JSON.stringify({
13855
- id: restored.id,
13856
- status: restored.status,
13857
- worktreePath: restored.worktreePath
14350
+ ...summary,
14351
+ files: summary.files.slice(0, maxFiles ?? 10)
13858
14352
  })
13859
14353
  }
13860
14354
  ]
13861
14355
  };
13862
- } catch (err) {
13863
- const message = err instanceof Error ? err.message : String(err);
13864
- return { content: [{ type: "text", text: message }], isError: true };
13865
14356
  }
13866
- }
13867
- );
13868
- server.tool(
13869
- "get_diff",
13870
- "Compact diff summary (capped hunks, paginated)",
13871
- {
13872
- ref: import_zod.z.string(),
13873
- maxFiles: import_zod.z.number().optional()
13874
- },
13875
- async ({ ref, maxFiles }) => {
13876
- const summary = await orch.diffSummary(ref);
13877
- return {
13878
- content: [
13879
- {
13880
- type: "text",
13881
- text: JSON.stringify({
13882
- ...summary,
13883
- files: summary.files.slice(0, maxFiles ?? 10)
13884
- })
14357
+ );
14358
+ server.tool(
14359
+ "request_review",
14360
+ '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.',
14361
+ { ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
14362
+ async ({ ref }) => {
14363
+ try {
14364
+ const tab = await orch.requestReview(ref);
14365
+ const from = orch.getThread(ref);
14366
+ return {
14367
+ content: [
14368
+ {
14369
+ type: "text",
14370
+ text: JSON.stringify({
14371
+ id: tab.id,
14372
+ title: tab.title,
14373
+ status: tab.status,
14374
+ fromThreadId: from?.id ?? ref,
14375
+ link: `sideboard://thread/${tab.id}`
14376
+ })
14377
+ }
14378
+ ]
14379
+ };
14380
+ } catch (err) {
14381
+ const message = err instanceof Error ? err.message : String(err);
14382
+ return { content: [{ type: "text", text: message }], isError: true };
14383
+ }
14384
+ }
14385
+ );
14386
+ const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
14387
+ server.tool(
14388
+ "list_models",
14389
+ "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.",
14390
+ {
14391
+ agent: agentEnum.optional().describe("Limit to one agent; omit for all")
14392
+ },
14393
+ async ({ agent }) => {
14394
+ try {
14395
+ const catalogs = await listModelsForAgent(agent);
14396
+ return {
14397
+ content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
14398
+ };
14399
+ } catch (err) {
14400
+ const message = err instanceof Error ? err.message : String(err);
14401
+ return { content: [{ type: "text", text: message }], isError: true };
14402
+ }
14403
+ }
14404
+ );
14405
+ server.tool(
14406
+ "fork_worktree",
14407
+ "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.",
14408
+ {
14409
+ ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
14410
+ through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14411
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14412
+ model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14413
+ title: import_zod.z.string().optional()
14414
+ },
14415
+ async ({ ref, through_index, agent, model, title }) => {
14416
+ try {
14417
+ const source = orch.getThread(ref);
14418
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
14419
+ const thread = await orch.forkThreadWorktree({
14420
+ threadId: ref,
14421
+ throughIndex: through_index,
14422
+ agent,
14423
+ model,
14424
+ title
14425
+ });
14426
+ return {
14427
+ content: [
14428
+ {
14429
+ type: "text",
14430
+ text: JSON.stringify({
14431
+ id: thread.id,
14432
+ title: thread.title,
14433
+ status: thread.status,
14434
+ agent: thread.agent,
14435
+ model: thread.model,
14436
+ branchName: thread.branchName,
14437
+ worktreePath: thread.worktreePath,
14438
+ fromThreadId: source?.id ?? ref,
14439
+ link: `sideboard://thread/${thread.id}`
14440
+ })
14441
+ }
14442
+ ]
14443
+ };
14444
+ } catch (err) {
14445
+ const message = err instanceof Error ? err.message : String(err);
14446
+ return { content: [{ type: "text", text: message }], isError: true };
14447
+ }
14448
+ }
14449
+ );
14450
+ server.tool(
14451
+ "fork_chat",
14452
+ "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.",
14453
+ {
14454
+ ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
14455
+ through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14456
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14457
+ model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14458
+ title: import_zod.z.string().optional()
14459
+ },
14460
+ async ({ ref, through_index, agent, model, title }) => {
14461
+ try {
14462
+ const source = orch.getThread(ref);
14463
+ if (!source) {
14464
+ return {
14465
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14466
+ isError: true
14467
+ };
13885
14468
  }
13886
- ]
13887
- };
13888
- }
13889
- );
13890
- server.tool(
13891
- "request_review",
13892
- '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.',
13893
- { ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
13894
- async ({ ref }) => {
13895
- try {
13896
- const tab = await orch.requestReview(ref);
13897
- const from = orch.getThread(ref);
14469
+ const tab = orch.forkChatTab({
14470
+ threadId: source.id,
14471
+ throughIndex: through_index,
14472
+ agent,
14473
+ model,
14474
+ title
14475
+ });
14476
+ return {
14477
+ content: [
14478
+ {
14479
+ type: "text",
14480
+ text: JSON.stringify({
14481
+ id: tab.id,
14482
+ title: tab.title,
14483
+ status: tab.status,
14484
+ agent: tab.agent,
14485
+ model: tab.model,
14486
+ sourceType: tab.sourceType,
14487
+ worktreePath: tab.worktreePath,
14488
+ fromThreadId: source.id,
14489
+ link: `sideboard://thread/${tab.id}`
14490
+ })
14491
+ }
14492
+ ]
14493
+ };
14494
+ } catch (err) {
14495
+ const message = err instanceof Error ? err.message : String(err);
14496
+ return { content: [{ type: "text", text: message }], isError: true };
14497
+ }
14498
+ }
14499
+ );
14500
+ server.tool(
14501
+ "run_dev_script",
14502
+ "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
14503
+ {
14504
+ ref: import_zod.z.string(),
14505
+ name: import_zod.z.string().optional()
14506
+ },
14507
+ async ({ ref, name }) => {
14508
+ const result = await orch.startDev(ref, name);
13898
14509
  return {
13899
14510
  content: [
13900
14511
  {
13901
14512
  type: "text",
13902
14513
  text: JSON.stringify({
13903
- id: tab.id,
13904
- title: tab.title,
13905
- status: tab.status,
13906
- fromThreadId: from?.id ?? ref,
13907
- link: `sideboard://thread/${tab.id}`
14514
+ port: result.port,
14515
+ scriptName: result.scriptName,
14516
+ ports: result.ports,
14517
+ url: `http://localhost:${result.port}`
13908
14518
  })
13909
14519
  }
13910
14520
  ]
13911
14521
  };
13912
- } catch (err) {
13913
- const message = err instanceof Error ? err.message : String(err);
13914
- return { content: [{ type: "text", text: message }], isError: true };
13915
14522
  }
13916
- }
13917
- );
13918
- const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
13919
- server.tool(
13920
- "list_models",
13921
- "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.",
13922
- {
13923
- agent: agentEnum.optional().describe("Limit to one agent; omit for all")
13924
- },
13925
- async ({ agent }) => {
13926
- try {
13927
- const catalogs = await listModelsForAgent(agent);
14523
+ );
14524
+ server.tool(
14525
+ "list_run_scripts",
14526
+ "List named run scripts available for a thread",
14527
+ { ref: import_zod.z.string() },
14528
+ async ({ ref }) => {
14529
+ const scripts = orch.listThreadRunScripts(ref);
14530
+ const active = orch.getActiveRuns(ref);
13928
14531
  return {
13929
- content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
14532
+ content: [
14533
+ {
14534
+ type: "text",
14535
+ text: JSON.stringify({ scripts, active }, null, 2)
14536
+ }
14537
+ ]
13930
14538
  };
13931
- } catch (err) {
13932
- const message = err instanceof Error ? err.message : String(err);
13933
- return { content: [{ type: "text", text: message }], isError: true };
13934
14539
  }
13935
- }
13936
- );
13937
- server.tool(
13938
- "fork_worktree",
13939
- "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.",
13940
- {
13941
- ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
13942
- through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
13943
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
13944
- model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
13945
- title: import_zod.z.string().optional()
13946
- },
13947
- async ({ ref, through_index, agent, model, title }) => {
13948
- try {
13949
- const source = orch.getThread(ref);
13950
- if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
13951
- const thread = await orch.forkThreadWorktree({
13952
- threadId: ref,
13953
- throughIndex: through_index,
13954
- agent,
13955
- model,
13956
- title
14540
+ );
14541
+ server.tool(
14542
+ "stop_dev_script",
14543
+ "Stop a running script for a thread (all scripts if name omitted)",
14544
+ {
14545
+ ref: import_zod.z.string(),
14546
+ name: import_zod.z.string().optional()
14547
+ },
14548
+ async ({ ref, name }) => {
14549
+ orch.stopDev(ref, name);
14550
+ return { content: [{ type: "text", text: "ok" }] };
14551
+ }
14552
+ );
14553
+ server.tool(
14554
+ "run_setup",
14555
+ "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
14556
+ { ref: import_zod.z.string() },
14557
+ async ({ ref }) => {
14558
+ const result = await orch.runSetup(ref);
14559
+ return {
14560
+ content: [{ type: "text", text: JSON.stringify(result) }]
14561
+ };
14562
+ }
14563
+ );
14564
+ server.tool(
14565
+ "add_workspace",
14566
+ "Register a git repo as a Sideboard workspace",
14567
+ { repoPath: import_zod.z.string() },
14568
+ async ({ repoPath }) => {
14569
+ const ws = await orch.addWorkspace(repoPath);
14570
+ return { content: [{ type: "text", text: JSON.stringify(ws) }] };
14571
+ }
14572
+ );
14573
+ server.tool(
14574
+ "remove_workspace",
14575
+ "Unregister a Sideboard workspace (does not archive threads)",
14576
+ { repoPath: import_zod.z.string() },
14577
+ async ({ repoPath }) => {
14578
+ orch.removeWorkspace(repoPath);
14579
+ return { content: [{ type: "text", text: "ok" }] };
14580
+ }
14581
+ );
14582
+ server.tool(
14583
+ "fanout",
14584
+ "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
14585
+ {
14586
+ prompt: import_zod.z.string(),
14587
+ agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
14588
+ repoPath: import_zod.z.string(),
14589
+ sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
14590
+ sourceRef: import_zod.z.string().optional(),
14591
+ title: import_zod.z.string().optional()
14592
+ },
14593
+ async (args) => {
14594
+ const threads = await orch.bestOfN(args);
14595
+ return {
14596
+ content: [
14597
+ {
14598
+ type: "text",
14599
+ text: JSON.stringify(
14600
+ threads.map((t) => ({
14601
+ id: t.id,
14602
+ agent: t.agent,
14603
+ branchName: t.branchName,
14604
+ worktreePath: t.worktreePath
14605
+ })),
14606
+ null,
14607
+ 2
14608
+ )
14609
+ }
14610
+ ]
14611
+ };
14612
+ }
14613
+ );
14614
+ server.tool(
14615
+ "list_branches",
14616
+ "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).",
14617
+ {
14618
+ repoPath: import_zod.z.string(),
14619
+ unmergedOnly: import_zod.z.boolean().optional()
14620
+ },
14621
+ async ({ repoPath, unmergedOnly }) => {
14622
+ const root = await resolveRepoRoot(repoPath);
14623
+ const branches = await listBranches(root, {
14624
+ unmergedOnly: unmergedOnly !== false
13957
14625
  });
13958
14626
  return {
13959
14627
  content: [
13960
14628
  {
13961
14629
  type: "text",
13962
- text: JSON.stringify({
13963
- id: thread.id,
13964
- title: thread.title,
13965
- status: thread.status,
13966
- agent: thread.agent,
13967
- model: thread.model,
13968
- branchName: thread.branchName,
13969
- worktreePath: thread.worktreePath,
13970
- fromThreadId: source?.id ?? ref,
13971
- link: `sideboard://thread/${thread.id}`
13972
- })
14630
+ text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
13973
14631
  }
13974
14632
  ]
13975
14633
  };
13976
- } catch (err) {
13977
- const message = err instanceof Error ? err.message : String(err);
13978
- return { content: [{ type: "text", text: message }], isError: true };
13979
14634
  }
13980
- }
13981
- );
13982
- server.tool(
13983
- "fork_chat",
13984
- "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.",
13985
- {
13986
- ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
13987
- through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
13988
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
13989
- model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
13990
- title: import_zod.z.string().optional()
13991
- },
13992
- async ({ ref, through_index, agent, model, title }) => {
13993
- try {
13994
- const source = orch.getThread(ref);
13995
- if (!source) {
13996
- return {
13997
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
13998
- isError: true
13999
- };
14000
- }
14001
- const tab = orch.forkChatTab({
14002
- threadId: source.id,
14003
- throughIndex: through_index,
14004
- agent,
14005
- model,
14006
- title
14635
+ );
14636
+ server.tool(
14637
+ "list_prs",
14638
+ "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.",
14639
+ { repoPath: import_zod.z.string() },
14640
+ async ({ repoPath }) => {
14641
+ const root = await resolveRepoRoot(repoPath);
14642
+ const prs = await listPrs(root);
14643
+ return {
14644
+ content: [
14645
+ {
14646
+ type: "text",
14647
+ text: prs.map(
14648
+ (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
14649
+ ).join("\n")
14650
+ }
14651
+ ]
14652
+ };
14653
+ }
14654
+ );
14655
+ server.tool(
14656
+ "get_pr_stack",
14657
+ "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.",
14658
+ { ref: import_zod.z.string() },
14659
+ async ({ ref }) => {
14660
+ const stack = await orch.getPrStack(ref);
14661
+ return {
14662
+ content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
14663
+ };
14664
+ }
14665
+ );
14666
+ server.tool(
14667
+ "open_pr_stack_layers",
14668
+ "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
14669
+ {
14670
+ ref: import_zod.z.string(),
14671
+ layer: import_zod.z.number().int().positive().optional()
14672
+ },
14673
+ async ({ ref, layer }) => {
14674
+ const result = await orch.openPrStackLayers(ref, { layer });
14675
+ return {
14676
+ content: [
14677
+ {
14678
+ type: "text",
14679
+ text: JSON.stringify(
14680
+ {
14681
+ stackNumber: result.stack.stackNumber,
14682
+ trunk: result.stack.trunk,
14683
+ threads: result.threads.map((t) => ({
14684
+ id: t.id,
14685
+ title: t.title,
14686
+ branchName: t.branchName,
14687
+ stackLayer: t.stackLayer,
14688
+ worktreePath: t.worktreePath,
14689
+ prUrl: t.prUrl,
14690
+ link: `sideboard://thread/${t.id}`
14691
+ }))
14692
+ },
14693
+ null,
14694
+ 2
14695
+ )
14696
+ }
14697
+ ]
14698
+ };
14699
+ }
14700
+ );
14701
+ server.tool(
14702
+ "add_stack_layer",
14703
+ "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
14704
+ {
14705
+ ref: import_zod.z.string(),
14706
+ branchName: import_zod.z.string(),
14707
+ title: import_zod.z.string().optional()
14708
+ },
14709
+ async ({ ref, branchName, title }) => {
14710
+ const result = await orch.addStackLayer(ref, branchName, { title });
14711
+ return {
14712
+ content: [
14713
+ {
14714
+ type: "text",
14715
+ text: JSON.stringify(
14716
+ {
14717
+ id: result.thread.id,
14718
+ title: result.thread.title,
14719
+ branchName: result.thread.branchName,
14720
+ stackLayer: result.thread.stackLayer,
14721
+ worktreePath: result.thread.worktreePath,
14722
+ link: `sideboard://thread/${result.thread.id}`
14723
+ },
14724
+ null,
14725
+ 2
14726
+ )
14727
+ }
14728
+ ]
14729
+ };
14730
+ }
14731
+ );
14732
+ server.tool(
14733
+ "create_pr_stack",
14734
+ "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
14735
+ {
14736
+ repoPath: import_zod.z.string(),
14737
+ branches: import_zod.z.array(import_zod.z.string()).min(1),
14738
+ agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
14739
+ base: import_zod.z.string().optional(),
14740
+ title: import_zod.z.string().optional()
14741
+ },
14742
+ async (args) => {
14743
+ const result = await orch.createPrStack({
14744
+ repoPath: args.repoPath,
14745
+ branches: args.branches,
14746
+ agent: args.agent,
14747
+ base: args.base,
14748
+ title: args.title
14007
14749
  });
14008
14750
  return {
14009
14751
  content: [
14010
14752
  {
14011
14753
  type: "text",
14012
- text: JSON.stringify({
14013
- id: tab.id,
14014
- title: tab.title,
14015
- status: tab.status,
14016
- agent: tab.agent,
14017
- model: tab.model,
14018
- sourceType: tab.sourceType,
14019
- worktreePath: tab.worktreePath,
14020
- fromThreadId: source.id,
14021
- link: `sideboard://thread/${tab.id}`
14022
- })
14754
+ text: JSON.stringify(
14755
+ {
14756
+ stackNumber: result.stack.stackNumber,
14757
+ trunk: result.stack.trunk,
14758
+ threads: result.threads.map((t) => ({
14759
+ id: t.id,
14760
+ title: t.title,
14761
+ branchName: t.branchName,
14762
+ stackLayer: t.stackLayer,
14763
+ worktreePath: t.worktreePath,
14764
+ link: `sideboard://thread/${t.id}`
14765
+ }))
14766
+ },
14767
+ null,
14768
+ 2
14769
+ )
14023
14770
  }
14024
14771
  ]
14025
14772
  };
14026
- } catch (err) {
14027
- const message = err instanceof Error ? err.message : String(err);
14028
- return { content: [{ type: "text", text: message }], isError: true };
14029
14773
  }
14030
- }
14031
- );
14032
- server.tool(
14033
- "run_dev_script",
14034
- "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
14035
- {
14036
- ref: import_zod.z.string(),
14037
- name: import_zod.z.string().optional()
14038
- },
14039
- async ({ ref, name }) => {
14040
- const result = await orch.startDev(ref, name);
14041
- return {
14042
- content: [
14043
- {
14044
- type: "text",
14045
- text: JSON.stringify({
14046
- port: result.port,
14047
- scriptName: result.scriptName,
14048
- ports: result.ports,
14049
- url: `http://localhost:${result.port}`
14050
- })
14051
- }
14052
- ]
14053
- };
14054
- }
14055
- );
14056
- server.tool(
14057
- "list_run_scripts",
14058
- "List named run scripts available for a thread",
14059
- { ref: import_zod.z.string() },
14060
- async ({ ref }) => {
14061
- const scripts = orch.listThreadRunScripts(ref);
14062
- const active = orch.getActiveRuns(ref);
14063
- return {
14064
- content: [
14065
- {
14066
- type: "text",
14067
- text: JSON.stringify({ scripts, active }, null, 2)
14068
- }
14069
- ]
14070
- };
14071
- }
14072
- );
14073
- server.tool(
14074
- "stop_dev_script",
14075
- "Stop a running script for a thread (all scripts if name omitted)",
14076
- {
14077
- ref: import_zod.z.string(),
14078
- name: import_zod.z.string().optional()
14079
- },
14080
- async ({ ref, name }) => {
14081
- orch.stopDev(ref, name);
14082
- return { content: [{ type: "text", text: "ok" }] };
14083
- }
14084
- );
14085
- server.tool(
14086
- "run_setup",
14087
- "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
14088
- { ref: import_zod.z.string() },
14089
- async ({ ref }) => {
14090
- const result = await orch.runSetup(ref);
14091
- return {
14092
- content: [{ type: "text", text: JSON.stringify(result) }]
14093
- };
14094
- }
14095
- );
14096
- server.tool(
14097
- "add_workspace",
14098
- "Register a git repo as a Sideboard workspace",
14099
- { repoPath: import_zod.z.string() },
14100
- async ({ repoPath }) => {
14101
- const ws = await orch.addWorkspace(repoPath);
14102
- return { content: [{ type: "text", text: JSON.stringify(ws) }] };
14103
- }
14104
- );
14105
- server.tool(
14106
- "remove_workspace",
14107
- "Unregister a Sideboard workspace (does not archive threads)",
14108
- { repoPath: import_zod.z.string() },
14109
- async ({ repoPath }) => {
14110
- orch.removeWorkspace(repoPath);
14111
- return { content: [{ type: "text", text: "ok" }] };
14112
- }
14113
- );
14114
- server.tool(
14115
- "fanout",
14116
- "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
14117
- {
14118
- prompt: import_zod.z.string(),
14119
- agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
14120
- repoPath: import_zod.z.string(),
14121
- sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
14122
- sourceRef: import_zod.z.string().optional(),
14123
- title: import_zod.z.string().optional()
14124
- },
14125
- async (args) => {
14126
- const threads = await orch.bestOfN(args);
14127
- return {
14128
- content: [
14129
- {
14130
- type: "text",
14131
- text: JSON.stringify(
14132
- threads.map((t) => ({
14133
- id: t.id,
14134
- agent: t.agent,
14135
- branchName: t.branchName,
14136
- worktreePath: t.worktreePath
14137
- })),
14138
- null,
14139
- 2
14140
- )
14141
- }
14142
- ]
14143
- };
14144
- }
14145
- );
14146
- server.tool(
14147
- "list_branches",
14148
- "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).",
14149
- {
14150
- repoPath: import_zod.z.string(),
14151
- unmergedOnly: import_zod.z.boolean().optional()
14152
- },
14153
- async ({ repoPath, unmergedOnly }) => {
14154
- const root = await resolveRepoRoot(repoPath);
14155
- const branches = await listBranches(root, {
14156
- unmergedOnly: unmergedOnly !== false
14157
- });
14158
- return {
14159
- content: [
14160
- {
14161
- type: "text",
14162
- text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
14163
- }
14164
- ]
14165
- };
14166
- }
14167
- );
14168
- server.tool(
14169
- "list_prs",
14170
- "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.",
14171
- { repoPath: import_zod.z.string() },
14172
- async ({ repoPath }) => {
14173
- const root = await resolveRepoRoot(repoPath);
14174
- const prs = await listPrs(root);
14175
- return {
14176
- content: [
14177
- {
14178
- type: "text",
14179
- text: prs.map(
14180
- (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
14181
- ).join("\n")
14182
- }
14183
- ]
14184
- };
14185
- }
14186
- );
14187
- server.tool(
14188
- "get_pr_stack",
14189
- "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.",
14190
- { ref: import_zod.z.string() },
14191
- async ({ ref }) => {
14192
- const stack = await orch.getPrStack(ref);
14193
- return {
14194
- content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
14195
- };
14196
- }
14197
- );
14198
- server.tool(
14199
- "open_pr_stack_layers",
14200
- "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
14201
- {
14202
- ref: import_zod.z.string(),
14203
- layer: import_zod.z.number().int().positive().optional()
14204
- },
14205
- async ({ ref, layer }) => {
14206
- const result = await orch.openPrStackLayers(ref, { layer });
14207
- return {
14208
- content: [
14209
- {
14210
- type: "text",
14211
- text: JSON.stringify(
14212
- {
14213
- stackNumber: result.stack.stackNumber,
14214
- trunk: result.stack.trunk,
14215
- threads: result.threads.map((t) => ({
14216
- id: t.id,
14217
- title: t.title,
14218
- branchName: t.branchName,
14219
- stackLayer: t.stackLayer,
14220
- worktreePath: t.worktreePath,
14221
- prUrl: t.prUrl,
14222
- link: `sideboard://thread/${t.id}`
14223
- }))
14224
- },
14225
- null,
14226
- 2
14227
- )
14228
- }
14229
- ]
14230
- };
14231
- }
14232
- );
14233
- server.tool(
14234
- "add_stack_layer",
14235
- "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
14236
- {
14237
- ref: import_zod.z.string(),
14238
- branchName: import_zod.z.string(),
14239
- title: import_zod.z.string().optional()
14240
- },
14241
- async ({ ref, branchName, title }) => {
14242
- const result = await orch.addStackLayer(ref, branchName, { title });
14243
- return {
14244
- content: [
14245
- {
14246
- type: "text",
14247
- text: JSON.stringify(
14248
- {
14249
- id: result.thread.id,
14250
- title: result.thread.title,
14251
- branchName: result.thread.branchName,
14252
- stackLayer: result.thread.stackLayer,
14253
- worktreePath: result.thread.worktreePath,
14254
- link: `sideboard://thread/${result.thread.id}`
14255
- },
14256
- null,
14257
- 2
14258
- )
14259
- }
14260
- ]
14261
- };
14262
- }
14263
- );
14264
- server.tool(
14265
- "create_pr_stack",
14266
- "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
14267
- {
14268
- repoPath: import_zod.z.string(),
14269
- branches: import_zod.z.array(import_zod.z.string()).min(1),
14270
- agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
14271
- base: import_zod.z.string().optional(),
14272
- title: import_zod.z.string().optional()
14273
- },
14274
- async (args) => {
14275
- const result = await orch.createPrStack({
14276
- repoPath: args.repoPath,
14277
- branches: args.branches,
14278
- agent: args.agent,
14279
- base: args.base,
14280
- title: args.title
14281
- });
14282
- return {
14283
- content: [
14284
- {
14285
- type: "text",
14286
- text: JSON.stringify(
14287
- {
14288
- stackNumber: result.stack.stackNumber,
14289
- trunk: result.stack.trunk,
14290
- threads: result.threads.map((t) => ({
14291
- id: t.id,
14292
- title: t.title,
14293
- branchName: t.branchName,
14294
- stackLayer: t.stackLayer,
14295
- worktreePath: t.worktreePath,
14296
- link: `sideboard://thread/${t.id}`
14297
- }))
14298
- },
14299
- null,
14300
- 2
14301
- )
14302
- }
14303
- ]
14304
- };
14305
- }
14306
- );
14307
- server.tool(
14308
- "list_issues",
14309
- "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.",
14310
- { repoPath: import_zod.z.string() },
14311
- async ({ repoPath }) => {
14312
- const root = await resolveRepoRoot(repoPath);
14313
- const result = await listIssues(root);
14314
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
14315
- }
14316
- );
14317
- server.tool(
14318
- "list_linear_issues",
14319
- "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
14320
- {
14321
- agent: import_zod.z.enum(["claude", "codex", "opencode"]),
14322
- repoPath: import_zod.z.string()
14323
- },
14324
- async ({ agent, repoPath }) => {
14325
- const issues = await listLinearIssues(agent, repoPath);
14326
- return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
14327
- }
14328
- );
14774
+ );
14775
+ server.tool(
14776
+ "list_issues",
14777
+ "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.",
14778
+ { repoPath: import_zod.z.string() },
14779
+ async ({ repoPath }) => {
14780
+ const root = await resolveRepoRoot(repoPath);
14781
+ const result = await listIssues(root);
14782
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
14783
+ }
14784
+ );
14785
+ server.tool(
14786
+ "list_linear_issues",
14787
+ "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
14788
+ {
14789
+ agent: import_zod.z.enum(["claude", "codex", "opencode"]),
14790
+ repoPath: import_zod.z.string()
14791
+ },
14792
+ async ({ agent, repoPath }) => {
14793
+ const issues = await listLinearIssues(agent, repoPath);
14794
+ return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
14795
+ }
14796
+ );
14797
+ }
14329
14798
  const transport = new import_stdio.StdioServerTransport();
14330
14799
  await server.connect(transport);
14331
14800
  }
14332
14801
 
14333
14802
  // src/index.ts
14803
+ init_profile();
14334
14804
  init_config();
14335
14805
 
14336
14806
  // src/brightsy/api.ts
@@ -14707,6 +15177,7 @@ init_injected_mcp();
14707
15177
  REVIEW_REQUEST_PREFILL,
14708
15178
  SIDEBOARD_FORCE_STOP,
14709
15179
  SIDEBOARD_MCP_ALLOWED_TOOLS,
15180
+ SIDEBOARD_MCP_PROFILE_ENV,
14710
15181
  THINKING_EFFORTS,
14711
15182
  addPrStackLayer,
14712
15183
  addStackLayerFromThread,
@@ -14729,6 +15200,7 @@ init_injected_mcp();
14729
15200
  attachmentsFromBuffers,
14730
15201
  attachmentsFromWorktreePaths,
14731
15202
  attachmentsGitignoreBody,
15203
+ autoArchiveOnMergeEnabled,
14732
15204
  autoCleanupOrphansEnabled,
14733
15205
  autoRenameBranchEnabled,
14734
15206
  autoRunAfterSetupEnabled,
@@ -14737,6 +15209,7 @@ init_injected_mcp();
14737
15209
  brightsyCloudConnectAgent,
14738
15210
  brightsyCloudConnectEnabled,
14739
15211
  brightsyConfigPath,
15212
+ brightsyInjectWorktreeMcpEnabled,
14740
15213
  brightsyMcpAllowedTools,
14741
15214
  brightsyMcpServerName,
14742
15215
  buildCachedUserContent,
@@ -14788,7 +15261,9 @@ init_injected_mcp();
14788
15261
  detectLocalMergeConflicts,
14789
15262
  disconnectBrightsyTeam,
14790
15263
  discoverSkills,
15264
+ dropCachedPrefixOnResume,
14791
15265
  encodeBrightsyTarget,
15266
+ enrichPathWithNpmGlobalBin,
14792
15267
  enrichWorkspacesWithGithub,
14793
15268
  ensureAgentPath,
14794
15269
  ensureCloudCoordinator,
@@ -14826,6 +15301,7 @@ init_injected_mcp();
14826
15301
  formatTranscriptMarkdown,
14827
15302
  formatWorkspaceInventory,
14828
15303
  formatWorktreeDirective,
15304
+ formatWorktreeReminder,
14829
15305
  getAdapter,
14830
15306
  getAgentSetupInfo,
14831
15307
  getBrightsySession,
@@ -14952,7 +15428,10 @@ init_injected_mcp();
14952
15428
  pastedTextStats,
14953
15429
  permissionMode,
14954
15430
  planFileAbs,
15431
+ posixShellSingleQuote,
15432
+ prepareTerminalCommand,
14955
15433
  previewLand,
15434
+ promptMentionsBrightsy,
14956
15435
  pushBranch,
14957
15436
  readExistingReviewRequestFile,
14958
15437
  readPlanFile,
@@ -14968,7 +15447,9 @@ init_injected_mcp();
14968
15447
  requestReview,
14969
15448
  requireAgent,
14970
15449
  resetGhStackDetectCache,
15450
+ resolveAgentExecutable,
14971
15451
  resolveClaudeExecutable,
15452
+ resolveCommandBinarySync,
14972
15453
  resolveConductorCursorAgentId,
14973
15454
  resolveCursorModelId,
14974
15455
  resolveDefaultBranch,
@@ -14977,6 +15458,7 @@ init_injected_mcp();
14977
15458
  resolveFilesToCopy,
14978
15459
  resolveGhAuthToken,
14979
15460
  resolveGithubRepoSlug,
15461
+ resolveLoginCommand,
14980
15462
  resolveNewThreadOptions,
14981
15463
  resolvePlanMarkdown,
14982
15464
  resolvePrSelector,
@@ -14998,9 +15480,11 @@ init_injected_mcp();
14998
15480
  settingsSourceLabel,
14999
15481
  shouldAttachPastedText,
15000
15482
  shouldCompactContext,
15483
+ shouldInjectBrightsyMcp,
15001
15484
  shouldRefreshReviewRequestTemplate,
15002
15485
  shouldRunWorktreeCleanup,
15003
15486
  sideboardHomeDir,
15487
+ sideboardMcpProfile,
15004
15488
  sideboardReposDir,
15005
15489
  sideboardWorkspacesDir,
15006
15490
  slugify,
@@ -15028,6 +15512,7 @@ init_injected_mcp();
15028
15512
  threadDisplayLabel,
15029
15513
  threadFilePath,
15030
15514
  threadLockPath,
15515
+ threadRequestsBrightsyMcp,
15031
15516
  threadsDir,
15032
15517
  threadsSharingWorktree,
15033
15518
  toolDescription,
@@ -15035,14 +15520,18 @@ init_injected_mcp();
15035
15520
  toolFilePath,
15036
15521
  totalTokens,
15037
15522
  updateAdvancedSettings,
15523
+ updateAgentExecutable,
15038
15524
  updateAppEnvironment,
15039
15525
  updateBrightsySettings,
15040
15526
  updateClaudeSettings,
15527
+ updateCodexSettings,
15041
15528
  updateDefaultsSettings,
15042
15529
  updateIntegrationsSettings,
15530
+ updateOpencodeSettings,
15043
15531
  updateThread,
15044
15532
  validateLinearApiKey,
15045
15533
  withAgentInstructions,
15534
+ withExportedPath,
15046
15535
  withThreadLock,
15047
15536
  workspaceSettingsSourceLabel,
15048
15537
  worktreeCleanupSettings,