@sideboard-ai/core 0.1.237 → 0.1.239

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.
package/dist/index.cjs CHANGED
@@ -13264,10 +13264,10 @@ function eventsFromClaudeSystem(obj) {
13264
13264
  if (subtype === "api_retry") {
13265
13265
  const attempt = obj.attempt;
13266
13266
  const max = obj.max_retries;
13267
- const delay = obj.retry_delay_ms;
13267
+ const delay2 = obj.retry_delay_ms;
13268
13268
  return {
13269
13269
  type: "thinking",
13270
- data: `API retry ${attempt ?? "?"}/${max ?? "?"}${typeof delay === "number" ? ` (wait ${delay}ms)` : ""}`
13270
+ data: `API retry ${attempt ?? "?"}/${max ?? "?"}${typeof delay2 === "number" ? ` (wait ${delay2}ms)` : ""}`
13271
13271
  };
13272
13272
  }
13273
13273
  if (subtype === "status") {
@@ -17481,15 +17481,76 @@ var init_request_review = __esm({
17481
17481
  }
17482
17482
  });
17483
17483
 
17484
+ // src/hook/worktree-run-ports.ts
17485
+ function worktreeRunPortsPath(worktreePath) {
17486
+ return (0, import_node_path44.join)(worktreePath, RUN_PORTS_REL);
17487
+ }
17488
+ function isPort(value) {
17489
+ return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535;
17490
+ }
17491
+ function parseFile(raw) {
17492
+ try {
17493
+ const parsed = JSON.parse(raw);
17494
+ if (!parsed || typeof parsed !== "object") return {};
17495
+ const scripts = parsed.scripts;
17496
+ if (!scripts || typeof scripts !== "object") return {};
17497
+ const next = {};
17498
+ for (const [name, ports] of Object.entries(scripts)) {
17499
+ if (!name || !Array.isArray(ports)) continue;
17500
+ const cleaned = ports.filter(isPort);
17501
+ if (cleaned.length) next[name] = cleaned;
17502
+ }
17503
+ return { scripts: next };
17504
+ } catch {
17505
+ return {};
17506
+ }
17507
+ }
17508
+ function loadWorktreeRunPorts(worktreePath, scriptName) {
17509
+ if (!worktreePath.trim() || !scriptName.trim()) return null;
17510
+ try {
17511
+ const raw = (0, import_node_fs44.readFileSync)(worktreeRunPortsPath(worktreePath), "utf8");
17512
+ const ports = parseFile(raw).scripts?.[scriptName];
17513
+ return ports?.length ? ports : null;
17514
+ } catch {
17515
+ return null;
17516
+ }
17517
+ }
17518
+ function saveWorktreeRunPorts(worktreePath, scriptName, ports) {
17519
+ if (!worktreePath.trim() || !scriptName.trim()) return;
17520
+ const cleaned = ports.filter(isPort);
17521
+ if (!cleaned.length) return;
17522
+ const path2 = worktreeRunPortsPath(worktreePath);
17523
+ let current = {};
17524
+ try {
17525
+ current = parseFile((0, import_node_fs44.readFileSync)(path2, "utf8"));
17526
+ } catch {
17527
+ current = {};
17528
+ }
17529
+ const scripts = { ...current.scripts ?? {} };
17530
+ scripts[scriptName] = cleaned;
17531
+ (0, import_node_fs44.mkdirSync)((0, import_node_path44.dirname)(path2), { recursive: true });
17532
+ (0, import_node_fs44.writeFileSync)(path2, `${JSON.stringify({ scripts }, null, 2)}
17533
+ `, "utf8");
17534
+ }
17535
+ var import_node_fs44, import_node_path44, RUN_PORTS_REL;
17536
+ var init_worktree_run_ports = __esm({
17537
+ "src/hook/worktree-run-ports.ts"() {
17538
+ "use strict";
17539
+ import_node_fs44 = require("fs");
17540
+ import_node_path44 = require("path");
17541
+ RUN_PORTS_REL = ".context/.sideboard/run-ports.json";
17542
+ }
17543
+ });
17544
+
17484
17545
  // src/hook/conductor.ts
17485
17546
  function matchSimpleGlob(pattern, name) {
17486
17547
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
17487
17548
  return new RegExp(`^${escaped}$`).test(name);
17488
17549
  }
17489
17550
  function readWorktreeInclude(repoPath) {
17490
- const path2 = (0, import_node_path44.join)(repoPath, ".worktreeinclude");
17491
- if (!(0, import_node_fs44.existsSync)(path2)) return [];
17492
- return (0, import_node_fs44.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
17551
+ const path2 = (0, import_node_path45.join)(repoPath, ".worktreeinclude");
17552
+ if (!(0, import_node_fs45.existsSync)(path2)) return [];
17553
+ return (0, import_node_fs45.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
17493
17554
  }
17494
17555
  function resolveFilesToCopy(repoPath) {
17495
17556
  const fromInclude = readWorktreeInclude(repoPath);
@@ -17499,10 +17560,10 @@ function resolveFilesToCopy(repoPath) {
17499
17560
  if (settings?.fileIncludeGlobs?.length) {
17500
17561
  const matched = [];
17501
17562
  try {
17502
- for (const entry of (0, import_node_fs44.readdirSync)(repoPath, { withFileTypes: true })) {
17563
+ for (const entry of (0, import_node_fs45.readdirSync)(repoPath, { withFileTypes: true })) {
17503
17564
  if (!entry.isFile()) continue;
17504
17565
  for (const glob of settings.fileIncludeGlobs) {
17505
- if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path44.basename)(glob), entry.name)) {
17566
+ if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path45.basename)(glob), entry.name)) {
17506
17567
  matched.push(entry.name);
17507
17568
  break;
17508
17569
  }
@@ -17514,7 +17575,7 @@ function resolveFilesToCopy(repoPath) {
17514
17575
  }
17515
17576
  const defaults = [];
17516
17577
  try {
17517
- for (const entry of (0, import_node_fs44.readdirSync)(repoPath, { withFileTypes: true })) {
17578
+ for (const entry of (0, import_node_fs45.readdirSync)(repoPath, { withFileTypes: true })) {
17518
17579
  if (entry.isFile() && entry.name.startsWith(".env")) {
17519
17580
  defaults.push(entry.name);
17520
17581
  }
@@ -17528,11 +17589,11 @@ function copyConfiguredFiles(repoPath, worktreePath) {
17528
17589
  const patterns = resolveFilesToCopy(repoPath);
17529
17590
  const copied = [];
17530
17591
  for (const rel of patterns) {
17531
- const src = (0, import_node_path44.join)(repoPath, rel);
17532
- if (!(0, import_node_fs44.existsSync)(src)) continue;
17533
- const dest = (0, import_node_path44.join)(worktreePath, rel);
17534
- (0, import_node_fs44.mkdirSync)((0, import_node_path44.dirname)(dest), { recursive: true });
17535
- (0, import_node_fs44.copyFileSync)(src, dest);
17592
+ const src = (0, import_node_path45.join)(repoPath, rel);
17593
+ if (!(0, import_node_fs45.existsSync)(src)) continue;
17594
+ const dest = (0, import_node_path45.join)(worktreePath, rel);
17595
+ (0, import_node_fs45.mkdirSync)((0, import_node_path45.dirname)(dest), { recursive: true });
17596
+ (0, import_node_fs45.copyFileSync)(src, dest);
17536
17597
  copied.push(rel);
17537
17598
  }
17538
17599
  return copied;
@@ -17567,7 +17628,7 @@ function buildWorkspaceScriptEnv(opts, baseEnv) {
17567
17628
  const env = stripNestedElectronEnv({
17568
17629
  ...baseEnv ?? process.env
17569
17630
  });
17570
- const name = opts.workspaceName ?? (0, import_node_path44.basename)(opts.worktreePath);
17631
+ const name = opts.workspaceName ?? (0, import_node_path45.basename)(opts.worktreePath);
17571
17632
  const ports = opts.ports ?? [];
17572
17633
  const primary = ports[0];
17573
17634
  env.SIDEBOARD_WORKSPACE_NAME = name;
@@ -17607,6 +17668,57 @@ function killableListenerPids(raw, selfPid = process.pid) {
17607
17668
  }
17608
17669
  return pids;
17609
17670
  }
17671
+ function listUnixListenerPids(port, selfPid) {
17672
+ try {
17673
+ const raw = (0, import_node_child_process5.execFileSync)("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
17674
+ encoding: "utf8",
17675
+ stdio: ["ignore", "pipe", "ignore"],
17676
+ timeout: 2e3
17677
+ });
17678
+ return killableListenerPids(raw, selfPid);
17679
+ } catch {
17680
+ return [];
17681
+ }
17682
+ }
17683
+ function listenerCwd(pid) {
17684
+ if (!Number.isFinite(pid) || pid <= 0) return null;
17685
+ try {
17686
+ const raw = (0, import_node_child_process5.execFileSync)("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
17687
+ encoding: "utf8",
17688
+ stdio: ["ignore", "pipe", "ignore"],
17689
+ timeout: 2e3
17690
+ });
17691
+ for (const line of raw.split("\n")) {
17692
+ if (line.startsWith("n") && line.length > 1) return line.slice(1);
17693
+ }
17694
+ } catch {
17695
+ }
17696
+ return null;
17697
+ }
17698
+ function realpathOrResolve(path2) {
17699
+ try {
17700
+ return (0, import_node_fs45.realpathSync)(path2);
17701
+ } catch {
17702
+ return (0, import_node_path45.resolve)(path2);
17703
+ }
17704
+ }
17705
+ function isOtherWorktreeListenerCwd(cwd, worktreePath) {
17706
+ const wt = realpathOrResolve(worktreePath);
17707
+ const c = realpathOrResolve(cwd);
17708
+ if (c === wt || c.startsWith(wt + import_node_path45.sep)) return false;
17709
+ const parent = (0, import_node_path45.dirname)(wt);
17710
+ const thisName = (0, import_node_path45.basename)(wt);
17711
+ if (!thisName || c === parent) return false;
17712
+ if (!c.startsWith(parent + import_node_path45.sep)) return false;
17713
+ const first = c.slice(parent.length + 1).split(import_node_path45.sep)[0];
17714
+ return Boolean(first) && first !== thisName;
17715
+ }
17716
+ function signalPid(pid, signal) {
17717
+ try {
17718
+ process.kill(pid, signal);
17719
+ } catch {
17720
+ }
17721
+ }
17610
17722
  function killListenersOnPorts(ports) {
17611
17723
  const selfPid = process.pid;
17612
17724
  for (const port of ports) {
@@ -17624,26 +17736,32 @@ function killListenersOnPorts(ports) {
17624
17736
  );
17625
17737
  continue;
17626
17738
  }
17627
- let raw = "";
17628
- try {
17629
- raw = (0, import_node_child_process5.execFileSync)("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
17630
- encoding: "utf8",
17631
- stdio: ["ignore", "pipe", "ignore"],
17632
- timeout: 2e3
17633
- });
17634
- } catch {
17635
- continue;
17636
- }
17637
- for (const pid of killableListenerPids(raw, selfPid)) {
17638
- try {
17639
- process.kill(pid, "SIGTERM");
17640
- } catch {
17641
- }
17739
+ for (const pid of listUnixListenerPids(port, selfPid)) {
17740
+ signalPid(pid, "SIGTERM");
17642
17741
  }
17643
17742
  } catch {
17644
17743
  }
17645
17744
  }
17646
17745
  }
17746
+ function killStaleWorktreeListeners(ports, worktreePath, signal = "SIGTERM") {
17747
+ if (!worktreePath.trim()) {
17748
+ killListenersOnPorts(ports);
17749
+ return;
17750
+ }
17751
+ if (process.platform === "win32") {
17752
+ killListenersOnPorts(ports);
17753
+ return;
17754
+ }
17755
+ const selfPid = process.pid;
17756
+ for (const port of ports) {
17757
+ if (!Number.isFinite(port) || port <= 0) continue;
17758
+ for (const pid of listUnixListenerPids(port, selfPid)) {
17759
+ const cwd = listenerCwd(pid);
17760
+ if (cwd && isOtherWorktreeListenerCwd(cwd, worktreePath)) continue;
17761
+ signalPid(pid, signal);
17762
+ }
17763
+ }
17764
+ }
17647
17765
  function killScriptTree(child, ports = []) {
17648
17766
  const pid = child.pid;
17649
17767
  if (pid) {
@@ -17805,7 +17923,7 @@ function getRunMode(worktreePath, repoPath) {
17805
17923
  return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
17806
17924
  }
17807
17925
  async function reservePort() {
17808
- return new Promise((resolve, reject) => {
17926
+ return new Promise((resolve2, reject) => {
17809
17927
  const server = (0, import_node_net.createServer)();
17810
17928
  let released = false;
17811
17929
  const release = () => new Promise((res) => {
@@ -17822,7 +17940,7 @@ async function reservePort() {
17822
17940
  void release().then(() => reject(new Error("Failed to allocate port")));
17823
17941
  return;
17824
17942
  }
17825
- resolve({ port: addr.port, release });
17943
+ resolve2({ port: addr.port, release });
17826
17944
  });
17827
17945
  server.on("error", reject);
17828
17946
  });
@@ -17833,7 +17951,8 @@ async function allocatePort() {
17833
17951
  return held.port;
17834
17952
  }
17835
17953
  async function tryReservePort(port) {
17836
- return new Promise((resolve) => {
17954
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
17955
+ return new Promise((resolve2) => {
17837
17956
  const server = (0, import_node_net.createServer)();
17838
17957
  let released = false;
17839
17958
  const release = () => new Promise((res) => {
@@ -17844,24 +17963,54 @@ async function tryReservePort(port) {
17844
17963
  released = true;
17845
17964
  server.close(() => res());
17846
17965
  });
17847
- server.once("error", () => resolve(null));
17966
+ server.once("error", () => {
17967
+ try {
17968
+ server.close();
17969
+ } catch {
17970
+ }
17971
+ resolve2(null);
17972
+ });
17848
17973
  server.listen(port, "127.0.0.1", () => {
17849
- resolve({ port, release });
17974
+ resolve2({ port, release });
17850
17975
  });
17851
17976
  });
17852
17977
  }
17853
- async function reservePortRange(size = PORT_RANGE_SIZE) {
17854
- const held = [await reservePort()];
17855
- const base = held[0].port;
17978
+ async function tryReservePorts(ports) {
17979
+ const held = [];
17980
+ for (const port of ports) {
17981
+ const next = await tryReservePort(port);
17982
+ if (!next) {
17983
+ await Promise.all(held.map((h) => h.release()));
17984
+ return null;
17985
+ }
17986
+ held.push(next);
17987
+ }
17988
+ return held;
17989
+ }
17990
+ function normalizePreferredPorts(ports, size) {
17991
+ if (!ports?.length) return [];
17992
+ const seen = /* @__PURE__ */ new Set();
17993
+ const out = [];
17994
+ for (const p of ports) {
17995
+ if (!Number.isInteger(p) || p <= 0 || p > 65535 || seen.has(p)) continue;
17996
+ seen.add(p);
17997
+ out.push(p);
17998
+ if (out.length >= size) break;
17999
+ }
18000
+ return out;
18001
+ }
18002
+ function delay(ms) {
18003
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
18004
+ }
18005
+ async function fillPortRange(held, size) {
18006
+ if (held.length >= size) return held;
18007
+ const base = held[0]?.port;
17856
18008
  try {
17857
- for (let i = 1; i < size; i++) {
17858
- const candidate = base + i;
17859
- const next = await tryReservePort(candidate);
17860
- if (next) {
17861
- held.push(next);
17862
- } else {
17863
- held.push(await reservePort());
17864
- }
18009
+ for (let i = held.length; i < size; i++) {
18010
+ const candidate = base != null ? base + i : null;
18011
+ const next = candidate != null ? await tryReservePort(candidate) : null;
18012
+ if (next) held.push(next);
18013
+ else held.push(await reservePort());
17865
18014
  }
17866
18015
  return held;
17867
18016
  } catch (err) {
@@ -17869,6 +18018,33 @@ async function reservePortRange(size = PORT_RANGE_SIZE) {
17869
18018
  throw err;
17870
18019
  }
17871
18020
  }
18021
+ async function reclaimPreferredPorts(ports, worktreePath) {
18022
+ let held = await tryReservePorts(ports);
18023
+ if (held) return held;
18024
+ if (worktreePath) {
18025
+ killStaleWorktreeListeners(ports, worktreePath, "SIGTERM");
18026
+ } else {
18027
+ killListenersOnPorts(ports);
18028
+ }
18029
+ for (let i = 0; i < RECLAIM_TRIES; i++) {
18030
+ await delay(RECLAIM_RETRY_DELAY_MS);
18031
+ held = await tryReservePorts(ports);
18032
+ if (held) return held;
18033
+ if (i === 2 && worktreePath) {
18034
+ killStaleWorktreeListeners(ports, worktreePath, "SIGKILL");
18035
+ }
18036
+ }
18037
+ return null;
18038
+ }
18039
+ async function reservePortRange(size = PORT_RANGE_SIZE, opts) {
18040
+ const preferred = normalizePreferredPorts(opts?.preferred, size);
18041
+ if (preferred.length) {
18042
+ const reused = await reclaimPreferredPorts(preferred, opts?.worktreePath);
18043
+ if (reused) return fillPortRange(reused, size);
18044
+ }
18045
+ const held = [await reservePort()];
18046
+ return fillPortRange(held, size);
18047
+ }
17872
18048
  async function allocatePortRange(size = PORT_RANGE_SIZE) {
17873
18049
  const held = await reservePortRange(size);
17874
18050
  const ports = held.map((h) => h.port);
@@ -17878,8 +18054,13 @@ async function allocatePortRange(size = PORT_RANGE_SIZE) {
17878
18054
  async function startDevServer(repoPath, worktreePath, onLine, opts) {
17879
18055
  const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
17880
18056
  if (!script) return null;
17881
- const held = await reservePortRange(PORT_RANGE_SIZE);
18057
+ const preferred = loadWorktreeRunPorts(worktreePath, script.name) ?? opts?.preferredPorts;
18058
+ const held = await reservePortRange(PORT_RANGE_SIZE, {
18059
+ preferred: preferred ?? void 0,
18060
+ worktreePath
18061
+ });
17882
18062
  const ports = held.map((h) => h.port);
18063
+ saveWorktreeRunPorts(worktreePath, script.name, ports);
17883
18064
  try {
17884
18065
  const handle = await spawnWorkspaceScript(script.command, {
17885
18066
  worktreePath,
@@ -17902,14 +18083,14 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
17902
18083
  throw err;
17903
18084
  }
17904
18085
  }
17905
- var import_node_fs44, import_node_child_process5, import_node_net, import_node_path44, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
18086
+ var import_node_fs45, import_node_child_process5, import_node_net, import_node_path45, import_execa4, import_node_readline3, PORT_RANGE_SIZE, RECLAIM_RETRY_DELAY_MS, RECLAIM_TRIES, cachedLoginEnv;
17906
18087
  var init_conductor = __esm({
17907
18088
  "src/hook/conductor.ts"() {
17908
18089
  "use strict";
17909
- import_node_fs44 = require("fs");
18090
+ import_node_fs45 = require("fs");
17910
18091
  import_node_child_process5 = require("child_process");
17911
18092
  import_node_net = require("net");
17912
- import_node_path44 = require("path");
18093
+ import_node_path45 = require("path");
17913
18094
  import_execa4 = require("execa");
17914
18095
  import_node_readline3 = require("readline");
17915
18096
  init_settings();
@@ -17920,8 +18101,12 @@ var init_conductor = __esm({
17920
18101
  init_git_auth_mode();
17921
18102
  init_request_review();
17922
18103
  init_worktree_exclude();
18104
+ init_worktree_run_ports();
18105
+ init_worktree_run_ports();
17923
18106
  init_nested_electron_env();
17924
18107
  PORT_RANGE_SIZE = 10;
18108
+ RECLAIM_RETRY_DELAY_MS = 40;
18109
+ RECLAIM_TRIES = 8;
17925
18110
  cachedLoginEnv = null;
17926
18111
  }
17927
18112
  });
@@ -17937,9 +18122,9 @@ async function findOrphanWorktrees(repoPaths) {
17937
18122
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
17938
18123
  );
17939
18124
  const homeRoot = sideboardWorkspacesDir();
17940
- if ((0, import_node_fs45.existsSync)(homeRoot)) {
18125
+ if ((0, import_node_fs46.existsSync)(homeRoot)) {
17941
18126
  try {
17942
- for (const entry of (0, import_node_fs45.readdirSync)(homeRoot, { withFileTypes: true })) {
18127
+ for (const entry of (0, import_node_fs46.readdirSync)(homeRoot, { withFileTypes: true })) {
17943
18128
  if (!entry.isDirectory()) continue;
17944
18129
  void entry;
17945
18130
  }
@@ -17949,7 +18134,7 @@ async function findOrphanWorktrees(repoPaths) {
17949
18134
  const orphans = [];
17950
18135
  const seen = /* @__PURE__ */ new Set();
17951
18136
  for (const repoPath of repos) {
17952
- if (!repoPath || !(0, import_node_fs45.existsSync)(repoPath)) continue;
18137
+ if (!repoPath || !(0, import_node_fs46.existsSync)(repoPath)) continue;
17953
18138
  try {
17954
18139
  const wts = await listWorktrees(repoPath);
17955
18140
  for (const wt of wts) {
@@ -17960,7 +18145,7 @@ async function findOrphanWorktrees(repoPaths) {
17960
18145
  seen.add(path2);
17961
18146
  let mtimeMs = 0;
17962
18147
  try {
17963
- mtimeMs = (0, import_node_fs45.statSync)(path2).mtimeMs;
18148
+ mtimeMs = (0, import_node_fs46.statSync)(path2).mtimeMs;
17964
18149
  } catch {
17965
18150
  mtimeMs = 0;
17966
18151
  }
@@ -17970,16 +18155,16 @@ async function findOrphanWorktrees(repoPaths) {
17970
18155
  }
17971
18156
  try {
17972
18157
  const root = worktreesRoot(repoPath);
17973
- if ((0, import_node_fs45.existsSync)(root)) {
17974
- for (const entry of (0, import_node_fs45.readdirSync)(root, { withFileTypes: true })) {
18158
+ if ((0, import_node_fs46.existsSync)(root)) {
18159
+ for (const entry of (0, import_node_fs46.readdirSync)(root, { withFileTypes: true })) {
17975
18160
  if (!entry.isDirectory()) continue;
17976
- const path2 = (0, import_node_path45.join)(root, entry.name).replace(/\/$/, "");
18161
+ const path2 = (0, import_node_path46.join)(root, entry.name).replace(/\/$/, "");
17977
18162
  if (known.has(path2) || seen.has(path2)) continue;
17978
- if (!(0, import_node_fs45.existsSync)((0, import_node_path45.join)(path2, ".git"))) continue;
18163
+ if (!(0, import_node_fs46.existsSync)((0, import_node_path46.join)(path2, ".git"))) continue;
17979
18164
  seen.add(path2);
17980
18165
  let mtimeMs = 0;
17981
18166
  try {
17982
- mtimeMs = (0, import_node_fs45.statSync)(path2).mtimeMs;
18167
+ mtimeMs = (0, import_node_fs46.statSync)(path2).mtimeMs;
17983
18168
  } catch {
17984
18169
  mtimeMs = Date.now();
17985
18170
  }
@@ -18040,12 +18225,12 @@ function worktreeCleanupSettings() {
18040
18225
  autoCleanupOrphans: a.autoCleanupOrphans
18041
18226
  };
18042
18227
  }
18043
- var import_node_fs45, import_node_path45;
18228
+ var import_node_fs46, import_node_path46;
18044
18229
  var init_orphan_cleanup = __esm({
18045
18230
  "src/git/orphan-cleanup.ts"() {
18046
18231
  "use strict";
18047
- import_node_fs45 = require("fs");
18048
- import_node_path45 = require("path");
18232
+ import_node_fs46 = require("fs");
18233
+ import_node_path46 = require("path");
18049
18234
  init_worktree();
18050
18235
  init_thread_store();
18051
18236
  init_paths();
@@ -18220,12 +18405,12 @@ async function cloneRepoIntoSideboard(opts) {
18220
18405
  if (!url) throw new Error("Clone URL is required");
18221
18406
  let name = opts.name?.trim();
18222
18407
  if (!name) {
18223
- const leaf = (0, import_node_path46.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
18408
+ const leaf = (0, import_node_path47.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
18224
18409
  name = leaf || "repo";
18225
18410
  }
18226
18411
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
18227
- const dest = (0, import_node_path46.join)(sideboardReposDir(), name);
18228
- if ((0, import_node_fs46.existsSync)(dest)) {
18412
+ const dest = (0, import_node_path47.join)(sideboardReposDir(), name);
18413
+ if ((0, import_node_fs47.existsSync)(dest)) {
18229
18414
  const repoPath2 = await resolveRepoRoot(dest);
18230
18415
  const workspace2 = await ensureWorkspace(repoPath2);
18231
18416
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -18240,12 +18425,12 @@ async function cloneRepoIntoSideboard(opts) {
18240
18425
  const workspace = await ensureWorkspace(repoPath);
18241
18426
  return { repoPath, workspace };
18242
18427
  }
18243
- var import_node_fs46, import_node_path46, import_execa6;
18428
+ var import_node_fs47, import_node_path47, import_execa6;
18244
18429
  var init_clone_repo = __esm({
18245
18430
  "src/git/clone-repo.ts"() {
18246
18431
  "use strict";
18247
- import_node_fs46 = require("fs");
18248
- import_node_path46 = require("path");
18432
+ import_node_fs47 = require("fs");
18433
+ import_node_path47 = require("path");
18249
18434
  import_execa6 = require("execa");
18250
18435
  init_paths();
18251
18436
  init_workspaces();
@@ -18374,24 +18559,24 @@ function jobTreeAlive(pid) {
18374
18559
  return jobAlive(pid) || processGroupAlive(pid);
18375
18560
  }
18376
18561
  function readIntFile(file) {
18377
- if (!(0, import_node_fs47.existsSync)(file)) return null;
18378
- const n = Number.parseInt((0, import_node_fs47.readFileSync)(file, "utf8").trim(), 10);
18562
+ if (!(0, import_node_fs48.existsSync)(file)) return null;
18563
+ const n = Number.parseInt((0, import_node_fs48.readFileSync)(file, "utf8").trim(), 10);
18379
18564
  return Number.isInteger(n) ? n : null;
18380
18565
  }
18381
18566
  function jobDir(root, id, legacy = false) {
18382
- return (0, import_node_path47.join)(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
18567
+ return (0, import_node_path48.join)(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
18383
18568
  }
18384
18569
  function resolveJobDir(root, id) {
18385
18570
  const modern = jobDir(root, id, false);
18386
- if ((0, import_node_fs47.existsSync)(modern)) return modern;
18571
+ if ((0, import_node_fs48.existsSync)(modern)) return modern;
18387
18572
  const legacy = jobDir(root, id, true);
18388
- if ((0, import_node_fs47.existsSync)(legacy)) return legacy;
18573
+ if ((0, import_node_fs48.existsSync)(legacy)) return legacy;
18389
18574
  return modern;
18390
18575
  }
18391
18576
  function listJobIdsIn(root, rel) {
18392
- const dir = (0, import_node_path47.join)(root, rel);
18393
- if (!(0, import_node_fs47.existsSync)(dir)) return [];
18394
- return (0, import_node_fs47.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
18577
+ const dir = (0, import_node_path48.join)(root, rel);
18578
+ if (!(0, import_node_fs48.existsSync)(dir)) return [];
18579
+ return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
18395
18580
  }
18396
18581
  function listRunningDetachedJobs(worktreePath) {
18397
18582
  const root = worktreePath.trim();
@@ -18403,7 +18588,7 @@ function listRunningDetachedJobs(worktreePath) {
18403
18588
  const running = [];
18404
18589
  for (const id of ids) {
18405
18590
  const dir = resolveJobDir(root, id);
18406
- const pid = readIntFile((0, import_node_path47.join)(dir, "pid"));
18591
+ const pid = readIntFile((0, import_node_path48.join)(dir, "pid"));
18407
18592
  if (jobTreeAlive(pid)) running.push(id);
18408
18593
  }
18409
18594
  return running.sort();
@@ -18486,8 +18671,8 @@ function planJobContinue(opts) {
18486
18671
  return { action: "none" };
18487
18672
  }
18488
18673
  function tailProgress(logFile, maxLines = 12) {
18489
- if (!(0, import_node_fs47.existsSync)(logFile)) return "(no log yet)";
18490
- const lines = (0, import_node_fs47.readFileSync)(logFile, "utf8").split("\n");
18674
+ if (!(0, import_node_fs48.existsSync)(logFile)) return "(no log yet)";
18675
+ const lines = (0, import_node_fs48.readFileSync)(logFile, "utf8").split("\n");
18491
18676
  return lines.slice(-maxLines).join("\n");
18492
18677
  }
18493
18678
  function lastProgressLine(progress) {
@@ -18495,8 +18680,8 @@ function lastProgressLine(progress) {
18495
18680
  return last ? last.slice(0, 72) : void 0;
18496
18681
  }
18497
18682
  function readLogLines(file) {
18498
- if (!(0, import_node_fs47.existsSync)(file)) return [];
18499
- const lines = (0, import_node_fs47.readFileSync)(file, "utf8").split("\n");
18683
+ if (!(0, import_node_fs48.existsSync)(file)) return [];
18684
+ const lines = (0, import_node_fs48.readFileSync)(file, "utf8").split("\n");
18500
18685
  if (lines.length && lines[lines.length - 1] === "") lines.pop();
18501
18686
  return lines;
18502
18687
  }
@@ -18507,15 +18692,15 @@ function takeDelta(logFile, cursorFile) {
18507
18692
  return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
18508
18693
  }
18509
18694
  function snapshotJob(dir) {
18510
- const pid = readIntFile((0, import_node_path47.join)(dir, "pid"));
18695
+ const pid = readIntFile((0, import_node_path48.join)(dir, "pid"));
18511
18696
  const running = jobTreeAlive(pid);
18512
18697
  return {
18513
18698
  pid,
18514
18699
  running,
18515
- exitCode: readIntFile((0, import_node_path47.join)(dir, "exit")),
18516
- log: (0, import_node_path47.join)(dir, "log"),
18517
- cursor: (0, import_node_path47.join)(dir, "present.cursor"),
18518
- progress: tailProgress((0, import_node_path47.join)(dir, "log"))
18700
+ exitCode: readIntFile((0, import_node_path48.join)(dir, "exit")),
18701
+ log: (0, import_node_path48.join)(dir, "log"),
18702
+ cursor: (0, import_node_path48.join)(dir, "present.cursor"),
18703
+ progress: tailProgress((0, import_node_path48.join)(dir, "log"))
18519
18704
  };
18520
18705
  }
18521
18706
  function toResult(id, snap, extra) {
@@ -18524,8 +18709,8 @@ function toResult(id, snap, extra) {
18524
18709
  const stillRunning = snap.running && !ok;
18525
18710
  const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
18526
18711
  try {
18527
- (0, import_node_fs47.mkdirSync)((0, import_node_path47.dirname)(snap.cursor), { recursive: true });
18528
- (0, import_node_fs47.writeFileSync)(snap.cursor, `${nextCursor}
18712
+ (0, import_node_fs48.mkdirSync)((0, import_node_path48.dirname)(snap.cursor), { recursive: true });
18713
+ (0, import_node_fs48.writeFileSync)(snap.cursor, `${nextCursor}
18529
18714
  `);
18530
18715
  } catch {
18531
18716
  }
@@ -18546,7 +18731,7 @@ function toResult(id, snap, extra) {
18546
18731
  };
18547
18732
  }
18548
18733
  async function sleepMs(ms) {
18549
- await new Promise((resolve) => setTimeout(resolve, ms));
18734
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
18550
18735
  }
18551
18736
  async function waitForDetachedJob(cwd, id, opts) {
18552
18737
  const jobId = sanitizeDetachedJobId(id);
@@ -18554,7 +18739,7 @@ async function waitForDetachedJob(cwd, id, opts) {
18554
18739
  const dir = resolveJobDir(root, jobId);
18555
18740
  const timeoutMs = mcpWaitForJobTimeoutMs(opts?.timeoutMs);
18556
18741
  const sleep2 = opts?.sleep ?? sleepMs;
18557
- if (!(0, import_node_fs47.existsSync)(dir)) {
18742
+ if (!(0, import_node_fs48.existsSync)(dir)) {
18558
18743
  return {
18559
18744
  stillRunning: false,
18560
18745
  ok: false,
@@ -18600,10 +18785,10 @@ function killProcessTree(pid, signal) {
18600
18785
  }
18601
18786
  function appendJobLog(logFile, line) {
18602
18787
  try {
18603
- (0, import_node_fs47.mkdirSync)((0, import_node_path47.dirname)(logFile), { recursive: true });
18788
+ (0, import_node_fs48.mkdirSync)((0, import_node_path48.dirname)(logFile), { recursive: true });
18604
18789
  const body = line.endsWith("\n") ? line : `${line}
18605
18790
  `;
18606
- (0, import_node_fs47.appendFileSync)(logFile, body);
18791
+ (0, import_node_fs48.appendFileSync)(logFile, body);
18607
18792
  } catch {
18608
18793
  }
18609
18794
  }
@@ -18613,7 +18798,7 @@ async function stopDetachedJob(cwd, id, opts) {
18613
18798
  const dir = resolveJobDir(root, jobId);
18614
18799
  const sleep2 = opts?.sleep ?? sleepMs;
18615
18800
  const why = opts?.reason?.trim() ?? "";
18616
- if (!(0, import_node_fs47.existsSync)(dir)) {
18801
+ if (!(0, import_node_fs48.existsSync)(dir)) {
18617
18802
  return {
18618
18803
  stillRunning: false,
18619
18804
  ok: false,
@@ -18649,10 +18834,10 @@ async function stopDetachedJob(cwd, id, opts) {
18649
18834
  killProcessTree(targetPid, "SIGKILL");
18650
18835
  await sleep2(100);
18651
18836
  }
18652
- const exitFile = (0, import_node_path47.join)(dir, "exit");
18653
- if (!(0, import_node_fs47.existsSync)(exitFile)) {
18837
+ const exitFile = (0, import_node_path48.join)(dir, "exit");
18838
+ if (!(0, import_node_fs48.existsSync)(exitFile)) {
18654
18839
  try {
18655
- (0, import_node_fs47.writeFileSync)(exitFile, "1\n");
18840
+ (0, import_node_fs48.writeFileSync)(exitFile, "1\n");
18656
18841
  } catch {
18657
18842
  }
18658
18843
  }
@@ -18672,12 +18857,12 @@ async function stopDetachedJob(cwd, id, opts) {
18672
18857
  hint: stopped ? MCP_STOP_JOB_HINT : MCP_WAIT_JOB_STILL_RUNNING_HINT
18673
18858
  };
18674
18859
  }
18675
- var import_node_fs47, import_node_path47, MAX_JOB_CONTINUES, MAX_JOB_DELTA_LINES, MAX_JOB_DELTA_CHARS, DEFERRED_DONE_MAX_CHARS, GH_WATCH_REFRESH, MCP_WAIT_JOB_STILL_RUNNING_HINT, MCP_STOP_JOB_HINT, JOB_ID_RE;
18860
+ var import_node_fs48, import_node_path48, MAX_JOB_CONTINUES, MAX_JOB_DELTA_LINES, MAX_JOB_DELTA_CHARS, DEFERRED_DONE_MAX_CHARS, GH_WATCH_REFRESH, MCP_WAIT_JOB_STILL_RUNNING_HINT, MCP_STOP_JOB_HINT, JOB_ID_RE;
18676
18861
  var init_wait_for_job = __esm({
18677
18862
  "src/mcp/wait-for-job.ts"() {
18678
18863
  "use strict";
18679
- import_node_fs47 = require("fs");
18680
- import_node_path47 = require("path");
18864
+ import_node_fs48 = require("fs");
18865
+ import_node_path48 = require("path");
18681
18866
  init_workspace_scratch();
18682
18867
  init_wait_for_turn();
18683
18868
  MAX_JOB_CONTINUES = 8;
@@ -18922,14 +19107,14 @@ async function startAbleTimeOAuth(opts) {
18922
19107
  const pkce = createAbleTimePkce();
18923
19108
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
18924
19109
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
18925
- const code = await new Promise((resolve, reject) => {
19110
+ const code = await new Promise((resolve2, reject) => {
18926
19111
  let settled = false;
18927
19112
  const finish = (err, value) => {
18928
19113
  if (settled) return;
18929
19114
  settled = true;
18930
19115
  cleanup();
18931
19116
  if (err) reject(err);
18932
- else resolve(value);
19117
+ else resolve2(value);
18933
19118
  };
18934
19119
  const server = (0, import_node_http.createServer)((req, res) => {
18935
19120
  try {
@@ -20222,7 +20407,7 @@ function reuseLiveThread(input, repoPath, match) {
20222
20407
  }
20223
20408
  async function createThread(input, _onSetupLine) {
20224
20409
  const repoPath = await resolveRepoRoot(input.repoPath);
20225
- if (!(0, import_node_fs48.existsSync)(repoPath)) {
20410
+ if (!(0, import_node_fs49.existsSync)(repoPath)) {
20226
20411
  throw new Error(`Repo not found: ${repoPath}`);
20227
20412
  }
20228
20413
  const reused = reuseLiveThread(input, repoPath, {
@@ -20407,11 +20592,11 @@ async function listLinearIssues(agent, repoPath) {
20407
20592
  }
20408
20593
  return adapter.listLinearIssues(repoPath);
20409
20594
  }
20410
- var import_node_fs48;
20595
+ var import_node_fs49;
20411
20596
  var init_create = __esm({
20412
20597
  "src/threads/create.ts"() {
20413
20598
  "use strict";
20414
- import_node_fs48 = require("fs");
20599
+ import_node_fs49 = require("fs");
20415
20600
  init_detect();
20416
20601
  init_worktree();
20417
20602
  init_home_board();
@@ -20563,20 +20748,20 @@ function writeTurnLive(threadId, progress) {
20563
20748
  const path2 = threadLivePath(threadId);
20564
20749
  const tmp = `${path2}.${process.pid}.tmp`;
20565
20750
  try {
20566
- (0, import_node_fs49.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
20567
- (0, import_node_fs49.renameSync)(tmp, path2);
20751
+ (0, import_node_fs50.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
20752
+ (0, import_node_fs50.renameSync)(tmp, path2);
20568
20753
  } catch {
20569
20754
  try {
20570
- (0, import_node_fs49.unlinkSync)(tmp);
20755
+ (0, import_node_fs50.unlinkSync)(tmp);
20571
20756
  } catch {
20572
20757
  }
20573
20758
  }
20574
20759
  }
20575
20760
  function readTurnLive(threadId) {
20576
20761
  const path2 = threadLivePath(threadId);
20577
- if (!(0, import_node_fs49.existsSync)(path2)) return null;
20762
+ if (!(0, import_node_fs50.existsSync)(path2)) return null;
20578
20763
  try {
20579
- const raw = JSON.parse((0, import_node_fs49.readFileSync)(path2, "utf8"));
20764
+ const raw = JSON.parse((0, import_node_fs50.readFileSync)(path2, "utf8"));
20580
20765
  if (!raw || typeof raw.summary !== "string") return null;
20581
20766
  return raw;
20582
20767
  } catch {
@@ -20588,17 +20773,17 @@ function clearTurnLive(threadId) {
20588
20773
  if (buf?.timer) clearTimeout(buf.timer);
20589
20774
  buffers.delete(threadId);
20590
20775
  const path2 = threadLivePath(threadId);
20591
- if (!(0, import_node_fs49.existsSync)(path2)) return;
20776
+ if (!(0, import_node_fs50.existsSync)(path2)) return;
20592
20777
  try {
20593
- (0, import_node_fs49.unlinkSync)(path2);
20778
+ (0, import_node_fs50.unlinkSync)(path2);
20594
20779
  } catch {
20595
20780
  }
20596
20781
  }
20597
- var import_node_fs49, buffers, FLUSH_MS, MAX_PARTS;
20782
+ var import_node_fs50, buffers, FLUSH_MS, MAX_PARTS;
20598
20783
  var init_turn_live = __esm({
20599
20784
  "src/store/turn-live.ts"() {
20600
20785
  "use strict";
20601
- import_node_fs49 = require("fs");
20786
+ import_node_fs50 = require("fs");
20602
20787
  init_message_parts();
20603
20788
  init_paths();
20604
20789
  buffers = /* @__PURE__ */ new Map();
@@ -20842,7 +21027,7 @@ var init_quota_failover = __esm({
20842
21027
  // src/threads/adopt.ts
20843
21028
  function thisModuleFile2() {
20844
21029
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
20845
- return cjsFile || process.argv[1] || (0, import_node_path48.join)(process.cwd(), "package.json");
21030
+ return cjsFile || process.argv[1] || (0, import_node_path49.join)(process.cwd(), "package.json");
20846
21031
  }
20847
21032
  function openReadonlySqlite(file) {
20848
21033
  const req = (0, import_node_module5.createRequire)(thisModuleFile2());
@@ -20860,21 +21045,21 @@ function mapAgentType(raw) {
20860
21045
  return null;
20861
21046
  }
20862
21047
  function resolveConductorCursorAgentId(workspacePath) {
20863
- if (!workspacePath || !(0, import_node_fs50.existsSync)(CURSOR_SDK_STORE)) return null;
21048
+ if (!workspacePath || !(0, import_node_fs51.existsSync)(CURSOR_SDK_STORE)) return null;
20864
21049
  const normalized = workspacePath.replace(/\/$/, "");
20865
21050
  let best = null;
20866
21051
  let hashes;
20867
21052
  try {
20868
- hashes = (0, import_node_fs50.readdirSync)(CURSOR_SDK_STORE);
21053
+ hashes = (0, import_node_fs51.readdirSync)(CURSOR_SDK_STORE);
20869
21054
  } catch {
20870
21055
  return null;
20871
21056
  }
20872
21057
  for (const hash of hashes) {
20873
- const agentsFile = (0, import_node_path48.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
20874
- if (!(0, import_node_fs50.existsSync)(agentsFile)) continue;
21058
+ const agentsFile = (0, import_node_path49.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
21059
+ if (!(0, import_node_fs51.existsSync)(agentsFile)) continue;
20875
21060
  let text7;
20876
21061
  try {
20877
- text7 = (0, import_node_fs50.readFileSync)(agentsFile, "utf8");
21062
+ text7 = (0, import_node_fs51.readFileSync)(agentsFile, "utf8");
20878
21063
  } catch {
20879
21064
  continue;
20880
21065
  }
@@ -20898,7 +21083,7 @@ function resolveConductorCursorAgentId(workspacePath) {
20898
21083
  return best?.agentId ?? null;
20899
21084
  }
20900
21085
  async function adoptThread(input) {
20901
- if (!(0, import_node_fs50.existsSync)(input.worktreePath)) {
21086
+ if (!(0, import_node_fs51.existsSync)(input.worktreePath)) {
20902
21087
  throw new Error(`Worktree not found: ${input.worktreePath}`);
20903
21088
  }
20904
21089
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -20927,18 +21112,18 @@ function conductorDbPath() {
20927
21112
  return CONDUCTOR_DB;
20928
21113
  }
20929
21114
  function listConductorWorkspaces() {
20930
- if (!(0, import_node_fs50.existsSync)(CONDUCTOR_DB)) {
21115
+ if (!(0, import_node_fs51.existsSync)(CONDUCTOR_DB)) {
20931
21116
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
20932
21117
  }
20933
- const tmp = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os14.tmpdir)(), "sideboard-conductor-"));
20934
- const snapshot = (0, import_node_path48.join)(tmp, "conductor.db");
21118
+ const tmp = (0, import_node_fs51.mkdtempSync)((0, import_node_path49.join)((0, import_node_os14.tmpdir)(), "sideboard-conductor-"));
21119
+ const snapshot = (0, import_node_path49.join)(tmp, "conductor.db");
20935
21120
  try {
20936
- (0, import_node_fs50.copyFileSync)(CONDUCTOR_DB, snapshot);
21121
+ (0, import_node_fs51.copyFileSync)(CONDUCTOR_DB, snapshot);
20937
21122
  for (const suffix of ["-wal", "-shm"]) {
20938
21123
  const src = `${CONDUCTOR_DB}${suffix}`;
20939
- if ((0, import_node_fs50.existsSync)(src)) {
21124
+ if ((0, import_node_fs51.existsSync)(src)) {
20940
21125
  try {
20941
- (0, import_node_fs50.copyFileSync)(src, `${snapshot}${suffix}`);
21126
+ (0, import_node_fs51.copyFileSync)(src, `${snapshot}${suffix}`);
20942
21127
  } catch {
20943
21128
  }
20944
21129
  }
@@ -21014,22 +21199,22 @@ function listConductorWorkspaces() {
21014
21199
  db.close();
21015
21200
  }
21016
21201
  } finally {
21017
- (0, import_node_fs50.rmSync)(tmp, { recursive: true, force: true });
21202
+ (0, import_node_fs51.rmSync)(tmp, { recursive: true, force: true });
21018
21203
  }
21019
21204
  }
21020
21205
  function importConductorWorkspace(workspaceId) {
21021
- if (!(0, import_node_fs50.existsSync)(CONDUCTOR_DB)) {
21206
+ if (!(0, import_node_fs51.existsSync)(CONDUCTOR_DB)) {
21022
21207
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
21023
21208
  }
21024
- const tmp = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os14.tmpdir)(), "sideboard-conductor-"));
21025
- const snapshot = (0, import_node_path48.join)(tmp, "conductor.db");
21209
+ const tmp = (0, import_node_fs51.mkdtempSync)((0, import_node_path49.join)((0, import_node_os14.tmpdir)(), "sideboard-conductor-"));
21210
+ const snapshot = (0, import_node_path49.join)(tmp, "conductor.db");
21026
21211
  try {
21027
- (0, import_node_fs50.copyFileSync)(CONDUCTOR_DB, snapshot);
21212
+ (0, import_node_fs51.copyFileSync)(CONDUCTOR_DB, snapshot);
21028
21213
  for (const suffix of ["-wal", "-shm"]) {
21029
21214
  const src = `${CONDUCTOR_DB}${suffix}`;
21030
- if ((0, import_node_fs50.existsSync)(src)) {
21215
+ if ((0, import_node_fs51.existsSync)(src)) {
21031
21216
  try {
21032
- (0, import_node_fs50.copyFileSync)(src, `${snapshot}${suffix}`);
21217
+ (0, import_node_fs51.copyFileSync)(src, `${snapshot}${suffix}`);
21033
21218
  } catch {
21034
21219
  }
21035
21220
  }
@@ -21047,7 +21232,7 @@ function importConductorWorkspace(workspaceId) {
21047
21232
  ).get(workspaceId);
21048
21233
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
21049
21234
  const worktreePath = String(row.workspacePath);
21050
- if (!(0, import_node_fs50.existsSync)(worktreePath)) {
21235
+ if (!(0, import_node_fs51.existsSync)(worktreePath)) {
21051
21236
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
21052
21237
  }
21053
21238
  let sessionId = null;
@@ -21110,31 +21295,31 @@ function importConductorWorkspace(workspaceId) {
21110
21295
  db.close();
21111
21296
  }
21112
21297
  } finally {
21113
- (0, import_node_fs50.rmSync)(tmp, { recursive: true, force: true });
21298
+ (0, import_node_fs51.rmSync)(tmp, { recursive: true, force: true });
21114
21299
  }
21115
21300
  }
21116
21301
  async function importConductorWorkspaceAsync(workspaceId) {
21117
21302
  return importConductorWorkspace(workspaceId);
21118
21303
  }
21119
- var import_node_child_process6, import_node_fs50, import_node_os14, import_node_path48, import_node_module5, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
21304
+ var import_node_child_process6, import_node_fs51, import_node_os14, import_node_path49, import_node_module5, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
21120
21305
  var init_adopt = __esm({
21121
21306
  "src/threads/adopt.ts"() {
21122
21307
  "use strict";
21123
21308
  import_node_child_process6 = require("child_process");
21124
- import_node_fs50 = require("fs");
21309
+ import_node_fs51 = require("fs");
21125
21310
  import_node_os14 = require("os");
21126
- import_node_path48 = require("path");
21311
+ import_node_path49 = require("path");
21127
21312
  import_node_module5 = require("module");
21128
21313
  init_worktree();
21129
21314
  init_thread_store();
21130
- CONDUCTOR_APP_SUPPORT = (0, import_node_path48.join)(
21315
+ CONDUCTOR_APP_SUPPORT = (0, import_node_path49.join)(
21131
21316
  process.env.HOME ?? "",
21132
21317
  "Library",
21133
21318
  "Application Support",
21134
21319
  "com.conductor.app"
21135
21320
  );
21136
- CONDUCTOR_DB = (0, import_node_path48.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
21137
- CURSOR_SDK_STORE = (0, import_node_path48.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
21321
+ CONDUCTOR_DB = (0, import_node_path49.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
21322
+ CURSOR_SDK_STORE = (0, import_node_path49.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
21138
21323
  }
21139
21324
  });
21140
21325
 
@@ -21201,7 +21386,7 @@ async function openStackLayer(input, _onSetupLine) {
21201
21386
  let createdWorktree = false;
21202
21387
  const trees = await listWorktrees(repoPath);
21203
21388
  const checkedOut = trees.find((w) => w.branch === branchName);
21204
- if (checkedOut?.path && (0, import_node_fs51.existsSync)(checkedOut.path)) {
21389
+ if (checkedOut?.path && (0, import_node_fs52.existsSync)(checkedOut.path)) {
21205
21390
  if (input.reuseExistingWorktree !== false) {
21206
21391
  worktreePath = checkedOut.path;
21207
21392
  } else {
@@ -21343,7 +21528,7 @@ async function initStackFromThread(input, onSetupLine) {
21343
21528
  async function createPrStack(input, onSetupLine) {
21344
21529
  await requireAgent(input.agent);
21345
21530
  const repoPath = await resolveRepoRoot(input.repoPath);
21346
- if (!(0, import_node_fs51.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
21531
+ if (!(0, import_node_fs52.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
21347
21532
  if (!input.branches.length) throw new Error("At least one branch name required");
21348
21533
  const status = await detectGhStack(repoPath);
21349
21534
  if (!status.available) throw new Error(status.reason);
@@ -21410,7 +21595,7 @@ async function createPrStack(input, onSetupLine) {
21410
21595
  }
21411
21596
  }
21412
21597
  const claimed = new Set(threads.map((t) => t.worktreePath));
21413
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs51.existsSync)(bootstrap.worktreePath)) {
21598
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs52.existsSync)(bootstrap.worktreePath)) {
21414
21599
  try {
21415
21600
  await removeWorktree(repoPath, bootstrap.worktreePath, {
21416
21601
  deleteBranch: bootstrap.branchName
@@ -21430,11 +21615,11 @@ function stackAgentDefaultsFrom(input) {
21430
21615
  planMode: input.planMode
21431
21616
  };
21432
21617
  }
21433
- var import_node_fs51;
21618
+ var import_node_fs52;
21434
21619
  var init_stack_layers = __esm({
21435
21620
  "src/threads/stack-layers.ts"() {
21436
21621
  "use strict";
21437
- import_node_fs51 = require("fs");
21622
+ import_node_fs52 = require("fs");
21438
21623
  init_detect();
21439
21624
  init_run();
21440
21625
  init_stack();
@@ -21447,7 +21632,7 @@ var init_stack_layers = __esm({
21447
21632
 
21448
21633
  // src/diff/diff.ts
21449
21634
  async function inspectGitWorktree(worktreePath) {
21450
- if (!worktreePath || !(0, import_node_fs52.existsSync)(worktreePath)) return "missing_worktree";
21635
+ if (!worktreePath || !(0, import_node_fs53.existsSync)(worktreePath)) return "missing_worktree";
21451
21636
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
21452
21637
  reject: false
21453
21638
  });
@@ -21455,7 +21640,7 @@ async function inspectGitWorktree(worktreePath) {
21455
21640
  return "ok";
21456
21641
  }
21457
21642
  async function initializeGitRepository(worktreePath) {
21458
- if (!worktreePath || !(0, import_node_fs52.existsSync)(worktreePath)) {
21643
+ if (!worktreePath || !(0, import_node_fs53.existsSync)(worktreePath)) {
21459
21644
  throw new Error("Worktree not found");
21460
21645
  }
21461
21646
  const status = await inspectGitWorktree(worktreePath);
@@ -21589,11 +21774,11 @@ new file mode 100644
21589
21774
  };
21590
21775
  }
21591
21776
  async function untrackedPatch(worktreePath, path2, maxHunk) {
21592
- const abs = (0, import_node_path49.join)(worktreePath, path2);
21777
+ const abs = (0, import_node_path50.join)(worktreePath, path2);
21593
21778
  try {
21594
- const st = (0, import_node_fs52.statSync)(abs);
21779
+ const st = (0, import_node_fs53.statSync)(abs);
21595
21780
  if (st.isFile() && st.size > maxHunk) {
21596
- const buf = (0, import_node_fs52.readFileSync)(abs).subarray(0, maxHunk);
21781
+ const buf = (0, import_node_fs53.readFileSync)(abs).subarray(0, maxHunk);
21597
21782
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
21598
21783
  }
21599
21784
  } catch {
@@ -22060,9 +22245,9 @@ async function getDiff(worktreePath, repoPath, opts) {
22060
22245
  }
22061
22246
  function statWorktreePath(worktreePath, relativePath) {
22062
22247
  assertSafeRelativePath(relativePath);
22063
- const abs = (0, import_node_path49.join)(worktreePath, relativePath);
22064
- if (!(0, import_node_fs52.existsSync)(abs)) return "missing";
22065
- const st = (0, import_node_fs52.statSync)(abs);
22248
+ const abs = (0, import_node_path50.join)(worktreePath, relativePath);
22249
+ if (!(0, import_node_fs53.existsSync)(abs)) return "missing";
22250
+ const st = (0, import_node_fs53.statSync)(abs);
22066
22251
  if (st.isDirectory()) return "dir";
22067
22252
  if (st.isFile()) return "file";
22068
22253
  return "missing";
@@ -22091,8 +22276,8 @@ function isImageRelativePath(relativePath) {
22091
22276
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
22092
22277
  assertSafeRelativePath(relativePath);
22093
22278
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
22094
- const abs = (0, import_node_path49.join)(worktreePath, relativePath);
22095
- const st = (0, import_node_fs52.statSync)(abs);
22279
+ const abs = (0, import_node_path50.join)(worktreePath, relativePath);
22280
+ const st = (0, import_node_fs53.statSync)(abs);
22096
22281
  if (!st.isFile()) {
22097
22282
  throw new Error(`Not a file: ${relativePath}`);
22098
22283
  }
@@ -22101,7 +22286,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
22101
22286
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
22102
22287
  );
22103
22288
  }
22104
- const buf = (0, import_node_fs52.readFileSync)(abs);
22289
+ const buf = (0, import_node_fs53.readFileSync)(abs);
22105
22290
  return {
22106
22291
  path: relativePath,
22107
22292
  contentBase64: buf.toString("base64"),
@@ -22111,12 +22296,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
22111
22296
  function readWorktreeFile(worktreePath, relativePath, opts) {
22112
22297
  assertSafeRelativePath(relativePath);
22113
22298
  const maxBytes = opts?.maxBytes ?? 2e5;
22114
- const abs = (0, import_node_path49.join)(worktreePath, relativePath);
22115
- const st = (0, import_node_fs52.statSync)(abs);
22299
+ const abs = (0, import_node_path50.join)(worktreePath, relativePath);
22300
+ const st = (0, import_node_fs53.statSync)(abs);
22116
22301
  if (!st.isFile()) {
22117
22302
  throw new Error(`Not a file: ${relativePath}`);
22118
22303
  }
22119
- const buf = (0, import_node_fs52.readFileSync)(abs);
22304
+ const buf = (0, import_node_fs53.readFileSync)(abs);
22120
22305
  if (isImageRelativePath(relativePath)) {
22121
22306
  const maxImageBytes = Math.max(maxBytes, 15e6);
22122
22307
  const truncated2 = buf.length > maxImageBytes;
@@ -22159,9 +22344,9 @@ function assertSafeRelativePath(relativePath) {
22159
22344
  }
22160
22345
  function writeWorktreeFile(worktreePath, relativePath, content) {
22161
22346
  assertSafeRelativePath(relativePath);
22162
- const abs = (0, import_node_path49.join)(worktreePath, relativePath);
22163
- (0, import_node_fs52.mkdirSync)((0, import_node_path49.dirname)(abs), { recursive: true });
22164
- (0, import_node_fs52.writeFileSync)(abs, content, "utf8");
22347
+ const abs = (0, import_node_path50.join)(worktreePath, relativePath);
22348
+ (0, import_node_fs53.mkdirSync)((0, import_node_path50.dirname)(abs), { recursive: true });
22349
+ (0, import_node_fs53.writeFileSync)(abs, content, "utf8");
22165
22350
  return { path: relativePath };
22166
22351
  }
22167
22352
  async function getWorktreeDirtyStat(worktreePath) {
@@ -22201,12 +22386,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
22201
22386
  truncated: full.files.length > maxFiles
22202
22387
  };
22203
22388
  }
22204
- var import_node_fs52, import_node_path49, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
22389
+ var import_node_fs53, import_node_path50, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
22205
22390
  var init_diff = __esm({
22206
22391
  "src/diff/diff.ts"() {
22207
22392
  "use strict";
22208
- import_node_fs52 = require("fs");
22209
- import_node_path49 = require("path");
22393
+ import_node_fs53 = require("fs");
22394
+ import_node_path50 = require("path");
22210
22395
  init_run();
22211
22396
  init_worktree();
22212
22397
  mergeBaseCache = /* @__PURE__ */ new Map();
@@ -22563,7 +22748,7 @@ function parseFrontmatter(content) {
22563
22748
  }
22564
22749
  function readSkill(skillMd, source) {
22565
22750
  try {
22566
- const content = (0, import_node_fs53.readFileSync)(skillMd, "utf8");
22751
+ const content = (0, import_node_fs54.readFileSync)(skillMd, "utf8");
22567
22752
  const { name: fmName, description } = parseFrontmatter(content);
22568
22753
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
22569
22754
  const name = fmName || dirName;
@@ -22582,19 +22767,19 @@ function readSkill(skillMd, source) {
22582
22767
  }
22583
22768
  }
22584
22769
  function scanSkillsDir(dir, source, out) {
22585
- if (!(0, import_node_fs53.existsSync)(dir)) return;
22770
+ if (!(0, import_node_fs54.existsSync)(dir)) return;
22586
22771
  let entries;
22587
22772
  try {
22588
- entries = (0, import_node_fs53.readdirSync)(dir);
22773
+ entries = (0, import_node_fs54.readdirSync)(dir);
22589
22774
  } catch {
22590
22775
  return;
22591
22776
  }
22592
22777
  for (const entry of entries) {
22593
22778
  if (entry.startsWith(".")) continue;
22594
- const skillMd = (0, import_node_path50.join)(dir, entry, "SKILL.md");
22595
- if (!(0, import_node_fs53.existsSync)(skillMd)) continue;
22779
+ const skillMd = (0, import_node_path51.join)(dir, entry, "SKILL.md");
22780
+ if (!(0, import_node_fs54.existsSync)(skillMd)) continue;
22596
22781
  try {
22597
- if (!(0, import_node_fs53.statSync)(skillMd).isFile()) continue;
22782
+ if (!(0, import_node_fs54.statSync)(skillMd).isFile()) continue;
22598
22783
  } catch {
22599
22784
  continue;
22600
22785
  }
@@ -22603,24 +22788,24 @@ function scanSkillsDir(dir, source, out) {
22603
22788
  }
22604
22789
  }
22605
22790
  function scanClaudePluginSkills(pluginsRoot, out) {
22606
- if (!(0, import_node_fs53.existsSync)(pluginsRoot)) return;
22791
+ if (!(0, import_node_fs54.existsSync)(pluginsRoot)) return;
22607
22792
  const walk = (dir, depth, lookingForSkillsDir) => {
22608
22793
  if (depth > 7) return;
22609
22794
  let entries;
22610
22795
  try {
22611
- entries = (0, import_node_fs53.readdirSync)(dir);
22796
+ entries = (0, import_node_fs54.readdirSync)(dir);
22612
22797
  } catch {
22613
22798
  return;
22614
22799
  }
22615
22800
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
22616
- const skill = readSkill((0, import_node_path50.join)(dir, "SKILL.md"), "cli");
22801
+ const skill = readSkill((0, import_node_path51.join)(dir, "SKILL.md"), "cli");
22617
22802
  if (skill) out.push(skill);
22618
22803
  }
22619
22804
  for (const entry of entries) {
22620
22805
  if (entry === "node_modules" || entry === ".git") continue;
22621
- const full = (0, import_node_path50.join)(dir, entry);
22806
+ const full = (0, import_node_path51.join)(dir, entry);
22622
22807
  try {
22623
- if (!(0, import_node_fs53.statSync)(full).isDirectory()) continue;
22808
+ if (!(0, import_node_fs54.statSync)(full).isDirectory()) continue;
22624
22809
  } catch {
22625
22810
  continue;
22626
22811
  }
@@ -22638,17 +22823,17 @@ function discoverSkills(worktreePath) {
22638
22823
  const home = (0, import_node_os15.homedir)();
22639
22824
  const collected = [];
22640
22825
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
22641
- scanSkillsDir((0, import_node_path50.join)(worktreePath, rel), "workspace", collected);
22826
+ scanSkillsDir((0, import_node_path51.join)(worktreePath, rel), "workspace", collected);
22642
22827
  }
22643
22828
  for (const abs of [
22644
- (0, import_node_path50.join)(home, ".claude/skills"),
22645
- (0, import_node_path50.join)(home, ".cursor/skills"),
22646
- (0, import_node_path50.join)(home, ".sideboard/skills"),
22647
- (0, import_node_path50.join)(home, ".brightsy/skills")
22829
+ (0, import_node_path51.join)(home, ".claude/skills"),
22830
+ (0, import_node_path51.join)(home, ".cursor/skills"),
22831
+ (0, import_node_path51.join)(home, ".sideboard/skills"),
22832
+ (0, import_node_path51.join)(home, ".brightsy/skills")
22648
22833
  ]) {
22649
22834
  scanSkillsDir(abs, "user", collected);
22650
22835
  }
22651
- scanClaudePluginSkills((0, import_node_path50.join)(home, ".claude/plugins"), collected);
22836
+ scanClaudePluginSkills((0, import_node_path51.join)(home, ".claude/plugins"), collected);
22652
22837
  collected.push(...bundledSkills());
22653
22838
  const rank = {
22654
22839
  workspace: 0,
@@ -22674,7 +22859,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
22674
22859
  \u2026(truncated)` : body;
22675
22860
  }
22676
22861
  }
22677
- const raw = (0, import_node_fs53.readFileSync)(skillPath, "utf8");
22862
+ const raw = (0, import_node_fs54.readFileSync)(skillPath, "utf8");
22678
22863
  if (raw.startsWith("---")) {
22679
22864
  const end = raw.indexOf("\n---", 3);
22680
22865
  if (end >= 0) {
@@ -22688,13 +22873,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
22688
22873
 
22689
22874
  \u2026(truncated)` : raw;
22690
22875
  }
22691
- var import_node_fs53, import_node_os15, import_node_path50, BUNDLED_SKILL_PREFIX;
22876
+ var import_node_fs54, import_node_os15, import_node_path51, BUNDLED_SKILL_PREFIX;
22692
22877
  var init_discover = __esm({
22693
22878
  "src/skills/discover.ts"() {
22694
22879
  "use strict";
22695
- import_node_fs53 = require("fs");
22880
+ import_node_fs54 = require("fs");
22696
22881
  import_node_os15 = require("os");
22697
- import_node_path50 = require("path");
22882
+ import_node_path51 = require("path");
22698
22883
  init_long_running();
22699
22884
  BUNDLED_SKILL_PREFIX = "bundled:";
22700
22885
  }
@@ -22849,7 +23034,7 @@ function compiledModuleDir() {
22849
23034
  try {
22850
23035
  const url = import_meta4.url;
22851
23036
  if (typeof url === "string" && url.length > 0) {
22852
- return (0, import_node_path51.dirname)((0, import_node_url4.fileURLToPath)(url));
23037
+ return (0, import_node_path52.dirname)((0, import_node_url4.fileURLToPath)(url));
22853
23038
  }
22854
23039
  } catch {
22855
23040
  }
@@ -22857,8 +23042,8 @@ function compiledModuleDir() {
22857
23042
  }
22858
23043
  function detachedJobScriptIn(dir) {
22859
23044
  for (const name of DETACHED_JOB_FILENAMES) {
22860
- const script = (0, import_node_path51.join)(dir, "scripts", name);
22861
- if ((0, import_node_fs54.existsSync)(script)) return script;
23045
+ const script = (0, import_node_path52.join)(dir, "scripts", name);
23046
+ if ((0, import_node_fs55.existsSync)(script)) return script;
22862
23047
  }
22863
23048
  return null;
22864
23049
  }
@@ -22875,7 +23060,7 @@ function resolveDetachedJobScript() {
22875
23060
  for (let i = 0; i < 8; i++) {
22876
23061
  const candidate = detachedJobScriptIn(dir);
22877
23062
  if (candidate) return candidate;
22878
- const parent = (0, import_node_path51.dirname)(dir);
23063
+ const parent = (0, import_node_path52.dirname)(dir);
22879
23064
  if (parent === dir) break;
22880
23065
  dir = parent;
22881
23066
  }
@@ -22886,12 +23071,12 @@ function formatDetachedJobInvoke(scriptPath) {
22886
23071
  if (resolved) return `node ${JSON.stringify(resolved)}`;
22887
23072
  return "node scripts/detached-job.cjs";
22888
23073
  }
22889
- var import_node_fs54, import_node_path51, import_node_url4, import_meta4, DETACHED_JOB_FILENAMES;
23074
+ var import_node_fs55, import_node_path52, import_node_url4, import_meta4, DETACHED_JOB_FILENAMES;
22890
23075
  var init_detached_job_path = __esm({
22891
23076
  "src/skills/detached-job-path.ts"() {
22892
23077
  "use strict";
22893
- import_node_fs54 = require("fs");
22894
- import_node_path51 = require("path");
23078
+ import_node_fs55 = require("fs");
23079
+ import_node_path52 = require("path");
22895
23080
  import_node_url4 = require("url");
22896
23081
  init_packaged_runtime();
22897
23082
  import_meta4 = {};
@@ -22992,9 +23177,9 @@ function formatWorktreeDirective(thread, opts) {
22992
23177
  );
22993
23178
  } else {
22994
23179
  const slug = opts?.githubSlug ?? "<origin-owner/name>";
22995
- const resolve = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
23180
+ const resolve2 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
22996
23181
  lines.push(
22997
- `- Prefer a draft PR first: \`gh pr create --draft -R ${slug}\` (update via \`gh pr edit -R ${slug}\`). Always pass \`-R\` \u2014 bare \`gh pr create\` may target upstream on dual-remote checkouts.${resolve} Mark ready for review only when asked. If GitHub rejects the body as too long, retry with a short \`--body-file\` \u2014 do not paste a changelog or diff into \`--body\`.`
23182
+ `- Prefer a draft PR first: \`gh pr create --draft -R ${slug}\` (update via \`gh pr edit -R ${slug}\`). Always pass \`-R\` \u2014 bare \`gh pr create\` may target upstream on dual-remote checkouts.${resolve2} Mark ready for review only when asked. If GitHub rejects the body as too long, retry with a short \`--body-file\` \u2014 do not paste a changelog or diff into \`--body\`.`
22998
23183
  );
22999
23184
  }
23000
23185
  lines.push(
@@ -23103,9 +23288,9 @@ function formatPrGateDirective() {
23103
23288
  }
23104
23289
  function formatProcessGuideDirective(opts) {
23105
23290
  const worktree = opts?.worktreePath?.trim();
23106
- const hasGraphSkill = !!worktree && (0, import_node_fs55.existsSync)((0, import_node_path52.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
23107
- const hasReviewSkill = !!worktree && (0, import_node_fs55.existsSync)((0, import_node_path52.join)(worktree, ".claude/skills/review/SKILL.md"));
23108
- const hasInstallSkill = !!worktree && (0, import_node_fs55.existsSync)((0, import_node_path52.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
23291
+ const hasGraphSkill = !!worktree && (0, import_node_fs56.existsSync)((0, import_node_path53.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
23292
+ const hasReviewSkill = !!worktree && (0, import_node_fs56.existsSync)((0, import_node_path53.join)(worktree, ".claude/skills/review/SKILL.md"));
23293
+ const hasInstallSkill = !!worktree && (0, import_node_fs56.existsSync)((0, import_node_path53.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
23109
23294
  return [
23110
23295
  "Process guides (recurring work only):",
23111
23296
  hasGraphSkill ? "- Migrations, ports, batch fixes, and other fan-out: follow `.claude/skills/graph-engineering/SKILL.md` (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch around it." : null,
@@ -23144,12 +23329,12 @@ function formatArtifactDirective() {
23144
23329
  function formatUiReminder() {
23145
23330
  return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. ask_user wakes the parent orchestrator; notify_orchestrator if blocked without a picker. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button) returns {url, port, preview} (SIDEBOARD_PORT \u2014 never guess :3000). preview=url \u2192 open url; preview=window \u2192 Electron window is the app, url is renderer/HMR only. list_run_scripts active[].url if already running; get_run_log for the Run-tab terminal; stop_dev_script to stop.";
23146
23331
  }
23147
- var import_node_fs55, import_node_path52, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
23332
+ var import_node_fs56, import_node_path53, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
23148
23333
  var init_instructions = __esm({
23149
23334
  "src/agents/instructions.ts"() {
23150
23335
  "use strict";
23151
- import_node_fs55 = require("fs");
23152
- import_node_path52 = require("path");
23336
+ import_node_fs56 = require("fs");
23337
+ import_node_path53 = require("path");
23153
23338
  init_git_auth_mode();
23154
23339
  init_branch_prefix();
23155
23340
  init_worktree_labels();
@@ -23535,40 +23720,40 @@ __export(plan_file_exports, {
23535
23720
  writePlanFile: () => writePlanFile
23536
23721
  });
23537
23722
  function ensureAttachmentsGitignore(worktreePath) {
23538
- const gitignoreAbs = (0, import_node_path53.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
23539
- if ((0, import_node_fs56.existsSync)(gitignoreAbs)) return;
23540
- (0, import_node_fs56.mkdirSync)((0, import_node_path53.dirname)(gitignoreAbs), { recursive: true });
23541
- (0, import_node_fs56.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
23723
+ const gitignoreAbs = (0, import_node_path54.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
23724
+ if ((0, import_node_fs57.existsSync)(gitignoreAbs)) return;
23725
+ (0, import_node_fs57.mkdirSync)((0, import_node_path54.dirname)(gitignoreAbs), { recursive: true });
23726
+ (0, import_node_fs57.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
23542
23727
  }
23543
23728
  function planFileAbs(worktreePath) {
23544
- return (0, import_node_path53.join)(worktreePath, PLAN_FILE_REL);
23729
+ return (0, import_node_path54.join)(worktreePath, PLAN_FILE_REL);
23545
23730
  }
23546
23731
  function readTextIfPresent2(abs) {
23547
- if (!(0, import_node_fs56.existsSync)(abs)) return null;
23732
+ if (!(0, import_node_fs57.existsSync)(abs)) return null;
23548
23733
  try {
23549
- const content = (0, import_node_fs56.readFileSync)(abs, "utf8");
23734
+ const content = (0, import_node_fs57.readFileSync)(abs, "utf8");
23550
23735
  return content.trim() ? content : null;
23551
23736
  } catch {
23552
23737
  return null;
23553
23738
  }
23554
23739
  }
23555
23740
  function readPlanFile(worktreePath) {
23556
- return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path53.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path53.join)(worktreePath, LEGACY_PLAN_FILE_REL));
23741
+ return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path54.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path54.join)(worktreePath, LEGACY_PLAN_FILE_REL));
23557
23742
  }
23558
23743
  function writePlanFile(worktreePath, content) {
23559
23744
  ensureAttachmentsGitignore(worktreePath);
23560
23745
  const abs = planFileAbs(worktreePath);
23561
- (0, import_node_fs56.mkdirSync)((0, import_node_path53.dirname)(abs), { recursive: true });
23746
+ (0, import_node_fs57.mkdirSync)((0, import_node_path54.dirname)(abs), { recursive: true });
23562
23747
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
23563
- (0, import_node_fs56.writeFileSync)(abs, body, "utf8");
23748
+ (0, import_node_fs57.writeFileSync)(abs, body, "utf8");
23564
23749
  return PLAN_FILE_REL;
23565
23750
  }
23566
- var import_node_fs56, import_node_path53;
23751
+ var import_node_fs57, import_node_path54;
23567
23752
  var init_plan_file = __esm({
23568
23753
  "src/plan/plan-file.ts"() {
23569
23754
  "use strict";
23570
- import_node_fs56 = require("fs");
23571
- import_node_path53 = require("path");
23755
+ import_node_fs57 = require("fs");
23756
+ import_node_path54 = require("path");
23572
23757
  init_workspace_scratch();
23573
23758
  init_plan_present();
23574
23759
  init_plan_present();
@@ -23630,9 +23815,9 @@ function recoverFinishedCursorRun(opts) {
23630
23815
  return best;
23631
23816
  }
23632
23817
  function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
23633
- if (!(0, import_node_fs57.existsSync)(runsPath)) return null;
23818
+ if (!(0, import_node_fs58.existsSync)(runsPath)) return null;
23634
23819
  try {
23635
- const lines = (0, import_node_fs57.readFileSync)(runsPath, "utf8").split("\n");
23820
+ const lines = (0, import_node_fs58.readFileSync)(runsPath, "utf8").split("\n");
23636
23821
  let best = null;
23637
23822
  for (const line of lines) {
23638
23823
  const trimmed = line.trim();
@@ -23658,11 +23843,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
23658
23843
  return null;
23659
23844
  }
23660
23845
  }
23661
- var import_node_fs57;
23846
+ var import_node_fs58;
23662
23847
  var init_cursor_recover = __esm({
23663
23848
  "src/agents/cursor-recover.ts"() {
23664
23849
  "use strict";
23665
- import_node_fs57 = require("fs");
23850
+ import_node_fs58 = require("fs");
23666
23851
  init_cursor_store();
23667
23852
  }
23668
23853
  });
@@ -23816,7 +24001,7 @@ async function startOrchestration(opts) {
23816
24001
  }
23817
24002
  return updated;
23818
24003
  }
23819
- var import_node_events, import_node_crypto13, import_node_fs58, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton;
24004
+ var import_node_events, import_node_crypto13, import_node_fs59, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton;
23820
24005
  var init_orchestrator = __esm({
23821
24006
  "src/orchestrator/orchestrator.ts"() {
23822
24007
  "use strict";
@@ -23824,7 +24009,7 @@ var init_orchestrator = __esm({
23824
24009
  init_injected_notices();
23825
24010
  init_task_state();
23826
24011
  import_node_crypto13 = require("crypto");
23827
- import_node_fs58 = require("fs");
24012
+ import_node_fs59 = require("fs");
23828
24013
  init_error_detail();
23829
24014
  init_run();
23830
24015
  init_stale_lock();
@@ -24003,7 +24188,7 @@ var init_orchestrator = __esm({
24003
24188
  this.lightReconcileAt.set(repoPath, Date.now());
24004
24189
  for (const thread of listThreads()) {
24005
24190
  if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
24006
- if (!(0, import_node_fs58.existsSync)(thread.worktreePath)) {
24191
+ if (!(0, import_node_fs59.existsSync)(thread.worktreePath)) {
24007
24192
  setStatus(thread.id, "broken", "Worktree missing on disk");
24008
24193
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
24009
24194
  }
@@ -24032,7 +24217,7 @@ var init_orchestrator = __esm({
24032
24217
  }
24033
24218
  continue;
24034
24219
  }
24035
- if (!(0, import_node_fs58.existsSync)(thread.worktreePath)) {
24220
+ if (!(0, import_node_fs59.existsSync)(thread.worktreePath)) {
24036
24221
  setStatus(thread.id, "broken", "Worktree missing on disk");
24037
24222
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
24038
24223
  continue;
@@ -24200,8 +24385,8 @@ var init_orchestrator = __esm({
24200
24385
  scheduleQuotaResume(threadId, resumeAt) {
24201
24386
  this.clearQuotaResumeTimer(threadId);
24202
24387
  updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
24203
- const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
24204
- const capped = Math.min(delay, 2147483647);
24388
+ const delay2 = Math.max(5e3, resumeAt.getTime() - Date.now());
24389
+ const capped = Math.min(delay2, 2147483647);
24205
24390
  const timer = setTimeout(() => {
24206
24391
  this.quotaResumeTimers.delete(threadId);
24207
24392
  void this.resumeAfterQuotaWait(threadId);
@@ -25256,6 +25441,7 @@ var init_orchestrator = __esm({
25256
25441
  return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
25257
25442
  }
25258
25443
  }
25444
+ const preferredPorts = active?.ports?.length ? active.ports : active?.port != null ? [active.port] : void 0;
25259
25445
  if (active && !existing) {
25260
25446
  killListenersOnPorts(collectActiveRunPorts([active]));
25261
25447
  this.syncWorktreeRuns(
@@ -25294,7 +25480,7 @@ var init_orchestrator = __esm({
25294
25480
  thread.repoPath,
25295
25481
  thread.worktreePath,
25296
25482
  runOutput.push,
25297
- { scriptName: resolvedName }
25483
+ { scriptName: resolvedName, preferredPorts }
25298
25484
  );
25299
25485
  } catch (err) {
25300
25486
  runOutput.flush();
@@ -25743,7 +25929,7 @@ var init_orchestrator = __esm({
25743
25929
  return thread;
25744
25930
  }
25745
25931
  const start = Date.now();
25746
- return new Promise((resolve, reject) => {
25932
+ return new Promise((resolve2, reject) => {
25747
25933
  let settled = false;
25748
25934
  let timer;
25749
25935
  const finish = (fn) => {
@@ -25761,7 +25947,7 @@ var init_orchestrator = __esm({
25761
25947
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
25762
25948
  return;
25763
25949
  }
25764
- finish(() => resolve(latest));
25950
+ finish(() => resolve2(latest));
25765
25951
  return;
25766
25952
  }
25767
25953
  if (event.type === "status_changed") {
@@ -25771,7 +25957,7 @@ var init_orchestrator = __esm({
25771
25957
  return;
25772
25958
  }
25773
25959
  if (!["running", "queued"].includes(latest.status)) {
25774
- finish(() => resolve(latest));
25960
+ finish(() => resolve2(latest));
25775
25961
  }
25776
25962
  }
25777
25963
  });
@@ -25783,12 +25969,12 @@ var init_orchestrator = __esm({
25783
25969
  }
25784
25970
  const current = this.healStaleReportedActivity(raw);
25785
25971
  if (!this.threadLooksLive(current)) {
25786
- finish(() => resolve(current));
25972
+ finish(() => resolve2(current));
25787
25973
  return;
25788
25974
  }
25789
25975
  if (Date.now() - start > timeoutMs) {
25790
25976
  if (opts?.resolveIfStillRunning) {
25791
- finish(() => resolve(current));
25977
+ finish(() => resolve2(current));
25792
25978
  } else {
25793
25979
  finish(() => reject(new Error("wait_for_turn timed out")));
25794
25980
  }
@@ -26380,7 +26566,7 @@ var init_orchestrator = __esm({
26380
26566
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
26381
26567
  return restored2;
26382
26568
  }
26383
- if (!(0, import_node_fs58.existsSync)(thread.worktreePath)) {
26569
+ if (!(0, import_node_fs59.existsSync)(thread.worktreePath)) {
26384
26570
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
26385
26571
  throw new Error(
26386
26572
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
@@ -26526,14 +26712,14 @@ function armOne(schedule) {
26526
26712
  if (!schedule.enabled) return;
26527
26713
  const at = new Date(schedule.nextRunAt);
26528
26714
  if (Number.isNaN(at.getTime())) return;
26529
- const delay = Math.max(0, at.getTime() - Date.now());
26530
- if (delay <= 250) {
26715
+ const delay2 = Math.max(0, at.getTime() - Date.now());
26716
+ if (delay2 <= 250) {
26531
26717
  void fireSchedule(schedule.id).then(() => {
26532
26718
  if (isThisProcessDesktopHost()) armSchedules();
26533
26719
  });
26534
26720
  return;
26535
26721
  }
26536
- const capped = Math.min(delay, SET_TIMEOUT_MAX);
26722
+ const capped = Math.min(delay2, SET_TIMEOUT_MAX);
26537
26723
  const timer = setTimeout(() => {
26538
26724
  timers.delete(schedule.id);
26539
26725
  const live = getSchedule(schedule.id);
@@ -26671,6 +26857,7 @@ __export(index_exports, {
26671
26857
  PLAN_FILE_REL: () => PLAN_FILE_REL,
26672
26858
  PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
26673
26859
  PLAN_QUESTION_ANSWERS_PREFIX: () => PLAN_QUESTION_ANSWERS_PREFIX,
26860
+ PORT_RANGE_SIZE: () => PORT_RANGE_SIZE,
26674
26861
  PROFILE_NOTES_MAX: () => PROFILE_NOTES_MAX,
26675
26862
  PR_DETAILS_JSON_FIELDS: () => PR_DETAILS_JSON_FIELDS,
26676
26863
  REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
@@ -26681,6 +26868,7 @@ __export(index_exports, {
26681
26868
  REVIEW_REQUEST_TEMPLATE: () => REVIEW_REQUEST_TEMPLATE,
26682
26869
  REVIEW_SKILL_NAME: () => REVIEW_SKILL_NAME,
26683
26870
  REVIEW_SKILL_PATH: () => REVIEW_SKILL_PATH,
26871
+ RUN_PORTS_REL: () => RUN_PORTS_REL,
26684
26872
  RUN_SCRIPT_DESKTOP_ADOPT_MS: () => RUN_SCRIPT_DESKTOP_ADOPT_MS,
26685
26873
  SEED_FULL_TOOL_MESSAGES: () => SEED_FULL_TOOL_MESSAGES,
26686
26874
  SEED_MAX_CHARS: () => SEED_MAX_CHARS,
@@ -27111,6 +27299,7 @@ __export(index_exports, {
27111
27299
  isOptionalServiceId: () => isOptionalServiceId,
27112
27300
  isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
27113
27301
  isOrchestratorThread: () => isOrchestratorThread,
27302
+ isOtherWorktreeListenerCwd: () => isOtherWorktreeListenerCwd,
27114
27303
  isPidAlive: () => isPidAlive,
27115
27304
  isPlaceholderBranch: () => isPlaceholderBranch,
27116
27305
  isPlanQuestionAnswersMessage: () => isPlanQuestionAnswersMessage,
@@ -27142,6 +27331,7 @@ __export(index_exports, {
27142
27331
  issueSourceLabel: () => issueSourceLabel,
27143
27332
  issueTicketFromThread: () => issueTicketFromThread,
27144
27333
  killListenersOnPorts: () => killListenersOnPorts,
27334
+ killStaleWorktreeListeners: () => killStaleWorktreeListeners,
27145
27335
  killableListenerPids: () => killableListenerPids,
27146
27336
  lastAgentReply: () => lastAgentReply,
27147
27337
  lastAssistantMessageText: () => lastAssistantMessageText,
@@ -27189,6 +27379,7 @@ __export(index_exports, {
27189
27379
  listWorkspaces: () => listWorkspaces,
27190
27380
  listWorktreeFiles: () => listWorktreeFiles,
27191
27381
  listWorktrees: () => listWorktrees,
27382
+ listenerCwd: () => listenerCwd,
27192
27383
  liveActivitySummary: () => liveActivitySummary,
27193
27384
  loadAppSettings: () => loadAppSettings,
27194
27385
  loadBrightsyConfig: () => loadBrightsyConfig,
@@ -27196,6 +27387,7 @@ __export(index_exports, {
27196
27387
  loadHomeBoardInputs: () => loadHomeBoardInputs,
27197
27388
  loadRepoSettings: () => loadRepoSettings,
27198
27389
  loadWorkspaceSettings: () => loadWorkspaceSettings,
27390
+ loadWorktreeRunPorts: () => loadWorktreeRunPorts,
27199
27391
  localhostPreviewUrl: () => localhostPreviewUrl,
27200
27392
  locksDir: () => locksDir,
27201
27393
  loginAgent: () => loginAgent,
@@ -27389,6 +27581,7 @@ __export(index_exports, {
27389
27581
  saveAbleTimeConnection: () => saveAbleTimeConnection,
27390
27582
  saveAppSettings: () => saveAppSettings,
27391
27583
  saveLinearOAuth: () => saveLinearOAuth,
27584
+ saveWorktreeRunPorts: () => saveWorktreeRunPorts,
27392
27585
  schedulesPath: () => schedulesPath,
27393
27586
  scrubGithubTokensFromChildEnv: () => scrubGithubTokensFromChildEnv,
27394
27587
  searchAbleTimeTasks: () => searchAbleTimeTasks,
@@ -27478,6 +27671,8 @@ __export(index_exports, {
27478
27671
  toolFilePath: () => toolFilePath,
27479
27672
  totalTokens: () => totalTokens,
27480
27673
  trustSystemCertificates: () => trustSystemCertificates,
27674
+ tryReservePort: () => tryReservePort,
27675
+ tryReservePorts: () => tryReservePorts,
27481
27676
  turnCostUsdFromCursorUsage: () => turnCostUsdFromCursorUsage,
27482
27677
  updateAbleTimeTask: () => updateAbleTimeTask,
27483
27678
  updateAdvancedSettings: () => updateAdvancedSettings,
@@ -27516,6 +27711,7 @@ __export(index_exports, {
27516
27711
  worktreeDisplayLabel: () => worktreeDisplayLabel,
27517
27712
  worktreeDisplayLabelForGroup: () => worktreeDisplayLabelForGroup,
27518
27713
  worktreeNameFromPath: () => worktreeNameFromPath,
27714
+ worktreeRunPortsPath: () => worktreeRunPortsPath,
27519
27715
  worktreeSlugForTicket: () => worktreeSlugForTicket,
27520
27716
  worktreesRoot: () => worktreesRoot,
27521
27717
  wrapReviewSkillMarkdown: () => wrapReviewSkillMarkdown,
@@ -28002,14 +28198,14 @@ async function startLinearOAuth(opts) {
28002
28198
  const pkce = createLinearPkce();
28003
28199
  const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
28004
28200
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
28005
- const code = await new Promise((resolve, reject) => {
28201
+ const code = await new Promise((resolve2, reject) => {
28006
28202
  let settled = false;
28007
28203
  const finish = (err, value) => {
28008
28204
  if (settled) return;
28009
28205
  settled = true;
28010
28206
  cleanup();
28011
28207
  if (err) reject(err);
28012
- else resolve(value);
28208
+ else resolve2(value);
28013
28209
  };
28014
28210
  const server = (0, import_node_http2.createServer)((req, res) => {
28015
28211
  try {
@@ -30171,7 +30367,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
30171
30367
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
30172
30368
  var import_zod9 = require("zod");
30173
30369
  var import_node_crypto17 = require("crypto");
30174
- var import_node_path59 = require("path");
30370
+ var import_node_path60 = require("path");
30175
30371
  init_orchestrator();
30176
30372
  init_task_state();
30177
30373
  init_worktree();
@@ -30507,8 +30703,8 @@ ${link}` : link;
30507
30703
  }
30508
30704
 
30509
30705
  // src/slack/outbound-watch.ts
30510
- var import_node_fs60 = require("fs");
30511
- var import_node_path56 = require("path");
30706
+ var import_node_fs61 = require("fs");
30707
+ var import_node_path57 = require("path");
30512
30708
  init_paths();
30513
30709
  init_private_file();
30514
30710
  init_secure_file();
@@ -30516,19 +30712,19 @@ init_thread_store();
30516
30712
  init_injected_notices();
30517
30713
 
30518
30714
  // src/slack/reply-target.ts
30519
- var import_node_fs59 = require("fs");
30520
- var import_node_path54 = require("path");
30715
+ var import_node_fs60 = require("fs");
30716
+ var import_node_path55 = require("path");
30521
30717
  init_paths();
30522
30718
  init_private_file();
30523
30719
  init_secure_file();
30524
30720
  function storePath2() {
30525
- return (0, import_node_path54.join)(appDataDir(), "slack-reply-to.json");
30721
+ return (0, import_node_path55.join)(appDataDir(), "slack-reply-to.json");
30526
30722
  }
30527
30723
  function readStore2() {
30528
30724
  const path2 = storePath2();
30529
- if (!(0, import_node_fs59.existsSync)(path2)) return {};
30725
+ if (!(0, import_node_fs60.existsSync)(path2)) return {};
30530
30726
  try {
30531
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs59.readFileSync)(path2, "utf8"));
30727
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs60.readFileSync)(path2, "utf8"));
30532
30728
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
30533
30729
  } catch {
30534
30730
  return {};
@@ -30545,11 +30741,11 @@ function getSlackReplyTarget(threadId) {
30545
30741
  }
30546
30742
 
30547
30743
  // src/slack/workspaces.ts
30548
- var import_node_path55 = require("path");
30744
+ var import_node_path56 = require("path");
30549
30745
  init_paths();
30550
30746
  init_secure_file();
30551
30747
  function storePath3() {
30552
- return (0, import_node_path55.join)(appDataDir(), "slack-workspaces.json");
30748
+ return (0, import_node_path56.join)(appDataDir(), "slack-workspaces.json");
30553
30749
  }
30554
30750
  function readStore3() {
30555
30751
  try {
@@ -30665,7 +30861,7 @@ var POLL_INTERVAL_MS = 12e3;
30665
30861
  var lastPollMs = 0;
30666
30862
  var nameCache = /* @__PURE__ */ new Map();
30667
30863
  function storePath4() {
30668
- return (0, import_node_path56.join)(appDataDir(), "slack-outbound-watch.json");
30864
+ return (0, import_node_path57.join)(appDataDir(), "slack-outbound-watch.json");
30669
30865
  }
30670
30866
  function watchId(teamId, channelId, ts) {
30671
30867
  return `${teamId}:${channelId}:${ts}`;
@@ -30678,9 +30874,9 @@ function tsNewer(a, b) {
30678
30874
  }
30679
30875
  function readStore4() {
30680
30876
  const path2 = storePath4();
30681
- if (!(0, import_node_fs60.existsSync)(path2)) return [];
30877
+ if (!(0, import_node_fs61.existsSync)(path2)) return [];
30682
30878
  try {
30683
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs60.readFileSync)(path2, "utf8"));
30879
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs61.readFileSync)(path2, "utf8"));
30684
30880
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
30685
30881
  } catch {
30686
30882
  return [];
@@ -32431,27 +32627,27 @@ init_gh_errors();
32431
32627
  init_git_auth_mode();
32432
32628
 
32433
32629
  // src/board/load-home-board.ts
32434
- var import_node_fs62 = require("fs");
32435
- var import_node_path58 = require("path");
32630
+ var import_node_fs63 = require("fs");
32631
+ var import_node_path59 = require("path");
32436
32632
  init_worktree();
32437
32633
  init_paths();
32438
32634
 
32439
32635
  // src/board/board-pins.ts
32440
32636
  var import_node_crypto16 = require("crypto");
32441
- var import_node_fs61 = require("fs");
32442
- var import_node_path57 = require("path");
32637
+ var import_node_fs62 = require("fs");
32638
+ var import_node_path58 = require("path");
32443
32639
  init_paths();
32444
32640
  init_home_board();
32445
32641
  var FILE = "home-board-pins.json";
32446
32642
  var VERSION = 1;
32447
32643
  function pinsFile() {
32448
- return (0, import_node_path57.join)(appDataDir(), FILE);
32644
+ return (0, import_node_path58.join)(appDataDir(), FILE);
32449
32645
  }
32450
32646
  function readDisk() {
32451
32647
  const path2 = pinsFile();
32452
- if (!(0, import_node_fs61.existsSync)(path2)) return [];
32648
+ if (!(0, import_node_fs62.existsSync)(path2)) return [];
32453
32649
  try {
32454
- const raw = JSON.parse((0, import_node_fs61.readFileSync)(path2, "utf8"));
32650
+ const raw = JSON.parse((0, import_node_fs62.readFileSync)(path2, "utf8"));
32455
32651
  if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
32456
32652
  return raw.items.filter((item) => item?.id && item.kind && item.ref);
32457
32653
  } catch {
@@ -32459,8 +32655,8 @@ function readDisk() {
32459
32655
  }
32460
32656
  }
32461
32657
  function writeDisk(items) {
32462
- (0, import_node_fs61.mkdirSync)(appDataDir(), { recursive: true });
32463
- (0, import_node_fs61.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
32658
+ (0, import_node_fs62.mkdirSync)(appDataDir(), { recursive: true });
32659
+ (0, import_node_fs62.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
32464
32660
  }
32465
32661
  function listBoardPins() {
32466
32662
  return readDisk();
@@ -32512,7 +32708,7 @@ function replaceBoardPins(items) {
32512
32708
  }
32513
32709
  function clearBoardPins() {
32514
32710
  try {
32515
- (0, import_node_fs61.unlinkSync)(pinsFile());
32711
+ (0, import_node_fs62.unlinkSync)(pinsFile());
32516
32712
  } catch {
32517
32713
  }
32518
32714
  }
@@ -32529,7 +32725,7 @@ function homeBoardWorkspaceKey(workspaces) {
32529
32725
  return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
32530
32726
  }
32531
32727
  function cacheFile() {
32532
- return (0, import_node_path58.join)(appDataDir(), "home-board-cache.json");
32728
+ return (0, import_node_path59.join)(appDataDir(), "home-board-cache.json");
32533
32729
  }
32534
32730
  function emptyInputs() {
32535
32731
  return {
@@ -32555,9 +32751,9 @@ function shouldCacheHomeBoardInputs(inputs) {
32555
32751
  }
32556
32752
  function readDiskCache() {
32557
32753
  const path2 = cacheFile();
32558
- if (!(0, import_node_fs62.existsSync)(path2)) return null;
32754
+ if (!(0, import_node_fs63.existsSync)(path2)) return null;
32559
32755
  try {
32560
- const raw = JSON.parse((0, import_node_fs62.readFileSync)(path2, "utf8"));
32756
+ const raw = JSON.parse((0, import_node_fs63.readFileSync)(path2, "utf8"));
32561
32757
  if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
32562
32758
  return null;
32563
32759
  }
@@ -32571,14 +32767,14 @@ function readDiskCache() {
32571
32767
  }
32572
32768
  function writeDiskCache(entry) {
32573
32769
  try {
32574
- (0, import_node_fs62.mkdirSync)(appDataDir(), { recursive: true });
32575
- (0, import_node_fs62.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
32770
+ (0, import_node_fs63.mkdirSync)(appDataDir(), { recursive: true });
32771
+ (0, import_node_fs63.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
32576
32772
  } catch {
32577
32773
  }
32578
32774
  }
32579
32775
  function deleteDiskCache() {
32580
32776
  try {
32581
- (0, import_node_fs62.unlinkSync)(cacheFile());
32777
+ (0, import_node_fs63.unlinkSync)(cacheFile());
32582
32778
  } catch {
32583
32779
  }
32584
32780
  }
@@ -32765,14 +32961,14 @@ function orchCreateThreadAgentNote(input) {
32765
32961
  var MAX_ORCH_THREADS = 5;
32766
32962
  var CREATE_THREAD_TIMEOUT_MS = 9e4;
32767
32963
  function withTimeout2(promise, ms, label) {
32768
- return new Promise((resolve, reject) => {
32964
+ return new Promise((resolve2, reject) => {
32769
32965
  const timer = setTimeout(() => {
32770
32966
  reject(new Error(`${label} timed out after ${Math.round(ms / 1e3)}s`));
32771
32967
  }, ms);
32772
32968
  promise.then(
32773
32969
  (value) => {
32774
32970
  clearTimeout(timer);
32775
- resolve(value);
32971
+ resolve2(value);
32776
32972
  },
32777
32973
  (err) => {
32778
32974
  clearTimeout(timer);
@@ -32944,7 +33140,7 @@ async function startMcpServer() {
32944
33140
  async () => {
32945
33141
  const threads = orch.getThreads(true);
32946
33142
  const lines = threads.map((t) => {
32947
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID2 ? "Orchestration" : (0, import_node_path59.basename)(t.repoPath) || t.repoPath;
33143
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID2 ? "Orchestration" : (0, import_node_path60.basename)(t.repoPath) || t.repoPath;
32948
33144
  const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
32949
33145
  const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
32950
33146
  const preview = lastMessagePreview(t.messages, 80);
@@ -34398,13 +34594,13 @@ async function runCloudConnect(opts) {
34398
34594
  log(`poll error: ${msg}`);
34399
34595
  }
34400
34596
  }
34401
- await new Promise((resolve) => {
34402
- const t = setTimeout(resolve, pollMs);
34597
+ await new Promise((resolve2) => {
34598
+ const t = setTimeout(resolve2, pollMs);
34403
34599
  opts.signal?.addEventListener(
34404
34600
  "abort",
34405
34601
  () => {
34406
34602
  clearTimeout(t);
34407
- resolve();
34603
+ resolve2();
34408
34604
  },
34409
34605
  { once: true }
34410
34606
  );
@@ -34612,14 +34808,14 @@ function slackOAuthAuthorizeUrl(clientId, state) {
34612
34808
  return `https://slack.com/oauth/v2/authorize?${params.toString()}`;
34613
34809
  }
34614
34810
  function sleep(ms, signal) {
34615
- return new Promise((resolve, reject) => {
34811
+ return new Promise((resolve2, reject) => {
34616
34812
  if (signal?.aborted) {
34617
34813
  reject(new SlackOAuthCancelledError());
34618
34814
  return;
34619
34815
  }
34620
34816
  const timer = setTimeout(() => {
34621
34817
  signal?.removeEventListener("abort", onAbort);
34622
- resolve();
34818
+ resolve2();
34623
34819
  }, ms);
34624
34820
  const onAbort = () => {
34625
34821
  clearTimeout(timer);
@@ -34811,12 +35007,12 @@ function resolveWebSocket(ctor) {
34811
35007
  return nodeWebSocketCtor();
34812
35008
  }
34813
35009
  function wait(ms, signal) {
34814
- return new Promise((resolve, reject) => {
35010
+ return new Promise((resolve2, reject) => {
34815
35011
  if (signal?.aborted) {
34816
35012
  reject(new Error("aborted"));
34817
35013
  return;
34818
35014
  }
34819
- const t = setTimeout(resolve, ms);
35015
+ const t = setTimeout(resolve2, ms);
34820
35016
  signal?.addEventListener(
34821
35017
  "abort",
34822
35018
  () => {
@@ -34861,14 +35057,14 @@ async function runSlackSocketMode(opts) {
34861
35057
  }
34862
35058
  }
34863
35059
  function connectSession(ws, opts, log) {
34864
- return new Promise((resolve, reject) => {
35060
+ return new Promise((resolve2, reject) => {
34865
35061
  let settled = false;
34866
35062
  const finish = (err) => {
34867
35063
  if (settled) return;
34868
35064
  settled = true;
34869
35065
  opts.signal?.removeEventListener("abort", onAbort);
34870
35066
  if (err) reject(err);
34871
- else resolve();
35067
+ else resolve2();
34872
35068
  };
34873
35069
  const onAbort = () => {
34874
35070
  try {
@@ -35039,12 +35235,12 @@ var CLAIM_FALLBACK_MS = 400;
35039
35235
  var BACKOFF_START_MS2 = 1e3;
35040
35236
  var BACKOFF_MAX_MS2 = 3e4;
35041
35237
  function wait2(ms, signal) {
35042
- return new Promise((resolve, reject) => {
35238
+ return new Promise((resolve2, reject) => {
35043
35239
  if (signal?.aborted) {
35044
35240
  reject(new Error("aborted"));
35045
35241
  return;
35046
35242
  }
35047
- const t = setTimeout(resolve, ms);
35243
+ const t = setTimeout(resolve2, ms);
35048
35244
  signal?.addEventListener(
35049
35245
  "abort",
35050
35246
  () => {
@@ -35103,7 +35299,7 @@ async function runSlackRelayClient(opts) {
35103
35299
  }
35104
35300
  }
35105
35301
  function connectSession2(ws, opts, log) {
35106
- return new Promise((resolve, reject) => {
35302
+ return new Promise((resolve2, reject) => {
35107
35303
  let settled = false;
35108
35304
  const pending = /* @__PURE__ */ new Map();
35109
35305
  let relayAnswersClaims = false;
@@ -35137,7 +35333,7 @@ function connectSession2(ws, opts, log) {
35137
35333
  stopKeepalive();
35138
35334
  opts.signal?.removeEventListener("abort", onAbort);
35139
35335
  if (err) reject(err);
35140
- else resolve();
35336
+ else resolve2();
35141
35337
  };
35142
35338
  const onAbort = () => {
35143
35339
  try {
@@ -36063,9 +36259,9 @@ var import_node_http4 = require("http");
36063
36259
  var import_ws3 = require("ws");
36064
36260
 
36065
36261
  // src/slack/relay-static.ts
36066
- var import_node_fs63 = require("fs");
36262
+ var import_node_fs64 = require("fs");
36067
36263
  var import_promises = require("fs/promises");
36068
- var import_node_path60 = __toESM(require("path"), 1);
36264
+ var import_node_path61 = __toESM(require("path"), 1);
36069
36265
  var TYPES = {
36070
36266
  ".css": "text/css; charset=utf-8",
36071
36267
  ".html": "text/html; charset=utf-8",
@@ -36096,9 +36292,9 @@ function resolveStaticPath(root, requestUrl) {
36096
36292
  return null;
36097
36293
  }
36098
36294
  if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
36099
- const rootResolved = import_node_path60.default.resolve(root);
36100
- const candidate = import_node_path60.default.resolve(rootResolved, `.${pathname}`);
36101
- if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path60.default.sep)) {
36295
+ const rootResolved = import_node_path61.default.resolve(root);
36296
+ const candidate = import_node_path61.default.resolve(rootResolved, `.${pathname}`);
36297
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path61.default.sep)) {
36102
36298
  return null;
36103
36299
  }
36104
36300
  return candidate;
@@ -36112,7 +36308,7 @@ async function fileSize(file) {
36112
36308
  }
36113
36309
  }
36114
36310
  function sendFile(req, res, file, size) {
36115
- const ext = import_node_path60.default.extname(file).toLowerCase();
36311
+ const ext = import_node_path61.default.extname(file).toLowerCase();
36116
36312
  res.writeHead(200, {
36117
36313
  "Content-Type": TYPES[ext] ?? "application/octet-stream",
36118
36314
  "Content-Length": size,
@@ -36122,7 +36318,7 @@ function sendFile(req, res, file, size) {
36122
36318
  res.end();
36123
36319
  return true;
36124
36320
  }
36125
- (0, import_node_fs63.createReadStream)(file).pipe(res);
36321
+ (0, import_node_fs64.createReadStream)(file).pipe(res);
36126
36322
  return true;
36127
36323
  }
36128
36324
  async function tryServeStatic(req, res, root) {
@@ -36131,7 +36327,7 @@ async function tryServeStatic(req, res, root) {
36131
36327
  if (!candidate) return false;
36132
36328
  const direct = await fileSize(candidate);
36133
36329
  if (direct != null) return sendFile(req, res, candidate, direct);
36134
- const asIndex = import_node_path60.default.join(candidate, "index.html");
36330
+ const asIndex = import_node_path61.default.join(candidate, "index.html");
36135
36331
  const indexSize = await fileSize(asIndex);
36136
36332
  if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
36137
36333
  return false;
@@ -36331,9 +36527,9 @@ async function startSlackRelayServer(opts) {
36331
36527
  });
36332
36528
  const port = opts.port ?? 0;
36333
36529
  const host = opts.host ?? "0.0.0.0";
36334
- await new Promise((resolve, reject) => {
36530
+ await new Promise((resolve2, reject) => {
36335
36531
  httpServer.once("error", reject);
36336
- httpServer.listen(port, host, () => resolve());
36532
+ httpServer.listen(port, host, () => resolve2());
36337
36533
  });
36338
36534
  const address = httpServer.address();
36339
36535
  const boundPort = typeof address === "object" && address ? address.port : typeof port === "number" ? port : 0;
@@ -36362,11 +36558,11 @@ async function startSlackRelayServer(opts) {
36362
36558
  close: async () => {
36363
36559
  opts.signal?.removeEventListener("abort", onAbort);
36364
36560
  ac.abort();
36365
- await new Promise((resolve) => {
36366
- wss.close(() => resolve());
36561
+ await new Promise((resolve2) => {
36562
+ wss.close(() => resolve2());
36367
36563
  });
36368
- await new Promise((resolve, reject) => {
36369
- httpServer.close((err) => err ? reject(err) : resolve());
36564
+ await new Promise((resolve2, reject) => {
36565
+ httpServer.close((err) => err ? reject(err) : resolve2());
36370
36566
  });
36371
36567
  await socketModeDone.catch(() => void 0);
36372
36568
  }
@@ -36462,6 +36658,7 @@ async function startSlackRelayServer(opts) {
36462
36658
  PLAN_FILE_REL,
36463
36659
  PLAN_MODE_INSTRUCTION,
36464
36660
  PLAN_QUESTION_ANSWERS_PREFIX,
36661
+ PORT_RANGE_SIZE,
36465
36662
  PROFILE_NOTES_MAX,
36466
36663
  PR_DETAILS_JSON_FIELDS,
36467
36664
  REPO_REVIEW_NAME,
@@ -36472,6 +36669,7 @@ async function startSlackRelayServer(opts) {
36472
36669
  REVIEW_REQUEST_TEMPLATE,
36473
36670
  REVIEW_SKILL_NAME,
36474
36671
  REVIEW_SKILL_PATH,
36672
+ RUN_PORTS_REL,
36475
36673
  RUN_SCRIPT_DESKTOP_ADOPT_MS,
36476
36674
  SEED_FULL_TOOL_MESSAGES,
36477
36675
  SEED_MAX_CHARS,
@@ -36902,6 +37100,7 @@ async function startSlackRelayServer(opts) {
36902
37100
  isOptionalServiceId,
36903
37101
  isOrchestratorCapableAgent,
36904
37102
  isOrchestratorThread,
37103
+ isOtherWorktreeListenerCwd,
36905
37104
  isPidAlive,
36906
37105
  isPlaceholderBranch,
36907
37106
  isPlanQuestionAnswersMessage,
@@ -36933,6 +37132,7 @@ async function startSlackRelayServer(opts) {
36933
37132
  issueSourceLabel,
36934
37133
  issueTicketFromThread,
36935
37134
  killListenersOnPorts,
37135
+ killStaleWorktreeListeners,
36936
37136
  killableListenerPids,
36937
37137
  lastAgentReply,
36938
37138
  lastAssistantMessageText,
@@ -36980,6 +37180,7 @@ async function startSlackRelayServer(opts) {
36980
37180
  listWorkspaces,
36981
37181
  listWorktreeFiles,
36982
37182
  listWorktrees,
37183
+ listenerCwd,
36983
37184
  liveActivitySummary,
36984
37185
  loadAppSettings,
36985
37186
  loadBrightsyConfig,
@@ -36987,6 +37188,7 @@ async function startSlackRelayServer(opts) {
36987
37188
  loadHomeBoardInputs,
36988
37189
  loadRepoSettings,
36989
37190
  loadWorkspaceSettings,
37191
+ loadWorktreeRunPorts,
36990
37192
  localhostPreviewUrl,
36991
37193
  locksDir,
36992
37194
  loginAgent,
@@ -37180,6 +37382,7 @@ async function startSlackRelayServer(opts) {
37180
37382
  saveAbleTimeConnection,
37181
37383
  saveAppSettings,
37182
37384
  saveLinearOAuth,
37385
+ saveWorktreeRunPorts,
37183
37386
  schedulesPath,
37184
37387
  scrubGithubTokensFromChildEnv,
37185
37388
  searchAbleTimeTasks,
@@ -37269,6 +37472,8 @@ async function startSlackRelayServer(opts) {
37269
37472
  toolFilePath,
37270
37473
  totalTokens,
37271
37474
  trustSystemCertificates,
37475
+ tryReservePort,
37476
+ tryReservePorts,
37272
37477
  turnCostUsdFromCursorUsage,
37273
37478
  updateAbleTimeTask,
37274
37479
  updateAdvancedSettings,
@@ -37307,6 +37512,7 @@ async function startSlackRelayServer(opts) {
37307
37512
  worktreeDisplayLabel,
37308
37513
  worktreeDisplayLabelForGroup,
37309
37514
  worktreeNameFromPath,
37515
+ worktreeRunPortsPath,
37310
37516
  worktreeSlugForTicket,
37311
37517
  worktreesRoot,
37312
37518
  wrapReviewSkillMarkdown,