@sideboard-ai/core 0.1.238 → 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.
@@ -792,13 +792,14 @@ function shouldAutoArchiveOnPrMerge(opts) {
792
792
  import {
793
793
  copyFileSync,
794
794
  existsSync as existsSync3,
795
- mkdirSync as mkdirSync3,
795
+ mkdirSync as mkdirSync4,
796
796
  readdirSync,
797
- readFileSync as readFileSync2
797
+ readFileSync as readFileSync3,
798
+ realpathSync
798
799
  } from "fs";
799
800
  import { execFileSync } from "child_process";
800
801
  import { createServer } from "net";
801
- import { basename, dirname as dirname2, join as join4 } from "path";
802
+ import { basename, dirname as dirname3, join as join5, resolve, sep } from "path";
802
803
  import { execa as execa2 } from "execa";
803
804
  import { createInterface as createInterface2 } from "readline";
804
805
 
@@ -1658,16 +1659,73 @@ async function requestReview(threadRef, send) {
1658
1659
  return { tab: started, from };
1659
1660
  }
1660
1661
 
1662
+ // src/hook/worktree-run-ports.ts
1663
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1664
+ import { dirname as dirname2, join as join4 } from "path";
1665
+ var RUN_PORTS_REL = ".context/.sideboard/run-ports.json";
1666
+ function worktreeRunPortsPath(worktreePath) {
1667
+ return join4(worktreePath, RUN_PORTS_REL);
1668
+ }
1669
+ function isPort(value) {
1670
+ return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535;
1671
+ }
1672
+ function parseFile(raw) {
1673
+ try {
1674
+ const parsed = JSON.parse(raw);
1675
+ if (!parsed || typeof parsed !== "object") return {};
1676
+ const scripts = parsed.scripts;
1677
+ if (!scripts || typeof scripts !== "object") return {};
1678
+ const next = {};
1679
+ for (const [name, ports] of Object.entries(scripts)) {
1680
+ if (!name || !Array.isArray(ports)) continue;
1681
+ const cleaned = ports.filter(isPort);
1682
+ if (cleaned.length) next[name] = cleaned;
1683
+ }
1684
+ return { scripts: next };
1685
+ } catch {
1686
+ return {};
1687
+ }
1688
+ }
1689
+ function loadWorktreeRunPorts(worktreePath, scriptName) {
1690
+ if (!worktreePath.trim() || !scriptName.trim()) return null;
1691
+ try {
1692
+ const raw = readFileSync2(worktreeRunPortsPath(worktreePath), "utf8");
1693
+ const ports = parseFile(raw).scripts?.[scriptName];
1694
+ return ports?.length ? ports : null;
1695
+ } catch {
1696
+ return null;
1697
+ }
1698
+ }
1699
+ function saveWorktreeRunPorts(worktreePath, scriptName, ports) {
1700
+ if (!worktreePath.trim() || !scriptName.trim()) return;
1701
+ const cleaned = ports.filter(isPort);
1702
+ if (!cleaned.length) return;
1703
+ const path = worktreeRunPortsPath(worktreePath);
1704
+ let current = {};
1705
+ try {
1706
+ current = parseFile(readFileSync2(path, "utf8"));
1707
+ } catch {
1708
+ current = {};
1709
+ }
1710
+ const scripts = { ...current.scripts ?? {} };
1711
+ scripts[scriptName] = cleaned;
1712
+ mkdirSync3(dirname2(path), { recursive: true });
1713
+ writeFileSync2(path, `${JSON.stringify({ scripts }, null, 2)}
1714
+ `, "utf8");
1715
+ }
1716
+
1661
1717
  // src/hook/conductor.ts
1662
1718
  var PORT_RANGE_SIZE = 10;
1719
+ var RECLAIM_RETRY_DELAY_MS = 40;
1720
+ var RECLAIM_TRIES = 8;
1663
1721
  function matchSimpleGlob(pattern, name) {
1664
1722
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1665
1723
  return new RegExp(`^${escaped}$`).test(name);
1666
1724
  }
1667
1725
  function readWorktreeInclude(repoPath) {
1668
- const path = join4(repoPath, ".worktreeinclude");
1726
+ const path = join5(repoPath, ".worktreeinclude");
1669
1727
  if (!existsSync3(path)) return [];
1670
- return readFileSync2(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1728
+ return readFileSync3(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1671
1729
  }
1672
1730
  function resolveFilesToCopy(repoPath) {
1673
1731
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1706,10 +1764,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
1706
1764
  const patterns = resolveFilesToCopy(repoPath);
1707
1765
  const copied = [];
1708
1766
  for (const rel of patterns) {
1709
- const src = join4(repoPath, rel);
1767
+ const src = join5(repoPath, rel);
1710
1768
  if (!existsSync3(src)) continue;
1711
- const dest = join4(worktreePath, rel);
1712
- mkdirSync3(dirname2(dest), { recursive: true });
1769
+ const dest = join5(worktreePath, rel);
1770
+ mkdirSync4(dirname3(dest), { recursive: true });
1713
1771
  copyFileSync(src, dest);
1714
1772
  copied.push(rel);
1715
1773
  }
@@ -1786,6 +1844,57 @@ function killableListenerPids(raw, selfPid = process.pid) {
1786
1844
  }
1787
1845
  return pids;
1788
1846
  }
1847
+ function listUnixListenerPids(port, selfPid) {
1848
+ try {
1849
+ const raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
1850
+ encoding: "utf8",
1851
+ stdio: ["ignore", "pipe", "ignore"],
1852
+ timeout: 2e3
1853
+ });
1854
+ return killableListenerPids(raw, selfPid);
1855
+ } catch {
1856
+ return [];
1857
+ }
1858
+ }
1859
+ function listenerCwd(pid) {
1860
+ if (!Number.isFinite(pid) || pid <= 0) return null;
1861
+ try {
1862
+ const raw = execFileSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
1863
+ encoding: "utf8",
1864
+ stdio: ["ignore", "pipe", "ignore"],
1865
+ timeout: 2e3
1866
+ });
1867
+ for (const line of raw.split("\n")) {
1868
+ if (line.startsWith("n") && line.length > 1) return line.slice(1);
1869
+ }
1870
+ } catch {
1871
+ }
1872
+ return null;
1873
+ }
1874
+ function realpathOrResolve(path) {
1875
+ try {
1876
+ return realpathSync(path);
1877
+ } catch {
1878
+ return resolve(path);
1879
+ }
1880
+ }
1881
+ function isOtherWorktreeListenerCwd(cwd, worktreePath) {
1882
+ const wt = realpathOrResolve(worktreePath);
1883
+ const c = realpathOrResolve(cwd);
1884
+ if (c === wt || c.startsWith(wt + sep)) return false;
1885
+ const parent = dirname3(wt);
1886
+ const thisName = basename(wt);
1887
+ if (!thisName || c === parent) return false;
1888
+ if (!c.startsWith(parent + sep)) return false;
1889
+ const first = c.slice(parent.length + 1).split(sep)[0];
1890
+ return Boolean(first) && first !== thisName;
1891
+ }
1892
+ function signalPid(pid, signal) {
1893
+ try {
1894
+ process.kill(pid, signal);
1895
+ } catch {
1896
+ }
1897
+ }
1789
1898
  function killListenersOnPorts(ports) {
1790
1899
  const selfPid = process.pid;
1791
1900
  for (const port of ports) {
@@ -1803,26 +1912,32 @@ function killListenersOnPorts(ports) {
1803
1912
  );
1804
1913
  continue;
1805
1914
  }
1806
- let raw = "";
1807
- try {
1808
- raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
1809
- encoding: "utf8",
1810
- stdio: ["ignore", "pipe", "ignore"],
1811
- timeout: 2e3
1812
- });
1813
- } catch {
1814
- continue;
1815
- }
1816
- for (const pid of killableListenerPids(raw, selfPid)) {
1817
- try {
1818
- process.kill(pid, "SIGTERM");
1819
- } catch {
1820
- }
1915
+ for (const pid of listUnixListenerPids(port, selfPid)) {
1916
+ signalPid(pid, "SIGTERM");
1821
1917
  }
1822
1918
  } catch {
1823
1919
  }
