@sideboard-ai/core 0.1.62 → 0.1.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/{agents-KF3J3C53.js → agents-OTBZVTZO.js} +27 -7
  2. package/dist/{agents-QLSFIQAO.js → agents-TS3EI2YI.js} +27 -7
  3. package/dist/{app-settings-LTSFG7QJ.js → app-settings-GJVZGU3M.js} +13 -3
  4. package/dist/{app-settings-HK4KAM4R.js → app-settings-ONKMFSEJ.js} +13 -3
  5. package/dist/{chunk-UGXSSBFJ.js → chunk-4BUCGNZS.js} +1 -1
  6. package/dist/chunk-4UIYN56C.js +173 -0
  7. package/dist/{chunk-NVU7EC2E.js → chunk-57WWNMFP.js} +2 -2
  8. package/dist/{chunk-A6FOKNOU.js → chunk-5HTTCYWA.js} +2 -2
  9. package/dist/{chunk-DXTGDXO2.js → chunk-7ZOQY4A7.js} +31 -6
  10. package/dist/{chunk-ZF5YJCXO.js → chunk-B4PRRKMA.js} +284 -93
  11. package/dist/{chunk-DDN3UF4R.js → chunk-BPOTVI5J.js} +115 -3
  12. package/dist/{chunk-MBAL5FE6.js → chunk-DB3I75FI.js} +4 -4
  13. package/dist/chunk-FGDJOJ2L.js +176 -0
  14. package/dist/{chunk-2FEA4LDA.js → chunk-JWZNK3CQ.js} +31 -6
  15. package/dist/{chunk-2MCNURQV.js → chunk-K7LM4CLD.js} +279 -93
  16. package/dist/{chunk-3KKNTHUC.js → chunk-LW5CMQGZ.js} +1 -1
  17. package/dist/{chunk-JB2H7E7V.js → chunk-M5V4QIWF.js} +1 -1
  18. package/dist/{chunk-6CUJQKTJ.js → chunk-MHR4OGZ4.js} +115 -3
  19. package/dist/{chunk-46KMKHSD.js → chunk-WAFIF22N.js} +2 -2
  20. package/dist/{chunk-27MAWNKY.js → chunk-ZRFAZVGL.js} +2 -2
  21. package/dist/{connected-teams-MKIYKOYA.js → connected-teams-OMDHTL6A.js} +2 -2
  22. package/dist/{connected-teams-RSL4VB3X.js → connected-teams-RUNMOCG5.js} +2 -2
  23. package/dist/{coordinator-prompt-N2R6FGZK.js → coordinator-prompt-3BSMKZL4.js} +4 -4
  24. package/dist/{coordinator-prompt-LKG3SHMI.js → coordinator-prompt-6HMTOX4G.js} +4 -4
  25. package/dist/{global-workspace-LALWQWRJ.js → global-workspace-QS7A23CB.js} +5 -6
  26. package/dist/{global-workspace-YOAUQCMA.js → global-workspace-YZ2NJVLL.js} +5 -6
  27. package/dist/index.cjs +1345 -933
  28. package/dist/index.d.cts +96 -1
  29. package/dist/index.d.ts +96 -1
  30. package/dist/index.js +750 -715
  31. package/dist/mcp/run-stdio.cjs +1293 -977
  32. package/dist/mcp/run-stdio.js +691 -748
  33. package/dist/{run-GPSVV5OH.js → run-JLD2Y52J.js} +1 -1
  34. package/dist/{run-HZTBWGQR.js → run-Y2MI3EVV.js} +1 -1
  35. package/dist/{workspaces-E75POJXP.js → workspaces-AIUYUXB7.js} +6 -7
  36. package/dist/{workspaces-VCAIHVIE.js → workspaces-EBFW7I7S.js} +6 -7
  37. package/dist/{worktree-MV4WRW6G.js → worktree-7NBPIVFZ.js} +2 -2
  38. package/dist/{worktree-LZEGVQSN.js → worktree-WMDJW2BL.js} +2 -2
  39. package/package.json +1 -1
  40. package/dist/chunk-73AT5K4A.js +0 -104
  41. package/dist/chunk-GI7E5733.js +0 -30
  42. package/dist/chunk-RIWUU7FO.js +0 -107
  43. package/dist/chunk-S2LL5HA4.js +0 -33