1824
1920
  }
1825
1921
  }
1922
+ function killStaleWorktreeListeners(ports, worktreePath, signal = "SIGTERM") {
1923
+ if (!worktreePath.trim()) {
1924
+ killListenersOnPorts(ports);
1925
+ return;
1926
+ }
1927
+ if (process.platform === "win32") {
1928
+ killListenersOnPorts(ports);
1929
+ return;
1930
+ }
1931
+ const selfPid = process.pid;
1932
+ for (const port of ports) {
1933
+ if (!Number.isFinite(port) || port <= 0) continue;
1934
+ for (const pid of listUnixListenerPids(port, selfPid)) {
1935
+ const cwd = listenerCwd(pid);
1936
+ if (cwd && isOtherWorktreeListenerCwd(cwd, worktreePath)) continue;
1937
+ signalPid(pid, signal);
1938
+ }
1939
+ }
1940
+ }
1826
1941
  function killScriptTree(child, ports = []) {
1827
1942
  const pid = child.pid;
1828
1943
  if (pid) {
@@ -1984,7 +2099,7 @@ function getRunMode(worktreePath, repoPath) {
1984
2099
  return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
1985
2100
  }
1986
2101
  async function reservePort() {
1987
- return new Promise((resolve, reject) => {
2102
+ return new Promise((resolve2, reject) => {
1988
2103
  const server = createServer();
1989
2104
  let released = false;
1990
2105
  const release = () => new Promise((res) => {
@@ -2001,13 +2116,14 @@ async function reservePort() {
2001
2116
  void release().then(() => reject(new Error("Failed to allocate port")));
2002
2117
  return;
2003
2118
  }
2004
- resolve({ port: addr.port, release });
2119
+ resolve2({ port: addr.port, release });
2005
2120
  });
2006
2121
  server.on("error", reject);
2007
2122
  });
2008
2123
  }
2009
2124
  async function tryReservePort(port) {
2010
- return new Promise((resolve) => {
2125
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
2126
+ return new Promise((resolve2) => {
2011
2127
  const server = createServer();
2012
2128
  let released = false;
2013
2129
  const release = () => new Promise((res) => {
@@ -2018,24 +2134,54 @@ async function tryReservePort(port) {
2018
2134
  released = true;
2019
2135
  server.close(() => res());
2020
2136
  });
2021
- server.once("error", () => resolve(null));
2137
+ server.once("error", () => {
2138
+ try {
2139
+ server.close();
2140
+ } catch {
2141
+ }
2142
+ resolve2(null);
2143
+ });
2022
2144
  server.listen(port, "127.0.0.1", () => {
2023
- resolve({ port, release });
2145
+ resolve2({ port, release });
2024
2146
  });
2025
2147
  });
2026
2148
  }
2027
- async function reservePortRange(size = PORT_RANGE_SIZE) {
2028
- const held = [await reservePort()];
2029
- const base = held[0].port;
2149
+ async function tryReservePorts(ports) {
2150
+ const held = [];
2151
+ for (const port of ports) {
2152
+ const next = await tryReservePort(port);
2153
+ if (!next) {
2154
+ await Promise.all(held.map((h) => h.release()));
2155
+ return null;
2156
+ }
2157
+ held.push(next);
2158
+ }
2159
+ return held;
2160
+ }
2161
+ function normalizePreferredPorts(ports, size) {
2162
+ if (!ports?.length) return [];
2163
+ const seen = /* @__PURE__ */ new Set();
2164
+ const out = [];
2165
+ for (const p of ports) {
2166
+ if (!Number.isInteger(p) || p <= 0 || p > 65535 || seen.has(p)) continue;
2167
+ seen.add(p);
2168
+ out.push(p);
2169
+ if (out.length >= size) break;
2170
+ }
2171
+ return out;
2172
+ }
2173
+ function delay(ms) {
2174
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2175
+ }
2176
+ async function fillPortRange(held, size) {
2177
+ if (held.length >= size) return held;
2178
+ const base = held[0]?.port;
2030
2179
  try {
2031
- for (let i = 1; i < size; i++) {
2032
- const candidate = base + i;
2033
- const next = await tryReservePort(candidate);
2034
- if (next) {
2035
- held.push(next);
2036
- } else {
2037
- held.push(await reservePort());
2038
- }
2180
+ for (let i = held.length; i < size; i++) {
2181
+ const candidate = base != null ? base + i : null;
2182
+ const next = candidate != null ? await tryReservePort(candidate) : null;
2183
+ if (next) held.push(next);
2184
+ else held.push(await reservePort());
2039
2185
  }
2040
2186
  return held;
2041
2187
  } catch (err) {
@@ -2043,11 +2189,43 @@ async function reservePortRange(size = PORT_RANGE_SIZE) {
2043
2189
  throw err;
2044
2190
  }
2045
2191
  }
2192
+ async function reclaimPreferredPorts(ports, worktreePath) {
2193
+ let held = await tryReservePorts(ports);
2194
+ if (held) return held;
2195
+ if (worktreePath) {
2196
+ killStaleWorktreeListeners(ports, worktreePath, "SIGTERM");
2197
+ } else {
2198
+ killListenersOnPorts(ports);
2199
+ }
2200
+ for (let i = 0; i < RECLAIM_TRIES; i++) {
2201
+ await delay(RECLAIM_RETRY_DELAY_MS);
2202
+ held = await tryReservePorts(ports);
2203
+ if (held) return held;
2204
+ if (i === 2 && worktreePath) {
2205
+ killStaleWorktreeListeners(ports, worktreePath, "SIGKILL");
2206
+ }
2207
+ }
2208
+ return null;
2209
+ }
2210
+ async function reservePortRange(size = PORT_RANGE_SIZE, opts) {
2211
+ const preferred = normalizePreferredPorts(opts?.preferred, size);
2212
+ if (preferred.length) {
2213
+ const reused = await reclaimPreferredPorts(preferred, opts?.worktreePath);
2214
+ if (reused) return fillPortRange(reused, size);
2215
+ }
2216
+ const held = [await reservePort()];
2217
+ return fillPortRange(held, size);
2218
+ }
2046
2219
  async function startDevServer(repoPath, worktreePath, onLine, opts) {
2047
2220
  const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
2048
2221
  if (!script) return null;
2049
- const held = await reservePortRange(PORT_RANGE_SIZE);
2222
+ const preferred = loadWorktreeRunPorts(worktreePath, script.name) ?? opts?.preferredPorts;
2223
+ const held = await reservePortRange(PORT_RANGE_SIZE, {
2224
+ preferred: preferred ?? void 0,
2225
+ worktreePath
2226
+ });
2050
2227
  const ports = held.map((h) => h.port);
2228
+ saveWorktreeRunPorts(worktreePath, script.name, ports);
2051
2229
  try {
2052
2230
  const handle = await spawnWorkspaceScript(script.command, {
2053
2231
  worktreePath,
@@ -2073,8 +2251,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2073
2251
 
2074
2252
  // src/store/setup-log.ts
2075
2253
  import { createHash } from "crypto";
2076
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2077
- import { join as join5 } from "path";
2254
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2255
+ import { join as join6 } from "path";
2078
2256
  var EMPTY = {
2079
2257
  output: "",
2080
2258
  running: false,
@@ -2116,7 +2294,7 @@ function materialize(mem) {
2116
2294
  };
2117
2295
  }
2118
2296
  function setupLogPath(threadId) {
2119
- return join5(threadsDir(), `${threadId}.setup.log.json`);
2297
+ return join6(threadsDir(), `${threadId}.setup.log.json`);
2120
2298
  }
2121
2299
  function setupLogKeyForWorktree(worktreePath) {
2122
2300
  const norm = normalizeWorktreePath(worktreePath);
@@ -2135,7 +2313,7 @@ function persistNow(threadId) {
2135
2313
  const mem = memory.get(threadId);
2136
2314
  if (!mem) return;
2137
2315
  try {
2138
- writeFileSync2(
2316
+ writeFileSync3(
2139
2317
  setupLogPath(threadId),
2140
2318
  `${JSON.stringify(materialize(mem))}
2141
2319
  `,
@@ -2158,7 +2336,7 @@ function loadFromDisk(threadId) {
2158
2336
  const path = setupLogPath(threadId);
2159
2337
  if (!existsSync4(path)) return null;
2160
2338
  try {
2161
- const raw = JSON.parse(readFileSync3(path, "utf8"));
2339
+ const raw = JSON.parse(readFileSync4(path, "utf8"));
2162
2340
  return {
2163
2341
  output: typeof raw.output === "string" ? raw.output : "",
2164
2342
  running: Boolean(raw.running),
@@ -2217,8 +2395,8 @@ function finishSetupLog(threadId, exitCode, source) {
2217
2395
 
2218
2396
  // src/store/run-log.ts
2219
2397
  import { createHash as createHash2 } from "crypto";
2220
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2221
- import { join as join6 } from "path";
2398
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2399
+ import { join as join7 } from "path";
2222
2400
  var EMPTY2 = {
2223
2401
  output: "",
2224
2402
  running: false,
@@ -2257,7 +2435,7 @@ function safeScriptToken(scriptName) {
2257
2435
  return token || "dev";
2258
2436
  }
2259
2437
  function runLogPath(key) {
2260
- return join6(threadsDir(), `${key}.run.log.json`);
2438
+ return join7(threadsDir(), `${key}.run.log.json`);
2261
2439
  }
2262
2440
  function runLogKeyForWorktree(worktreePath, scriptName) {
2263
2441
  const norm = normalizeWorktreePath(worktreePath);
@@ -2286,7 +2464,7 @@ function persistNow2(key) {
2286
2464
  const mem = memory2.get(key);
2287
2465
  if (!mem) return;
2288
2466
  try {
2289
- writeFileSync3(runLogPath(key), `${JSON.stringify(materialize2(mem))}
2467
+ writeFileSync4(runLogPath(key), `${JSON.stringify(materialize2(mem))}
2290
2468
  `, "utf8");
2291
2469
  } catch {
2292
2470
  }
@@ -2305,7 +2483,7 @@ function loadFromDisk2(key) {
2305
2483
  const path = runLogPath(key);
2306
2484
  if (!existsSync5(path)) return null;
2307
2485
  try {
2308
- const raw = JSON.parse(readFileSync4(path, "utf8"));
2486
+ const raw = JSON.parse(readFileSync5(path, "utf8"));
2309
2487
  return {
2310
2488
  output: typeof raw.output === "string" ? raw.output : "",
2311
2489
  running: Boolean(raw.running),
@@ -2366,7 +2544,7 @@ function flushRunLog(key) {
2366
2544
 
2367
2545
  // src/git/orphan-cleanup.ts
2368
2546
  import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
2369
- import { join as join7 } from "path";
2547
+ import { join as join8 } from "path";
2370
2548
  function isSideboardWorktreePath(path) {
2371
2549
  return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
2372
2550
  }
@@ -2413,9 +2591,9 @@ async function findOrphanWorktrees(repoPaths) {
2413
2591
  if (existsSync6(root)) {
2414
2592
  for (const entry of readdirSync2(root, { withFileTypes: true })) {
2415
2593
  if (!entry.isDirectory()) continue;
2416
- const path = join7(root, entry.name).replace(/\/$/, "");
2594
+ const path = join8(root, entry.name).replace(/\/$/, "");
2417
2595
  if (known.has(path) || seen.has(path)) continue;
2418
- if (!existsSync6(join7(path, ".git"))) continue;
2596
+ if (!existsSync6(join8(path, ".git"))) continue;
2419
2597
  seen.add(path);
2420
2598
  let mtimeMs = 0;
2421
2599
  try {
@@ -2620,7 +2798,7 @@ async function applyThreadIntoMain(thread, opts) {
2620
2798
 
2621
2799
  // src/git/clone-repo.ts
2622
2800
  import { existsSync as existsSync7 } from "fs";
2623
- import { basename as basename2, join as join8 } from "path";
2801
+ import { basename as basename2, join as join9 } from "path";
2624
2802
  import { execa as execa4 } from "execa";
2625
2803
  async function cloneRepoIntoSideboard(opts) {
2626
2804
  const url = opts.url.trim();
@@ -2631,7 +2809,7 @@ async function cloneRepoIntoSideboard(opts) {
2631
2809
  name = leaf || "repo";
2632
2810
  }
2633
2811
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
2634
- const dest = join8(sideboardReposDir(), name);
2812
+ const dest = join9(sideboardReposDir(), name);
2635
2813
  if (existsSync7(dest)) {
2636
2814
  const repoPath2 = await resolveRepoRoot(dest);
2637
2815
  const workspace2 = await ensureWorkspace(repoPath2);
@@ -2654,14 +2832,14 @@ function isAdoptableRunScriptRequest(req) {
2654
2832
  }
2655
2833
 
2656
2834
  // src/store/desktop-host.ts
2657
- import { readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
2658
- import { join as join9 } from "path";
2835
+ import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
2836
+ import { join as join10 } from "path";
2659
2837
  function desktopHostPidPath() {
2660
- return join9(appDataDir(), "desktop-host.pid");
2838
+ return join10(appDataDir(), "desktop-host.pid");
2661
2839
  }
2662
2840
  function readDesktopHostPid() {
2663
2841
  try {
2664
- const pid = Number.parseInt(readFileSync5(desktopHostPidPath(), "utf8").trim(), 10);
2842
+ const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
2665
2843
  if (!Number.isFinite(pid) || pid <= 0) return null;
2666
2844
  return pid;
2667
2845
  } catch {
@@ -2734,12 +2912,12 @@ function notifyParentOfChildHalt(child, status, send) {
2734
2912
  import {
2735
2913
  appendFileSync as appendFileSync2,
2736
2914
  existsSync as existsSync8,
2737
- mkdirSync as mkdirSync4,
2915
+ mkdirSync as mkdirSync5,
2738
2916
  readdirSync as readdirSync3,
2739
- readFileSync as readFileSync6,
2740
- writeFileSync as writeFileSync5
2917
+ readFileSync as readFileSync7,
2918
+ writeFileSync as writeFileSync6
2741
2919
  } from "fs";
2742
- import { dirname as dirname3, join as join10 } from "path";
2920
+ import { dirname as dirname4, join as join11 } from "path";
2743
2921
 
2744
2922
  // src/mcp/wait-for-turn.ts
2745
2923
  var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
@@ -2815,11 +2993,11 @@ function jobTreeAlive(pid) {
2815
2993
  }
2816
2994
  function readIntFile(file) {
2817
2995
  if (!existsSync8(file)) return null;
2818
- const n = Number.parseInt(readFileSync6(file, "utf8").trim(), 10);
2996
+ const n = Number.parseInt(readFileSync7(file, "utf8").trim(), 10);
2819
2997
  return Number.isInteger(n) ? n : null;
2820
2998
  }
2821
2999
  function jobDir(root, id, legacy = false) {
2822
- return join10(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
3000
+ return join11(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
2823
3001
  }
2824
3002
  function resolveJobDir(root, id) {
2825
3003
  const modern = jobDir(root, id, false);
@@ -2829,7 +3007,7 @@ function resolveJobDir(root, id) {
2829
3007
  return modern;
2830
3008
  }
2831
3009
  function listJobIdsIn(root, rel) {
2832
- const dir = join10(root, rel);
3010
+ const dir = join11(root, rel);
2833
3011
  if (!existsSync8(dir)) return [];
2834
3012
  return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
2835
3013
  }
@@ -2843,7 +3021,7 @@ function listRunningDetachedJobs(worktreePath) {
2843
3021
  const running = [];
2844
3022
  for (const id of ids) {
2845
3023
  const dir = resolveJobDir(root, id);
2846
- const pid = readIntFile(join10(dir, "pid"));
3024
+ const pid = readIntFile(join11(dir, "pid"));
2847
3025
  if (jobTreeAlive(pid)) running.push(id);
2848
3026
  }
2849
3027
  return running.sort();
@@ -2927,7 +3105,7 @@ function planJobContinue(opts) {
2927
3105
  }
2928
3106
  function tailProgress(logFile, maxLines = 12) {
2929
3107
  if (!existsSync8(logFile)) return "(no log yet)";
2930
- const lines = readFileSync6(logFile, "utf8").split("\n");
3108
+ const lines = readFileSync7(logFile, "utf8").split("\n");
2931
3109
  return lines.slice(-maxLines).join("\n");
2932
3110
  }
2933
3111
  function lastProgressLine(progress) {
@@ -2936,7 +3114,7 @@ function lastProgressLine(progress) {
2936
3114
  }
2937
3115
  function readLogLines(file) {
2938
3116
  if (!existsSync8(file)) return [];
2939
- const lines = readFileSync6(file, "utf8").split("\n");
3117
+ const lines = readFileSync7(file, "utf8").split("\n");
2940
3118
  if (lines.length && lines[lines.length - 1] === "") lines.pop();
2941
3119
  return lines;
2942
3120
  }
@@ -2947,15 +3125,15 @@ function takeDelta(logFile, cursorFile) {
2947
3125
  return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
2948
3126
  }
2949
3127
  function snapshotJob(dir) {
2950
- const pid = readIntFile(join10(dir, "pid"));
3128
+ const pid = readIntFile(join11(dir, "pid"));
2951
3129
  const running = jobTreeAlive(pid);
2952
3130
  return {
2953
3131
  pid,
2954
3132
  running,
2955
- exitCode: readIntFile(join10(dir, "exit")),
2956
- log: join10(dir, "log"),
2957
- cursor: join10(dir, "present.cursor"),
2958
- progress: tailProgress(join10(dir, "log"))
3133
+ exitCode: readIntFile(join11(dir, "exit")),
3134
+ log: join11(dir, "log"),
3135
+ cursor: join11(dir, "present.cursor"),
3136
+ progress: tailProgress(join11(dir, "log"))
2959
3137
  };
2960
3138
  }
2961
3139
  function toResult(id, snap, extra) {
@@ -2964,8 +3142,8 @@ function toResult(id, snap, extra) {
2964
3142
  const stillRunning = snap.running && !ok;
2965
3143
  const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
2966
3144
  try {
2967
- mkdirSync4(dirname3(snap.cursor), { recursive: true });
2968
- writeFileSync5(snap.cursor, `${nextCursor}
3145
+ mkdirSync5(dirname4(snap.cursor), { recursive: true });
3146
+ writeFileSync6(snap.cursor, `${nextCursor}
2969
3147
  `);
2970
3148
  } catch {
2971
3149
  }
@@ -2986,7 +3164,7 @@ function toResult(id, snap, extra) {
2986
3164
  };
2987
3165
  }
2988
3166
  async function sleepMs(ms) {
2989
- await new Promise((resolve) => setTimeout(resolve, ms));
3167
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
2990
3168
  }
2991
3169
  async function waitForDetachedJob(cwd, id, opts) {
2992
3170
  const jobId = sanitizeDetachedJobId(id);
@@ -3040,7 +3218,7 @@ function killProcessTree(pid, signal) {
3040
3218
  }
3041
3219
  function appendJobLog(logFile, line) {
3042
3220
  try {
3043
- mkdirSync4(dirname3(logFile), { recursive: true });
3221
+ mkdirSync5(dirname4(logFile), { recursive: true });
3044
3222
  const body = line.endsWith("\n") ? line : `${line}
3045
3223
  `;
3046
3224
  appendFileSync2(logFile, body);
@@ -3089,10 +3267,10 @@ async function stopDetachedJob(cwd, id, opts) {
3089
3267
  killProcessTree(targetPid, "SIGKILL");
3090
3268
  await sleep(100);
3091
3269
  }
3092
- const exitFile = join10(dir, "exit");
3270
+ const exitFile = join11(dir, "exit");
3093
3271
  if (!existsSync8(exitFile)) {
3094
3272
  try {
3095
- writeFileSync5(exitFile, "1\n");
3273
+ writeFileSync6(exitFile, "1\n");
3096
3274
  } catch {
3097
3275
  }
3098
3276
  }
@@ -3899,8 +4077,8 @@ import {
3899
4077
  existsSync as existsSync10,
3900
4078
  renameSync,
3901
4079
  unlinkSync as unlinkSync2,
3902
- writeFileSync as writeFileSync6,
3903
- readFileSync as readFileSync7
4080
+ writeFileSync as writeFileSync7,
4081
+ readFileSync as readFileSync8
3904
4082
  } from "fs";
3905
4083
  var buffers = /* @__PURE__ */ new Map();
3906
4084
  var FLUSH_MS = 800;
@@ -3977,7 +4155,7 @@ function writeTurnLive(threadId, progress) {
3977
4155
  const path = threadLivePath(threadId);
3978
4156
  const tmp = `${path}.${process.pid}.tmp`;
3979
4157
  try {
3980
- writeFileSync6(tmp, JSON.stringify(progress), "utf8");
4158
+ writeFileSync7(tmp, JSON.stringify(progress), "utf8");
3981
4159
  renameSync(tmp, path);
3982
4160
  } catch {
3983
4161
  try {
@@ -3990,7 +4168,7 @@ function readTurnLive(threadId) {
3990
4168
  const path = threadLivePath(threadId);
3991
4169
  if (!existsSync10(path)) return null;
3992
4170
  try {
3993
- const raw = JSON.parse(readFileSync7(path, "utf8"));
4171
+ const raw = JSON.parse(readFileSync8(path, "utf8"));
3994
4172
  if (!raw || typeof raw.summary !== "string") return null;
3995
4173
  return raw;
3996
4174
  } catch {
@@ -4214,23 +4392,23 @@ import {
4214
4392
  existsSync as existsSync11,
4215
4393
  mkdtempSync,
4216
4394
  readdirSync as readdirSync4,
4217
- readFileSync as readFileSync8,
4395
+ readFileSync as readFileSync9,
4218
4396
  rmSync
4219
4397
  } from "fs";
4220
4398
  import { tmpdir } from "os";
4221
- import { join as join11 } from "path";
4399
+ import { join as join12 } from "path";
4222
4400
  import { createRequire } from "module";
4223
- var CONDUCTOR_APP_SUPPORT = join11(
4401
+ var CONDUCTOR_APP_SUPPORT = join12(
4224
4402
  process.env.HOME ?? "",
4225
4403
  "Library",
4226
4404
  "Application Support",
4227
4405
  "com.conductor.app"
4228
4406
  );
4229
- var CONDUCTOR_DB = join11(CONDUCTOR_APP_SUPPORT, "conductor.db");
4230
- var CURSOR_SDK_STORE = join11(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4407
+ var CONDUCTOR_DB = join12(CONDUCTOR_APP_SUPPORT, "conductor.db");
4408
+ var CURSOR_SDK_STORE = join12(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4231
4409
  function thisModuleFile() {
4232
4410
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
4233
- return cjsFile || process.argv[1] || join11(process.cwd(), "package.json");
4411
+ return cjsFile || process.argv[1] || join12(process.cwd(), "package.json");
4234
4412
  }
4235
4413
  function openReadonlySqlite(file) {
4236
4414
  const req = createRequire(thisModuleFile());
@@ -4258,11 +4436,11 @@ function resolveConductorCursorAgentId(workspacePath) {
4258
4436
  return null;
4259
4437
  }
4260
4438
  for (const hash of hashes) {
4261
- const agentsFile = join11(CURSOR_SDK_STORE, hash, "agents.ndjson");
4439
+ const agentsFile = join12(CURSOR_SDK_STORE, hash, "agents.ndjson");
4262
4440
  if (!existsSync11(agentsFile)) continue;
4263
4441
  let text;
4264
4442
  try {
4265
- text = readFileSync8(agentsFile, "utf8");
4443
+ text = readFileSync9(agentsFile, "utf8");
4266
4444
  } catch {
4267
4445
  continue;
4268
4446
  }
@@ -4315,8 +4493,8 @@ function listConductorWorkspaces() {
4315
4493
  if (!existsSync11(CONDUCTOR_DB)) {
4316
4494
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4317
4495
  }
4318
- const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
4319
- const snapshot = join11(tmp, "conductor.db");
4496
+ const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
4497
+ const snapshot = join12(tmp, "conductor.db");
4320
4498
  try {
4321
4499
  copyFileSync2(CONDUCTOR_DB, snapshot);
4322
4500
  for (const suffix of ["-wal", "-shm"]) {
@@ -4406,8 +4584,8 @@ function importConductorWorkspace(workspaceId) {
4406
4584
  if (!existsSync11(CONDUCTOR_DB)) {
4407
4585
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4408
4586
  }
4409
- const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
4410
- const snapshot = join11(tmp, "conductor.db");
4587
+ const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
4588
+ const snapshot = join12(tmp, "conductor.db");
4411
4589
  try {
4412
4590
  copyFileSync2(CONDUCTOR_DB, snapshot);
4413
4591
  for (const suffix of ["-wal", "-shm"]) {
@@ -4787,8 +4965,8 @@ async function createPrStack(input, onSetupLine) {
4787
4965
  }
4788
4966
 
4789
4967
  // src/diff/diff.ts
4790
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync9, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
4791
- import { dirname as dirname4, join as join12 } from "path";
4968
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
4969
+ import { dirname as dirname5, join as join13 } from "path";
4792
4970
  async function inspectGitWorktree(worktreePath) {
4793
4971
  if (!worktreePath || !existsSync13(worktreePath)) return "missing_worktree";
4794
4972
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -4934,11 +5112,11 @@ new file mode 100644
4934
5112
  };
4935
5113
  }
4936
5114
  async function untrackedPatch(worktreePath, path, maxHunk) {
4937
- const abs = join12(worktreePath, path);
5115
+ const abs = join13(worktreePath, path);
4938
5116
  try {
4939
5117
  const st = statSync2(abs);
4940
5118
  if (st.isFile() && st.size > maxHunk) {
4941
- const buf = readFileSync9(abs).subarray(0, maxHunk);
5119
+ const buf = readFileSync10(abs).subarray(0, maxHunk);
4942
5120
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
4943
5121
  }
4944
5122
  } catch {
@@ -5406,7 +5584,7 @@ async function getDiff(worktreePath, repoPath, opts) {
5406
5584
  }
5407
5585
  function statWorktreePath(worktreePath, relativePath) {
5408
5586
  assertSafeRelativePath(relativePath);
5409
- const abs = join12(worktreePath, relativePath);
5587
+ const abs = join13(worktreePath, relativePath);
5410
5588
  if (!existsSync13(abs)) return "missing";
5411
5589
  const st = statSync2(abs);
5412
5590
  if (st.isDirectory()) return "dir";
@@ -5448,7 +5626,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
5448
5626
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5449
5627
  assertSafeRelativePath(relativePath);
5450
5628
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
5451
- const abs = join12(worktreePath, relativePath);
5629
+ const abs = join13(worktreePath, relativePath);
5452
5630
  const st = statSync2(abs);
5453
5631
  if (!st.isFile()) {
5454
5632
  throw new Error(`Not a file: ${relativePath}`);
@@ -5458,7 +5636,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5458
5636
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
5459
5637
  );
5460
5638
  }
5461
- const buf = readFileSync9(abs);
5639
+ const buf = readFileSync10(abs);
5462
5640
  return {
5463
5641
  path: relativePath,
5464
5642
  contentBase64: buf.toString("base64"),
@@ -5468,12 +5646,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5468
5646
  function readWorktreeFile(worktreePath, relativePath, opts) {
5469
5647
  assertSafeRelativePath(relativePath);
5470
5648
  const maxBytes = opts?.maxBytes ?? 2e5;
5471
- const abs = join12(worktreePath, relativePath);
5649
+ const abs = join13(worktreePath, relativePath);
5472
5650
  const st = statSync2(abs);
5473
5651
  if (!st.isFile()) {
5474
5652
  throw new Error(`Not a file: ${relativePath}`);
5475
5653
  }
5476
- const buf = readFileSync9(abs);
5654
+ const buf = readFileSync10(abs);
5477
5655
  if (isImageRelativePath(relativePath)) {
5478
5656
  const maxImageBytes = Math.max(maxBytes, 15e6);
5479
5657
  const truncated2 = buf.length > maxImageBytes;
@@ -5516,9 +5694,9 @@ function assertSafeRelativePath(relativePath) {
5516
5694
  }
5517
5695
  function writeWorktreeFile(worktreePath, relativePath, content) {
5518
5696
  assertSafeRelativePath(relativePath);
5519
- const abs = join12(worktreePath, relativePath);
5520
- mkdirSync5(dirname4(abs), { recursive: true });
5521
- writeFileSync7(abs, content, "utf8");
5697
+ const abs = join13(worktreePath, relativePath);
5698
+ mkdirSync6(dirname5(abs), { recursive: true });
5699
+ writeFileSync8(abs, content, "utf8");
5522
5700
  return { path: relativePath };
5523
5701
  }
5524
5702
  async function getWorktreeDirtyStat(worktreePath) {
@@ -5740,9 +5918,9 @@ async function confirmLand(thread, opts) {
5740
5918
  }
5741
5919
 
5742
5920
  // src/skills/discover.ts
5743
- import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
5921
+ import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
5744
5922
  import { homedir } from "os";
5745
- import { join as join13 } from "path";
5923
+ import { join as join14 } from "path";
5746
5924
 
5747
5925
  // src/skills/bundled/long-running.ts
5748
5926
  var LONG_RUNNING_SKILL_COMMAND = "long-running";
@@ -5879,7 +6057,7 @@ function parseFrontmatter(content) {
5879
6057
  }
5880
6058
  function readSkill(skillMd, source) {
5881
6059
  try {
5882
- const content = readFileSync10(skillMd, "utf8");
6060
+ const content = readFileSync11(skillMd, "utf8");
5883
6061
  const { name: fmName, description } = parseFrontmatter(content);
5884
6062
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
5885
6063
  const name = fmName || dirName;
@@ -5907,7 +6085,7 @@ function scanSkillsDir(dir, source, out) {
5907
6085
  }
5908
6086
  for (const entry of entries) {
5909
6087
  if (entry.startsWith(".")) continue;
5910
- const skillMd = join13(dir, entry, "SKILL.md");
6088
+ const skillMd = join14(dir, entry, "SKILL.md");
5911
6089
  if (!existsSync14(skillMd)) continue;
5912
6090
  try {
5913
6091
  if (!statSync3(skillMd).isFile()) continue;
@@ -5929,12 +6107,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
5929
6107
  return;
5930
6108
  }
5931
6109
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
5932
- const skill = readSkill(join13(dir, "SKILL.md"), "cli");
6110
+ const skill = readSkill(join14(dir, "SKILL.md"), "cli");
5933
6111
  if (skill) out.push(skill);
5934
6112
  }
5935
6113
  for (const entry of entries) {
5936
6114
  if (entry === "node_modules" || entry === ".git") continue;
5937
- const full = join13(dir, entry);
6115
+ const full = join14(dir, entry);
5938
6116
  try {
5939
6117
  if (!statSync3(full).isDirectory()) continue;
5940
6118
  } catch {
@@ -5954,17 +6132,17 @@ function discoverSkills(worktreePath) {
5954
6132
  const home = homedir();
5955
6133
  const collected = [];
5956
6134
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
5957
- scanSkillsDir(join13(worktreePath, rel), "workspace", collected);
6135
+ scanSkillsDir(join14(worktreePath, rel), "workspace", collected);
5958
6136
  }
5959
6137
  for (const abs of [
5960
- join13(home, ".claude/skills"),
5961
- join13(home, ".cursor/skills"),
5962
- join13(home, ".sideboard/skills"),
5963
- join13(home, ".brightsy/skills")
6138
+ join14(home, ".claude/skills"),
6139
+ join14(home, ".cursor/skills"),
6140
+ join14(home, ".sideboard/skills"),
6141
+ join14(home, ".brightsy/skills")
5964
6142
  ]) {
5965
6143
  scanSkillsDir(abs, "user", collected);
5966
6144
  }
5967
- scanClaudePluginSkills(join13(home, ".claude/plugins"), collected);
6145
+ scanClaudePluginSkills(join14(home, ".claude/plugins"), collected);
5968
6146
  collected.push(...bundledSkills());
5969
6147
  const rank = {
5970
6148
  workspace: 0,
@@ -5990,7 +6168,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
5990
6168
  \u2026(truncated)` : body;
5991
6169
  }
5992
6170
  }
5993
- const raw = readFileSync10(skillPath, "utf8");
6171
+ const raw = readFileSync11(skillPath, "utf8");
5994
6172
  if (raw.startsWith("---")) {
5995
6173
  const end = raw.indexOf("\n---", 3);
5996
6174
  if (end >= 0) {
@@ -6136,11 +6314,11 @@ function shiftQueuedItem(queue, queueAttachments) {
6136
6314
 
6137
6315
  // src/agents/instructions.ts
6138
6316
  import { existsSync as existsSync16 } from "fs";
6139
- import { join as join15 } from "path";
6317
+ import { join as join16 } from "path";
6140
6318
 
6141
6319
  // src/skills/detached-job-path.ts
6142
6320
  import { existsSync as existsSync15 } from "fs";
6143
- import { dirname as dirname5, join as join14 } from "path";
6321
+ import { dirname as dirname6, join as join15 } from "path";
6144
6322
  import { fileURLToPath } from "url";
6145
6323
  var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
6146
6324
  function compiledModuleDir() {
@@ -6149,7 +6327,7 @@ function compiledModuleDir() {
6149
6327
  try {
6150
6328
  const url = import.meta.url;
6151
6329
  if (typeof url === "string" && url.length > 0) {
6152
- return dirname5(fileURLToPath(url));
6330
+ return dirname6(fileURLToPath(url));
6153
6331
  }
6154
6332
  } catch {
6155
6333
  }
@@ -6157,7 +6335,7 @@ function compiledModuleDir() {
6157
6335
  }
6158
6336
  function detachedJobScriptIn(dir) {
6159
6337
  for (const name of DETACHED_JOB_FILENAMES) {
6160
- const script = join14(dir, "scripts", name);
6338
+ const script = join15(dir, "scripts", name);
6161
6339
  if (existsSync15(script)) return script;
6162
6340
  }
6163
6341
  return null;
@@ -6175,7 +6353,7 @@ function resolveDetachedJobScript() {
6175
6353
  for (let i = 0; i < 8; i++) {
6176
6354
  const candidate = detachedJobScriptIn(dir);
6177
6355
  if (candidate) return candidate;
6178
- const parent = dirname5(dir);
6356
+ const parent = dirname6(dir);
6179
6357
  if (parent === dir) break;
6180
6358
  dir = parent;
6181
6359
  }
@@ -6280,9 +6458,9 @@ function formatWorktreeDirective(thread, opts) {
6280
6458
  );
6281
6459
  } else {
6282
6460
  const slug = opts?.githubSlug ?? "<origin-owner/name>";
6283
- const resolve = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
6461
+ const resolve2 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
6284
6462
  lines.push(
6285
- `- 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\`.`
6463
+ `- 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\`.`
6286
6464
  );
6287
6465
  }
6288
6466
  lines.push(
@@ -6372,9 +6550,9 @@ function formatPrGateDirective() {
6372
6550
  }
6373
6551
  function formatProcessGuideDirective(opts) {
6374
6552
  const worktree = opts?.worktreePath?.trim();
6375
- const hasGraphSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/graph-engineering/SKILL.md"));
6376
- const hasReviewSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/review/SKILL.md"));
6377
- const hasInstallSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/worktree-install/SKILL.md"));
6553
+ const hasGraphSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/graph-engineering/SKILL.md"));
6554
+ const hasReviewSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/review/SKILL.md"));
6555
+ const hasInstallSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/worktree-install/SKILL.md"));
6378
6556
  return [
6379
6557
  "Process guides (recurring work only):",
6380
6558
  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,
@@ -6575,8 +6753,8 @@ async function syncThreadBranchFromGit(threadId) {
6575
6753
 
6576
6754
  // src/store/schedules.ts
6577
6755
  import { randomUUID as randomUUID4 } from "crypto";
6578
- import { existsSync as existsSync17, readFileSync as readFileSync11 } from "fs";
6579
- import { join as join16 } from "path";
6756
+ import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
6757
+ import { join as join17 } from "path";
6580
6758
  import { Cron } from "croner";
6581
6759
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
6582
6760
  var UNIT_MS = {
@@ -6586,7 +6764,7 @@ var UNIT_MS = {
6586
6764
  d: 864e5
6587
6765
  };
6588
6766
  function schedulesPath() {
6589
- return join16(appDataDir(), "schedules.json");
6767
+ return join17(appDataDir(), "schedules.json");
6590
6768
  }
6591
6769
  function parseDurationMs(every) {
6592
6770
  const m = every.trim().match(DURATION_RE);
@@ -6688,7 +6866,7 @@ function readAll() {
6688
6866
  const path = schedulesPath();
6689
6867
  if (!existsSync17(path)) return [];
6690
6868
  try {
6691
- const parsed = JSON.parse(readFileSync11(path, "utf8"));
6869
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
6692
6870
  if (!Array.isArray(parsed)) return [];
6693
6871
  return parsed.map((row) => {
6694
6872
  try {
@@ -6811,7 +6989,7 @@ function formatScheduledPrompt(name, prompt) {
6811
6989
  ${prompt}`;
6812
6990
  }
6813
6991
  async function defaultDeps() {
6814
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-AAYKR4AI.js");
6992
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-3ZP4WTQ4.js");
6815
6993
  const orch = getOrchestrator2();
6816
6994
  return {
6817
6995
  findThread: (id) => findThreadByRef(id),
@@ -6876,14 +7054,14 @@ function armOne(schedule) {
6876
7054
  if (!schedule.enabled) return;
6877
7055
  const at = new Date(schedule.nextRunAt);
6878
7056
  if (Number.isNaN(at.getTime())) return;
6879
- const delay = Math.max(0, at.getTime() - Date.now());
6880
- if (delay <= 250) {
7057
+ const delay2 = Math.max(0, at.getTime() - Date.now());
7058
+ if (delay2 <= 250) {
6881
7059
  void fireSchedule(schedule.id).then(() => {
6882
7060
  if (isThisProcessDesktopHost()) armSchedules();
6883
7061
  });
6884
7062
  return;
6885
7063
  }
6886
- const capped = Math.min(delay, SET_TIMEOUT_MAX);
7064
+ const capped = Math.min(delay2, SET_TIMEOUT_MAX);
6887
7065
  const timer = setTimeout(() => {
6888
7066
  timers.delete(schedule.id);
6889
7067
  const live = getSchedule(schedule.id);
@@ -7267,8 +7445,8 @@ var Orchestrator = class {
7267
7445
  scheduleQuotaResume(threadId, resumeAt) {
7268
7446
  this.clearQuotaResumeTimer(threadId);
7269
7447
  updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
7270
- const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
7271
- const capped = Math.min(delay, 2147483647);
7448
+ const delay2 = Math.max(5e3, resumeAt.getTime() - Date.now());
7449
+ const capped = Math.min(delay2, 2147483647);
7272
7450
  const timer = setTimeout(() => {
7273
7451
  this.quotaResumeTimers.delete(threadId);
7274
7452
  void this.resumeAfterQuotaWait(threadId);
@@ -8323,6 +8501,7 @@ var Orchestrator = class {
8323
8501
  return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
8324
8502
  }
8325
8503
  }
8504
+ const preferredPorts = active?.ports?.length ? active.ports : active?.port != null ? [active.port] : void 0;
8326
8505
  if (active && !existing) {
8327
8506
  killListenersOnPorts(collectActiveRunPorts([active]));
8328
8507
  this.syncWorktreeRuns(
@@ -8361,7 +8540,7 @@ var Orchestrator = class {
8361
8540
  thread.repoPath,
8362
8541
  thread.worktreePath,
8363
8542
  runOutput.push,
8364
- { scriptName: resolvedName }
8543
+ { scriptName: resolvedName, preferredPorts }
8365
8544
  );
8366
8545
  } catch (err) {
8367
8546
  runOutput.flush();
@@ -8810,7 +8989,7 @@ var Orchestrator = class {
8810
8989
  return thread;
8811
8990
  }
8812
8991
  const start = Date.now();
8813
- return new Promise((resolve, reject) => {
8992
+ return new Promise((resolve2, reject) => {
8814
8993
  let settled = false;
8815
8994
  let timer;
8816
8995
  const finish = (fn) => {
@@ -8828,7 +9007,7 @@ var Orchestrator = class {
8828
9007
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
8829
9008
  return;
8830
9009
  }
8831
- finish(() => resolve(latest));
9010
+ finish(() => resolve2(latest));
8832
9011
  return;
8833
9012
  }
8834
9013
  if (event.type === "status_changed") {
@@ -8838,7 +9017,7 @@ var Orchestrator = class {
8838
9017
  return;
8839
9018
  }
8840
9019
  if (!["running", "queued"].includes(latest.status)) {
8841
- finish(() => resolve(latest));
9020
+ finish(() => resolve2(latest));
8842
9021
  }
8843
9022
  }
8844
9023
  });
@@ -8850,12 +9029,12 @@ var Orchestrator = class {
8850
9029
  }
8851
9030
  const current = this.healStaleReportedActivity(raw);
8852
9031
  if (!this.threadLooksLive(current)) {
8853
- finish(() => resolve(current));
9032
+ finish(() => resolve2(current));
8854
9033
  return;
8855
9034
  }
8856
9035
  if (Date.now() - start > timeoutMs) {
8857
9036
  if (opts?.resolveIfStillRunning) {
8858
- finish(() => resolve(current));
9037
+ finish(() => resolve2(current));
8859
9038
  } else {
8860
9039
  finish(() => reject(new Error("wait_for_turn timed out")));
8861
9040
  }