package/dist/index.cjs CHANGED
@@ -397,6 +397,7 @@ __export(app_settings_exports, {
397
397
  autoRunAfterSetupEnabled: () => autoRunAfterSetupEnabled,
398
398
  brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
399
399
  brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
400
+ brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
400
401
  caffeinateWhileCloudConnectEnabled: () => caffeinateWhileCloudConnectEnabled,
401
402
  caffeinateWhileRunningEnabled: () => caffeinateWhileRunningEnabled,
402
403
  childEnvWithAppSettings: () => childEnvWithAppSettings,
@@ -415,17 +416,21 @@ __export(app_settings_exports, {
415
416
  maxConcurrentAgents: () => maxConcurrentAgents,
416
417
  orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
417
418
  orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
419
+ resolveAgentExecutable: () => resolveAgentExecutable,
418
420
  resolveClaudeExecutable: () => resolveClaudeExecutable,
419
421
  resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
420
422
  resolveNewThreadOptions: () => resolveNewThreadOptions,
421
423
  resolveThreadDefaults: () => resolveThreadDefaults,
422
424
  saveAppSettings: () => saveAppSettings,
423
425
  updateAdvancedSettings: () => updateAdvancedSettings,
426
+ updateAgentExecutable: () => updateAgentExecutable,
424
427
  updateAppEnvironment: () => updateAppEnvironment,
425
428
  updateBrightsySettings: () => updateBrightsySettings,
426
429
  updateClaudeSettings: () => updateClaudeSettings,
430
+ updateCodexSettings: () => updateCodexSettings,
427
431
  updateDefaultsSettings: () => updateDefaultsSettings,
428
- updateIntegrationsSettings: () => updateIntegrationsSettings
432
+ updateIntegrationsSettings: () => updateIntegrationsSettings,
433
+ updateOpencodeSettings: () => updateOpencodeSettings
429
434
  });
430
435
  function appSettingsPath() {
431
436
  return (0, import_node_path4.join)(appDataDir(), "settings.json");
@@ -446,16 +451,33 @@ function normalizeClaude(raw) {
446
451
  }
447
452
  return out;
448
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
+ }
449
464
  function normalizeBrightsy(raw) {
450
465
  if (!raw || typeof raw !== "object") return {};
451
466
  const source = raw;
452
467
  const out = {};
468
+ if (typeof source.executablePath === "string") {
469
+ const path = source.executablePath.trim();
470
+ if (path) out.executablePath = path;
471
+ }
453
472
  if (typeof source.cloudConnectEnabled === "boolean") {
454
473
  out.cloudConnectEnabled = source.cloudConnectEnabled;
455
474
  }
456
475
  if (typeof source.cloudConnectAgent === "string" && CLOUD_CONNECT_AGENTS.has(source.cloudConnectAgent)) {
457
476
  out.cloudConnectAgent = source.cloudConnectAgent;
458
477
  }
478
+ if (typeof source.injectWorktreeMcp === "boolean") {
479
+ out.injectWorktreeMcp = source.injectWorktreeMcp;
480
+ }
459
481
  return out;
460
482
  }
461
483
  function normalizeIntegrations(raw) {
@@ -541,6 +563,8 @@ function normalizeAdvanced(raw) {
541
563
  function normalizeSettings(raw) {
542
564
  const env = {};
543
565
  let claude = {};
566
+ let codex = {};
567
+ let opencode = {};
544
568
  let brightsy = {};
545
569
  let integrations = {};
546
570
  let defaults = {};
@@ -559,6 +583,12 @@ function normalizeSettings(raw) {
559
583
  if ("claude" in raw) {
560
584
  claude = normalizeClaude(raw.claude);
561
585
  }
586
+ if ("codex" in raw) {
587
+ codex = normalizeCliExecutable(raw.codex);
588
+ }
589
+ if ("opencode" in raw) {
590
+ opencode = normalizeCliExecutable(raw.opencode);
591
+ }
562
592
  if ("brightsy" in raw) {
563
593
  brightsy = normalizeBrightsy(raw.brightsy);
564
594
  }
@@ -574,7 +604,16 @@ function normalizeSettings(raw) {
574
604
  advanced = normalizeAdvanced(raw.advanced);
575
605
  }
576
606
  }
577
- 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
+ };
578
617
  }
579
618
  function loadAppSettings() {
580
619
  const path = appSettingsPath();
@@ -627,6 +666,13 @@ function updateClaudeSettings(patch) {
627
666
  function updateBrightsySettings(patch) {
628
667
  const current = loadAppSettings();
629
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
+ }
630
676
  if (typeof patch.cloudConnectEnabled === "boolean") {
631
677
  brightsy.cloudConnectEnabled = patch.cloudConnectEnabled;
632
678
  }
@@ -637,8 +683,35 @@ function updateBrightsySettings(patch) {
637
683
  brightsy.cloudConnectAgent = patch.cloudConnectAgent;
638
684
  }
639
685
  }
686
+ if (typeof patch.injectWorktreeMcp === "boolean") {
687
+ brightsy.injectWorktreeMcp = patch.injectWorktreeMcp;
688
+ }
640
689
  return saveAppSettings({ ...current, brightsy });
641
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
+ }
642
715
  function updateIntegrationsSettings(patch) {
643
716
  const current = loadAppSettings();
644
717
  const integrations = { ...current.integrations };
@@ -746,6 +819,9 @@ function getLinearApiKey(settings = loadAppSettings()) {
746
819
  function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
747
820
  return Boolean(settings.brightsy.cloudConnectEnabled);
748
821
  }
822
+ function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
823
+ return Boolean(settings.brightsy.injectWorktreeMcp);
824
+ }
749
825
  function brightsyCloudConnectAgent(settings = loadAppSettings()) {
750
826
  const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
751
827
  const preferred = getDefaultAgent(settings);
@@ -840,8 +916,36 @@ function maxConcurrentAgents(settings = loadAppSettings()) {
840
916
  return 3;
841
917
  }
842
918
  function resolveClaudeExecutable(settings = loadAppSettings()) {
843
- const override = settings.claude.executablePath?.trim();
844
- 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 });
845
949
  }
846
950
  function claudeChromeEnabled(settings = loadAppSettings()) {
847
951
  return Boolean(settings.claude.chromeEnabled);
@@ -869,7 +973,7 @@ function childEnvWithAppSettings(extra) {
869
973
  function harnessEnvKey(harness) {
870
974
  return HARNESS_ENV_KEYS[harness];
871
975
  }
872
- 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;
873
977
  var init_app_settings = __esm({
874
978
  "src/store/app-settings.ts"() {
875
979
  "use strict";
@@ -902,11 +1006,19 @@ var init_app_settings = __esm({
902
1006
  EMPTY_SETTINGS = {
903
1007
  environment: {},
904
1008
  claude: {},
1009
+ codex: {},
1010
+ opencode: {},
905
1011
  brightsy: {},
906
1012
  integrations: {},
907
1013
  defaults: {},
908
1014
  advanced: {}
909
1015
  };
1016
+ DEFAULT_CLI_BIN = {
1017
+ claude: "claude",
1018
+ codex: "codex",
1019
+ opencode: "opencode",
1020
+ brightsy: "brightsy"
1021
+ };
910
1022
  }
911
1023
  });
912
1024
 
@@ -2041,18 +2153,37 @@ var init_gh_errors = __esm({
2041
2153
  });
2042
2154
 
2043
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
+ }
2166
+ function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os4.homedir)()) {
2167
+ return (0, import_node_path5.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
2168
+ }
2169
+ function isConductorBundledCli(filePath) {
2170
+ const p = (filePath ?? "").replace(/\\/g, "/");
2171
+ return p.includes("/com.conductor.app/bin/") || p.endsWith("/com.conductor.app/bin");
2172
+ }
2044
2173
  function ensureAgentPath(env = process.env) {
2045
2174
  const home = env.HOME || env.USERPROFILE || (0, import_node_os4.homedir)();
2046
2175
  const current = env.PATH ?? "";
2047
2176
  const parts = current.split(import_node_path5.delimiter).filter(Boolean);
2048
2177
  const seen = new Set(parts);
2049
2178
  const extras = [
2050
- ...EXTRA_BIN_DIRS.map((rel) => `${home}/${rel}`),
2179
+ ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path5.join)(home, rel)),
2051
2180
  "/opt/homebrew/bin",
2052
- "/usr/local/bin"
2181
+ "/usr/local/bin",
2182
+ // Keep after Homebrew/npm so a user-installed CLI still wins.
2183
+ conductorBundledBinDir(home)
2053
2184
  ];
2054
2185
  for (const dir of extras.reverse()) {
2055
- if (!dir || seen.has(dir)) continue;
2186
+ if (!dir || seen.has(dir) || !(0, import_node_fs5.existsSync)(dir)) continue;
2056
2187
  parts.unshift(dir);
2057
2188
  seen.add(dir);
2058
2189
  }
@@ -2060,10 +2191,50 @@ function ensureAgentPath(env = process.env) {
2060
2191
  env.PATH = next;
2061
2192
  return next;
2062
2193
  }
2063
- var import_node_os4, import_node_path5, EXTRA_BIN_DIRS;
2194
+ function enrichPathWithNpmGlobalBin(env = process.env) {
2195
+ ensureAgentPath(env);
2196
+ try {
2197
+ const prefix = (0, import_node_child_process.execFileSync)("npm", ["prefix", "-g"], {
2198
+ encoding: "utf8",
2199
+ env: { ...process.env, ...env },
2200
+ timeout: 8e3,
2201
+ stdio: ["ignore", "pipe", "ignore"]
2202
+ }).trim().split(/\r?\n/).find(Boolean);
2203
+ if (prefix) {
2204
+ const binDir = process.platform === "win32" ? prefix : (0, import_node_path5.join)(prefix, "bin");
2205
+ prependPathDir(env, binDir);
2206
+ }
2207
+ } catch {
2208
+ }
2209
+ return env.PATH ?? "";
2210
+ }
2211
+ function posixShellSingleQuote(value) {
2212
+ return `'${value.replace(/'/g, `'\\''`)}'`;
2213
+ }
2214
+ function resolveCommandBinarySync(command, whichPath) {
2215
+ const trimmed = command.trim();
2216
+ if (!trimmed) return trimmed;
2217
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
2218
+ if (!match) return trimmed;
2219
+ const bin = match[1];
2220
+ const rest = match[2] ?? "";
2221
+ if (bin.includes("/") || bin.includes("\\")) return trimmed;
2222
+ const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
2223
+ if (!abs) return trimmed;
2224
+ return `${abs}${rest}`;
2225
+ }
2226
+ function withExportedPath(command, pathValue) {
2227
+ const trimmed = command.trim();
2228
+ if (!trimmed) return trimmed;
2229
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
2230
+ return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
2231
+ }
2232
+ var import_node_fs5, import_node_child_process, import_node_os4, import_node_path5, EXTRA_BIN_DIRS;
2064
2233
  var init_path = __esm({
2065
2234
  "src/agents/path.ts"() {
2066
2235
  "use strict";
2236
+ import_node_fs5 = require("fs");
2237
+ import_node_child_process = require("child_process");
2067
2238
  import_node_os4 = require("os");
2068
2239
  import_node_path5 = require("path");
2069
2240
  EXTRA_BIN_DIRS = [
@@ -2071,6 +2242,10 @@ var init_path = __esm({
2071
2242
  ".cargo/bin",
2072
2243
  ".nvm/current/bin",
2073
2244
  ".asdf/shims",
2245
+ ".volta/bin",
2246
+ ".npm-global/bin",
2247
+ // fnm default alias (common when shell init is skipped)
2248
+ ".local/share/fnm/aliases/default/bin",
2074
2249
  // pnpm / npm global bins (where `@brightsy/cli` typically lands)
2075
2250
  "Library/pnpm",
2076
2251
  ".pnpm"
@@ -3158,7 +3333,7 @@ function isLocalPrFetchBranch(ref) {
3158
3333
  async function createThreadWorktree(opts) {
3159
3334
  let branchName = `thread/${opts.slug}`;
3160
3335
  const worktreePath = (0, import_node_path6.join)(worktreesRoot(opts.repoPath), opts.slug);
3161
- if ((0, import_node_fs5.existsSync)(worktreePath)) {
3336
+ if ((0, import_node_fs6.existsSync)(worktreePath)) {
3162
3337
  throw new Error(`Worktree already exists at ${worktreePath}`);
3163
3338
  }
3164
3339
  await ensureGhPreferOrigin(opts.repoPath);
@@ -3228,7 +3403,7 @@ async function createExistingBranchWorktree(opts) {
3228
3403
  const branchName = opts.branchName.trim();
3229
3404
  if (!branchName) throw new Error("branch name required");
3230
3405
  const worktreePath = (0, import_node_path6.join)(worktreesRoot(opts.repoPath), opts.slug);
3231
- if ((0, import_node_fs5.existsSync)(worktreePath)) {
3406
+ if ((0, import_node_fs6.existsSync)(worktreePath)) {
3232
3407
  throw new Error(`Worktree already exists at ${worktreePath}`);
3233
3408
  }
3234
3409
  await ensureGhPreferOrigin(opts.repoPath);
@@ -3489,9 +3664,9 @@ function sameRepoPath(a, b) {
3489
3664
  }
3490
3665
  function listLocalThreadBranchSlugs(repoPath) {
3491
3666
  const refsDir = (0, import_node_path6.join)(repoPath, ".git", "refs", "heads", "thread");
3492
- if (!(0, import_node_fs5.existsSync)(refsDir)) return [];
3667
+ if (!(0, import_node_fs6.existsSync)(refsDir)) return [];
3493
3668
  try {
3494
- return (0, import_node_fs5.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
3669
+ return (0, import_node_fs6.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
3495
3670
  } catch {
3496
3671
  return [];
3497
3672
  }
@@ -3499,8 +3674,8 @@ function listLocalThreadBranchSlugs(repoPath) {
3499
3674
  function collectTakenTeamSlugs(repoPath) {
3500
3675
  const taken = /* @__PURE__ */ new Set();
3501
3676
  const root = worktreesRoot(repoPath);
3502
- if ((0, import_node_fs5.existsSync)(root)) {
3503
- for (const entry of (0, import_node_fs5.readdirSync)(root, { withFileTypes: true })) {
3677
+ if ((0, import_node_fs6.existsSync)(root)) {
3678
+ for (const entry of (0, import_node_fs6.readdirSync)(root, { withFileTypes: true })) {
3504
3679
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
3505
3680
  taken.add(normalizeTakenSlug(entry.name));
3506
3681
  }
@@ -3522,16 +3697,16 @@ function allocateTeamSlug(repoPath) {
3522
3697
  for (let attempt = 0; attempt < 32; attempt++) {
3523
3698
  const team = allocateTeamName(taken);
3524
3699
  const path = (0, import_node_path6.join)(worktreesRoot(repoPath), team.slug);
3525
- if (!(0, import_node_fs5.existsSync)(path)) return team;
3700
+ if (!(0, import_node_fs6.existsSync)(path)) return team;
3526
3701
  taken.add(team.slug);
3527
3702
  }
3528
3703
  throw new Error("No available soccer team worktree directories left");
3529
3704
  }
3530
- var import_node_fs5, import_node_path6;
3705
+ var import_node_fs6, import_node_path6;
3531
3706
  var init_worktree = __esm({
3532
3707
  "src/git/worktree.ts"() {
3533
3708
  "use strict";
3534
- import_node_fs5 = require("fs");
3709
+ import_node_fs6 = require("fs");
3535
3710
  import_node_path6 = require("path");
3536
3711
  init_paths();
3537
3712
  init_thread_store();
@@ -3611,7 +3786,7 @@ function coordinatorTurnReminder(opts) {
3611
3786
  function ensureGlobalCoordinatorCwd(opts) {
3612
3787
  const dir = globalAgentCwd();
3613
3788
  try {
3614
- (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
3789
+ (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
3615
3790
  } catch {
3616
3791
  return dir;
3617
3792
  }
@@ -3619,7 +3794,7 @@ function ensureGlobalCoordinatorCwd(opts) {
3619
3794
  let orchId = opts?.orchestratorThreadId?.trim() || "";
3620
3795
  if (!orchId) {
3621
3796
  try {
3622
- const existing = (0, import_node_fs6.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
3797
+ const existing = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), "utf8");
3623
3798
  const m = existing.match(
3624
3799
  /YOUR orchestration thread id is `([0-9a-f-]{36})`/i
3625
3800
  );
@@ -3661,9 +3836,9 @@ function ensureGlobalCoordinatorCwd(opts) {
3661
3836
  "Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
3662
3837
  ].join("\n");
3663
3838
  try {
3664
- (0, import_node_fs6.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
3839
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "CLAUDE.md"), `${body}
3665
3840
  `, "utf8");
3666
- (0, import_node_fs6.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
3841
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(dir, "AGENTS.md"), `${body}
3667
3842
  `, "utf8");
3668
3843
  } catch {
3669
3844
  }
@@ -3696,11 +3871,11 @@ function coordinatorSystemPrompt(opts) {
3696
3871
  formatWorkspaceInventory(opts.workspaces)
3697
3872
  ].join("\n");
3698
3873
  }
3699
- var import_node_fs6, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
3874
+ var import_node_fs7, import_node_path7, COORDINATOR_TOOL_PLAYBOOK;
3700
3875
  var init_coordinator_prompt = __esm({
3701
3876
  "src/orchestrator/coordinator-prompt.ts"() {
3702
3877
  "use strict";
3703
- import_node_fs6 = require("fs");
3878
+ import_node_fs7 = require("fs");
3704
3879
  import_node_path7 = require("path");
3705
3880
  init_worktree();
3706
3881
  init_app_settings();
@@ -3937,31 +4112,31 @@ function removedWorkspacesFile() {
3937
4112
  }
3938
4113
  function readAll() {
3939
4114
  const path = workspacesFile();
3940
- if (!(0, import_node_fs7.existsSync)(path)) return [];
4115
+ if (!(0, import_node_fs8.existsSync)(path)) return [];
3941
4116
  try {
3942
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
4117
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
3943
4118
  return Array.isArray(raw) ? raw : [];
3944
4119
  } catch {
3945
4120
  return [];
3946
4121
  }
3947
4122
  }
3948
4123
  function writeAll(list) {
3949
- (0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
3950
- (0, import_node_fs7.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
4124
+ (0, import_node_fs8.mkdirSync)(appDataDir(), { recursive: true });
4125
+ (0, import_node_fs8.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
3951
4126
  }
3952
4127
  function readRemoved() {
3953
4128
  const path = removedWorkspacesFile();
3954
- if (!(0, import_node_fs7.existsSync)(path)) return /* @__PURE__ */ new Set();
4129
+ if (!(0, import_node_fs8.existsSync)(path)) return /* @__PURE__ */ new Set();
3955
4130
  try {
3956
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
4131
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
3957
4132
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
3958
4133
  } catch {
3959
4134
  return /* @__PURE__ */ new Set();
3960
4135
  }
3961
4136
  }
3962
4137
  function writeRemoved(paths) {
3963
- (0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
3964
- (0, import_node_fs7.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
4138
+ (0, import_node_fs8.mkdirSync)(appDataDir(), { recursive: true });
4139
+ (0, import_node_fs8.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
3965
4140
  }
3966
4141
  function rememberRemoved(repoPath) {
3967
4142
  const next = readRemoved();
@@ -3984,7 +4159,7 @@ function listWorkspaces() {
3984
4159
  async function addWorkspace(repoPath) {
3985
4160
  const root = await resolveRepoRoot(repoPath);
3986
4161
  if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
3987
- if (!(0, import_node_fs7.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
4162
+ if (!(0, import_node_fs8.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
3988
4163
  forgetRemoved(root);
3989
4164
  await ensureGhPreferOrigin(root);
3990
4165
  const current = readAll();
@@ -4014,7 +4189,7 @@ function syncWorkspacesFromThreads(repoPaths) {
4014
4189
  if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
4015
4190
  continue;
4016
4191
  }
4017
- if (!(0, import_node_fs7.existsSync)(path)) continue;
4192
+ if (!(0, import_node_fs8.existsSync)(path)) continue;
4018
4193
  const ws = {
4019
4194
  path,
4020
4195
  name: (0, import_node_path8.basename)(path),
@@ -4027,11 +4202,11 @@ function syncWorkspacesFromThreads(repoPaths) {
4027
4202
  if (dirty) writeAll(next);
4028
4203
  return next.sort((a, b) => a.name.localeCompare(b.name));
4029
4204
  }
4030
- var import_node_fs7, import_node_path8;
4205
+ var import_node_fs8, import_node_path8;
4031
4206
  var init_workspaces = __esm({
4032
4207
  "src/store/workspaces.ts"() {
4033
4208
  "use strict";
4034
- import_node_fs7 = require("fs");
4209
+ import_node_fs8 = require("fs");
4035
4210
  import_node_path8 = require("path");
4036
4211
  init_paths();
4037
4212
  init_global_workspace();
@@ -4045,26 +4220,26 @@ function brightsyConfigPath() {
4045
4220
  }
4046
4221
  function loadBrightsyConfig() {
4047
4222
  const path = brightsyConfigPath();
4048
- if (!(0, import_node_fs8.existsSync)(path)) {
4223
+ if (!(0, import_node_fs9.existsSync)(path)) {
4049
4224
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
4050
4225
  }
4051
- const raw = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
4226
+ const raw = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
4052
4227
  if (!raw.access_token || !raw.account_id) {
4053
4228
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
4054
4229
  }
4055
4230
  return raw;
4056
4231
  }
4057
4232
  function saveBrightsyConfig(cfg) {
4058
- (0, import_node_fs8.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
4233
+ (0, import_node_fs9.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
4059
4234
  `, {
4060
4235
  mode: 384
4061
4236
  });
4062
4237
  }
4063
- var import_node_fs8, import_node_os5, import_node_path9;
4238
+ var import_node_fs9, import_node_os5, import_node_path9;
4064
4239
  var init_config = __esm({
4065
4240
  "src/brightsy/config.ts"() {
4066
4241
  "use strict";
4067
- import_node_fs8 = require("fs");
4242
+ import_node_fs9 = require("fs");
4068
4243
  import_node_os5 = require("os");
4069
4244
  import_node_path9 = require("path");
4070
4245
  }
@@ -4173,18 +4348,18 @@ function storePath() {
4173
4348
  }
4174
4349
  function readStore() {
4175
4350
  const path = storePath();
4176
- if (!(0, import_node_fs9.existsSync)(path)) return [];
4351
+ if (!(0, import_node_fs10.existsSync)(path)) return [];
4177
4352
  try {
4178
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
4353
+ const parsed = JSON.parse((0, import_node_fs10.readFileSync)(path, "utf8"));
4179
4354
  return Array.isArray(parsed.teams) ? parsed.teams : [];
4180
4355
  } catch {
4181
4356
  return [];
4182
4357
  }
4183
4358
  }
4184
4359
  function writeStore(teams) {
4185
- (0, import_node_fs9.mkdirSync)(appDataDir(), { recursive: true });
4360
+ (0, import_node_fs10.mkdirSync)(appDataDir(), { recursive: true });
4186
4361
  const path = storePath();
4187
- (0, import_node_fs9.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
4362
+ (0, import_node_fs10.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
4188
4363
  `, {
4189
4364
  mode: 384
4190
4365
  });
@@ -4346,11 +4521,11 @@ function brightsyMcpServerName(slug) {
4346
4521
  const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
4347
4522
  return `brightsy_${cleaned || "team"}`;
4348
4523
  }
4349
- var import_node_fs9, import_node_path10;
4524
+ var import_node_fs10, import_node_path10;
4350
4525
  var init_connected_teams = __esm({
4351
4526
  "src/brightsy/connected-teams.ts"() {
4352
4527
  "use strict";
4353
- import_node_fs9 = require("fs");
4528
+ import_node_fs10 = require("fs");
4354
4529
  import_node_path10 = require("path");
4355
4530
  init_paths();
4356
4531
  init_accounts();
@@ -4525,6 +4700,11 @@ function normalizeTurnInput(input) {
4525
4700
  if (typeof input === "string") return { prompt: input };
4526
4701
  return { prompt: input.prompt, cachedPrefix: input.cachedPrefix?.trim() || void 0 };
4527
4702
  }
4703
+ function dropCachedPrefixOnResume(input, sessionId) {
4704
+ const turn = normalizeTurnInput(input);
4705
+ if (sessionId) return { prompt: turn.prompt };
4706
+ return turn;
4707
+ }
4528
4708
  function flattenTurnInput(input) {
4529
4709
  const { cachedPrefix, prompt } = normalizeTurnInput(input);
4530
4710
  if (!cachedPrefix) return prompt;
@@ -4713,9 +4893,13 @@ async function fetchTeamChatTargets(team) {
4713
4893
  };
4714
4894
  }
4715
4895
  async function listBrightsyChatTargetsViaCli() {
4716
- const listed = await run("brightsy", ["chat", "--list-targets", "--json"], {
4717
- reject: false
4718
- });
4896
+ const listed = await run(
4897
+ resolveAgentExecutable("brightsy"),
4898
+ ["chat", "--list-targets", "--json"],
4899
+ {
4900
+ reject: false
4901
+ }
4902
+ );
4719
4903
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
4720
4904
  throw new Error(
4721
4905
  listed.stderr.trim() || "Failed to list Brightsy chat targets \u2014 is `brightsy` installed and logged in?"
@@ -4799,13 +4983,15 @@ async function syncCliForTarget(accountId) {
4799
4983
  }
4800
4984
  applyConnectedTeamToCli(team);
4801
4985
  }
4802
- var brightsyAdapter;
4986
+ var import_node_fs11, brightsyAdapter;
4803
4987
  var init_brightsy = __esm({
4804
4988
  "src/agents/brightsy.ts"() {
4805
4989
  "use strict";
4990
+ import_node_fs11 = require("fs");
4806
4991
  init_run();
4807
4992
  init_connected_teams();
4808
4993
  init_config();
4994
+ init_app_settings();
4809
4995
  init_brightsy_targets();
4810
4996
  init_error_detail();
4811
4997
  init_turn_input();
@@ -4813,18 +4999,32 @@ var init_brightsy = __esm({
4813
4999
  brightsyAdapter = {
4814
5000
  kind: "brightsy",
4815
5001
  async detect() {
4816
- const which = await run("which", ["brightsy"], { reject: false });
4817
- if (which.exitCode !== 0) {
4818
- return {
4819
- agent: "brightsy",
4820
- installed: false,
4821
- authenticated: false,
4822
- linearMcp: false,
4823
- warnings: [],
4824
- reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
4825
- };
5002
+ const brightsy = resolveAgentExecutable("brightsy");
5003
+ if (brightsy !== "brightsy") {
5004
+ if (!(0, import_node_fs11.existsSync)(brightsy)) {
5005
+ return {
5006
+ agent: "brightsy",
5007
+ installed: false,
5008
+ authenticated: false,
5009
+ linearMcp: false,
5010
+ warnings: [],
5011
+ reason: `Brightsy executable not found: ${brightsy}`
5012
+ };
5013
+ }
5014
+ } else {
5015
+ const which = await run("which", ["brightsy"], { reject: false });
5016
+ if (which.exitCode !== 0) {
5017
+ return {
5018
+ agent: "brightsy",
5019
+ installed: false,
5020
+ authenticated: false,
5021
+ linearMcp: false,
5022
+ warnings: [],
5023
+ reason: "brightsy CLI not found on PATH \u2014 npm i -g @brightsy/cli"
5024
+ };
5025
+ }
4826
5026
  }
4827
- const who = await run("brightsy", ["whoami"], { reject: false });
5027
+ const who = await run(brightsy, ["whoami"], { reject: false });
4828
5028
  const authenticated = who.exitCode === 0 && /logged in as/i.test(who.stdout);
4829
5029
  return {
4830
5030
  agent: "brightsy",
@@ -4849,7 +5049,7 @@ var init_brightsy = __esm({
4849
5049
  target.id
4850
5050
  ];
4851
5051
  return {
4852
- file: "brightsy",
5052
+ file: resolveAgentExecutable("brightsy"),
4853
5053
  args,
4854
5054
  cwd: thread.worktreePath,
4855
5055
  // Piped stdin becomes the message body (avoids ARG_MAX for long seeds).
@@ -4866,7 +5066,11 @@ var init_brightsy = __esm({
4866
5066
  const target = decodeBrightsyTarget(thread.model);
4867
5067
  await syncCliForTarget(target.accountId);
4868
5068
  const args = target.type === "model" ? ["chat", "--model", target.id] : ["chat", "--agent", target.id];
4869
- return { file: "brightsy", args, cwd: thread.worktreePath };
5069
+ return {
5070
+ file: resolveAgentExecutable("brightsy"),
5071
+ args,
5072
+ cwd: thread.worktreePath
5073
+ };
4870
5074
  }
4871
5075
  };
4872
5076
  }
@@ -4918,6 +5122,18 @@ var init_claude_mcp = __esm({
4918
5122
  }
4919
5123
  });
4920
5124
 
5125
+ // src/mcp/profile.ts
5126
+ function sideboardMcpProfile(env = process.env) {
5127
+ return env[SIDEBOARD_MCP_PROFILE_ENV]?.trim().toLowerCase() === "worktree" ? "worktree" : "orchestration";
5128
+ }
5129
+ var SIDEBOARD_MCP_PROFILE_ENV;
5130
+ var init_profile = __esm({
5131
+ "src/mcp/profile.ts"() {
5132
+ "use strict";
5133
+ SIDEBOARD_MCP_PROFILE_ENV = "SIDEBOARD_MCP_PROFILE";
5134
+ }
5135
+ });
5136
+
4921
5137
  // src/agents/node-launch.ts
4922
5138
  function isAsarPath(filePath) {
4923
5139
  return /\.asar([/\\]|$)/.test(filePath);
@@ -4965,6 +5181,38 @@ function isBrightsyConnected() {
4965
5181
  return false;
4966
5182
  }
4967
5183
  }
5184
+ function promptMentionsBrightsy(text) {
5185
+ return BRIGHTSY_WORD.test(text ?? "");
5186
+ }
5187
+ function isBrightsyMcpToolName(name) {
5188
+ const n = name.toLowerCase();
5189
+ return n === "brightsy" || n.startsWith("mcp__brightsy") || n.startsWith("brightsy_");
5190
+ }
5191
+ function toolInputUsesBrightsyDatasource(input) {
5192
+ const ds = input?.datasource;
5193
+ return typeof ds === "string" && ds.toLowerCase() === "brightsy";
5194
+ }
5195
+ function messageUsedBrightsyMcp(msg) {
5196
+ if (msg.role === "user" && promptMentionsBrightsy(msg.text)) return true;
5197
+ for (const part of msg.parts ?? []) {
5198
+ if (part.type !== "tool") continue;
5199
+ if (isBrightsyMcpToolName(part.name)) return true;
5200
+ if (/present_(schema|files)$/i.test(part.name) && toolInputUsesBrightsyDatasource(part.input)) {
5201
+ return true;
5202
+ }
5203
+ }
5204
+ return false;
5205
+ }
5206
+ function threadRequestsBrightsyMcp(thread) {
5207
+ return (thread?.messages ?? []).some(messageUsedBrightsyMcp);
5208
+ }
5209
+ function shouldInjectBrightsyMcp(thread, opts) {
5210
+ if (!(opts?.connected ?? isBrightsyConnected())) return false;
5211
+ if (opts?.orchestrator) return true;
5212
+ const alwaysOn = opts?.alwaysOnWorktree ?? Boolean(loadAppSettings().brightsy?.injectWorktreeMcp);
5213
+ if (alwaysOn) return true;
5214
+ return threadRequestsBrightsyMcp(thread);
5215
+ }
4968
5216
  function mcpLaunch(cmd, name, env) {
4969
5217
  if (cmd === "brightsy-mcp") {
4970
5218
  return { name, command: "brightsy-mcp", ...env ? { env } : {} };
@@ -5010,7 +5258,7 @@ function corePackageDir() {
5010
5258
  }
5011
5259
  function findSideboardMcpJsEntry() {
5012
5260
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
5013
- if (override && (0, import_node_fs10.existsSync)(override)) return override;
5261
+ if (override && (0, import_node_fs12.existsSync)(override)) return override;
5014
5262
  let dir = corePackageDir();
5015
5263
  for (let i = 0; i < 10; i++) {
5016
5264
  const candidates = [
@@ -5023,7 +5271,7 @@ function findSideboardMcpJsEntry() {
5023
5271
  (0, import_node_path11.join)(dir, "cli/dist/index.js")
5024
5272
  ];
5025
5273
  for (const p of candidates) {
5026
- if ((0, import_node_fs10.existsSync)(p)) return p;
5274
+ if ((0, import_node_fs12.existsSync)(p)) return p;
5027
5275
  }
5028
5276
  const parent = (0, import_node_path11.dirname)(dir);
5029
5277
  if (parent === dir) break;
@@ -5054,11 +5302,13 @@ async function buildInjectedMcpServers(opts) {
5054
5302
  const servers = [];
5055
5303
  if (opts.includeSideboard) {
5056
5304
  const sideboard = await resolveSideboardMcpServer();
5305
+ const orchId = opts.orchestratorThreadId?.trim();
5306
+ const profile = orchId ? "orchestration" : "worktree";
5057
5307
  sideboard.env = {
5058
5308
  ...sideboard.env ?? {},
5059
- SIDEBOARD_APP_DATA: appDataDir()
5309
+ SIDEBOARD_APP_DATA: appDataDir(),
5310
+ [SIDEBOARD_MCP_PROFILE_ENV]: profile
5060
5311
  };
5061
- const orchId = opts.orchestratorThreadId?.trim();
5062
5312
  if (orchId) {
5063
5313
  sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
5064
5314
  }
@@ -5133,19 +5383,19 @@ function writeMcpServersConfig(servers) {
5133
5383
  ...s.env ? { env: s.env } : {}
5134
5384
  };
5135
5385
  }
5136
- const dir = (0, import_node_fs10.mkdtempSync)((0, import_node_path11.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
5386
+ const dir = (0, import_node_fs12.mkdtempSync)((0, import_node_path11.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
5137
5387
  const cfgPath = (0, import_node_path11.join)(dir, "mcp.json");
5138
- (0, import_node_fs10.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
5388
+ (0, import_node_fs12.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
5139
5389
  return cfgPath;
5140
5390
  }
5141
5391
  async function writeInjectedMcpConfig(opts) {
5142
5392
  return writeMcpServersConfig(await buildInjectedMcpServers(opts));
5143
5393
  }
5144
- 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;
5394
+ 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;
5145
5395
  var init_injected_mcp = __esm({
5146
5396
  "src/agents/injected-mcp.ts"() {
5147
5397
  "use strict";
5148
- import_node_fs10 = require("fs");
5398
+ import_node_fs12 = require("fs");
5149
5399
  import_node_module = require("module");
5150
5400
  import_node_os6 = require("os");
5151
5401
  import_node_path11 = require("path");
@@ -5153,6 +5403,8 @@ var init_injected_mcp = __esm({
5153
5403
  init_run();
5154
5404
  init_config();
5155
5405
  init_connected_teams();
5406
+ init_profile();
5407
+ init_app_settings();
5156
5408
  init_paths();
5157
5409
  init_node_launch();
5158
5410
  import_meta = {};
@@ -5172,6 +5424,7 @@ var init_injected_mcp = __esm({
5172
5424
  "mcp__brightsy__*"
5173
5425
  ];
5174
5426
  brightsyMcpCommandCache = null;
5427
+ BRIGHTSY_WORD = /(?<![a-z0-9_-])brightsy(?![a-z0-9_-])/i;
5175
5428
  }
5176
5429
  });
5177
5430
 
@@ -5326,11 +5579,11 @@ function parseIssuesJson(raw) {
5326
5579
  }
5327
5580
  return [];
5328
5581
  }
5329
- var import_node_fs11, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
5582
+ var import_node_fs13, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
5330
5583
  var init_claude = __esm({
5331
5584
  "src/agents/claude.ts"() {
5332
5585
  "use strict";
5333
- import_node_fs11 = require("fs");
5586
+ import_node_fs13 = require("fs");
5334
5587
  init_run();
5335
5588
  init_app_settings();
5336
5589
  init_claude_mcp();
@@ -5359,7 +5612,7 @@ var init_claude = __esm({
5359
5612
  async detect() {
5360
5613
  const claude = resolveClaudeExecutable();
5361
5614
  if (claude !== "claude") {
5362
- if (!(0, import_node_fs11.existsSync)(claude)) {
5615
+ if (!(0, import_node_fs13.existsSync)(claude)) {
5363
5616
  return {
5364
5617
  agent: "claude",
5365
5618
  installed: false,
@@ -5398,9 +5651,8 @@ var init_claude = __esm({
5398
5651
  },
5399
5652
  async buildTurn(thread, input) {
5400
5653
  const claude = resolveClaudeExecutable();
5401
- const turn = normalizeTurnInput(input);
5402
5654
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
5403
- const effective = sessionId ? { prompt: turn.prompt } : turn;
5655
+ const effective = dropCachedPrefixOnResume(input, sessionId);
5404
5656
  const promptText = flattenTurnInput(effective);
5405
5657
  const useStdin = promptText.length > CLAUDE_PROMPT_ARG_MAX;
5406
5658
  if (process.env.SIDEBOARD_DEBUG_CLAUDE_TURN === "1") {
@@ -5413,7 +5665,9 @@ var init_claude = __esm({
5413
5665
  const isOrchestrator = isOrchestratorThread2(thread);
5414
5666
  const injectedServers = await buildInjectedMcpServers({
5415
5667
  includeSideboard: true,
5416
- includeBrightsy: isBrightsyConnected(),
5668
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
5669
+ orchestrator: isOrchestrator
5670
+ }),
5417
5671
  orchestratorThreadId: isOrchestrator ? thread.id : null
5418
5672
  });
5419
5673
  const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
@@ -5587,9 +5841,14 @@ async function listCodexModels() {
5587
5841
  if (cachedCodexModels && now - cachedCodexModels.at < CODEX_MODEL_CACHE_MS) {
5588
5842
  return cachedCodexModels.models;
5589
5843
  }
5590
- const which = await run("which", ["codex"], { reject: false });
5591
- if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
5592
- const listed = await run("codex", ["debug", "models"], { reject: false });
5844
+ const codex = resolveAgentExecutable("codex");
5845
+ if (codex === "codex") {
5846
+ const which = await run("which", ["codex"], { reject: false });
5847
+ if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
5848
+ } else if (!(0, import_node_fs14.existsSync)(codex)) {
5849
+ return FALLBACK_CODEX_MODELS;
5850
+ }
5851
+ const listed = await run(codex, ["debug", "models"], { reject: false });
5593
5852
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
5594
5853
  return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
5595
5854
  }
@@ -5628,8 +5887,8 @@ function codexConfigHasNetworkAccess() {
5628
5887
  (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
5629
5888
  ];
5630
5889
  for (const path of candidates) {
5631
- if (!(0, import_node_fs12.existsSync)(path)) continue;
5632
- const text = (0, import_node_fs12.readFileSync)(path, "utf8");
5890
+ if (!(0, import_node_fs14.existsSync)(path)) continue;
5891
+ const text = (0, import_node_fs14.readFileSync)(path, "utf8");
5633
5892
  if (/network_access\s*=\s*true/.test(text)) return true;
5634
5893
  }
5635
5894
  return false;
@@ -5662,21 +5921,23 @@ function asRecord(value) {
5662
5921
  }
5663
5922
  function codexLooksAuthenticated() {
5664
5923
  const authPath = (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
5665
- if (!(0, import_node_fs12.existsSync)(authPath)) return false;
5924
+ if (!(0, import_node_fs14.existsSync)(authPath)) return false;
5666
5925
  try {
5667
- return (0, import_node_fs12.statSync)(authPath).size > 2;
5926
+ return (0, import_node_fs14.statSync)(authPath).size > 2;
5668
5927
  } catch {
5669
5928
  return false;
5670
5929
  }
5671
5930
  }
5672
- var import_node_fs12, import_node_os7, import_node_path12, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
5931
+ var import_node_fs14, import_node_os7, import_node_path12, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
5673
5932
  var init_codex = __esm({
5674
5933
  "src/agents/codex.ts"() {
5675
5934
  "use strict";
5676
- import_node_fs12 = require("fs");
5935
+ import_node_fs14 = require("fs");
5677
5936
  import_node_os7 = require("os");
5678
5937
  import_node_path12 = require("path");
5679
5938
  init_run();
5939
+ init_app_settings();
5940
+ init_global_workspace();
5680
5941
  init_error_detail();
5681
5942
  init_injected_mcp();
5682
5943
  init_turn_input();
@@ -5694,16 +5955,30 @@ var init_codex = __esm({
5694
5955
  codexAdapter = {
5695
5956
  kind: "codex",
5696
5957
  async detect() {
5697
- const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
5698
- if (which.exitCode !== 0) {
5699
- return {
5700
- agent: "codex",
5701
- installed: false,
5702
- authenticated: false,
5703
- linearMcp: false,
5704
- warnings: [],
5705
- reason: "codex CLI not found on PATH"
5706
- };
5958
+ const codex = resolveAgentExecutable("codex");
5959
+ if (codex !== "codex") {
5960
+ if (!(0, import_node_fs14.existsSync)(codex)) {
5961
+ return {
5962
+ agent: "codex",
5963
+ installed: false,
5964
+ authenticated: false,
5965
+ linearMcp: false,
5966
+ warnings: [],
5967
+ reason: `Codex executable not found: ${codex}`
5968
+ };
5969
+ }
5970
+ } else {
5971
+ const which = await run("which", ["codex"], { reject: false, timeoutMs: 3e3 });
5972
+ if (which.exitCode !== 0) {
5973
+ return {
5974
+ agent: "codex",
5975
+ installed: false,
5976
+ authenticated: false,
5977
+ linearMcp: false,
5978
+ warnings: [],
5979
+ reason: "codex CLI not found on PATH"
5980
+ };
5981
+ }
5707
5982
  }
5708
5983
  const authenticated = codexLooksAuthenticated();
5709
5984
  const linearMcp = false;
@@ -5723,22 +5998,24 @@ var init_codex = __esm({
5723
5998
  };
5724
5999
  },
5725
6000
  async buildTurn(thread, input) {
5726
- const prompt = flattenTurnInput(input);
5727
6001
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
6002
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
5728
6003
  const useStdin = prompt.length > CODEX_PROMPT_ARG_MAX;
5729
6004
  const promptArg = useStdin ? "-" : prompt;
5730
6005
  if (process.env.SIDEBOARD_DEBUG_CODEX_TURN === "1") {
5731
- const { cachedPrefix } = normalizeTurnInput(input);
5732
6006
  console.error(
5733
- `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${Boolean(cachedPrefix)}`
6007
+ `[sideboard/codex] promptChars=${prompt.length} resumed=${Boolean(sessionId)} stdin=${useStdin} hasPrefix=${prompt.includes("Current request:")}`
5734
6008
  );
5735
6009
  }
5736
6010
  const mode = permissionMode(thread);
5737
6011
  const model = thread.model?.trim();
6012
+ const isOrchestrator = isOrchestratorThread(thread);
5738
6013
  const injected = await buildInjectedMcpServers({
5739
6014
  includeSideboard: true,
5740
- includeBrightsy: isBrightsyConnected(),
5741
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6015
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6016
+ orchestrator: isOrchestrator
6017
+ }),
6018
+ orchestratorThreadId: isOrchestrator ? thread.id : null
5742
6019
  });
5743
6020
  const mcpOverrides = toCodexMcpConfigArgs(injected);
5744
6021
  const execOpts = [
@@ -5758,7 +6035,7 @@ var init_codex = __esm({
5758
6035
  ];
5759
6036
  const args = sessionId ? ["exec", ...execOpts, "resume", sessionId, promptArg] : ["exec", ...execOpts, promptArg];
5760
6037
  return {
5761
- file: "codex",
6038
+ file: resolveAgentExecutable("codex"),
5762
6039
  args,
5763
6040
  cwd: thread.worktreePath,
5764
6041
  stdin: useStdin ? `${prompt}
@@ -5854,16 +6131,17 @@ var init_codex = __esm({
5854
6131
  return cached;
5855
6132
  },
5856
6133
  async buildAttach(thread) {
6134
+ const codex = resolveAgentExecutable("codex");
5857
6135
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
5858
6136
  if (sessionId) {
5859
6137
  return {
5860
- file: "codex",
6138
+ file: codex,
5861
6139
  args: ["exec", "--cd", thread.worktreePath, "resume", sessionId],
5862
6140
  cwd: thread.worktreePath
5863
6141
  };
5864
6142
  }
5865
6143
  return {
5866
- file: "codex",
6144
+ file: codex,
5867
6145
  args: ["--cd", thread.worktreePath],
5868
6146
  cwd: thread.worktreePath
5869
6147
  };
@@ -5871,7 +6149,7 @@ var init_codex = __esm({
5871
6149
  async listLinearIssues(_repoPath) {
5872
6150
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
5873
6151
  const { stdout, exitCode } = await run(
5874
- "codex",
6152
+ resolveAgentExecutable("codex"),
5875
6153
  [
5876
6154
  "exec",
5877
6155
  "--json",
@@ -6134,21 +6412,22 @@ function cursorRunnerPath() {
6134
6412
  (0, import_node_path13.join)(root, "src", "agents", "cursor-runner.ts")
6135
6413
  ];
6136
6414
  for (const candidate of candidates) {
6137
- if ((0, import_node_fs13.existsSync)(candidate)) return candidate;
6415
+ if ((0, import_node_fs15.existsSync)(candidate)) return candidate;
6138
6416
  }
6139
6417
  return candidates[0];
6140
6418
  }
6141
- 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;
6419
+ 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;
6142
6420
  var init_cursor = __esm({
6143
6421
  "src/agents/cursor.ts"() {
6144
6422
  "use strict";
6145
- import_node_fs13 = require("fs");
6423
+ import_node_fs15 = require("fs");
6146
6424
  import_node_module2 = require("module");
6147
6425
  import_node_path13 = require("path");
6148
6426
  import_node_url2 = require("url");
6149
6427
  import_sdk = require("@cursor/sdk");
6150
6428
  init_run();
6151
6429
  init_app_settings();
6430
+ init_global_workspace();
6152
6431
  init_cursor_events();
6153
6432
  init_injected_mcp();
6154
6433
  init_node_launch();
@@ -6185,13 +6464,16 @@ var init_cursor = __esm({
6185
6464
  };
6186
6465
  },
6187
6466
  async buildTurn(thread, input) {
6188
- const prompt = flattenTurnInput(input);
6189
6467
  const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
6468
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, agentId));
6190
6469
  const apiKey = resolveCursorApiKey() || void 0;
6470
+ const isOrchestrator = isOrchestratorThread(thread);
6191
6471
  const injected = await buildInjectedMcpServers({
6192
6472
  includeSideboard: true,
6193
- includeBrightsy: isBrightsyConnected(),
6194
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6473
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6474
+ orchestrator: isOrchestrator
6475
+ }),
6476
+ orchestratorThreadId: isOrchestrator ? thread.id : null
6195
6477
  });
6196
6478
  const mcpServers = toCursorMcpServers(injected);
6197
6479
  const req = {
@@ -6262,9 +6544,14 @@ async function listOpencodeModels() {
6262
6544
  if (cachedOpencodeModels && now - cachedOpencodeModels.at < OPENCODE_MODEL_CACHE_MS) {
6263
6545
  return cachedOpencodeModels.models;
6264
6546
  }
6265
- const which = await run("which", ["opencode"], { reject: false });
6266
- if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
6267
- const listed = await run("opencode", ["models"], { reject: false });
6547
+ const opencode = resolveAgentExecutable("opencode");
6548
+ if (opencode === "opencode") {
6549
+ const which = await run("which", ["opencode"], { reject: false });
6550
+ if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
6551
+ } else if (!(0, import_node_fs16.existsSync)(opencode)) {
6552
+ return FALLBACK_OPENCODE_MODELS;
6553
+ }
6554
+ const listed = await run(opencode, ["models"], { reject: false });
6268
6555
  if (listed.exitCode !== 0 || !listed.stdout.trim()) {
6269
6556
  return cachedOpencodeModels?.models ?? FALLBACK_OPENCODE_MODELS;
6270
6557
  }
@@ -6291,11 +6578,14 @@ function usageFromOpencode(tokens) {
6291
6578
  cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
6292
6579
  };
6293
6580
  }
6294
- var FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
6581
+ var import_node_fs16, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
6295
6582
  var init_opencode = __esm({
6296
6583
  "src/agents/opencode.ts"() {
6297
6584
  "use strict";
6585
+ import_node_fs16 = require("fs");
6298
6586
  init_run();
6587
+ init_app_settings();
6588
+ init_global_workspace();
6299
6589
  init_error_detail();
6300
6590
  init_injected_mcp();
6301
6591
  init_turn_input();
@@ -6316,20 +6606,34 @@ var init_opencode = __esm({
6316
6606
  opencodeAdapter = {
6317
6607
  kind: "opencode",
6318
6608
  async detect() {
6319
- const which = await run("which", ["opencode"], { reject: false });
6320
- if (which.exitCode !== 0) {
6321
- return {
6322
- agent: "opencode",
6323
- installed: false,
6324
- authenticated: false,
6325
- linearMcp: false,
6326
- warnings: [],
6327
- reason: "opencode CLI not found on PATH"
6328
- };
6609
+ const opencode = resolveAgentExecutable("opencode");
6610
+ if (opencode !== "opencode") {
6611
+ if (!(0, import_node_fs16.existsSync)(opencode)) {
6612
+ return {
6613
+ agent: "opencode",
6614
+ installed: false,
6615
+ authenticated: false,
6616
+ linearMcp: false,
6617
+ warnings: [],
6618
+ reason: `OpenCode executable not found: ${opencode}`
6619
+ };
6620
+ }
6621
+ } else {
6622
+ const which = await run("which", ["opencode"], { reject: false });
6623
+ if (which.exitCode !== 0) {
6624
+ return {
6625
+ agent: "opencode",
6626
+ installed: false,
6627
+ authenticated: false,
6628
+ linearMcp: false,
6629
+ warnings: [],
6630
+ reason: "opencode CLI not found on PATH"
6631
+ };
6632
+ }
6329
6633
  }
6330
- const auth = await run("opencode", ["auth", "list"], { reject: false });
6634
+ const auth = await run(opencode, ["auth", "list"], { reject: false });
6331
6635
  const authenticated = auth.exitCode === 0 && auth.stdout.trim().length > 0;
6332
- const mcp = await run("opencode", ["mcp", "list"], { reject: false });
6636
+ const mcp = await run(opencode, ["mcp", "list"], { reject: false });
6333
6637
  const linearMcp = /linear/i.test(mcp.stdout + mcp.stderr);
6334
6638
  return {
6335
6639
  agent: "opencode",
@@ -6341,8 +6645,8 @@ var init_opencode = __esm({
6341
6645
  };
6342
6646
  },
6343
6647
  async buildTurn(thread, input) {
6344
- const prompt = flattenTurnInput(input);
6345
6648
  const sessionId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
6649
+ const prompt = flattenTurnInput(dropCachedPrefixOnResume(input, sessionId));
6346
6650
  const mode = permissionMode(thread);
6347
6651
  const model = thread.model?.trim();
6348
6652
  const args = [
@@ -6358,14 +6662,17 @@ var init_opencode = __esm({
6358
6662
  if (model) {
6359
6663
  args.push("--model", model);
6360
6664
  }
6665
+ const isOrchestrator = isOrchestratorThread(thread);
6361
6666
  const injected = await buildInjectedMcpServers({
6362
6667
  includeSideboard: true,
6363
- includeBrightsy: isBrightsyConnected(),
6364
- orchestratorThreadId: thread.sourceType === "orchestration" ? thread.id : null
6668
+ includeBrightsy: shouldInjectBrightsyMcp(thread, {
6669
+ orchestrator: isOrchestrator
6670
+ }),
6671
+ orchestratorThreadId: isOrchestrator ? thread.id : null
6365
6672
  });
6366
6673
  const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
6367
6674
  return {
6368
- file: "opencode",
6675
+ file: resolveAgentExecutable("opencode"),
6369
6676
  args,
6370
6677
  cwd: thread.worktreePath,
6371
6678
  // `opencode run` treats non-TTY stdin as the message body when no positional
@@ -6441,7 +6748,7 @@ var init_opencode = __esm({
6441
6748
  async resolveSessionId(worktreePath, cached) {
6442
6749
  const cachedId = cached?.trim() || null;
6443
6750
  const listed = await run(
6444
- "opencode",
6751
+ resolveAgentExecutable("opencode"),
6445
6752
  ["session", "list", "--format", "json"],
6446
6753
  { cwd: worktreePath, reject: false }
6447
6754
  );
@@ -6472,7 +6779,7 @@ var init_opencode = __esm({
6472
6779
  const args = ["--dir", thread.worktreePath];
6473
6780
  if (sessionId) args.push("--session", sessionId);
6474
6781
  return {
6475
- file: "opencode",
6782
+ file: resolveAgentExecutable("opencode"),
6476
6783
  args,
6477
6784
  cwd: thread.worktreePath,
6478
6785
  env: {
@@ -6483,7 +6790,7 @@ var init_opencode = __esm({
6483
6790
  async listLinearIssues(_repoPath) {
6484
6791
  const prompt = "List my assigned Linear issues as JSON array only: id, identifier, title, url, labels.";
6485
6792
  const { stdout, exitCode } = await run(
6486
- "opencode",
6793
+ resolveAgentExecutable("opencode"),
6487
6794
  ["run", prompt, "--format", "json"],
6488
6795
  {
6489
6796
  reject: false,
@@ -6716,9 +7023,30 @@ function getAgentSetupInfo(agent) {
6716
7023
  function listAgentSetupInfo() {
6717
7024
  return Object.values(SETUP);
6718
7025
  }
6719
- async function openInSystemTerminal(command) {
6720
- ensureAgentPath();
7026
+ function resolveLoginCommand(agent) {
7027
+ const info = getAgentSetupInfo(agent);
7028
+ if (!info.loginCommand) return null;
7029
+ if (!CLI_LOGIN_AGENTS.has(agent)) return info.loginCommand;
7030
+ const exe = resolveAgentExecutable(agent);
7031
+ return info.loginCommand.replace(/^\S+/, exe);
7032
+ }
7033
+ async function prepareTerminalCommand(command) {
7034
+ enrichPathWithNpmGlobalBin();
6721
7035
  const trimmed = command.trim();
7036
+ if (!trimmed) return trimmed;
7037
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
7038
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
7039
+ const bin = match?.[1];
7040
+ let whichPath = null;
7041
+ if (bin && !bin.includes("/") && !bin.includes("\\")) {
7042
+ const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
7043
+ whichPath = which.exitCode === 0 ? which.stdout.trim() : null;
7044
+ }
7045
+ const resolved = resolveCommandBinarySync(trimmed, whichPath);
7046
+ return withExportedPath(resolved, process.env.PATH ?? "");
7047
+ }
7048
+ async function openInSystemTerminal(command) {
7049
+ const trimmed = (await prepareTerminalCommand(command)).trim();
6722
7050
  if (!trimmed) throw new Error("Command is empty");
6723
7051
  if (process.platform === "darwin") {
6724
7052
  const escaped = trimmed.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
@@ -6759,6 +7087,11 @@ async function openInSystemTerminal(command) {
6759
7087
  `No terminal found to run: ${trimmed}. Install gnome-terminal (or similar), or run the command manually.`
6760
7088
  );
6761
7089
  }
7090
+ async function whichCli(bin) {
7091
+ const which = await run("which", [bin], { reject: false, timeoutMs: 3e3 });
7092
+ if (which.exitCode !== 0) return null;
7093
+ return which.stdout.trim().split(/\r?\n/).find(Boolean) ?? null;
7094
+ }
6762
7095
  async function installAgent(agent) {
6763
7096
  const info = getAgentSetupInfo(agent);
6764
7097
  if (info.kind === "bundled-sdk" || info.kind === "api-key") {
@@ -6779,7 +7112,18 @@ async function installAgent(agent) {
6779
7112
  message: `Opened Terminal to run: ${info.installCommand}`
6780
7113
  };
6781
7114
  }
6782
- ensureAgentPath();
7115
+ enrichPathWithNpmGlobalBin();
7116
+ const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
7117
+ if (cliBin) {
7118
+ const existing = await whichCli(cliBin);
7119
+ if (existing) {
7120
+ return {
7121
+ ok: true,
7122
+ command: existing,
7123
+ message: isConductorBundledCli(existing) ? `Using Conductor\u2019s ${cliBin} at ${existing}. No extra install \u2014 Log in only if auth is missing.` : `Already on PATH: ${existing}`
7124
+ };
7125
+ }
7126
+ }
6783
7127
  const result = await run("npm", ["install", "-g", info.npmPackage], { reject: false });
6784
7128
  const ok = result.exitCode === 0;
6785
7129
  if (!ok) {
@@ -6805,36 +7149,44 @@ async function installAgent(agent) {
6805
7149
  };
6806
7150
  }
6807
7151
  }
7152
+ enrichPathWithNpmGlobalBin();
7153
+ let binPath = null;
7154
+ if (cliBin) {
7155
+ binPath = await whichCli(cliBin);
7156
+ }
6808
7157
  return {
6809
7158
  ok: true,
6810
7159
  command: info.installCommand,
6811
7160
  exitCode: 0,
6812
7161
  stdout: result.stdout,
6813
7162
  stderr: result.stderr,
6814
- message: `Installed ${info.npmPackage}`
7163
+ 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)`
6815
7164
  };
6816
7165
  }
6817
7166
  async function loginAgent(agent) {
6818
7167
  const info = getAgentSetupInfo(agent);
6819
- if (!info.loginCommand) {
7168
+ const loginCommand = resolveLoginCommand(agent);
7169
+ if (!loginCommand) {
6820
7170
  return {
6821
7171
  ok: true,
6822
7172
  message: info.kind === "bundled-sdk" || info.kind === "api-key" ? info.summary : `No login command for ${agent}`
6823
7173
  };
6824
7174
  }
6825
- await openInSystemTerminal(info.loginCommand);
7175
+ const prepared = await prepareTerminalCommand(loginCommand);
7176
+ await openInSystemTerminal(prepared);
6826
7177
  return {
6827
7178
  ok: true,
6828
7179
  openedTerminal: true,
6829
- command: info.loginCommand,
6830
- message: `Opened Terminal to run: ${info.loginCommand}`
7180
+ command: prepared,
7181
+ message: `Opened Terminal to run: ${prepared}`
6831
7182
  };
6832
7183
  }
6833
- var SETUP;
7184
+ var SETUP, CLI_BIN, CLI_LOGIN_AGENTS;
6834
7185
  var init_install = __esm({
6835
7186
  "src/agents/install.ts"() {
6836
7187
  "use strict";
6837
7188
  init_run();
7189
+ init_app_settings();
6838
7190
  init_path();
6839
7191
  SETUP = {
6840
7192
  claude: {
@@ -6883,6 +7235,18 @@ var init_install = __esm({
6883
7235
  npmPackage: "@brightsy/cli"
6884
7236
  }
6885
7237
  };
7238
+ CLI_BIN = {
7239
+ claude: "claude",
7240
+ codex: "codex",
7241
+ opencode: "opencode",
7242
+ brightsy: "brightsy"
7243
+ };
7244
+ CLI_LOGIN_AGENTS = /* @__PURE__ */ new Set([
7245
+ "claude",
7246
+ "codex",
7247
+ "opencode",
7248
+ "brightsy"
7249
+ ]);
6886
7250
  }
6887
7251
  });
6888
7252
 
@@ -6898,14 +7262,17 @@ __export(agents_exports, {
6898
7262
  claudeAdapter: () => claudeAdapter,
6899
7263
  codexAdapter: () => codexAdapter,
6900
7264
  coerceOrchestratorAgent: () => coerceOrchestratorAgent,
7265
+ conductorBundledBinDir: () => conductorBundledBinDir,
6901
7266
  cursorAdapter: () => cursorAdapter,
6902
7267
  cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
6903
7268
  decodeBrightsyTarget: () => decodeBrightsyTarget,
6904
7269
  encodeBrightsyTarget: () => encodeBrightsyTarget,
7270
+ enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
6905
7271
  ensureAgentPath: () => ensureAgentPath,
6906
7272
  getAdapter: () => getAdapter,
6907
7273
  getAgentSetupInfo: () => getAgentSetupInfo,
6908
7274
  installAgent: () => installAgent,
7275
+ isConductorBundledCli: () => isConductorBundledCli,
6909
7276
  isCursorAutoModel: () => isCursorAutoModel,
6910
7277
  isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
6911
7278
  isSessionQuotaLimit: () => isSessionQuotaLimit,
@@ -6921,8 +7288,13 @@ __export(agents_exports, {
6921
7288
  parseCursorRunnerLine: () => parseCursorRunnerLine,
6922
7289
  parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
6923
7290
  permissionMode: () => permissionMode,
7291
+ posixShellSingleQuote: () => posixShellSingleQuote,
7292
+ prepareTerminalCommand: () => prepareTerminalCommand,
7293
+ resolveCommandBinarySync: () => resolveCommandBinarySync,
6924
7294
  resolveCursorModelId: () => resolveCursorModelId,
6925
- resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent
7295
+ resolveLoginCommand: () => resolveLoginCommand,
7296
+ resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
7297
+ withExportedPath: () => withExportedPath
6926
7298
  });
6927
7299
  function getAdapter(kind) {
6928
7300
  return adapters[kind];
@@ -7034,17 +7406,17 @@ __export(plan_file_exports, {
7034
7406
  });
7035
7407
  function ensureAttachmentsGitignore2(worktreePath) {
7036
7408
  const gitignoreAbs = (0, import_node_path24.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
7037
- if ((0, import_node_fs26.existsSync)(gitignoreAbs)) return;
7038
- (0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
7039
- (0, import_node_fs26.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
7409
+ if ((0, import_node_fs29.existsSync)(gitignoreAbs)) return;
7410
+ (0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(gitignoreAbs), { recursive: true });
7411
+ (0, import_node_fs29.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
7040
7412
  }
7041
7413
  function planFileAbs(worktreePath) {
7042
7414
  return (0, import_node_path24.join)(worktreePath, PLAN_FILE_REL);
7043
7415
  }
7044
7416
  function readTextIfPresent2(abs) {
7045
- if (!(0, import_node_fs26.existsSync)(abs)) return null;
7417
+ if (!(0, import_node_fs29.existsSync)(abs)) return null;
7046
7418
  try {
7047
- const content = (0, import_node_fs26.readFileSync)(abs, "utf8");
7419
+ const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
7048
7420
  return content.trim() ? content : null;
7049
7421
  } catch {
7050
7422
  return null;
@@ -7056,16 +7428,16 @@ function readPlanFile(worktreePath) {
7056
7428
  function writePlanFile(worktreePath, content) {
7057
7429
  ensureAttachmentsGitignore2(worktreePath);
7058
7430
  const abs = planFileAbs(worktreePath);
7059
- (0, import_node_fs26.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
7431
+ (0, import_node_fs29.mkdirSync)((0, import_node_path24.dirname)(abs), { recursive: true });
7060
7432
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
7061
- (0, import_node_fs26.writeFileSync)(abs, body, "utf8");
7433
+ (0, import_node_fs29.writeFileSync)(abs, body, "utf8");
7062
7434
  return PLAN_FILE_REL;
7063
7435
  }
7064
- var import_node_fs26, import_node_path24;
7436
+ var import_node_fs29, import_node_path24;
7065
7437
  var init_plan_file = __esm({
7066
7438
  "src/plan/plan-file.ts"() {
7067
7439
  "use strict";
7068
- import_node_fs26 = require("fs");
7440
+ import_node_fs29 = require("fs");
7069
7441
  import_node_path24 = require("path");
7070
7442
  init_workspace_scratch();
7071
7443
  init_plan_present();
@@ -7082,9 +7454,9 @@ function recoverFinishedCursorRun(opts) {
7082
7454
  const agentId = opts.agentId.trim();
7083
7455
  if (!agentId) return null;
7084
7456
  const runsPath = (0, import_node_path25.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
7085
- if (!(0, import_node_fs27.existsSync)(runsPath)) return null;
7457
+ if (!(0, import_node_fs30.existsSync)(runsPath)) return null;
7086
7458
  try {
7087
- const lines = (0, import_node_fs27.readFileSync)(runsPath, "utf8").split("\n");
7459
+ const lines = (0, import_node_fs30.readFileSync)(runsPath, "utf8").split("\n");
7088
7460
  let best = null;
7089
7461
  for (const line of lines) {
7090
7462
  const trimmed = line.trim();
@@ -7110,11 +7482,11 @@ function recoverFinishedCursorRun(opts) {
7110
7482
  return null;
7111
7483
  }
7112
7484
  }
7113
- var import_node_fs27, import_node_path25;
7485
+ var import_node_fs30, import_node_path25;
7114
7486
  var init_cursor_recover = __esm({
7115
7487
  "src/agents/cursor-recover.ts"() {
7116
7488
  "use strict";
7117
- import_node_fs27 = require("fs");
7489
+ import_node_fs30 = require("fs");
7118
7490
  import_node_path25 = require("path");
7119
7491
  init_paths();
7120
7492
  }
@@ -7188,6 +7560,7 @@ __export(index_exports, {
7188
7560
  REVIEW_REQUEST_PREFILL: () => REVIEW_REQUEST_PREFILL,
7189
7561
  SIDEBOARD_FORCE_STOP: () => SIDEBOARD_FORCE_STOP,
7190
7562
  SIDEBOARD_MCP_ALLOWED_TOOLS: () => SIDEBOARD_MCP_ALLOWED_TOOLS,
7563
+ SIDEBOARD_MCP_PROFILE_ENV: () => SIDEBOARD_MCP_PROFILE_ENV,
7191
7564
  THINKING_EFFORTS: () => THINKING_EFFORTS,
7192
7565
  addPrStackLayer: () => addPrStackLayer,
7193
7566
  addStackLayerFromThread: () => addStackLayerFromThread,
@@ -7219,6 +7592,7 @@ __export(index_exports, {
7219
7592
  brightsyCloudConnectAgent: () => brightsyCloudConnectAgent,
7220
7593
  brightsyCloudConnectEnabled: () => brightsyCloudConnectEnabled,
7221
7594
  brightsyConfigPath: () => brightsyConfigPath,
7595
+ brightsyInjectWorktreeMcpEnabled: () => brightsyInjectWorktreeMcpEnabled,
7222
7596
  brightsyMcpAllowedTools: () => brightsyMcpAllowedTools,
7223
7597
  brightsyMcpServerName: () => brightsyMcpServerName,
7224
7598
  buildCachedUserContent: () => buildCachedUserContent,
@@ -7244,6 +7618,7 @@ __export(index_exports, {
7244
7618
  coerceOrchestratorAgent: () => coerceOrchestratorAgent,
7245
7619
  collectTakenTeamSlugs: () => collectTakenTeamSlugs,
7246
7620
  commitAll: () => commitAll,
7621
+ conductorBundledBinDir: () => conductorBundledBinDir,
7247
7622
  conductorDbPath: () => conductorDbPath,
7248
7623
  confirmLand: () => confirmLand,
7249
7624
  connectBrightsyTeam: () => connectBrightsyTeam,
@@ -7270,7 +7645,9 @@ __export(index_exports, {
7270
7645
  detectLocalMergeConflicts: () => detectLocalMergeConflicts,
7271
7646
  disconnectBrightsyTeam: () => disconnectBrightsyTeam,
7272
7647
  discoverSkills: () => discoverSkills,
7648
+ dropCachedPrefixOnResume: () => dropCachedPrefixOnResume,
7273
7649
  encodeBrightsyTarget: () => encodeBrightsyTarget,
7650
+ enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
7274
7651
  enrichWorkspacesWithGithub: () => enrichWorkspacesWithGithub,
7275
7652
  ensureAgentPath: () => ensureAgentPath,
7276
7653
  ensureCloudCoordinator: () => ensureCloudCoordinator,
@@ -7308,6 +7685,7 @@ __export(index_exports, {
7308
7685
  formatTranscriptMarkdown: () => formatTranscriptMarkdown,
7309
7686
  formatWorkspaceInventory: () => formatWorkspaceInventory,
7310
7687
  formatWorktreeDirective: () => formatWorktreeDirective,
7688
+ formatWorktreeReminder: () => formatWorktreeReminder,
7311
7689
  getAdapter: () => getAdapter,
7312
7690
  getAgentSetupInfo: () => getAgentSetupInfo,
7313
7691
  getBrightsySession: () => getBrightsySession,
@@ -7352,6 +7730,7 @@ __export(index_exports, {
7352
7730
  isBrightsyConnected: () => isBrightsyConnected,
7353
7731
  isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
7354
7732
  isCloudCoordinatorThread: () => isCloudCoordinatorThread,
7733
+ isConductorBundledCli: () => isConductorBundledCli,
7355
7734
  isCursorAutoModel: () => isCursorAutoModel,
7356
7735
  isDirty: () => isDirty,
7357
7736
  isGhRateLimitError: () => isGhRateLimitError,
@@ -7434,7 +7813,10 @@ __export(index_exports, {
7434
7813
  pastedTextStats: () => pastedTextStats,
7435
7814
  permissionMode: () => permissionMode,
7436
7815
  planFileAbs: () => planFileAbs,
7816
+ posixShellSingleQuote: () => posixShellSingleQuote,
7817
+ prepareTerminalCommand: () => prepareTerminalCommand,
7437
7818
  previewLand: () => previewLand,
7819
+ promptMentionsBrightsy: () => promptMentionsBrightsy,
7438
7820
  pushBranch: () => pushBranch,
7439
7821
  readExistingReviewRequestFile: () => readExistingReviewRequestFile,
7440
7822
  readPlanFile: () => readPlanFile,
@@ -7450,7 +7832,9 @@ __export(index_exports, {
7450
7832
  requestReview: () => requestReview,
7451
7833
  requireAgent: () => requireAgent,
7452
7834
  resetGhStackDetectCache: () => resetGhStackDetectCache,
7835
+ resolveAgentExecutable: () => resolveAgentExecutable,
7453
7836
  resolveClaudeExecutable: () => resolveClaudeExecutable,
7837
+ resolveCommandBinarySync: () => resolveCommandBinarySync,
7454
7838
  resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
7455
7839
  resolveCursorModelId: () => resolveCursorModelId,
7456
7840
  resolveDefaultBranch: () => resolveDefaultBranch,
@@ -7459,6 +7843,7 @@ __export(index_exports, {
7459
7843
  resolveFilesToCopy: () => resolveFilesToCopy,
7460
7844
  resolveGhAuthToken: () => resolveGhAuthToken,
7461
7845
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
7846
+ resolveLoginCommand: () => resolveLoginCommand,
7462
7847
  resolveNewThreadOptions: () => resolveNewThreadOptions,
7463
7848
  resolvePlanMarkdown: () => resolvePlanMarkdown,
7464
7849
  resolvePrSelector: () => resolvePrSelector,
@@ -7480,9 +7865,11 @@ __export(index_exports, {
7480
7865
  settingsSourceLabel: () => settingsSourceLabel,
7481
7866
  shouldAttachPastedText: () => shouldAttachPastedText,
7482
7867
  shouldCompactContext: () => shouldCompactContext,
7868
+ shouldInjectBrightsyMcp: () => shouldInjectBrightsyMcp,
7483
7869
  shouldRefreshReviewRequestTemplate: () => shouldRefreshReviewRequestTemplate,
7484
7870
  shouldRunWorktreeCleanup: () => shouldRunWorktreeCleanup,
7485
7871
  sideboardHomeDir: () => sideboardHomeDir,
7872
+ sideboardMcpProfile: () => sideboardMcpProfile,
7486
7873
  sideboardReposDir: () => sideboardReposDir,
7487
7874
  sideboardWorkspacesDir: () => sideboardWorkspacesDir,
7488
7875
  slugify: () => slugify,
@@ -7510,6 +7897,7 @@ __export(index_exports, {
7510
7897
  threadDisplayLabel: () => threadDisplayLabel,
7511
7898
  threadFilePath: () => threadFilePath,
7512
7899
  threadLockPath: () => threadLockPath,
7900
+ threadRequestsBrightsyMcp: () => threadRequestsBrightsyMcp,
7513
7901
  threadsDir: () => threadsDir,
7514
7902
  threadsSharingWorktree: () => threadsSharingWorktree,
7515
7903
  toolDescription: () => toolDescription,
@@ -7517,14 +7905,18 @@ __export(index_exports, {
7517
7905
  toolFilePath: () => toolFilePath,
7518
7906
  totalTokens: () => totalTokens,
7519
7907
  updateAdvancedSettings: () => updateAdvancedSettings,
7908
+ updateAgentExecutable: () => updateAgentExecutable,
7520
7909
  updateAppEnvironment: () => updateAppEnvironment,
7521
7910
  updateBrightsySettings: () => updateBrightsySettings,
7522
7911
  updateClaudeSettings: () => updateClaudeSettings,
7912
+ updateCodexSettings: () => updateCodexSettings,
7523
7913
  updateDefaultsSettings: () => updateDefaultsSettings,
7524
7914
  updateIntegrationsSettings: () => updateIntegrationsSettings,
7915
+ updateOpencodeSettings: () => updateOpencodeSettings,
7525
7916
  updateThread: () => updateThread,
7526
7917
  validateLinearApiKey: () => validateLinearApiKey,
7527
7918
  withAgentInstructions: () => withAgentInstructions,
7919
+ withExportedPath: () => withExportedPath,
7528
7920
  withThreadLock: () => withThreadLock,
7529
7921
  workspaceSettingsSourceLabel: () => workspaceSettingsSourceLabel,
7530
7922
  worktreeCleanupSettings: () => worktreeCleanupSettings,
@@ -8101,7 +8493,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
8101
8493
  init_claude_mcp();
8102
8494
 
8103
8495
  // src/agents/instructions.ts
8104
- var import_node_fs14 = require("fs");
8496
+ var import_node_fs17 = require("fs");
8105
8497
  var import_node_path14 = require("path");
8106
8498
  init_worktree_labels();
8107
8499
  function normPath2(p) {
@@ -8201,6 +8593,9 @@ function formatWorktreeDirective(thread, opts) {
8201
8593
  );
8202
8594
  return lines.join("\n");
8203
8595
  }
8596
+ function formatWorktreeReminder() {
8597
+ 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.";
8598
+ }
8204
8599
  function formatArtifactDirective() {
8205
8600
  return [
8206
8601
  "Sideboard side column (desktop UI):",
@@ -8242,10 +8637,10 @@ function loadAgentInstructions(worktreePath, agent) {
8242
8637
  for (const rel of candidates) {
8243
8638
  if (seen.has(rel)) continue;
8244
8639
  const abs = (0, import_node_path14.join)(worktreePath, rel);
8245
- if (!(0, import_node_fs14.existsSync)(abs)) continue;
8640
+ if (!(0, import_node_fs17.existsSync)(abs)) continue;
8246
8641
  try {
8247
- if (!(0, import_node_fs14.statSync)(abs).isFile()) continue;
8248
- let content = (0, import_node_fs14.readFileSync)(abs, "utf8");
8642
+ if (!(0, import_node_fs17.statSync)(abs).isFile()) continue;
8643
+ let content = (0, import_node_fs17.readFileSync)(abs, "utf8");
8249
8644
  if (!content.trim()) continue;
8250
8645
  if (content.length > MAX_CHARS_PER_FILE) {
8251
8646
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -8290,11 +8685,13 @@ init_turn_input();
8290
8685
  init_agents();
8291
8686
  async function detectAgents() {
8292
8687
  ensureAgentPath();
8688
+ enrichPathWithNpmGlobalBin();
8293
8689
  return Promise.all(allAdapters().map((a) => a.detect()));
8294
8690
  }
8295
8691
  var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
8296
8692
  async function requireAgent(agent, opts) {
8297
8693
  ensureAgentPath();
8694
+ enrichPathWithNpmGlobalBin();
8298
8695
  const status = await Promise.race([
8299
8696
  getAdapter(agent).detect(),
8300
8697
  new Promise((_, reject) => {
@@ -8322,7 +8719,7 @@ async function requireAgent(agent, opts) {
8322
8719
  init_settings();
8323
8720
 
8324
8721
  // src/hook/conductor.ts
8325
- var import_node_fs15 = require("fs");
8722
+ var import_node_fs18 = require("fs");
8326
8723
  var import_node_net = require("net");
8327
8724
  var import_node_path15 = require("path");
8328
8725
  var import_execa3 = require("execa");
@@ -8335,8 +8732,8 @@ function matchSimpleGlob(pattern, name) {
8335
8732
  }
8336
8733
  function readWorktreeInclude(repoPath) {
8337
8734
  const path = (0, import_node_path15.join)(repoPath, ".worktreeinclude");
8338
- if (!(0, import_node_fs15.existsSync)(path)) return [];
8339
- return (0, import_node_fs15.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
8735
+ if (!(0, import_node_fs18.existsSync)(path)) return [];
8736
+ return (0, import_node_fs18.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
8340
8737
  }
8341
8738
  function resolveFilesToCopy(repoPath) {
8342
8739
  const fromInclude = readWorktreeInclude(repoPath);
@@ -8346,7 +8743,7 @@ function resolveFilesToCopy(repoPath) {
8346
8743
  if (settings?.fileIncludeGlobs?.length) {
8347
8744
  const matched = [];
8348
8745
  try {
8349
- for (const entry of (0, import_node_fs15.readdirSync)(repoPath, { withFileTypes: true })) {
8746
+ for (const entry of (0, import_node_fs18.readdirSync)(repoPath, { withFileTypes: true })) {
8350
8747
  if (!entry.isFile()) continue;
8351
8748
  for (const glob of settings.fileIncludeGlobs) {
8352
8749
  if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path15.basename)(glob), entry.name)) {
@@ -8361,7 +8758,7 @@ function resolveFilesToCopy(repoPath) {
8361
8758
  }
8362
8759
  const defaults = [];
8363
8760
  try {
8364
- for (const entry of (0, import_node_fs15.readdirSync)(repoPath, { withFileTypes: true })) {
8761
+ for (const entry of (0, import_node_fs18.readdirSync)(repoPath, { withFileTypes: true })) {
8365
8762
  if (entry.isFile() && entry.name.startsWith(".env")) {
8366
8763
  defaults.push(entry.name);
8367
8764
  }
@@ -8376,10 +8773,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
8376
8773
  const copied = [];
8377
8774
  for (const rel of patterns) {
8378
8775
  const src = (0, import_node_path15.join)(repoPath, rel);
8379
- if (!(0, import_node_fs15.existsSync)(src)) continue;
8776
+ if (!(0, import_node_fs18.existsSync)(src)) continue;
8380
8777
  const dest = (0, import_node_path15.join)(worktreePath, rel);
8381
- (0, import_node_fs15.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
8382
- (0, import_node_fs15.copyFileSync)(src, dest);
8778
+ (0, import_node_fs18.mkdirSync)((0, import_node_path15.dirname)(dest), { recursive: true });
8779
+ (0, import_node_fs18.copyFileSync)(src, dest);
8383
8780
  copied.push(rel);
8384
8781
  }
8385
8782
  return copied;
@@ -8632,15 +9029,15 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
8632
9029
  }
8633
9030
 
8634
9031
  // src/hook/cursor-worktrees.ts
8635
- var import_node_fs16 = require("fs");
9032
+ var import_node_fs19 = require("fs");
8636
9033
  var import_node_path16 = require("path");
8637
9034
  var import_execa4 = require("execa");
8638
9035
  var import_node_readline3 = require("readline");
8639
9036
  function loadCursorWorktreesJson(rootPath) {
8640
9037
  const path = (0, import_node_path16.join)(rootPath, ".cursor", "worktrees.json");
8641
- if (!(0, import_node_fs16.existsSync)(path)) return null;
9038
+ if (!(0, import_node_fs19.existsSync)(path)) return null;
8642
9039
  try {
8643
- return JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
9040
+ return JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
8644
9041
  } catch {
8645
9042
  return null;
8646
9043
  }
@@ -8705,12 +9102,12 @@ function hasCursorWorktreeSetup(worktreePath, repoPath) {
8705
9102
  }
8706
9103
 
8707
9104
  // src/diff/diff.ts
8708
- var import_node_fs17 = require("fs");
9105
+ var import_node_fs20 = require("fs");
8709
9106
  var import_node_path17 = require("path");
8710
9107
  init_run();
8711
9108
  init_worktree();
8712
9109
  async function inspectGitWorktree(worktreePath) {
8713
- if (!worktreePath || !(0, import_node_fs17.existsSync)(worktreePath)) return "missing_worktree";
9110
+ if (!worktreePath || !(0, import_node_fs20.existsSync)(worktreePath)) return "missing_worktree";
8714
9111
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
8715
9112
  reject: false
8716
9113
  });
@@ -8718,7 +9115,7 @@ async function inspectGitWorktree(worktreePath) {
8718
9115
  return "ok";
8719
9116
  }
8720
9117
  async function initializeGitRepository(worktreePath) {
8721
- if (!worktreePath || !(0, import_node_fs17.existsSync)(worktreePath)) {
9118
+ if (!worktreePath || !(0, import_node_fs20.existsSync)(worktreePath)) {
8722
9119
  throw new Error("Worktree not found");
8723
9120
  }
8724
9121
  const status = await inspectGitWorktree(worktreePath);
@@ -9156,7 +9553,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
9156
9553
  assertSafeRelativePath(relativePath);
9157
9554
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
9158
9555
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9159
- const st = (0, import_node_fs17.statSync)(abs);
9556
+ const st = (0, import_node_fs20.statSync)(abs);
9160
9557
  if (!st.isFile()) {
9161
9558
  throw new Error(`Not a file: ${relativePath}`);
9162
9559
  }
@@ -9165,7 +9562,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
9165
9562
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
9166
9563
  );
9167
9564
  }
9168
- const buf = (0, import_node_fs17.readFileSync)(abs);
9565
+ const buf = (0, import_node_fs20.readFileSync)(abs);
9169
9566
  return {
9170
9567
  path: relativePath,
9171
9568
  contentBase64: buf.toString("base64"),
@@ -9176,11 +9573,11 @@ function readWorktreeFile(worktreePath, relativePath, opts) {
9176
9573
  assertSafeRelativePath(relativePath);
9177
9574
  const maxBytes = opts?.maxBytes ?? 2e5;
9178
9575
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9179
- const st = (0, import_node_fs17.statSync)(abs);
9576
+ const st = (0, import_node_fs20.statSync)(abs);
9180
9577
  if (!st.isFile()) {
9181
9578
  throw new Error(`Not a file: ${relativePath}`);
9182
9579
  }
9183
- const buf = (0, import_node_fs17.readFileSync)(abs);
9580
+ const buf = (0, import_node_fs20.readFileSync)(abs);
9184
9581
  if (isImageRelativePath(relativePath)) {
9185
9582
  const maxImageBytes = Math.max(maxBytes, 15e6);
9186
9583
  const truncated2 = buf.length > maxImageBytes;
@@ -9224,8 +9621,8 @@ function assertSafeRelativePath(relativePath) {
9224
9621
  function writeWorktreeFile(worktreePath, relativePath, content) {
9225
9622
  assertSafeRelativePath(relativePath);
9226
9623
  const abs = (0, import_node_path17.join)(worktreePath, relativePath);
9227
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(abs), { recursive: true });
9228
- (0, import_node_fs17.writeFileSync)(abs, content, "utf8");
9624
+ (0, import_node_fs20.mkdirSync)((0, import_node_path17.dirname)(abs), { recursive: true });
9625
+ (0, import_node_fs20.writeFileSync)(abs, content, "utf8");
9229
9626
  return { path: relativePath };
9230
9627
  }
9231
9628
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -9244,7 +9641,7 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
9244
9641
  }
9245
9642
 
9246
9643
  // src/skills/discover.ts
9247
- var import_node_fs18 = require("fs");
9644
+ var import_node_fs21 = require("fs");
9248
9645
  var import_node_os8 = require("os");
9249
9646
  var import_node_path18 = require("path");
9250
9647
  function toCommand(name) {
@@ -9278,7 +9675,7 @@ function parseFrontmatter(content) {
9278
9675
  }
9279
9676
  function readSkill(skillMd, source) {
9280
9677
  try {
9281
- const content = (0, import_node_fs18.readFileSync)(skillMd, "utf8");
9678
+ const content = (0, import_node_fs21.readFileSync)(skillMd, "utf8");
9282
9679
  const { name: fmName, description } = parseFrontmatter(content);
9283
9680
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
9284
9681
  const name = fmName || dirName;
@@ -9297,19 +9694,19 @@ function readSkill(skillMd, source) {
9297
9694
  }
9298
9695
  }
9299
9696
  function scanSkillsDir(dir, source, out) {
9300
- if (!(0, import_node_fs18.existsSync)(dir)) return;
9697
+ if (!(0, import_node_fs21.existsSync)(dir)) return;
9301
9698
  let entries;
9302
9699
  try {
9303
- entries = (0, import_node_fs18.readdirSync)(dir);
9700
+ entries = (0, import_node_fs21.readdirSync)(dir);
9304
9701
  } catch {
9305
9702
  return;
9306
9703
  }
9307
9704
  for (const entry of entries) {
9308
9705
  if (entry.startsWith(".")) continue;
9309
9706
  const skillMd = (0, import_node_path18.join)(dir, entry, "SKILL.md");
9310
- if (!(0, import_node_fs18.existsSync)(skillMd)) continue;
9707
+ if (!(0, import_node_fs21.existsSync)(skillMd)) continue;
9311
9708
  try {
9312
- if (!(0, import_node_fs18.statSync)(skillMd).isFile()) continue;
9709
+ if (!(0, import_node_fs21.statSync)(skillMd).isFile()) continue;
9313
9710
  } catch {
9314
9711
  continue;
9315
9712
  }
@@ -9318,12 +9715,12 @@ function scanSkillsDir(dir, source, out) {
9318
9715
  }
9319
9716
  }
9320
9717
  function scanClaudePluginSkills(pluginsRoot, out) {
9321
- if (!(0, import_node_fs18.existsSync)(pluginsRoot)) return;
9718
+ if (!(0, import_node_fs21.existsSync)(pluginsRoot)) return;
9322
9719
  const walk = (dir, depth, lookingForSkillsDir) => {
9323
9720
  if (depth > 7) return;
9324
9721
  let entries;
9325
9722
  try {
9326
- entries = (0, import_node_fs18.readdirSync)(dir);
9723
+ entries = (0, import_node_fs21.readdirSync)(dir);
9327
9724
  } catch {
9328
9725
  return;
9329
9726
  }
@@ -9335,7 +9732,7 @@ function scanClaudePluginSkills(pluginsRoot, out) {
9335
9732
  if (entry === "node_modules" || entry === ".git") continue;
9336
9733
  const full = (0, import_node_path18.join)(dir, entry);
9337
9734
  try {
9338
- if (!(0, import_node_fs18.statSync)(full).isDirectory()) continue;
9735
+ if (!(0, import_node_fs21.statSync)(full).isDirectory()) continue;
9339
9736
  } catch {
9340
9737
  continue;
9341
9738
  }
@@ -9375,7 +9772,7 @@ function discoverSkills(worktreePath) {
9375
9772
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
9376
9773
  }
9377
9774
  function readSkillBody(skillPath, maxChars = 12e3) {
9378
- const raw = (0, import_node_fs18.readFileSync)(skillPath, "utf8");
9775
+ const raw = (0, import_node_fs21.readFileSync)(skillPath, "utf8");
9379
9776
  if (raw.startsWith("---")) {
9380
9777
  const end = raw.indexOf("\n---", 3);
9381
9778
  if (end >= 0) {
@@ -9516,7 +9913,7 @@ function buildDiffCommentAttachment(input) {
9516
9913
  }
9517
9914
 
9518
9915
  // src/composer/stage-files.ts
9519
- var import_node_fs19 = require("fs");
9916
+ var import_node_fs22 = require("fs");
9520
9917
  var import_node_path19 = require("path");
9521
9918
  var import_node_crypto2 = require("crypto");
9522
9919
  init_workspace_scratch();
@@ -9554,21 +9951,21 @@ function imageMimeType(filePath) {
9554
9951
  }
9555
9952
  function ensureAttachmentsDir(worktreePath) {
9556
9953
  const dir = (0, import_node_path19.join)(worktreePath, ATTACHMENTS_DIR);
9557
- (0, import_node_fs19.mkdirSync)(dir, { recursive: true });
9954
+ (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
9558
9955
  const gi = (0, import_node_path19.join)(dir, ".gitignore");
9559
- if (!(0, import_node_fs19.existsSync)(gi)) {
9560
- (0, import_node_fs19.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
9956
+ if (!(0, import_node_fs22.existsSync)(gi)) {
9957
+ (0, import_node_fs22.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
9561
9958
  }
9562
9959
  return dir;
9563
9960
  }
9564
9961
  function uniqueAttachmentName(dir, originalName) {
9565
9962
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
9566
- if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
9963
+ if (!(0, import_node_fs22.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
9567
9964
  const ext = (0, import_node_path19.extname)(safe);
9568
9965
  const stem = ext ? safe.slice(0, -ext.length) : safe;
9569
9966
  for (let i = 1; i < 1e4; i++) {
9570
9967
  const candidate = `${stem}-${i}${ext}`;
9571
- if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
9968
+ if (!(0, import_node_fs22.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
9572
9969
  }
9573
9970
  return `${stem}-${(0, import_node_crypto2.randomUUID)()}${ext}`;
9574
9971
  }
@@ -9622,7 +10019,7 @@ function attachmentFromBuffer(name, buf, opts) {
9622
10019
  function attachmentFromAbsolutePath(absolutePath) {
9623
10020
  const name = (0, import_node_path19.basename)(absolutePath);
9624
10021
  try {
9625
- const st = (0, import_node_fs19.statSync)(absolutePath);
10022
+ const st = (0, import_node_fs22.statSync)(absolutePath);
9626
10023
  if (!st.isFile()) {
9627
10024
  return {
9628
10025
  id: (0, import_node_crypto2.randomUUID)(),
@@ -9631,7 +10028,7 @@ function attachmentFromAbsolutePath(absolutePath) {
9631
10028
  content: `(not a file: ${absolutePath})`
9632
10029
  };
9633
10030
  }
9634
- const buf = (0, import_node_fs19.readFileSync)(absolutePath);
10031
+ const buf = (0, import_node_fs22.readFileSync)(absolutePath);
9635
10032
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
9636
10033
  } catch (err) {
9637
10034
  return {
@@ -9649,13 +10046,13 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
9649
10046
  for (const abs of absolutePaths) {
9650
10047
  const originalName = (0, import_node_path19.basename)(abs);
9651
10048
  try {
9652
- const st = (0, import_node_fs19.statSync)(abs);
10049
+ const st = (0, import_node_fs22.statSync)(abs);
9653
10050
  if (!st.isFile()) continue;
9654
10051
  const name = uniqueAttachmentName(dir, originalName);
9655
10052
  const destAbs = (0, import_node_path19.join)(dir, name);
9656
- (0, import_node_fs19.copyFileSync)(abs, destAbs);
10053
+ (0, import_node_fs22.copyFileSync)(abs, destAbs);
9657
10054
  const rel = `${ATTACHMENTS_DIR}/${name}`;
9658
- const buf = (0, import_node_fs19.readFileSync)(destAbs);
10055
+ const buf = (0, import_node_fs22.readFileSync)(destAbs);
9659
10056
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9660
10057
  } catch (err) {
9661
10058
  out.push({
@@ -9678,7 +10075,7 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
9678
10075
  const buf = Buffer.from(item.dataBase64, "base64");
9679
10076
  const name = uniqueAttachmentName(dir, originalName);
9680
10077
  const destAbs = (0, import_node_path19.join)(dir, name);
9681
- (0, import_node_fs19.writeFileSync)(destAbs, buf);
10078
+ (0, import_node_fs22.writeFileSync)(destAbs, buf);
9682
10079
  const rel = `${ATTACHMENTS_DIR}/${name}`;
9683
10080
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
9684
10081
  } catch (err) {
@@ -9723,9 +10120,9 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
9723
10120
  const name = (0, import_node_path19.basename)(rel);
9724
10121
  try {
9725
10122
  const abs = (0, import_node_path19.join)(worktreePath, rel);
9726
- const st = (0, import_node_fs19.statSync)(abs);
10123
+ const st = (0, import_node_fs22.statSync)(abs);
9727
10124
  if (!st.isFile()) continue;
9728
- const buf = (0, import_node_fs19.readFileSync)(abs);
10125
+ const buf = (0, import_node_fs22.readFileSync)(abs);
9729
10126
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9730
10127
  } catch (err) {
9731
10128
  out.push({
@@ -10213,7 +10610,7 @@ async function confirmLand(thread, opts) {
10213
10610
  }
10214
10611
 
10215
10612
  // src/threads/create.ts
10216
- var import_node_fs20 = require("fs");
10613
+ var import_node_fs23 = require("fs");
10217
10614
  init_worktree();
10218
10615
  init_app_settings();
10219
10616
  init_thread_store();
@@ -10227,7 +10624,7 @@ async function createThread(input, _onSetupLine) {
10227
10624
  });
10228
10625
  await requireAgent(resolved.agent);
10229
10626
  const repoPath = await resolveRepoRoot(input.repoPath);
10230
- if (!(0, import_node_fs20.existsSync)(repoPath)) {
10627
+ if (!(0, import_node_fs23.existsSync)(repoPath)) {
10231
10628
  throw new Error(`Repo not found: ${repoPath}`);
10232
10629
  }
10233
10630
  let sourceRef = input.sourceRef;
@@ -10454,7 +10851,7 @@ async function forkThreadWorktree(input, onSetupLine) {
10454
10851
  }
10455
10852
 
10456
10853
  // src/threads/stack-layers.ts
10457
- var import_node_fs21 = require("fs");
10854
+ var import_node_fs24 = require("fs");
10458
10855
  init_run();
10459
10856
  init_stack();
10460
10857
  init_worktree();
@@ -10522,7 +10919,7 @@ async function openStackLayer(input, _onSetupLine) {
10522
10919
  let createdWorktree = false;
10523
10920
  const trees = await listWorktrees(repoPath);
10524
10921
  const checkedOut = trees.find((w) => w.branch === branchName);
10525
- if (checkedOut?.path && (0, import_node_fs21.existsSync)(checkedOut.path)) {
10922
+ if (checkedOut?.path && (0, import_node_fs24.existsSync)(checkedOut.path)) {
10526
10923
  if (input.reuseExistingWorktree !== false) {
10527
10924
  worktreePath = checkedOut.path;
10528
10925
  } else {
@@ -10664,7 +11061,7 @@ async function initStackFromThread(input, onSetupLine) {
10664
11061
  async function createPrStack(input, onSetupLine) {
10665
11062
  await requireAgent(input.agent);
10666
11063
  const repoPath = await resolveRepoRoot(input.repoPath);
10667
- if (!(0, import_node_fs21.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
11064
+ if (!(0, import_node_fs24.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
10668
11065
  if (!input.branches.length) throw new Error("At least one branch name required");
10669
11066
  const status = await detectGhStack(repoPath);
10670
11067
  if (!status.available) throw new Error(status.reason);
@@ -10731,7 +11128,7 @@ async function createPrStack(input, onSetupLine) {
10731
11128
  }
10732
11129
  }
10733
11130
  const claimed = new Set(threads.map((t) => t.worktreePath));
10734
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs21.existsSync)(bootstrap.worktreePath)) {
11131
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs24.existsSync)(bootstrap.worktreePath)) {
10735
11132
  try {
10736
11133
  await removeWorktree(repoPath, bootstrap.worktreePath, {
10737
11134
  deleteBranch: bootstrap.branchName
@@ -10753,8 +11150,8 @@ function stackAgentDefaultsFrom(input) {
10753
11150
  }
10754
11151
 
10755
11152
  // src/threads/adopt.ts
10756
- var import_node_child_process = require("child_process");
10757
- var import_node_fs22 = require("fs");
11153
+ var import_node_child_process2 = require("child_process");
11154
+ var import_node_fs25 = require("fs");
10758
11155
  var import_node_os9 = require("os");
10759
11156
  var import_node_path20 = require("path");
10760
11157
  var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
@@ -10779,21 +11176,21 @@ function mapAgentType(raw) {
10779
11176
  return null;
10780
11177
  }
10781
11178
  function resolveConductorCursorAgentId(workspacePath) {
10782
- if (!workspacePath || !(0, import_node_fs22.existsSync)(CURSOR_SDK_STORE)) return null;
11179
+ if (!workspacePath || !(0, import_node_fs25.existsSync)(CURSOR_SDK_STORE)) return null;
10783
11180
  const normalized = workspacePath.replace(/\/$/, "");
10784
11181
  let best = null;
10785
11182
  let hashes;
10786
11183
  try {
10787
- hashes = (0, import_node_fs22.readdirSync)(CURSOR_SDK_STORE);
11184
+ hashes = (0, import_node_fs25.readdirSync)(CURSOR_SDK_STORE);
10788
11185
  } catch {
10789
11186
  return null;
10790
11187
  }
10791
11188
  for (const hash of hashes) {
10792
11189
  const agentsFile = (0, import_node_path20.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
10793
- if (!(0, import_node_fs22.existsSync)(agentsFile)) continue;
11190
+ if (!(0, import_node_fs25.existsSync)(agentsFile)) continue;
10794
11191
  let text;
10795
11192
  try {
10796
- text = (0, import_node_fs22.readFileSync)(agentsFile, "utf8");
11193
+ text = (0, import_node_fs25.readFileSync)(agentsFile, "utf8");
10797
11194
  } catch {
10798
11195
  continue;
10799
11196
  }
@@ -10817,7 +11214,7 @@ function resolveConductorCursorAgentId(workspacePath) {
10817
11214
  return best?.agentId ?? null;
10818
11215
  }
10819
11216
  async function adoptThread(input) {
10820
- if (!(0, import_node_fs22.existsSync)(input.worktreePath)) {
11217
+ if (!(0, import_node_fs25.existsSync)(input.worktreePath)) {
10821
11218
  throw new Error(`Worktree not found: ${input.worktreePath}`);
10822
11219
  }
10823
11220
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -10844,18 +11241,18 @@ function conductorDbPath() {
10844
11241
  return CONDUCTOR_DB;
10845
11242
  }
10846
11243
  function listConductorWorkspaces() {
10847
- if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
11244
+ if (!(0, import_node_fs25.existsSync)(CONDUCTOR_DB)) {
10848
11245
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
10849
11246
  }
10850
- const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
11247
+ const tmp = (0, import_node_fs25.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
10851
11248
  const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
10852
11249
  try {
10853
- (0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
11250
+ (0, import_node_fs25.copyFileSync)(CONDUCTOR_DB, snapshot);
10854
11251
  for (const suffix of ["-wal", "-shm"]) {
10855
11252
  const src = `${CONDUCTOR_DB}${suffix}`;
10856
- if ((0, import_node_fs22.existsSync)(src)) {
11253
+ if ((0, import_node_fs25.existsSync)(src)) {
10857
11254
  try {
10858
- (0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
11255
+ (0, import_node_fs25.copyFileSync)(src, `${snapshot}${suffix}`);
10859
11256
  } catch {
10860
11257
  }
10861
11258
  }
@@ -10931,22 +11328,22 @@ function listConductorWorkspaces() {
10931
11328
  db.close();
10932
11329
  }
10933
11330
  } finally {
10934
- (0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
11331
+ (0, import_node_fs25.rmSync)(tmp, { recursive: true, force: true });
10935
11332
  }
10936
11333
  }
10937
11334
  function importConductorWorkspace(workspaceId) {
10938
- if (!(0, import_node_fs22.existsSync)(CONDUCTOR_DB)) {
11335
+ if (!(0, import_node_fs25.existsSync)(CONDUCTOR_DB)) {
10939
11336
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
10940
11337
  }
10941
- const tmp = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
11338
+ const tmp = (0, import_node_fs25.mkdtempSync)((0, import_node_path20.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
10942
11339
  const snapshot = (0, import_node_path20.join)(tmp, "conductor.db");
10943
11340
  try {
10944
- (0, import_node_fs22.copyFileSync)(CONDUCTOR_DB, snapshot);
11341
+ (0, import_node_fs25.copyFileSync)(CONDUCTOR_DB, snapshot);
10945
11342
  for (const suffix of ["-wal", "-shm"]) {
10946
11343
  const src = `${CONDUCTOR_DB}${suffix}`;
10947
- if ((0, import_node_fs22.existsSync)(src)) {
11344
+ if ((0, import_node_fs25.existsSync)(src)) {
10948
11345
  try {
10949
- (0, import_node_fs22.copyFileSync)(src, `${snapshot}${suffix}`);
11346
+ (0, import_node_fs25.copyFileSync)(src, `${snapshot}${suffix}`);
10950
11347
  } catch {
10951
11348
  }
10952
11349
  }
@@ -10964,7 +11361,7 @@ function importConductorWorkspace(workspaceId) {
10964
11361
  ).get(workspaceId);
10965
11362
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
10966
11363
  const worktreePath = String(row.workspacePath);
10967
- if (!(0, import_node_fs22.existsSync)(worktreePath)) {
11364
+ if (!(0, import_node_fs25.existsSync)(worktreePath)) {
10968
11365
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
10969
11366
  }
10970
11367
  let sessionId = null;
@@ -11004,7 +11401,7 @@ function importConductorWorkspace(workspaceId) {
11004
11401
  }
11005
11402
  let repoPath = worktreePath;
11006
11403
  try {
11007
- repoPath = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
11404
+ repoPath = (0, import_node_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
11008
11405
  cwd: worktreePath,
11009
11406
  encoding: "utf8"
11010
11407
  }).trim();
@@ -11027,7 +11424,7 @@ function importConductorWorkspace(workspaceId) {
11027
11424
  db.close();
11028
11425
  }
11029
11426
  } finally {
11030
- (0, import_node_fs22.rmSync)(tmp, { recursive: true, force: true });
11427
+ (0, import_node_fs25.rmSync)(tmp, { recursive: true, force: true });
11031
11428
  }
11032
11429
  }
11033
11430
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -11036,7 +11433,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
11036
11433
 
11037
11434
  // src/orchestrator/orchestrator.ts
11038
11435
  var import_node_events = require("events");
11039
- var import_node_fs28 = require("fs");
11436
+ var import_node_fs31 = require("fs");
11040
11437
  init_error_detail();
11041
11438
  init_agents();
11042
11439
  init_worktree();
@@ -11057,7 +11454,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
11057
11454
  }
11058
11455
 
11059
11456
  // src/git/orphan-cleanup.ts
11060
- var import_node_fs23 = require("fs");
11457
+ var import_node_fs26 = require("fs");
11061
11458
  var import_node_path21 = require("path");
11062
11459
  init_worktree();
11063
11460
  init_thread_store();
@@ -11073,9 +11470,9 @@ async function findOrphanWorktrees(repoPaths) {
11073
11470
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
11074
11471
  );
11075
11472
  const homeRoot = sideboardWorkspacesDir();
11076
- if ((0, import_node_fs23.existsSync)(homeRoot)) {
11473
+ if ((0, import_node_fs26.existsSync)(homeRoot)) {
11077
11474
  try {
11078
- for (const entry of (0, import_node_fs23.readdirSync)(homeRoot, { withFileTypes: true })) {
11475
+ for (const entry of (0, import_node_fs26.readdirSync)(homeRoot, { withFileTypes: true })) {
11079
11476
  if (!entry.isDirectory()) continue;
11080
11477
  void entry;
11081
11478
  }
@@ -11085,7 +11482,7 @@ async function findOrphanWorktrees(repoPaths) {
11085
11482
  const orphans = [];
11086
11483
  const seen = /* @__PURE__ */ new Set();
11087
11484
  for (const repoPath of repos) {
11088
- if (!repoPath || !(0, import_node_fs23.existsSync)(repoPath)) continue;
11485
+ if (!repoPath || !(0, import_node_fs26.existsSync)(repoPath)) continue;
11089
11486
  try {
11090
11487
  const wts = await listWorktrees(repoPath);
11091
11488
  for (const wt of wts) {
@@ -11096,7 +11493,7 @@ async function findOrphanWorktrees(repoPaths) {
11096
11493
  seen.add(path);
11097
11494
  let mtimeMs = 0;
11098
11495
  try {
11099
- mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
11496
+ mtimeMs = (0, import_node_fs26.statSync)(path).mtimeMs;
11100
11497
  } catch {
11101
11498
  mtimeMs = 0;
11102
11499
  }
@@ -11106,16 +11503,16 @@ async function findOrphanWorktrees(repoPaths) {
11106
11503
  }
11107
11504
  try {
11108
11505
  const root = worktreesRoot(repoPath);
11109
- if ((0, import_node_fs23.existsSync)(root)) {
11110
- for (const entry of (0, import_node_fs23.readdirSync)(root, { withFileTypes: true })) {
11506
+ if ((0, import_node_fs26.existsSync)(root)) {
11507
+ for (const entry of (0, import_node_fs26.readdirSync)(root, { withFileTypes: true })) {
11111
11508
  if (!entry.isDirectory()) continue;
11112
11509
  const path = (0, import_node_path21.join)(root, entry.name).replace(/\/$/, "");
11113
11510
  if (known.has(path) || seen.has(path)) continue;
11114
- if (!(0, import_node_fs23.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
11511
+ if (!(0, import_node_fs26.existsSync)((0, import_node_path21.join)(path, ".git"))) continue;
11115
11512
  seen.add(path);
11116
11513
  let mtimeMs = 0;
11117
11514
  try {
11118
- mtimeMs = (0, import_node_fs23.statSync)(path).mtimeMs;
11515
+ mtimeMs = (0, import_node_fs26.statSync)(path).mtimeMs;
11119
11516
  } catch {
11120
11517
  mtimeMs = Date.now();
11121
11518
  }
@@ -11261,7 +11658,7 @@ async function applyThreadIntoMain(thread, opts) {
11261
11658
  }
11262
11659
 
11263
11660
  // src/git/clone-repo.ts
11264
- var import_node_fs24 = require("fs");
11661
+ var import_node_fs27 = require("fs");
11265
11662
  var import_node_path22 = require("path");
11266
11663
  var import_execa6 = require("execa");
11267
11664
  init_paths();
@@ -11277,7 +11674,7 @@ async function cloneRepoIntoSideboard(opts) {
11277
11674
  }
11278
11675
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
11279
11676
  const dest = (0, import_node_path22.join)(sideboardReposDir(), name);
11280
- if ((0, import_node_fs24.existsSync)(dest)) {
11677
+ if ((0, import_node_fs27.existsSync)(dest)) {
11281
11678
  const repoPath2 = await resolveRepoRoot(dest);
11282
11679
  const workspace2 = await ensureWorkspace(repoPath2);
11283
11680
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -11299,7 +11696,7 @@ init_orchestrator_capable();
11299
11696
 
11300
11697
  // src/review/request-review.ts
11301
11698
  var import_node_crypto5 = require("crypto");
11302
- var import_node_fs25 = require("fs");
11699
+ var import_node_fs28 = require("fs");
11303
11700
  var import_node_path23 = require("path");
11304
11701
  init_global_workspace();
11305
11702
  init_thread_store();
@@ -11444,9 +11841,9 @@ function shouldRefreshReviewRequestTemplate(content) {
11444
11841
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
11445
11842
  }
11446
11843
  function readTextIfPresent(abs) {
11447
- if (!(0, import_node_fs25.existsSync)(abs)) return null;
11844
+ if (!(0, import_node_fs28.existsSync)(abs)) return null;
11448
11845
  try {
11449
- const content = (0, import_node_fs25.readFileSync)(abs, "utf8");
11846
+ const content = (0, import_node_fs28.readFileSync)(abs, "utf8");
11450
11847
  return content.trim() ? content : null;
11451
11848
  } catch {
11452
11849
  return null;
@@ -11454,9 +11851,9 @@ function readTextIfPresent(abs) {
11454
11851
  }
11455
11852
  function ensureAttachmentsGitignore(worktreePath) {
11456
11853
  const gitignoreAbs = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
11457
- if ((0, import_node_fs25.existsSync)(gitignoreAbs)) return;
11458
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
11459
- (0, import_node_fs25.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11854
+ if ((0, import_node_fs28.existsSync)(gitignoreAbs)) return;
11855
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
11856
+ (0, import_node_fs28.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11460
11857
  }
11461
11858
  function resolveReviewGuidelines(worktreePath) {
11462
11859
  const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
@@ -11490,8 +11887,8 @@ function resolveReviewGuidelines(worktreePath) {
11490
11887
  };
11491
11888
  }
11492
11889
  ensureAttachmentsGitignore(worktreePath);
11493
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
11494
- (0, import_node_fs25.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11890
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
11891
+ (0, import_node_fs28.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11495
11892
  return {
11496
11893
  path: REVIEW_REQUEST_PATH,
11497
11894
  name: REVIEW_REQUEST_NAME,
@@ -11513,7 +11910,7 @@ function ensureReviewRequestFile(worktreePath) {
11513
11910
  const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
11514
11911
  const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
11515
11912
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
11516
- const path = (0, import_node_fs25.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
11913
+ const path = (0, import_node_fs28.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
11517
11914
  return {
11518
11915
  path,
11519
11916
  name: REVIEW_REQUEST_NAME,
@@ -11521,8 +11918,8 @@ function ensureReviewRequestFile(worktreePath) {
11521
11918
  source: "local"
11522
11919
  };
11523
11920
  }
11524
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
11525
- (0, import_node_fs25.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11921
+ (0, import_node_fs28.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
11922
+ (0, import_node_fs28.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11526
11923
  return {
11527
11924
  path: REPO_REVIEW_PATH,
11528
11925
  name: REPO_REVIEW_NAME,
@@ -11802,7 +12199,7 @@ var Orchestrator = class {
11802
12199
  }
11803
12200
  continue;
11804
12201
  }
11805
- if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
12202
+ if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
11806
12203
  setStatus(thread.id, "broken", "Worktree missing on disk");
11807
12204
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
11808
12205
  continue;
@@ -12219,13 +12616,14 @@ var Orchestrator = class {
12219
12616
  "Files column: MCP present_files (brightsy account storage or memory).",
12220
12617
  "Do not say artifacts/CMS UI are unavailable."
12221
12618
  ].join(" ") : null;
12619
+ const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
12222
12620
  const agentPrompt = [
12223
12621
  thread.planMode ? PLAN_MODE_INSTRUCTION : null,
12224
12622
  orchestrationReminder,
12623
+ worktreeReminder,
12225
12624
  artifactReminder,
12226
12625
  expandedPrompt
12227
12626
  ].filter(Boolean).join("\n\n");
12228
- const instructionFiles = loadAgentInstructions(thread.worktreePath, thread.agent);
12229
12627
  if (thread.attachments.length > 0) {
12230
12628
  updateThread(threadId, { attachments: [] });
12231
12629
  }
@@ -12253,7 +12651,6 @@ var Orchestrator = class {
12253
12651
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
12254
12652
  customPrompt: settings?.prompts?.renameBranch
12255
12653
  }) : null;
12256
- const instructions = fresh.agent === "claude" || isBrightsy ? null : formatAgentInstructions(instructionFiles);
12257
12654
  let seed = null;
12258
12655
  if (!fresh.sessionId) {
12259
12656
  const prior = fresh.messages.slice(0, -1);
@@ -12274,12 +12671,12 @@ var Orchestrator = class {
12274
12671
  });
12275
12672
  }
12276
12673
  }
12277
- const cachedPrefix = [
12674
+ const cachedPrefix = fresh.sessionId ? "" : [
12278
12675
  coordinatorDirective,
12279
12676
  worktreeDirective,
12280
12677
  artifactDirective,
12281
12678
  renameBranchDirective,
12282
- ...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
12679
+ seed
12283
12680
  ].filter(Boolean).join("\n\n---\n\n");
12284
12681
  try {
12285
12682
  const stderrTail = [];
@@ -12366,15 +12763,11 @@ var Orchestrator = class {
12366
12763
  const retryThread = this.requireThread(threadId);
12367
12764
  const prior = retryThread.messages.slice(0, -1);
12368
12765
  const retrySeed = buildSessionSeed(prior);
12369
- const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
12370
- loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
12371
- );
12372
12766
  const retryPrefix = [
12373
12767
  coordinatorDirective,
12374
12768
  worktreeDirective,
12375
12769
  artifactDirective,
12376
12770
  renameBranchDirective,
12377
- retryInstructions,
12378
12771
  retrySeed
12379
12772
  ].filter(Boolean).join("\n\n---\n\n");
12380
12773
  const retryHandle = await spawnAgentTurn(
@@ -13209,7 +13602,7 @@ var Orchestrator = class {
13209
13602
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
13210
13603
  return restored2;
13211
13604
  }
13212
- if (!(0, import_node_fs28.existsSync)(thread.worktreePath)) {
13605
+ if (!(0, import_node_fs31.existsSync)(thread.worktreePath)) {
13213
13606
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
13214
13607
  const { execa: execa7 } = await import("execa");
13215
13608
  const slug = thread.worktreePath.split("/").pop();
@@ -13465,6 +13858,7 @@ function mcpArchiveBlockedReason(thread) {
13465
13858
  }
13466
13859
 
13467
13860
  // src/mcp/server.ts
13861
+ init_profile();
13468
13862
  var MAX_ORCH_THREADS = 5;
13469
13863
  var CREATE_THREAD_TIMEOUT_MS = 9e4;
13470
13864
  function withTimeout(promise, ms, label) {
@@ -13578,14 +13972,13 @@ async function startMcpServer() {
13578
13972
  ),
13579
13973
  artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
13580
13974
  },
13581
- async ({ title, type, content, artifact_id }) => {
13975
+ async ({ title, type, artifact_id }) => {
13582
13976
  const id = artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
13583
13977
  const payload = {
13584
13978
  ok: true,
13585
13979
  artifact_id: id,
13586
13980
  title,
13587
13981
  type,
13588
- content,
13589
13982
  message: "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
13590
13983
  };
13591
13984
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13638,7 +14031,6 @@ async function startMcpServer() {
13638
14031
  ok: true,
13639
14032
  path,
13640
14033
  title: title?.trim() || "Plan",
13641
- content,
13642
14034
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
13643
14035
  };
13644
14036
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13668,9 +14060,6 @@ async function startMcpServer() {
13668
14060
  datasource: args.datasource ?? (args.resource ? "inline" : "brightsy"),
13669
14061
  resource_id: args.resource_id,
13670
14062
  record_id: args.record_id,
13671
- resource: args.resource,
13672
- record: args.record,
13673
- records: args.records,
13674
14063
  message: "Schema pane accepted. Sideboard desktop opens the CMS column beside chat."
13675
14064
  };
13676
14065
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
@@ -13698,744 +14087,747 @@ async function startMcpServer() {
13698
14087
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
13699
14088
  }
13700
14089
  );
13701
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
13702
- const accountDefaults = resolveThreadDefaults2();
13703
- const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
13704
- server.tool(
13705
- "create_thread",
13706
- `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.`,
13707
- {
13708
- sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
13709
- sourceRef: import_zod.z.string(),
13710
- agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
13711
- model: import_zod.z.string().nullable().optional().describe(
13712
- `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
13713
- ),
13714
- repoPath: import_zod.z.string(),
13715
- title: import_zod.z.string().optional(),
13716
- parentThreadId: import_zod.z.string().optional().describe(
13717
- "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
13718
- )
13719
- },
13720
- async (args) => {
13721
- const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
13722
- let parentId = args.parentThreadId?.trim() || "";
13723
- let parent = parentId ? orch.getThread(parentId) : null;
13724
- let parentCorrectedFrom;
13725
- if (envParentId) {
13726
- const envParent = orch.getThread(envParentId);
13727
- if (envParent) {
13728
- if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
13729
- else if (!parentId) parentCorrectedFrom = void 0;
13730
- parentId = envParentId;
13731
- parent = envParent;
14090
+ if (sideboardMcpProfile() !== "worktree") {
14091
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
14092
+ const accountDefaults = resolveThreadDefaults2();
14093
+ const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
14094
+ server.tool(
14095
+ "create_thread",
14096
+ `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.`,
14097
+ {
14098
+ sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
14099
+ sourceRef: import_zod.z.string(),
14100
+ agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
14101
+ model: import_zod.z.string().nullable().optional().describe(
14102
+ `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
14103
+ ),
14104
+ repoPath: import_zod.z.string(),
14105
+ title: import_zod.z.string().optional(),
14106
+ parentThreadId: import_zod.z.string().optional().describe(
14107
+ "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
14108
+ )
14109
+ },
14110
+ async (args) => {
14111
+ const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
14112
+ let parentId = args.parentThreadId?.trim() || "";
14113
+ let parent = parentId ? orch.getThread(parentId) : null;
14114
+ let parentCorrectedFrom;
14115
+ if (envParentId) {
14116
+ const envParent = orch.getThread(envParentId);
14117
+ if (envParent) {
14118
+ if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
14119
+ else if (!parentId) parentCorrectedFrom = void 0;
14120
+ parentId = envParentId;
14121
+ parent = envParent;
14122
+ }
13732
14123
  }
13733
- }
13734
- if (parentId && !parent) {
13735
- parentCorrectedFrom = parentId;
13736
- parentId = "";
13737
- parent = null;
13738
- }
13739
- if (parentId) {
13740
- const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
13741
- if (children.length >= MAX_ORCH_THREADS) {
13742
- return {
13743
- content: [
13744
- {
13745
- type: "text",
13746
- text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
13747
- }
13748
- ],
13749
- isError: true
14124
+ if (parentId && !parent) {
14125
+ parentCorrectedFrom = parentId;
14126
+ parentId = "";
14127
+ parent = null;
14128
+ }
14129
+ if (parentId) {
14130
+ const children = orch.getThreads(false).filter((t) => t.parentThreadId === parentId);
14131
+ if (children.length >= MAX_ORCH_THREADS) {
14132
+ return {
14133
+ content: [
14134
+ {
14135
+ type: "text",
14136
+ text: `Thread-creation cap (${MAX_ORCH_THREADS}) reached for this orchestration session`
14137
+ }
14138
+ ],
14139
+ isError: true
14140
+ };
14141
+ }
14142
+ }
14143
+ let agentArg = args.agent;
14144
+ let agentCoercedFrom;
14145
+ const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
14146
+ if (resolvedProbe === "codex") {
14147
+ agentCoercedFrom = agentArg ?? "codex";
14148
+ const accountAgent = resolveNewThreadOptions2({}).agent;
14149
+ agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
14150
+ }
14151
+ const opts = resolveNewThreadOptions2({
14152
+ agent: agentArg,
14153
+ model: args.model
14154
+ });
14155
+ try {
14156
+ const thread = await withTimeout(
14157
+ orch.createThread({
14158
+ sourceType: args.sourceType,
14159
+ sourceRef: args.sourceRef,
14160
+ agent: opts.agent,
14161
+ model: opts.model,
14162
+ effort: opts.effort,
14163
+ fast: opts.fast,
14164
+ repoPath: args.repoPath,
14165
+ title: args.title,
14166
+ parentThreadId: parentId || null
14167
+ }),
14168
+ CREATE_THREAD_TIMEOUT_MS,
14169
+ "create_thread"
14170
+ );
14171
+ const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
14172
+ return {
14173
+ content: [
14174
+ {
14175
+ type: "text",
14176
+ text: JSON.stringify({
14177
+ id: thread.id,
14178
+ title: thread.title,
14179
+ branchName: thread.branchName,
14180
+ worktreePath: thread.worktreePath,
14181
+ agent: thread.agent,
14182
+ model: thread.model,
14183
+ status: thread.status,
14184
+ link: `sideboard://thread/${thread.id}`,
14185
+ parentThreadId: thread.parentThreadId,
14186
+ ...agentCoercedFrom ? {
14187
+ agentCoercedFrom,
14188
+ note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
14189
+ } : {},
14190
+ ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
14191
+ })
14192
+ }
14193
+ ]
14194
+ };
14195
+ } catch (err) {
14196
+ const message = err instanceof Error ? err.message : String(err);
14197
+ return {
14198
+ content: [
14199
+ {
14200
+ type: "text",
14201
+ text: `create_thread failed: ${message}`
14202
+ }
14203
+ ],
14204
+ isError: true
13750
14205
  };
13751
14206
  }
13752
14207
  }
13753
- let agentArg = args.agent;
13754
- let agentCoercedFrom;
13755
- const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
13756
- if (resolvedProbe === "codex") {
13757
- agentCoercedFrom = agentArg ?? "codex";
13758
- const accountAgent = resolveNewThreadOptions2({}).agent;
13759
- agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
13760
- }
13761
- const opts = resolveNewThreadOptions2({
13762
- agent: agentArg,
13763
- model: args.model
13764
- });
13765
- try {
13766
- const thread = await withTimeout(
13767
- orch.createThread({
13768
- sourceType: args.sourceType,
13769
- sourceRef: args.sourceRef,
13770
- agent: opts.agent,
13771
- model: opts.model,
13772
- effort: opts.effort,
13773
- fast: opts.fast,
13774
- repoPath: args.repoPath,
13775
- title: args.title,
13776
- parentThreadId: parentId || null
13777
- }),
13778
- CREATE_THREAD_TIMEOUT_MS,
13779
- "create_thread"
13780
- );
13781
- const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
14208
+ );
14209
+ server.tool(
14210
+ "send_to_thread",
14211
+ "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.",
14212
+ {
14213
+ ref: import_zod.z.string(),
14214
+ prompt: import_zod.z.string(),
14215
+ force_stop: import_zod.z.boolean().optional()
14216
+ },
14217
+ async ({ ref, prompt, force_stop }) => {
14218
+ if (force_stop) {
14219
+ const existing = orch.getThread(ref);
14220
+ if (existing) {
14221
+ orch.stop(ref, { clearQueue: true });
14222
+ }
14223
+ }
14224
+ const thread = await orch.send(ref, prompt);
13782
14225
  return {
13783
14226
  content: [
13784
14227
  {
13785
14228
  type: "text",
13786
14229
  text: JSON.stringify({
13787
14230
  id: thread.id,
13788
- title: thread.title,
13789
- branchName: thread.branchName,
13790
- worktreePath: thread.worktreePath,
13791
- agent: thread.agent,
13792
- model: thread.model,
13793
14231
  status: thread.status,
13794
- link: `sideboard://thread/${thread.id}`,
13795
- parentThreadId: thread.parentThreadId,
13796
- ...agentCoercedFrom ? {
13797
- agentCoercedFrom,
13798
- note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
13799
- } : {},
13800
- ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
14232
+ queueLength: thread.queue.length,
14233
+ forceStopped: Boolean(force_stop)
13801
14234
  })
13802
14235
  }
13803
14236
  ]
13804
14237
  };
13805
- } catch (err) {
13806
- const message = err instanceof Error ? err.message : String(err);
14238
+ }
14239
+ );
14240
+ server.tool(
14241
+ "wait_for_turn",
14242
+ "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
14243
+ {
14244
+ ref: import_zod.z.string(),
14245
+ timeoutMs: import_zod.z.number().optional()
14246
+ },
14247
+ async ({ ref, timeoutMs }) => {
14248
+ const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
14249
+ const result = orch.getTurnResult(thread.id);
13807
14250
  return {
13808
14251
  content: [
13809
14252
  {
13810
14253
  type: "text",
13811
- text: `create_thread failed: ${message}`
14254
+ text: JSON.stringify({
14255
+ id: thread.id,
14256
+ status: result.status,
14257
+ text: result.text
14258
+ })
13812
14259
  }
13813
- ],
13814
- isError: true
14260
+ ]
13815
14261
  };
13816
14262
  }
13817
- }
13818
- );
13819
- server.tool(
13820
- "send_to_thread",
13821
- "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.",
13822
- {
13823
- ref: import_zod.z.string(),
13824
- prompt: import_zod.z.string(),
13825
- force_stop: import_zod.z.boolean().optional()
13826
- },
13827
- async ({ ref, prompt, force_stop }) => {
13828
- if (force_stop) {
13829
- const existing = orch.getThread(ref);
13830
- if (existing) {
13831
- orch.stop(ref, { clearQueue: true });
13832
- }
14263
+ );
14264
+ server.tool(
14265
+ "get_turn_result",
14266
+ "Final assistant message only (not full transcript)",
14267
+ { ref: import_zod.z.string() },
14268
+ async ({ ref }) => {
14269
+ const result = orch.getTurnResult(ref);
14270
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
13833
14271
  }
13834
- const thread = await orch.send(ref, prompt);
13835
- return {
13836
- content: [
13837
- {
13838
- type: "text",
13839
- text: JSON.stringify({
13840
- id: thread.id,
13841
- status: thread.status,
13842
- queueLength: thread.queue.length,
13843
- forceStopped: Boolean(force_stop)
13844
- })
13845
- }
13846
- ]
13847
- };
13848
- }
13849
- );
13850
- server.tool(
13851
- "wait_for_turn",
13852
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
13853
- {
13854
- ref: import_zod.z.string(),
13855
- timeoutMs: import_zod.z.number().optional()
13856
- },
13857
- async ({ ref, timeoutMs }) => {
13858
- const thread = await orch.waitForTurn(ref, timeoutMs ?? 6e5);
13859
- const result = orch.getTurnResult(thread.id);
13860
- return {
13861
- content: [
13862
- {
13863
- type: "text",
13864
- text: JSON.stringify({
13865
- id: thread.id,
13866
- status: result.status,
13867
- text: result.text
13868
- })
13869
- }
13870
- ]
13871
- };
13872
- }
13873
- );
13874
- server.tool(
13875
- "get_turn_result",
13876
- "Final assistant message only (not full transcript)",
13877
- { ref: import_zod.z.string() },
13878
- async ({ ref }) => {
13879
- const result = orch.getTurnResult(ref);
13880
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
13881
- }
13882
- );
13883
- server.tool(
13884
- "stop_thread",
13885
- "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.",
13886
- {
13887
- ref: import_zod.z.string(),
13888
- force: import_zod.z.boolean().optional()
13889
- },
13890
- async ({ ref, force }) => {
13891
- const t = orch.getThread(ref);
13892
- if (!t) {
14272
+ );
14273
+ server.tool(
14274
+ "stop_thread",
14275
+ "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.",
14276
+ {
14277
+ ref: import_zod.z.string(),
14278
+ force: import_zod.z.boolean().optional()
14279
+ },
14280
+ async ({ ref, force }) => {
14281
+ const t = orch.getThread(ref);
14282
+ if (!t) {
14283
+ return {
14284
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14285
+ isError: true
14286
+ };
14287
+ }
14288
+ const clearQueue = force !== false;
14289
+ const hadQueued = t.queue.length > 0;
14290
+ const stopped = orch.stop(ref, { clearQueue });
13893
14291
  return {
13894
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
13895
- isError: true
14292
+ content: [
14293
+ {
14294
+ type: "text",
14295
+ text: JSON.stringify({
14296
+ id: stopped.id,
14297
+ status: stopped.status,
14298
+ clearedQueue: clearQueue && hadQueued
14299
+ })
14300
+ }
14301
+ ]
13896
14302
  };
13897
14303
  }
13898
- const clearQueue = force !== false;
13899
- const hadQueued = t.queue.length > 0;
13900
- const stopped = orch.stop(ref, { clearQueue });
13901
- return {
13902
- content: [
13903
- {
13904
- type: "text",
13905
- text: JSON.stringify({
13906
- id: stopped.id,
13907
- status: stopped.status,
13908
- clearedQueue: clearQueue && hadQueued
13909
- })
13910
- }
13911
- ]
13912
- };
13913
- }
13914
- );
13915
- server.tool(
13916
- "archive_thread",
13917
- "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.",
13918
- { ref: import_zod.z.string() },
13919
- async ({ ref }) => {
13920
- const t = orch.getThread(ref);
13921
- if (!t) {
14304
+ );
14305
+ server.tool(
14306
+ "archive_thread",
14307
+ "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.",
14308
+ { ref: import_zod.z.string() },
14309
+ async ({ ref }) => {
14310
+ const t = orch.getThread(ref);
14311
+ if (!t) {
14312
+ return {
14313
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14314
+ isError: true
14315
+ };
14316
+ }
14317
+ const blocked = mcpArchiveBlockedReason(t);
14318
+ if (blocked) {
14319
+ return {
14320
+ content: [{ type: "text", text: blocked }],
14321
+ isError: true
14322
+ };
14323
+ }
14324
+ const archived = await orch.archive(ref);
13922
14325
  return {
13923
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
13924
- isError: true
14326
+ content: [
14327
+ {
14328
+ type: "text",
14329
+ text: JSON.stringify({
14330
+ id: archived.id,
14331
+ status: archived.status
14332
+ })
14333
+ }
14334
+ ]
13925
14335
  };
13926
14336
  }
13927
- const blocked = mcpArchiveBlockedReason(t);
13928
- if (blocked) {
13929
- return {
13930
- content: [{ type: "text", text: blocked }],
13931
- isError: true
13932
- };
14337
+ );
14338
+ server.tool(
14339
+ "restore_thread",
14340
+ "Restore an archived thread (recreates worktree from branch when needed)",
14341
+ { ref: import_zod.z.string() },
14342
+ async ({ ref }) => {
14343
+ try {
14344
+ const restored = await orch.restore(ref);
14345
+ return {
14346
+ content: [
14347
+ {
14348
+ type: "text",
14349
+ text: JSON.stringify({
14350
+ id: restored.id,
14351
+ status: restored.status,
14352
+ worktreePath: restored.worktreePath
14353
+ })
14354
+ }
14355
+ ]
14356
+ };
14357
+ } catch (err) {
14358
+ const message = err instanceof Error ? err.message : String(err);
14359
+ return { content: [{ type: "text", text: message }], isError: true };
14360
+ }
13933
14361
  }
13934
- const archived = await orch.archive(ref);
13935
- return {
13936
- content: [
13937
- {
13938
- type: "text",
13939
- text: JSON.stringify({
13940
- id: archived.id,
13941
- status: archived.status
13942
- })
13943
- }
13944
- ]
13945
- };
13946
- }
13947
- );
13948
- server.tool(
13949
- "restore_thread",
13950
- "Restore an archived thread (recreates worktree from branch when needed)",
13951
- { ref: import_zod.z.string() },
13952
- async ({ ref }) => {
13953
- try {
13954
- const restored = await orch.restore(ref);
14362
+ );
14363
+ server.tool(
14364
+ "get_diff",
14365
+ "Compact diff summary (capped hunks, paginated)",
14366
+ {
14367
+ ref: import_zod.z.string(),
14368
+ maxFiles: import_zod.z.number().optional()
14369
+ },
14370
+ async ({ ref, maxFiles }) => {
14371
+ const summary = await orch.diffSummary(ref);
13955
14372
  return {
13956
14373
  content: [
13957
14374
  {
13958
14375
  type: "text",
13959
14376
  text: JSON.stringify({
13960
- id: restored.id,
13961
- status: restored.status,
13962
- worktreePath: restored.worktreePath
14377
+ ...summary,
14378
+ files: summary.files.slice(0, maxFiles ?? 10)
13963
14379
  })
13964
14380
  }
13965
14381
  ]
13966
14382
  };
13967
- } catch (err) {
13968
- const message = err instanceof Error ? err.message : String(err);
13969
- return { content: [{ type: "text", text: message }], isError: true };
13970
14383
  }
13971
- }
13972
- );
13973
- server.tool(
13974
- "get_diff",
13975
- "Compact diff summary (capped hunks, paginated)",
13976
- {
13977
- ref: import_zod.z.string(),
13978
- maxFiles: import_zod.z.number().optional()
13979
- },
13980
- async ({ ref, maxFiles }) => {
13981
- const summary = await orch.diffSummary(ref);
13982
- return {
13983
- content: [
13984
- {
13985
- type: "text",
13986
- text: JSON.stringify({
13987
- ...summary,
13988
- files: summary.files.slice(0, maxFiles ?? 10)
13989
- })
14384
+ );
14385
+ server.tool(
14386
+ "request_review",
14387
+ '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.',
14388
+ { ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
14389
+ async ({ ref }) => {
14390
+ try {
14391
+ const tab = await orch.requestReview(ref);
14392
+ const from = orch.getThread(ref);
14393
+ return {
14394
+ content: [
14395
+ {
14396
+ type: "text",
14397
+ text: JSON.stringify({
14398
+ id: tab.id,
14399
+ title: tab.title,
14400
+ status: tab.status,
14401
+ fromThreadId: from?.id ?? ref,
14402
+ link: `sideboard://thread/${tab.id}`
14403
+ })
14404
+ }
14405
+ ]
14406
+ };
14407
+ } catch (err) {
14408
+ const message = err instanceof Error ? err.message : String(err);
14409
+ return { content: [{ type: "text", text: message }], isError: true };
14410
+ }
14411
+ }
14412
+ );
14413
+ const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
14414
+ server.tool(
14415
+ "list_models",
14416
+ "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.",
14417
+ {
14418
+ agent: agentEnum.optional().describe("Limit to one agent; omit for all")
14419
+ },
14420
+ async ({ agent }) => {
14421
+ try {
14422
+ const catalogs = await listModelsForAgent(agent);
14423
+ return {
14424
+ content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
14425
+ };
14426
+ } catch (err) {
14427
+ const message = err instanceof Error ? err.message : String(err);
14428
+ return { content: [{ type: "text", text: message }], isError: true };
14429
+ }
14430
+ }
14431
+ );
14432
+ server.tool(
14433
+ "fork_worktree",
14434
+ "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.",
14435
+ {
14436
+ ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
14437
+ through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14438
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14439
+ model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14440
+ title: import_zod.z.string().optional()
14441
+ },
14442
+ async ({ ref, through_index, agent, model, title }) => {
14443
+ try {
14444
+ const source = orch.getThread(ref);
14445
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
14446
+ const thread = await orch.forkThreadWorktree({
14447
+ threadId: ref,
14448
+ throughIndex: through_index,
14449
+ agent,
14450
+ model,
14451
+ title
14452
+ });
14453
+ return {
14454
+ content: [
14455
+ {
14456
+ type: "text",
14457
+ text: JSON.stringify({
14458
+ id: thread.id,
14459
+ title: thread.title,
14460
+ status: thread.status,
14461
+ agent: thread.agent,
14462
+ model: thread.model,
14463
+ branchName: thread.branchName,
14464
+ worktreePath: thread.worktreePath,
14465
+ fromThreadId: source?.id ?? ref,
14466
+ link: `sideboard://thread/${thread.id}`
14467
+ })
14468
+ }
14469
+ ]
14470
+ };
14471
+ } catch (err) {
14472
+ const message = err instanceof Error ? err.message : String(err);
14473
+ return { content: [{ type: "text", text: message }], isError: true };
14474
+ }
14475
+ }
14476
+ );
14477
+ server.tool(
14478
+ "fork_chat",
14479
+ "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.",
14480
+ {
14481
+ ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
14482
+ through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14483
+ agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14484
+ model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14485
+ title: import_zod.z.string().optional()
14486
+ },
14487
+ async ({ ref, through_index, agent, model, title }) => {
14488
+ try {
14489
+ const source = orch.getThread(ref);
14490
+ if (!source) {
14491
+ return {
14492
+ content: [{ type: "text", text: `Thread not found: ${ref}` }],
14493
+ isError: true
14494
+ };
13990
14495
  }
13991
- ]
13992
- };
13993
- }
13994
- );
13995
- server.tool(
13996
- "request_review",
13997
- '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.',
13998
- { ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
13999
- async ({ ref }) => {
14000
- try {
14001
- const tab = await orch.requestReview(ref);
14002
- const from = orch.getThread(ref);
14496
+ const tab = orch.forkChatTab({
14497
+ threadId: source.id,
14498
+ throughIndex: through_index,
14499
+ agent,
14500
+ model,
14501
+ title
14502
+ });
14503
+ return {
14504
+ content: [
14505
+ {
14506
+ type: "text",
14507
+ text: JSON.stringify({
14508
+ id: tab.id,
14509
+ title: tab.title,
14510
+ status: tab.status,
14511
+ agent: tab.agent,
14512
+ model: tab.model,
14513
+ sourceType: tab.sourceType,
14514
+ worktreePath: tab.worktreePath,
14515
+ fromThreadId: source.id,
14516
+ link: `sideboard://thread/${tab.id}`
14517
+ })
14518
+ }
14519
+ ]
14520
+ };
14521
+ } catch (err) {
14522
+ const message = err instanceof Error ? err.message : String(err);
14523
+ return { content: [{ type: "text", text: message }], isError: true };
14524
+ }
14525
+ }
14526
+ );
14527
+ server.tool(
14528
+ "run_dev_script",
14529
+ "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
14530
+ {
14531
+ ref: import_zod.z.string(),
14532
+ name: import_zod.z.string().optional()
14533
+ },
14534
+ async ({ ref, name }) => {
14535
+ const result = await orch.startDev(ref, name);
14003
14536
  return {
14004
14537
  content: [
14005
14538
  {
14006
14539
  type: "text",
14007
14540
  text: JSON.stringify({
14008
- id: tab.id,
14009
- title: tab.title,
14010
- status: tab.status,
14011
- fromThreadId: from?.id ?? ref,
14012
- link: `sideboard://thread/${tab.id}`
14541
+ port: result.port,
14542
+ scriptName: result.scriptName,
14543
+ ports: result.ports,
14544
+ url: `http://localhost:${result.port}`
14013
14545
  })
14014
14546
  }
14015
14547
  ]
14016
14548
  };
14017
- } catch (err) {
14018
- const message = err instanceof Error ? err.message : String(err);
14019
- return { content: [{ type: "text", text: message }], isError: true };
14020
14549
  }
14021
- }
14022
- );
14023
- const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
14024
- server.tool(
14025
- "list_models",
14026
- "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.",
14027
- {
14028
- agent: agentEnum.optional().describe("Limit to one agent; omit for all")
14029
- },
14030
- async ({ agent }) => {
14031
- try {
14032
- const catalogs = await listModelsForAgent(agent);
14550
+ );
14551
+ server.tool(
14552
+ "list_run_scripts",
14553
+ "List named run scripts available for a thread",
14554
+ { ref: import_zod.z.string() },
14555
+ async ({ ref }) => {
14556
+ const scripts = orch.listThreadRunScripts(ref);
14557
+ const active = orch.getActiveRuns(ref);
14033
14558
  return {
14034
- content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
14559
+ content: [
14560
+ {
14561
+ type: "text",
14562
+ text: JSON.stringify({ scripts, active }, null, 2)
14563
+ }
14564
+ ]
14035
14565
  };
14036
- } catch (err) {
14037
- const message = err instanceof Error ? err.message : String(err);
14038
- return { content: [{ type: "text", text: message }], isError: true };
14039
14566
  }
14040
- }
14041
- );
14042
- server.tool(
14043
- "fork_worktree",
14044
- "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.",
14045
- {
14046
- ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
14047
- through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14048
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14049
- model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14050
- title: import_zod.z.string().optional()
14051
- },
14052
- async ({ ref, through_index, agent, model, title }) => {
14053
- try {
14054
- const source = orch.getThread(ref);
14055
- if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
14056
- const thread = await orch.forkThreadWorktree({
14057
- threadId: ref,
14058
- throughIndex: through_index,
14059
- agent,
14060
- model,
14061
- title
14567
+ );
14568
+ server.tool(
14569
+ "stop_dev_script",
14570
+ "Stop a running script for a thread (all scripts if name omitted)",
14571
+ {
14572
+ ref: import_zod.z.string(),
14573
+ name: import_zod.z.string().optional()
14574
+ },
14575
+ async ({ ref, name }) => {
14576
+ orch.stopDev(ref, name);
14577
+ return { content: [{ type: "text", text: "ok" }] };
14578
+ }
14579
+ );
14580
+ server.tool(
14581
+ "run_setup",
14582
+ "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
14583
+ { ref: import_zod.z.string() },
14584
+ async ({ ref }) => {
14585
+ const result = await orch.runSetup(ref);
14586
+ return {
14587
+ content: [{ type: "text", text: JSON.stringify(result) }]
14588
+ };
14589
+ }
14590
+ );
14591
+ server.tool(
14592
+ "add_workspace",
14593
+ "Register a git repo as a Sideboard workspace",
14594
+ { repoPath: import_zod.z.string() },
14595
+ async ({ repoPath }) => {
14596
+ const ws = await orch.addWorkspace(repoPath);
14597
+ return { content: [{ type: "text", text: JSON.stringify(ws) }] };
14598
+ }
14599
+ );
14600
+ server.tool(
14601
+ "remove_workspace",
14602
+ "Unregister a Sideboard workspace (does not archive threads)",
14603
+ { repoPath: import_zod.z.string() },
14604
+ async ({ repoPath }) => {
14605
+ orch.removeWorkspace(repoPath);
14606
+ return { content: [{ type: "text", text: "ok" }] };
14607
+ }
14608
+ );
14609
+ server.tool(
14610
+ "fanout",
14611
+ "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
14612
+ {
14613
+ prompt: import_zod.z.string(),
14614
+ agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
14615
+ repoPath: import_zod.z.string(),
14616
+ sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
14617
+ sourceRef: import_zod.z.string().optional(),
14618
+ title: import_zod.z.string().optional()
14619
+ },
14620
+ async (args) => {
14621
+ const threads = await orch.bestOfN(args);
14622
+ return {
14623
+ content: [
14624
+ {
14625
+ type: "text",
14626
+ text: JSON.stringify(
14627
+ threads.map((t) => ({
14628
+ id: t.id,
14629
+ agent: t.agent,
14630
+ branchName: t.branchName,
14631
+ worktreePath: t.worktreePath
14632
+ })),
14633
+ null,
14634
+ 2
14635
+ )
14636
+ }
14637
+ ]
14638
+ };
14639
+ }
14640
+ );
14641
+ server.tool(
14642
+ "list_branches",
14643
+ "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).",
14644
+ {
14645
+ repoPath: import_zod.z.string(),
14646
+ unmergedOnly: import_zod.z.boolean().optional()
14647
+ },
14648
+ async ({ repoPath, unmergedOnly }) => {
14649
+ const root = await resolveRepoRoot(repoPath);
14650
+ const branches = await listBranches(root, {
14651
+ unmergedOnly: unmergedOnly !== false
14062
14652
  });
14063
14653
  return {
14064
14654
  content: [
14065
14655
  {
14066
14656
  type: "text",
14067
- text: JSON.stringify({
14068
- id: thread.id,
14069
- title: thread.title,
14070
- status: thread.status,
14071
- agent: thread.agent,
14072
- model: thread.model,
14073
- branchName: thread.branchName,
14074
- worktreePath: thread.worktreePath,
14075
- fromThreadId: source?.id ?? ref,
14076
- link: `sideboard://thread/${thread.id}`
14077
- })
14657
+ text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
14078
14658
  }
14079
14659
  ]
14080
14660
  };
14081
- } catch (err) {
14082
- const message = err instanceof Error ? err.message : String(err);
14083
- return { content: [{ type: "text", text: message }], isError: true };
14084
14661
  }
14085
- }
14086
- );
14087
- server.tool(
14088
- "fork_chat",
14089
- "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.",
14090
- {
14091
- ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
14092
- through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
14093
- agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
14094
- model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
14095
- title: import_zod.z.string().optional()
14096
- },
14097
- async ({ ref, through_index, agent, model, title }) => {
14098
- try {
14099
- const source = orch.getThread(ref);
14100
- if (!source) {
14101
- return {
14102
- content: [{ type: "text", text: `Thread not found: ${ref}` }],
14103
- isError: true
14104
- };
14105
- }
14106
- const tab = orch.forkChatTab({
14107
- threadId: source.id,
14108
- throughIndex: through_index,
14109
- agent,
14110
- model,
14111
- title
14662
+ );
14663
+ server.tool(
14664
+ "list_prs",
14665
+ "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.",
14666
+ { repoPath: import_zod.z.string() },
14667
+ async ({ repoPath }) => {
14668
+ const root = await resolveRepoRoot(repoPath);
14669
+ const prs = await listPrs(root);
14670
+ return {
14671
+ content: [
14672
+ {
14673
+ type: "text",
14674
+ text: prs.map(
14675
+ (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
14676
+ ).join("\n")
14677
+ }
14678
+ ]
14679
+ };
14680
+ }
14681
+ );
14682
+ server.tool(
14683
+ "get_pr_stack",
14684
+ "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.",
14685
+ { ref: import_zod.z.string() },
14686
+ async ({ ref }) => {
14687
+ const stack = await orch.getPrStack(ref);
14688
+ return {
14689
+ content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
14690
+ };
14691
+ }
14692
+ );
14693
+ server.tool(
14694
+ "open_pr_stack_layers",
14695
+ "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
14696
+ {
14697
+ ref: import_zod.z.string(),
14698
+ layer: import_zod.z.number().int().positive().optional()
14699
+ },
14700
+ async ({ ref, layer }) => {
14701
+ const result = await orch.openPrStackLayers(ref, { layer });
14702
+ return {
14703
+ content: [
14704
+ {
14705
+ type: "text",
14706
+ text: JSON.stringify(
14707
+ {
14708
+ stackNumber: result.stack.stackNumber,
14709
+ trunk: result.stack.trunk,
14710
+ threads: result.threads.map((t) => ({
14711
+ id: t.id,
14712
+ title: t.title,
14713
+ branchName: t.branchName,
14714
+ stackLayer: t.stackLayer,
14715
+ worktreePath: t.worktreePath,
14716
+ prUrl: t.prUrl,
14717
+ link: `sideboard://thread/${t.id}`
14718
+ }))
14719
+ },
14720
+ null,
14721
+ 2
14722
+ )
14723
+ }
14724
+ ]
14725
+ };
14726
+ }
14727
+ );
14728
+ server.tool(
14729
+ "add_stack_layer",
14730
+ "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
14731
+ {
14732
+ ref: import_zod.z.string(),
14733
+ branchName: import_zod.z.string(),
14734
+ title: import_zod.z.string().optional()
14735
+ },
14736
+ async ({ ref, branchName, title }) => {
14737
+ const result = await orch.addStackLayer(ref, branchName, { title });
14738
+ return {
14739
+ content: [
14740
+ {
14741
+ type: "text",
14742
+ text: JSON.stringify(
14743
+ {
14744
+ id: result.thread.id,
14745
+ title: result.thread.title,
14746
+ branchName: result.thread.branchName,
14747
+ stackLayer: result.thread.stackLayer,
14748
+ worktreePath: result.thread.worktreePath,
14749
+ link: `sideboard://thread/${result.thread.id}`
14750
+ },
14751
+ null,
14752
+ 2
14753
+ )
14754
+ }
14755
+ ]
14756
+ };
14757
+ }
14758
+ );
14759
+ server.tool(
14760
+ "create_pr_stack",
14761
+ "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
14762
+ {
14763
+ repoPath: import_zod.z.string(),
14764
+ branches: import_zod.z.array(import_zod.z.string()).min(1),
14765
+ agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
14766
+ base: import_zod.z.string().optional(),
14767
+ title: import_zod.z.string().optional()
14768
+ },
14769
+ async (args) => {
14770
+ const result = await orch.createPrStack({
14771
+ repoPath: args.repoPath,
14772
+ branches: args.branches,
14773
+ agent: args.agent,
14774
+ base: args.base,
14775
+ title: args.title
14112
14776
  });
14113
14777
  return {
14114
14778
  content: [
14115
14779
  {
14116
14780
  type: "text",
14117
- text: JSON.stringify({
14118
- id: tab.id,
14119
- title: tab.title,
14120
- status: tab.status,
14121
- agent: tab.agent,
14122
- model: tab.model,
14123
- sourceType: tab.sourceType,
14124
- worktreePath: tab.worktreePath,
14125
- fromThreadId: source.id,
14126
- link: `sideboard://thread/${tab.id}`
14127
- })
14781
+ text: JSON.stringify(
14782
+ {
14783
+ stackNumber: result.stack.stackNumber,
14784
+ trunk: result.stack.trunk,
14785
+ threads: result.threads.map((t) => ({
14786
+ id: t.id,
14787
+ title: t.title,
14788
+ branchName: t.branchName,
14789
+ stackLayer: t.stackLayer,
14790
+ worktreePath: t.worktreePath,
14791
+ link: `sideboard://thread/${t.id}`
14792
+ }))
14793
+ },
14794
+ null,
14795
+ 2
14796
+ )
14128
14797
  }
14129
14798
  ]
14130
14799
  };
14131
- } catch (err) {
14132
- const message = err instanceof Error ? err.message : String(err);
14133
- return { content: [{ type: "text", text: message }], isError: true };
14134
14800
  }
14135
- }
14136
- );
14137
- server.tool(
14138
- "run_dev_script",
14139
- "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
14140
- {
14141
- ref: import_zod.z.string(),
14142
- name: import_zod.z.string().optional()
14143
- },
14144
- async ({ ref, name }) => {
14145
- const result = await orch.startDev(ref, name);
14146
- return {
14147
- content: [
14148
- {
14149
- type: "text",
14150
- text: JSON.stringify({
14151
- port: result.port,
14152
- scriptName: result.scriptName,
14153
- ports: result.ports,
14154
- url: `http://localhost:${result.port}`
14155
- })
14156
- }
14157
- ]
14158
- };
14159
- }
14160
- );
14161
- server.tool(
14162
- "list_run_scripts",
14163
- "List named run scripts available for a thread",
14164
- { ref: import_zod.z.string() },
14165
- async ({ ref }) => {
14166
- const scripts = orch.listThreadRunScripts(ref);
14167
- const active = orch.getActiveRuns(ref);
14168
- return {
14169
- content: [
14170
- {
14171
- type: "text",
14172
- text: JSON.stringify({ scripts, active }, null, 2)
14173
- }
14174
- ]
14175
- };
14176
- }
14177
- );
14178
- server.tool(
14179
- "stop_dev_script",
14180
- "Stop a running script for a thread (all scripts if name omitted)",
14181
- {
14182
- ref: import_zod.z.string(),
14183
- name: import_zod.z.string().optional()
14184
- },
14185
- async ({ ref, name }) => {
14186
- orch.stopDev(ref, name);
14187
- return { content: [{ type: "text", text: "ok" }] };
14188
- }
14189
- );
14190
- server.tool(
14191
- "run_setup",
14192
- "Re-run workspace setup (Sideboard/Conductor settings or .cursor/worktrees.json)",
14193
- { ref: import_zod.z.string() },
14194
- async ({ ref }) => {
14195
- const result = await orch.runSetup(ref);
14196
- return {
14197
- content: [{ type: "text", text: JSON.stringify(result) }]
14198
- };
14199
- }
14200
- );
14201
- server.tool(
14202
- "add_workspace",
14203
- "Register a git repo as a Sideboard workspace",
14204
- { repoPath: import_zod.z.string() },
14205
- async ({ repoPath }) => {
14206
- const ws = await orch.addWorkspace(repoPath);
14207
- return { content: [{ type: "text", text: JSON.stringify(ws) }] };
14208
- }
14209
- );
14210
- server.tool(
14211
- "remove_workspace",
14212
- "Unregister a Sideboard workspace (does not archive threads)",
14213
- { repoPath: import_zod.z.string() },
14214
- async ({ repoPath }) => {
14215
- orch.removeWorkspace(repoPath);
14216
- return { content: [{ type: "text", text: "ok" }] };
14217
- }
14218
- );
14219
- server.tool(
14220
- "fanout",
14221
- "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
14222
- {
14223
- prompt: import_zod.z.string(),
14224
- agents: import_zod.z.array(import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
14225
- repoPath: import_zod.z.string(),
14226
- sourceType: import_zod.z.enum(["branch", "pr", "ticket"]).optional(),
14227
- sourceRef: import_zod.z.string().optional(),
14228
- title: import_zod.z.string().optional()
14229
- },
14230
- async (args) => {
14231
- const threads = await orch.bestOfN(args);
14232
- return {
14233
- content: [
14234
- {
14235
- type: "text",
14236
- text: JSON.stringify(
14237
- threads.map((t) => ({
14238
- id: t.id,
14239
- agent: t.agent,
14240
- branchName: t.branchName,
14241
- worktreePath: t.worktreePath
14242
- })),
14243
- null,
14244
- 2
14245
- )
14246
- }
14247
- ]
14248
- };
14249
- }
14250
- );
14251
- server.tool(
14252
- "list_branches",
14253
- "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).",
14254
- {
14255
- repoPath: import_zod.z.string(),
14256
- unmergedOnly: import_zod.z.boolean().optional()
14257
- },
14258
- async ({ repoPath, unmergedOnly }) => {
14259
- const root = await resolveRepoRoot(repoPath);
14260
- const branches = await listBranches(root, {
14261
- unmergedOnly: unmergedOnly !== false
14262
- });
14263
- return {
14264
- content: [
14265
- {
14266
- type: "text",
14267
- text: branches.map((b) => `${b.current ? "*" : " "} ${b.name}`).join("\n")
14268
- }
14269
- ]
14270
- };
14271
- }
14272
- );
14273
- server.tool(
14274
- "list_prs",
14275
- "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.",
14276
- { repoPath: import_zod.z.string() },
14277
- async ({ repoPath }) => {
14278
- const root = await resolveRepoRoot(repoPath);
14279
- const prs = await listPrs(root);
14280
- return {
14281
- content: [
14282
- {
14283
- type: "text",
14284
- text: prs.map(
14285
- (p) => `#${p.number} ${p.title} (${p.headRefName})${p.isCrossRepository ? " [fork]" : ""}`
14286
- ).join("\n")
14287
- }
14288
- ]
14289
- };
14290
- }
14291
- );
14292
- server.tool(
14293
- "get_pr_stack",
14294
- "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.",
14295
- { ref: import_zod.z.string() },
14296
- async ({ ref }) => {
14297
- const stack = await orch.getPrStack(ref);
14298
- return {
14299
- content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
14300
- };
14301
- }
14302
- );
14303
- server.tool(
14304
- "open_pr_stack_layers",
14305
- "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
14306
- {
14307
- ref: import_zod.z.string(),
14308
- layer: import_zod.z.number().int().positive().optional()
14309
- },
14310
- async ({ ref, layer }) => {
14311
- const result = await orch.openPrStackLayers(ref, { layer });
14312
- return {
14313
- content: [
14314
- {
14315
- type: "text",
14316
- text: JSON.stringify(
14317
- {
14318
- stackNumber: result.stack.stackNumber,
14319
- trunk: result.stack.trunk,
14320
- threads: result.threads.map((t) => ({
14321
- id: t.id,
14322
- title: t.title,
14323
- branchName: t.branchName,
14324
- stackLayer: t.stackLayer,
14325
- worktreePath: t.worktreePath,
14326
- prUrl: t.prUrl,
14327
- link: `sideboard://thread/${t.id}`
14328
- }))
14329
- },
14330
- null,
14331
- 2
14332
- )
14333
- }
14334
- ]
14335
- };
14336
- }
14337
- );
14338
- server.tool(
14339
- "add_stack_layer",
14340
- "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
14341
- {
14342
- ref: import_zod.z.string(),
14343
- branchName: import_zod.z.string(),
14344
- title: import_zod.z.string().optional()
14345
- },
14346
- async ({ ref, branchName, title }) => {
14347
- const result = await orch.addStackLayer(ref, branchName, { title });
14348
- return {
14349
- content: [
14350
- {
14351
- type: "text",
14352
- text: JSON.stringify(
14353
- {
14354
- id: result.thread.id,
14355
- title: result.thread.title,
14356
- branchName: result.thread.branchName,
14357
- stackLayer: result.thread.stackLayer,
14358
- worktreePath: result.thread.worktreePath,
14359
- link: `sideboard://thread/${result.thread.id}`
14360
- },
14361
- null,
14362
- 2
14363
- )
14364
- }
14365
- ]
14366
- };
14367
- }
14368
- );
14369
- server.tool(
14370
- "create_pr_stack",
14371
- "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
14372
- {
14373
- repoPath: import_zod.z.string(),
14374
- branches: import_zod.z.array(import_zod.z.string()).min(1),
14375
- agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
14376
- base: import_zod.z.string().optional(),
14377
- title: import_zod.z.string().optional()
14378
- },
14379
- async (args) => {
14380
- const result = await orch.createPrStack({
14381
- repoPath: args.repoPath,
14382
- branches: args.branches,
14383
- agent: args.agent,
14384
- base: args.base,
14385
- title: args.title
14386
- });
14387
- return {
14388
- content: [
14389
- {
14390
- type: "text",
14391
- text: JSON.stringify(
14392
- {
14393
- stackNumber: result.stack.stackNumber,
14394
- trunk: result.stack.trunk,
14395
- threads: result.threads.map((t) => ({
14396
- id: t.id,
14397
- title: t.title,
14398
- branchName: t.branchName,
14399
- stackLayer: t.stackLayer,
14400
- worktreePath: t.worktreePath,
14401
- link: `sideboard://thread/${t.id}`
14402
- }))
14403
- },
14404
- null,
14405
- 2
14406
- )
14407
- }
14408
- ]
14409
- };
14410
- }
14411
- );
14412
- server.tool(
14413
- "list_issues",
14414
- "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.",
14415
- { repoPath: import_zod.z.string() },
14416
- async ({ repoPath }) => {
14417
- const root = await resolveRepoRoot(repoPath);
14418
- const result = await listIssues(root);
14419
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
14420
- }
14421
- );
14422
- server.tool(
14423
- "list_linear_issues",
14424
- "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
14425
- {
14426
- agent: import_zod.z.enum(["claude", "codex", "opencode"]),
14427
- repoPath: import_zod.z.string()
14428
- },
14429
- async ({ agent, repoPath }) => {
14430
- const issues = await listLinearIssues(agent, repoPath);
14431
- return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
14432
- }
14433
- );
14801
+ );
14802
+ server.tool(
14803
+ "list_issues",
14804
+ "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.",
14805
+ { repoPath: import_zod.z.string() },
14806
+ async ({ repoPath }) => {
14807
+ const root = await resolveRepoRoot(repoPath);
14808
+ const result = await listIssues(root);
14809
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
14810
+ }
14811
+ );
14812
+ server.tool(
14813
+ "list_linear_issues",
14814
+ "Deprecated: prefer list_issues. Lists assigned Linear issues via the chosen agent MCP connector",
14815
+ {
14816
+ agent: import_zod.z.enum(["claude", "codex", "opencode"]),
14817
+ repoPath: import_zod.z.string()
14818
+ },
14819
+ async ({ agent, repoPath }) => {
14820
+ const issues = await listLinearIssues(agent, repoPath);
14821
+ return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
14822
+ }
14823
+ );
14824
+ }
14434
14825
  const transport = new import_stdio.StdioServerTransport();
14435
14826
  await server.connect(transport);
14436
14827
  }
14437
14828
 
14438
14829
  // src/index.ts
14830
+ init_profile();
14439
14831
  init_config();
14440
14832
 
14441
14833
  // src/brightsy/api.ts
@@ -14812,6 +15204,7 @@ init_injected_mcp();
14812
15204
  REVIEW_REQUEST_PREFILL,
14813
15205
  SIDEBOARD_FORCE_STOP,
14814
15206
  SIDEBOARD_MCP_ALLOWED_TOOLS,
15207
+ SIDEBOARD_MCP_PROFILE_ENV,
14815
15208
  THINKING_EFFORTS,
14816
15209
  addPrStackLayer,
14817
15210
  addStackLayerFromThread,
@@ -14843,6 +15236,7 @@ init_injected_mcp();
14843
15236
  brightsyCloudConnectAgent,
14844
15237
  brightsyCloudConnectEnabled,
14845
15238
  brightsyConfigPath,
15239
+ brightsyInjectWorktreeMcpEnabled,
14846
15240
  brightsyMcpAllowedTools,
14847
15241
  brightsyMcpServerName,
14848
15242
  buildCachedUserContent,
@@ -14868,6 +15262,7 @@ init_injected_mcp();
14868
15262
  coerceOrchestratorAgent,
14869
15263
  collectTakenTeamSlugs,
14870
15264
  commitAll,
15265
+ conductorBundledBinDir,
14871
15266
  conductorDbPath,
14872
15267
  confirmLand,
14873
15268
  connectBrightsyTeam,
@@ -14894,7 +15289,9 @@ init_injected_mcp();
14894
15289
  detectLocalMergeConflicts,
14895
15290
  disconnectBrightsyTeam,
14896
15291
  discoverSkills,
15292
+ dropCachedPrefixOnResume,
14897
15293
  encodeBrightsyTarget,
15294
+ enrichPathWithNpmGlobalBin,
14898
15295
  enrichWorkspacesWithGithub,
14899
15296
  ensureAgentPath,
14900
15297
  ensureCloudCoordinator,
@@ -14932,6 +15329,7 @@ init_injected_mcp();
14932
15329
  formatTranscriptMarkdown,
14933
15330
  formatWorkspaceInventory,
14934
15331
  formatWorktreeDirective,
15332
+ formatWorktreeReminder,
14935
15333
  getAdapter,
14936
15334
  getAgentSetupInfo,
14937
15335
  getBrightsySession,
@@ -14976,6 +15374,7 @@ init_injected_mcp();
14976
15374
  isBrightsyConnected,
14977
15375
  isBrightsyNdjsonLine,
14978
15376
  isCloudCoordinatorThread,
15377
+ isConductorBundledCli,
14979
15378
  isCursorAutoModel,
14980
15379
  isDirty,
14981
15380
  isGhRateLimitError,
@@ -15058,7 +15457,10 @@ init_injected_mcp();
15058
15457
  pastedTextStats,
15059
15458
  permissionMode,
15060
15459
  planFileAbs,
15460
+ posixShellSingleQuote,
15461
+ prepareTerminalCommand,
15061
15462
  previewLand,
15463
+ promptMentionsBrightsy,
15062
15464
  pushBranch,
15063
15465
  readExistingReviewRequestFile,
15064
15466
  readPlanFile,
@@ -15074,7 +15476,9 @@ init_injected_mcp();
15074
15476
  requestReview,
15075
15477
  requireAgent,
15076
15478
  resetGhStackDetectCache,
15479
+ resolveAgentExecutable,
15077
15480
  resolveClaudeExecutable,
15481
+ resolveCommandBinarySync,
15078
15482
  resolveConductorCursorAgentId,
15079
15483
  resolveCursorModelId,
15080
15484
  resolveDefaultBranch,
@@ -15083,6 +15487,7 @@ init_injected_mcp();
15083
15487
  resolveFilesToCopy,
15084
15488
  resolveGhAuthToken,
15085
15489
  resolveGithubRepoSlug,
15490
+ resolveLoginCommand,
15086
15491
  resolveNewThreadOptions,
15087
15492
  resolvePlanMarkdown,
15088
15493
  resolvePrSelector,
@@ -15104,9 +15509,11 @@ init_injected_mcp();
15104
15509
  settingsSourceLabel,
15105
15510
  shouldAttachPastedText,
15106
15511
  shouldCompactContext,
15512
+ shouldInjectBrightsyMcp,
15107
15513
  shouldRefreshReviewRequestTemplate,
15108
15514
  shouldRunWorktreeCleanup,
15109
15515
  sideboardHomeDir,
15516
+ sideboardMcpProfile,
15110
15517
  sideboardReposDir,
15111
15518
  sideboardWorkspacesDir,
15112
15519
  slugify,
@@ -15134,6 +15541,7 @@ init_injected_mcp();
15134
15541
  threadDisplayLabel,
15135
15542
  threadFilePath,
15136
15543
  threadLockPath,
15544
+ threadRequestsBrightsyMcp,
15137
15545
  threadsDir,
15138
15546
  threadsSharingWorktree,
15139
15547
  toolDescription,
@@ -15141,14 +15549,18 @@ init_injected_mcp();
15141
15549
  toolFilePath,
15142
15550
  totalTokens,
15143
15551
  updateAdvancedSettings,
15552
+ updateAgentExecutable,
15144
15553
  updateAppEnvironment,
15145
15554
  updateBrightsySettings,
15146
15555
  updateClaudeSettings,
15556
+ updateCodexSettings,
15147
15557
  updateDefaultsSettings,
15148
15558
  updateIntegrationsSettings,
15559
+ updateOpencodeSettings,
15149
15560
  updateThread,
15150
15561
  validateLinearApiKey,
15151
15562
  withAgentInstructions,
15563
+ withExportedPath,
15152
15564
  withThreadLock,
15153
15565
  workspaceSettingsSourceLabel,
15154
15566
  worktreeCleanupSettings,