@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.
@@ -745,13 +745,14 @@ function shouldAutoArchiveOnPrMerge(opts) {
745
745
  import {
746
746
  copyFileSync,
747
747
  existsSync as existsSync3,
748
- mkdirSync as mkdirSync3,
748
+ mkdirSync as mkdirSync4,
749
749
  readdirSync,
750
- readFileSync as readFileSync2
750
+ readFileSync as readFileSync3,
751
+ realpathSync
751
752
  } from "fs";
752
753
  import { execFileSync } from "child_process";
753
754
  import { createServer } from "net";
754
- import { basename, dirname as dirname2, join as join4 } from "path";
755
+ import { basename, dirname as dirname3, join as join5, resolve, sep } from "path";
755
756
  import { execa as execa2 } from "execa";
756
757
  import { createInterface as createInterface2 } from "readline";
757
758
 
@@ -1664,16 +1665,73 @@ async function requestReview(threadRef, send) {
1664
1665
  return { tab: started, from };
1665
1666
  }
1666
1667
 
1668
+ // src/hook/worktree-run-ports.ts
1669
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1670
+ import { dirname as dirname2, join as join4 } from "path";
1671
+ var RUN_PORTS_REL = ".context/.sideboard/run-ports.json";
1672
+ function worktreeRunPortsPath(worktreePath) {
1673
+ return join4(worktreePath, RUN_PORTS_REL);
1674
+ }
1675
+ function isPort(value) {
1676
+ return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535;
1677
+ }
1678
+ function parseFile(raw) {
1679
+ try {
1680
+ const parsed = JSON.parse(raw);
1681
+ if (!parsed || typeof parsed !== "object") return {};
1682
+ const scripts = parsed.scripts;
1683
+ if (!scripts || typeof scripts !== "object") return {};
1684
+ const next = {};
1685
+ for (const [name, ports] of Object.entries(scripts)) {
1686
+ if (!name || !Array.isArray(ports)) continue;
1687
+ const cleaned = ports.filter(isPort);
1688
+ if (cleaned.length) next[name] = cleaned;
1689
+ }
1690
+ return { scripts: next };
1691
+ } catch {
1692
+ return {};
1693
+ }
1694
+ }
1695
+ function loadWorktreeRunPorts(worktreePath, scriptName) {
1696
+ if (!worktreePath.trim() || !scriptName.trim()) return null;
1697
+ try {
1698
+ const raw = readFileSync2(worktreeRunPortsPath(worktreePath), "utf8");
1699
+ const ports = parseFile(raw).scripts?.[scriptName];
1700
+ return ports?.length ? ports : null;
1701
+ } catch {
1702
+ return null;
1703
+ }
1704
+ }
1705
+ function saveWorktreeRunPorts(worktreePath, scriptName, ports) {
1706
+ if (!worktreePath.trim() || !scriptName.trim()) return;
1707
+ const cleaned = ports.filter(isPort);
1708
+ if (!cleaned.length) return;
1709
+ const path = worktreeRunPortsPath(worktreePath);
1710
+ let current = {};
1711
+ try {
1712
+ current = parseFile(readFileSync2(path, "utf8"));
1713
+ } catch {
1714
+ current = {};
1715
+ }
1716
+ const scripts = { ...current.scripts ?? {} };
1717
+ scripts[scriptName] = cleaned;
1718
+ mkdirSync3(dirname2(path), { recursive: true });
1719
+ writeFileSync2(path, `${JSON.stringify({ scripts }, null, 2)}
1720
+ `, "utf8");
1721
+ }
1722
+
1667
1723
  // src/hook/conductor.ts
1668
1724
  var PORT_RANGE_SIZE = 10;
1725
+ var RECLAIM_RETRY_DELAY_MS = 40;
1726
+ var RECLAIM_TRIES = 8;
1669
1727
  function matchSimpleGlob(pattern, name) {
1670
1728
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1671
1729
  return new RegExp(`^${escaped}$`).test(name);
1672
1730
  }
1673
1731
  function readWorktreeInclude(repoPath) {
1674
- const path = join4(repoPath, ".worktreeinclude");
1732
+ const path = join5(repoPath, ".worktreeinclude");
1675
1733
  if (!existsSync3(path)) return [];
1676
- return readFileSync2(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1734
+ return readFileSync3(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1677
1735
  }
1678
1736
  function resolveFilesToCopy(repoPath) {
1679
1737
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1712,10 +1770,10 @@ function copyConfiguredFiles(repoPath, worktreePath) {
1712
1770
  const patterns = resolveFilesToCopy(repoPath);
1713
1771
  const copied = [];
1714
1772
  for (const rel of patterns) {
1715
- const src = join4(repoPath, rel);
1773
+ const src = join5(repoPath, rel);
1716
1774
  if (!existsSync3(src)) continue;
1717
- const dest = join4(worktreePath, rel);
1718
- mkdirSync3(dirname2(dest), { recursive: true });
1775
+ const dest = join5(worktreePath, rel);
1776
+ mkdirSync4(dirname3(dest), { recursive: true });
1719
1777
  copyFileSync(src, dest);
1720
1778
  copied.push(rel);
1721
1779
  }
@@ -1792,6 +1850,57 @@ function killableListenerPids(raw, selfPid = process.pid) {
1792
1850
  }
1793
1851
  return pids;
1794
1852
  }
1853
+ function listUnixListenerPids(port, selfPid) {
1854
+ try {
1855
+ const raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
1856
+ encoding: "utf8",
1857
+ stdio: ["ignore", "pipe", "ignore"],
1858
+ timeout: 2e3
1859
+ });
1860
+ return killableListenerPids(raw, selfPid);
1861
+ } catch {
1862
+ return [];
1863
+ }
1864
+ }
1865
+ function listenerCwd(pid) {
1866
+ if (!Number.isFinite(pid) || pid <= 0) return null;
1867
+ try {
1868
+ const raw = execFileSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
1869
+ encoding: "utf8",
1870
+ stdio: ["ignore", "pipe", "ignore"],
1871
+ timeout: 2e3
1872
+ });
1873
+ for (const line of raw.split("\n")) {
1874
+ if (line.startsWith("n") && line.length > 1) return line.slice(1);
1875
+ }
1876
+ } catch {
1877
+ }
1878
+ return null;
1879
+ }
1880
+ function realpathOrResolve(path) {
1881
+ try {
1882
+ return realpathSync(path);
1883
+ } catch {
1884
+ return resolve(path);
1885
+ }
1886
+ }
1887
+ function isOtherWorktreeListenerCwd(cwd, worktreePath) {
1888
+ const wt = realpathOrResolve(worktreePath);
1889
+ const c = realpathOrResolve(cwd);
1890
+ if (c === wt || c.startsWith(wt + sep)) return false;
1891
+ const parent = dirname3(wt);
1892
+ const thisName = basename(wt);
1893
+ if (!thisName || c === parent) return false;
1894
+ if (!c.startsWith(parent + sep)) return false;
1895
+ const first = c.slice(parent.length + 1).split(sep)[0];
1896
+ return Boolean(first) && first !== thisName;
1897
+ }
1898
+ function signalPid(pid, signal) {
1899
+ try {
1900
+ process.kill(pid, signal);
1901
+ } catch {
1902
+ }
1903
+ }
1795
1904
  function killListenersOnPorts(ports) {
1796
1905
  const selfPid = process.pid;
1797
1906
  for (const port of ports) {
@@ -1809,26 +1918,32 @@ function killListenersOnPorts(ports) {
1809
1918
  );
1810
1919
  continue;
1811
1920
  }
1812
- let raw = "";
1813
- try {
1814
- raw = execFileSync("lsof", [`-tiTCP:${port}`, "-sTCP:LISTEN"], {
1815
- encoding: "utf8",
1816
- stdio: ["ignore", "pipe", "ignore"],
1817
- timeout: 2e3
1818
- });
1819
- } catch {
1820
- continue;
1821
- }
1822
- for (const pid of killableListenerPids(raw, selfPid)) {
1823
- try {
1824
- process.kill(pid, "SIGTERM");
1825
- } catch {
1826
- }
1921
+ for (const pid of listUnixListenerPids(port, selfPid)) {
1922
+ signalPid(pid, "SIGTERM");
1827
1923
  }
1828
1924
  } catch {
1829
1925
  }
1830
1926
  }
1831
1927
  }
1928
+ function killStaleWorktreeListeners(ports, worktreePath, signal = "SIGTERM") {
1929
+ if (!worktreePath.trim()) {
1930
+ killListenersOnPorts(ports);
1931
+ return;
1932
+ }
1933
+ if (process.platform === "win32") {
1934
+ killListenersOnPorts(ports);
1935
+ return;
1936
+ }
1937
+ const selfPid = process.pid;
1938
+ for (const port of ports) {
1939
+ if (!Number.isFinite(port) || port <= 0) continue;
1940
+ for (const pid of listUnixListenerPids(port, selfPid)) {
1941
+ const cwd = listenerCwd(pid);
1942
+ if (cwd && isOtherWorktreeListenerCwd(cwd, worktreePath)) continue;
1943
+ signalPid(pid, signal);
1944
+ }
1945
+ }
1946
+ }
1832
1947
  function killScriptTree(child, ports = []) {
1833
1948
  const pid = child.pid;
1834
1949
  if (pid) {
@@ -1990,7 +2105,7 @@ function getRunMode(worktreePath, repoPath) {
1990
2105
  return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
1991
2106
  }
1992
2107
  async function reservePort() {
1993
- return new Promise((resolve, reject) => {
2108
+ return new Promise((resolve2, reject) => {
1994
2109
  const server = createServer();
1995
2110
  let released = false;
1996
2111
  const release = () => new Promise((res) => {
@@ -2007,7 +2122,7 @@ async function reservePort() {
2007
2122
  void release().then(() => reject(new Error("Failed to allocate port")));
2008
2123
  return;
2009
2124
  }
2010
- resolve({ port: addr.port, release });
2125
+ resolve2({ port: addr.port, release });
2011
2126
  });
2012
2127
  server.on("error", reject);
2013
2128
  });
@@ -2018,7 +2133,8 @@ async function allocatePort() {
2018
2133
  return held.port;
2019
2134
  }
2020
2135
  async function tryReservePort(port) {
2021
- return new Promise((resolve) => {
2136
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
2137
+ return new Promise((resolve2) => {
2022
2138
  const server = createServer();
2023
2139
  let released = false;
2024
2140
  const release = () => new Promise((res) => {
@@ -2029,24 +2145,54 @@ async function tryReservePort(port) {
2029
2145
  released = true;
2030
2146
  server.close(() => res());
2031
2147
  });
2032
- server.once("error", () => resolve(null));
2148
+ server.once("error", () => {
2149
+ try {
2150
+ server.close();
2151
+ } catch {
2152
+ }
2153
+ resolve2(null);
2154
+ });
2033
2155
  server.listen(port, "127.0.0.1", () => {
2034
- resolve({ port, release });
2156
+ resolve2({ port, release });
2035
2157
  });
2036
2158
  });
2037
2159
  }
2038
- async function reservePortRange(size = PORT_RANGE_SIZE) {
2039
- const held = [await reservePort()];
2040
- const base = held[0].port;
2160
+ async function tryReservePorts(ports) {
2161
+ const held = [];
2162
+ for (const port of ports) {
2163
+ const next = await tryReservePort(port);
2164
+ if (!next) {
2165
+ await Promise.all(held.map((h) => h.release()));
2166
+ return null;
2167
+ }
2168
+ held.push(next);
2169
+ }
2170
+ return held;
2171
+ }
2172
+ function normalizePreferredPorts(ports, size) {
2173
+ if (!ports?.length) return [];
2174
+ const seen = /* @__PURE__ */ new Set();
2175
+ const out = [];
2176
+ for (const p of ports) {
2177
+ if (!Number.isInteger(p) || p <= 0 || p > 65535 || seen.has(p)) continue;
2178
+ seen.add(p);
2179
+ out.push(p);
2180
+ if (out.length >= size) break;
2181
+ }
2182
+ return out;
2183
+ }
2184
+ function delay(ms) {
2185
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2186
+ }
2187
+ async function fillPortRange(held, size) {
2188
+ if (held.length >= size) return held;
2189
+ const base = held[0]?.port;
2041
2190
  try {
2042
- for (let i = 1; i < size; i++) {
2043
- const candidate = base + i;
2044
- const next = await tryReservePort(candidate);
2045
- if (next) {
2046
- held.push(next);
2047
- } else {
2048
- held.push(await reservePort());
2049
- }
2191
+ for (let i = held.length; i < size; i++) {
2192
+ const candidate = base != null ? base + i : null;
2193
+ const next = candidate != null ? await tryReservePort(candidate) : null;
2194
+ if (next) held.push(next);
2195
+ else held.push(await reservePort());
2050
2196
  }
2051
2197
  return held;
2052
2198
  } catch (err) {
@@ -2054,6 +2200,33 @@ async function reservePortRange(size = PORT_RANGE_SIZE) {
2054
2200
  throw err;
2055
2201
  }
2056
2202
  }
2203
+ async function reclaimPreferredPorts(ports, worktreePath) {
2204
+ let held = await tryReservePorts(ports);
2205
+ if (held) return held;
2206
+ if (worktreePath) {
2207
+ killStaleWorktreeListeners(ports, worktreePath, "SIGTERM");
2208
+ } else {
2209
+ killListenersOnPorts(ports);
2210
+ }
2211
+ for (let i = 0; i < RECLAIM_TRIES; i++) {
2212
+ await delay(RECLAIM_RETRY_DELAY_MS);
2213
+ held = await tryReservePorts(ports);
2214
+ if (held) return held;
2215
+ if (i === 2 && worktreePath) {
2216
+ killStaleWorktreeListeners(ports, worktreePath, "SIGKILL");
2217
+ }
2218
+ }
2219
+ return null;
2220
+ }
2221
+ async function reservePortRange(size = PORT_RANGE_SIZE, opts) {
2222
+ const preferred = normalizePreferredPorts(opts?.preferred, size);
2223
+ if (preferred.length) {
2224
+ const reused = await reclaimPreferredPorts(preferred, opts?.worktreePath);
2225
+ if (reused) return fillPortRange(reused, size);
2226
+ }
2227
+ const held = [await reservePort()];
2228
+ return fillPortRange(held, size);
2229
+ }
2057
2230
  async function allocatePortRange(size = PORT_RANGE_SIZE) {
2058
2231
  const held = await reservePortRange(size);
2059
2232
  const ports = held.map((h) => h.port);
@@ -2063,8 +2236,13 @@ async function allocatePortRange(size = PORT_RANGE_SIZE) {
2063
2236
  async function startDevServer(repoPath, worktreePath, onLine, opts) {
2064
2237
  const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
2065
2238
  if (!script) return null;
2066
- const held = await reservePortRange(PORT_RANGE_SIZE);
2239
+ const preferred = loadWorktreeRunPorts(worktreePath, script.name) ?? opts?.preferredPorts;
2240
+ const held = await reservePortRange(PORT_RANGE_SIZE, {
2241
+ preferred: preferred ?? void 0,
2242
+ worktreePath
2243
+ });
2067
2244
  const ports = held.map((h) => h.port);
2245
+ saveWorktreeRunPorts(worktreePath, script.name, ports);
2068
2246
  try {
2069
2247
  const handle = await spawnWorkspaceScript(script.command, {
2070
2248
  worktreePath,
@@ -2090,8 +2268,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2090
2268
 
2091
2269
  // src/store/setup-log.ts
2092
2270
  import { createHash } from "crypto";
2093
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2094
- import { join as join5 } from "path";
2271
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2272
+ import { join as join6 } from "path";
2095
2273
  var EMPTY = {
2096
2274
  output: "",
2097
2275
  running: false,
@@ -2133,7 +2311,7 @@ function materialize(mem) {
2133
2311
  };
2134
2312
  }
2135
2313
  function setupLogPath(threadId) {
2136
- return join5(threadsDir(), `${threadId}.setup.log.json`);
2314
+ return join6(threadsDir(), `${threadId}.setup.log.json`);
2137
2315
  }
2138
2316
  function setupLogKeyForWorktree(worktreePath) {
2139
2317
  const norm = normalizeWorktreePath(worktreePath);
@@ -2160,7 +2338,7 @@ function persistNow(threadId) {
2160
2338
  const mem = memory.get(threadId);
2161
2339
  if (!mem) return;
2162
2340
  try {
2163
- writeFileSync2(
2341
+ writeFileSync3(
2164
2342
  setupLogPath(threadId),
2165
2343
  `${JSON.stringify(materialize(mem))}
2166
2344
  `,
@@ -2183,7 +2361,7 @@ function loadFromDisk(threadId) {
2183
2361
  const path = setupLogPath(threadId);
2184
2362
  if (!existsSync4(path)) return null;
2185
2363
  try {
2186
- const raw = JSON.parse(readFileSync3(path, "utf8"));
2364
+ const raw = JSON.parse(readFileSync4(path, "utf8"));
2187
2365
  return {
2188
2366
  output: typeof raw.output === "string" ? raw.output : "",
2189
2367
  running: Boolean(raw.running),
@@ -2242,8 +2420,8 @@ function finishSetupLog(threadId, exitCode, source) {
2242
2420
 
2243
2421
  // src/store/run-log.ts
2244
2422
  import { createHash as createHash2 } from "crypto";
2245
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2246
- import { join as join6 } from "path";
2423
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2424
+ import { join as join7 } from "path";
2247
2425
  var EMPTY2 = {
2248
2426
  output: "",
2249
2427
  running: false,
@@ -2282,7 +2460,7 @@ function safeScriptToken(scriptName) {
2282
2460
  return token || "dev";
2283
2461
  }
2284
2462
  function runLogPath(key) {
2285
- return join6(threadsDir(), `${key}.run.log.json`);
2463
+ return join7(threadsDir(), `${key}.run.log.json`);
2286
2464
  }
2287
2465
  function runLogKeyForWorktree(worktreePath, scriptName) {
2288
2466
  const norm = normalizeWorktreePath(worktreePath);
@@ -2311,7 +2489,7 @@ function persistNow2(key) {
2311
2489
  const mem = memory2.get(key);
2312
2490
  if (!mem) return;
2313
2491
  try {
2314
- writeFileSync3(runLogPath(key), `${JSON.stringify(materialize2(mem))}
2492
+ writeFileSync4(runLogPath(key), `${JSON.stringify(materialize2(mem))}
2315
2493
  `, "utf8");
2316
2494
  } catch {
2317
2495
  }
@@ -2330,7 +2508,7 @@ function loadFromDisk2(key) {
2330
2508
  const path = runLogPath(key);
2331
2509
  if (!existsSync5(path)) return null;
2332
2510
  try {
2333
- const raw = JSON.parse(readFileSync4(path, "utf8"));
2511
+ const raw = JSON.parse(readFileSync5(path, "utf8"));
2334
2512
  return {
2335
2513
  output: typeof raw.output === "string" ? raw.output : "",
2336
2514
  running: Boolean(raw.running),
@@ -2391,7 +2569,7 @@ function flushRunLog(key) {
2391
2569
 
2392
2570
  // src/git/orphan-cleanup.ts
2393
2571
  import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
2394
- import { join as join7 } from "path";
2572
+ import { join as join8 } from "path";
2395
2573
  function isSideboardWorktreePath(path) {
2396
2574
  return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
2397
2575
  }
@@ -2438,9 +2616,9 @@ async function findOrphanWorktrees(repoPaths) {
2438
2616
  if (existsSync6(root)) {
2439
2617
  for (const entry of readdirSync2(root, { withFileTypes: true })) {
2440
2618
  if (!entry.isDirectory()) continue;
2441
- const path = join7(root, entry.name).replace(/\/$/, "");
2619
+ const path = join8(root, entry.name).replace(/\/$/, "");
2442
2620
  if (known.has(path) || seen.has(path)) continue;
2443
- if (!existsSync6(join7(path, ".git"))) continue;
2621
+ if (!existsSync6(join8(path, ".git"))) continue;
2444
2622
  seen.add(path);
2445
2623
  let mtimeMs = 0;
2446
2624
  try {
@@ -2654,7 +2832,7 @@ async function applyThreadIntoMain(thread, opts) {
2654
2832
 
2655
2833
  // src/git/clone-repo.ts
2656
2834
  import { existsSync as existsSync7 } from "fs";
2657
- import { basename as basename2, join as join8 } from "path";
2835
+ import { basename as basename2, join as join9 } from "path";
2658
2836
  import { execa as execa4 } from "execa";
2659
2837
  async function cloneRepoIntoSideboard(opts) {
2660
2838
  const url = opts.url.trim();
@@ -2665,7 +2843,7 @@ async function cloneRepoIntoSideboard(opts) {
2665
2843
  name = leaf || "repo";
2666
2844
  }
2667
2845
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
2668
- const dest = join8(sideboardReposDir(), name);
2846
+ const dest = join9(sideboardReposDir(), name);
2669
2847
  if (existsSync7(dest)) {
2670
2848
  const repoPath2 = await resolveRepoRoot(dest);
2671
2849
  const workspace2 = await ensureWorkspace(repoPath2);
@@ -2691,13 +2869,13 @@ function isAdoptableRunScriptRequest(req) {
2691
2869
  }
2692
2870
 
2693
2871
  // src/store/desktop-host.ts
2694
- import { readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
2695
- import { join as join9 } from "path";
2872
+ import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
2873
+ import { join as join10 } from "path";
2696
2874
  function desktopHostPidPath() {
2697
- return join9(appDataDir(), "desktop-host.pid");
2875
+ return join10(appDataDir(), "desktop-host.pid");
2698
2876
  }
2699
2877
  function claimDesktopHost(pid = process.pid) {
2700
- writeFileSync4(desktopHostPidPath(), `${pid}
2878
+ writeFileSync5(desktopHostPidPath(), `${pid}
2701
2879
  `, "utf8");
2702
2880
  }
2703
2881
  function releaseDesktopHost(pid = process.pid) {
@@ -2709,7 +2887,7 @@ function releaseDesktopHost(pid = process.pid) {
2709
2887
  }
2710
2888
  function readDesktopHostPid() {
2711
2889
  try {
2712
- const pid = Number.parseInt(readFileSync5(desktopHostPidPath(), "utf8").trim(), 10);
2890
+ const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
2713
2891
  if (!Number.isFinite(pid) || pid <= 0) return null;
2714
2892
  return pid;
2715
2893
  } catch {
@@ -2782,12 +2960,12 @@ function notifyParentOfChildHalt(child, status, send) {
2782
2960
  import {
2783
2961
  appendFileSync as appendFileSync2,
2784
2962
  existsSync as existsSync8,
2785
- mkdirSync as mkdirSync4,
2963
+ mkdirSync as mkdirSync5,
2786
2964
  readdirSync as readdirSync3,
2787
- readFileSync as readFileSync6,
2788
- writeFileSync as writeFileSync5
2965
+ readFileSync as readFileSync7,
2966
+ writeFileSync as writeFileSync6
2789
2967
  } from "fs";
2790
- import { dirname as dirname3, join as join10 } from "path";
2968
+ import { dirname as dirname4, join as join11 } from "path";
2791
2969
 
2792
2970
  // src/mcp/wait-for-turn.ts
2793
2971
  var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
@@ -2863,11 +3041,11 @@ function jobTreeAlive(pid) {
2863
3041
  }
2864
3042
  function readIntFile(file) {
2865
3043
  if (!existsSync8(file)) return null;
2866
- const n = Number.parseInt(readFileSync6(file, "utf8").trim(), 10);
3044
+ const n = Number.parseInt(readFileSync7(file, "utf8").trim(), 10);
2867
3045
  return Number.isInteger(n) ? n : null;
2868
3046
  }
2869
3047
  function jobDir(root, id, legacy = false) {
2870
- return join10(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
3048
+ return join11(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
2871
3049
  }
2872
3050
  function resolveJobDir(root, id) {
2873
3051
  const modern = jobDir(root, id, false);
@@ -2877,7 +3055,7 @@ function resolveJobDir(root, id) {
2877
3055
  return modern;
2878
3056
  }
2879
3057
  function listJobIdsIn(root, rel) {
2880
- const dir = join10(root, rel);
3058
+ const dir = join11(root, rel);
2881
3059
  if (!existsSync8(dir)) return [];
2882
3060
  return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
2883
3061
  }
@@ -2891,7 +3069,7 @@ function listRunningDetachedJobs(worktreePath) {
2891
3069
  const running = [];
2892
3070
  for (const id of ids) {
2893
3071
  const dir = resolveJobDir(root, id);
2894
- const pid = readIntFile(join10(dir, "pid"));
3072
+ const pid = readIntFile(join11(dir, "pid"));
2895
3073
  if (jobTreeAlive(pid)) running.push(id);
2896
3074
  }
2897
3075
  return running.sort();
@@ -2975,7 +3153,7 @@ function planJobContinue(opts) {
2975
3153
  }
2976
3154
  function tailProgress(logFile, maxLines = 12) {
2977
3155
  if (!existsSync8(logFile)) return "(no log yet)";
2978
- const lines = readFileSync6(logFile, "utf8").split("\n");
3156
+ const lines = readFileSync7(logFile, "utf8").split("\n");
2979
3157
  return lines.slice(-maxLines).join("\n");
2980
3158
  }
2981
3159
  function lastProgressLine(progress) {
@@ -2984,7 +3162,7 @@ function lastProgressLine(progress) {
2984
3162
  }
2985
3163
  function readLogLines(file) {
2986
3164
  if (!existsSync8(file)) return [];
2987
- const lines = readFileSync6(file, "utf8").split("\n");
3165
+ const lines = readFileSync7(file, "utf8").split("\n");
2988
3166
  if (lines.length && lines[lines.length - 1] === "") lines.pop();
2989
3167
  return lines;
2990
3168
  }
@@ -2995,15 +3173,15 @@ function takeDelta(logFile, cursorFile) {
2995
3173
  return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
2996
3174
  }
2997
3175
  function snapshotJob(dir) {
2998
- const pid = readIntFile(join10(dir, "pid"));
3176
+ const pid = readIntFile(join11(dir, "pid"));
2999
3177
  const running = jobTreeAlive(pid);
3000
3178
  return {
3001
3179
  pid,
3002
3180
  running,
3003
- exitCode: readIntFile(join10(dir, "exit")),
3004
- log: join10(dir, "log"),
3005
- cursor: join10(dir, "present.cursor"),
3006
- progress: tailProgress(join10(dir, "log"))
3181
+ exitCode: readIntFile(join11(dir, "exit")),
3182
+ log: join11(dir, "log"),
3183
+ cursor: join11(dir, "present.cursor"),
3184
+ progress: tailProgress(join11(dir, "log"))
3007
3185
  };
3008
3186
  }
3009
3187
  function toResult(id, snap, extra) {
@@ -3012,8 +3190,8 @@ function toResult(id, snap, extra) {
3012
3190
  const stillRunning = snap.running && !ok;
3013
3191
  const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
3014
3192
  try {
3015
- mkdirSync4(dirname3(snap.cursor), { recursive: true });
3016
- writeFileSync5(snap.cursor, `${nextCursor}
3193
+ mkdirSync5(dirname4(snap.cursor), { recursive: true });
3194
+ writeFileSync6(snap.cursor, `${nextCursor}
3017
3195
  `);
3018
3196
  } catch {
3019
3197
  }
@@ -3034,7 +3212,7 @@ function toResult(id, snap, extra) {
3034
3212
  };
3035
3213
  }
3036
3214
  async function sleepMs(ms) {
3037
- await new Promise((resolve) => setTimeout(resolve, ms));
3215
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
3038
3216
  }
3039
3217
  async function waitForDetachedJob(cwd, id, opts) {
3040
3218
  const jobId = sanitizeDetachedJobId(id);
@@ -3088,7 +3266,7 @@ function killProcessTree(pid, signal) {
3088
3266
  }
3089
3267
  function appendJobLog(logFile, line) {
3090
3268
  try {
3091
- mkdirSync4(dirname3(logFile), { recursive: true });
3269
+ mkdirSync5(dirname4(logFile), { recursive: true });
3092
3270
  const body = line.endsWith("\n") ? line : `${line}
3093
3271
  `;
3094
3272
  appendFileSync2(logFile, body);
@@ -3137,10 +3315,10 @@ async function stopDetachedJob(cwd, id, opts) {
3137
3315
  killProcessTree(targetPid, "SIGKILL");
3138
3316
  await sleep(100);
3139
3317
  }
3140
- const exitFile = join10(dir, "exit");
3318
+ const exitFile = join11(dir, "exit");
3141
3319
  if (!existsSync8(exitFile)) {
3142
3320
  try {
3143
- writeFileSync5(exitFile, "1\n");
3321
+ writeFileSync6(exitFile, "1\n");
3144
3322
  } catch {
3145
3323
  }
3146
3324
  }
@@ -4027,8 +4205,8 @@ import {
4027
4205
  existsSync as existsSync10,
4028
4206
  renameSync,
4029
4207
  unlinkSync as unlinkSync2,
4030
- writeFileSync as writeFileSync6,
4031
- readFileSync as readFileSync7
4208
+ writeFileSync as writeFileSync7,
4209
+ readFileSync as readFileSync8
4032
4210
  } from "fs";
4033
4211
  var buffers = /* @__PURE__ */ new Map();
4034
4212
  var FLUSH_MS = 800;
@@ -4105,7 +4283,7 @@ function writeTurnLive(threadId, progress) {
4105
4283
  const path = threadLivePath(threadId);
4106
4284
  const tmp = `${path}.${process.pid}.tmp`;
4107
4285
  try {
4108
- writeFileSync6(tmp, JSON.stringify(progress), "utf8");
4286
+ writeFileSync7(tmp, JSON.stringify(progress), "utf8");
4109
4287
  renameSync(tmp, path);
4110
4288
  } catch {
4111
4289
  try {
@@ -4118,7 +4296,7 @@ function readTurnLive(threadId) {
4118
4296
  const path = threadLivePath(threadId);
4119
4297
  if (!existsSync10(path)) return null;
4120
4298
  try {
4121
- const raw = JSON.parse(readFileSync7(path, "utf8"));
4299
+ const raw = JSON.parse(readFileSync8(path, "utf8"));
4122
4300
  if (!raw || typeof raw.summary !== "string") return null;
4123
4301
  return raw;
4124
4302
  } catch {
@@ -4345,23 +4523,23 @@ import {
4345
4523
  existsSync as existsSync11,
4346
4524
  mkdtempSync,
4347
4525
  readdirSync as readdirSync4,
4348
- readFileSync as readFileSync8,
4526
+ readFileSync as readFileSync9,
4349
4527
  rmSync
4350
4528
  } from "fs";
4351
4529
  import { tmpdir } from "os";
4352
- import { join as join11 } from "path";
4530
+ import { join as join12 } from "path";
4353
4531
  import { createRequire } from "module";
4354
- var CONDUCTOR_APP_SUPPORT = join11(
4532
+ var CONDUCTOR_APP_SUPPORT = join12(
4355
4533
  process.env.HOME ?? "",
4356
4534
  "Library",
4357
4535
  "Application Support",
4358
4536
  "com.conductor.app"
4359
4537
  );
4360
- var CONDUCTOR_DB = join11(CONDUCTOR_APP_SUPPORT, "conductor.db");
4361
- var CURSOR_SDK_STORE = join11(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4538
+ var CONDUCTOR_DB = join12(CONDUCTOR_APP_SUPPORT, "conductor.db");
4539
+ var CURSOR_SDK_STORE = join12(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4362
4540
  function thisModuleFile() {
4363
4541
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
4364
- return cjsFile || process.argv[1] || join11(process.cwd(), "package.json");
4542
+ return cjsFile || process.argv[1] || join12(process.cwd(), "package.json");
4365
4543
  }
4366
4544
  function openReadonlySqlite(file) {
4367
4545
  const req = createRequire(thisModuleFile());
@@ -4389,11 +4567,11 @@ function resolveConductorCursorAgentId(workspacePath) {
4389
4567
  return null;
4390
4568
  }
4391
4569
  for (const hash of hashes) {
4392
- const agentsFile = join11(CURSOR_SDK_STORE, hash, "agents.ndjson");
4570
+ const agentsFile = join12(CURSOR_SDK_STORE, hash, "agents.ndjson");
4393
4571
  if (!existsSync11(agentsFile)) continue;
4394
4572
  let text;
4395
4573
  try {
4396
- text = readFileSync8(agentsFile, "utf8");
4574
+ text = readFileSync9(agentsFile, "utf8");
4397
4575
  } catch {
4398
4576
  continue;
4399
4577
  }
@@ -4449,8 +4627,8 @@ function listConductorWorkspaces() {
4449
4627
  if (!existsSync11(CONDUCTOR_DB)) {
4450
4628
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4451
4629
  }
4452
- const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
4453
- const snapshot = join11(tmp, "conductor.db");
4630
+ const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
4631
+ const snapshot = join12(tmp, "conductor.db");
4454
4632
  try {
4455
4633
  copyFileSync2(CONDUCTOR_DB, snapshot);
4456
4634
  for (const suffix of ["-wal", "-shm"]) {
@@ -4540,8 +4718,8 @@ function importConductorWorkspace(workspaceId) {
4540
4718
  if (!existsSync11(CONDUCTOR_DB)) {
4541
4719
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4542
4720
  }
4543
- const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
4544
- const snapshot = join11(tmp, "conductor.db");
4721
+ const tmp = mkdtempSync(join12(tmpdir(), "sideboard-conductor-"));
4722
+ const snapshot = join12(tmp, "conductor.db");
4545
4723
  try {
4546
4724
  copyFileSync2(CONDUCTOR_DB, snapshot);
4547
4725
  for (const suffix of ["-wal", "-shm"]) {
@@ -4931,8 +5109,8 @@ function stackAgentDefaultsFrom(input) {
4931
5109
  }
4932
5110
 
4933
5111
  // src/diff/diff.ts
4934
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync9, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
4935
- import { dirname as dirname4, join as join12 } from "path";
5112
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
5113
+ import { dirname as dirname5, join as join13 } from "path";
4936
5114
  async function inspectGitWorktree(worktreePath) {
4937
5115
  if (!worktreePath || !existsSync13(worktreePath)) return "missing_worktree";
4938
5116
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -5078,11 +5256,11 @@ new file mode 100644
5078
5256
  };
5079
5257
  }
5080
5258
  async function untrackedPatch(worktreePath, path, maxHunk) {
5081
- const abs = join12(worktreePath, path);
5259
+ const abs = join13(worktreePath, path);
5082
5260
  try {
5083
5261
  const st = statSync2(abs);
5084
5262
  if (st.isFile() && st.size > maxHunk) {
5085
- const buf = readFileSync9(abs).subarray(0, maxHunk);
5263
+ const buf = readFileSync10(abs).subarray(0, maxHunk);
5086
5264
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
5087
5265
  }
5088
5266
  } catch {
@@ -5550,7 +5728,7 @@ async function getDiff(worktreePath, repoPath, opts) {
5550
5728
  }
5551
5729
  function statWorktreePath(worktreePath, relativePath) {
5552
5730
  assertSafeRelativePath(relativePath);
5553
- const abs = join12(worktreePath, relativePath);
5731
+ const abs = join13(worktreePath, relativePath);
5554
5732
  if (!existsSync13(abs)) return "missing";
5555
5733
  const st = statSync2(abs);
5556
5734
  if (st.isDirectory()) return "dir";
@@ -5592,7 +5770,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
5592
5770
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5593
5771
  assertSafeRelativePath(relativePath);
5594
5772
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
5595
- const abs = join12(worktreePath, relativePath);
5773
+ const abs = join13(worktreePath, relativePath);
5596
5774
  const st = statSync2(abs);
5597
5775
  if (!st.isFile()) {
5598
5776
  throw new Error(`Not a file: ${relativePath}`);
@@ -5602,7 +5780,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5602
5780
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
5603
5781
  );
5604
5782
  }
5605
- const buf = readFileSync9(abs);
5783
+ const buf = readFileSync10(abs);
5606
5784
  return {
5607
5785
  path: relativePath,
5608
5786
  contentBase64: buf.toString("base64"),
@@ -5612,12 +5790,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
5612
5790
  function readWorktreeFile(worktreePath, relativePath, opts) {
5613
5791
  assertSafeRelativePath(relativePath);
5614
5792
  const maxBytes = opts?.maxBytes ?? 2e5;
5615
- const abs = join12(worktreePath, relativePath);
5793
+ const abs = join13(worktreePath, relativePath);
5616
5794
  const st = statSync2(abs);
5617
5795
  if (!st.isFile()) {
5618
5796
  throw new Error(`Not a file: ${relativePath}`);
5619
5797
  }
5620
- const buf = readFileSync9(abs);
5798
+ const buf = readFileSync10(abs);
5621
5799
  if (isImageRelativePath(relativePath)) {
5622
5800
  const maxImageBytes = Math.max(maxBytes, 15e6);
5623
5801
  const truncated2 = buf.length > maxImageBytes;
@@ -5660,9 +5838,9 @@ function assertSafeRelativePath(relativePath) {
5660
5838
  }
5661
5839
  function writeWorktreeFile(worktreePath, relativePath, content) {
5662
5840
  assertSafeRelativePath(relativePath);
5663
- const abs = join12(worktreePath, relativePath);
5664
- mkdirSync5(dirname4(abs), { recursive: true });
5665
- writeFileSync7(abs, content, "utf8");
5841
+ const abs = join13(worktreePath, relativePath);
5842
+ mkdirSync6(dirname5(abs), { recursive: true });
5843
+ writeFileSync8(abs, content, "utf8");
5666
5844
  return { path: relativePath };
5667
5845
  }
5668
5846
  async function getWorktreeDirtyStat(worktreePath) {
@@ -5884,9 +6062,9 @@ async function confirmLand(thread, opts) {
5884
6062
  }
5885
6063
 
5886
6064
  // src/skills/discover.ts
5887
- import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
6065
+ import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
5888
6066
  import { homedir } from "os";
5889
- import { join as join13 } from "path";
6067
+ import { join as join14 } from "path";
5890
6068
 
5891
6069
  // src/skills/bundled/long-running.ts
5892
6070
  var LONG_RUNNING_SKILL_COMMAND = "long-running";
@@ -6023,7 +6201,7 @@ function parseFrontmatter(content) {
6023
6201
  }
6024
6202
  function readSkill(skillMd, source) {
6025
6203
  try {
6026
- const content = readFileSync10(skillMd, "utf8");
6204
+ const content = readFileSync11(skillMd, "utf8");
6027
6205
  const { name: fmName, description } = parseFrontmatter(content);
6028
6206
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
6029
6207
  const name = fmName || dirName;
@@ -6051,7 +6229,7 @@ function scanSkillsDir(dir, source, out) {
6051
6229
  }
6052
6230
  for (const entry of entries) {
6053
6231
  if (entry.startsWith(".")) continue;
6054
- const skillMd = join13(dir, entry, "SKILL.md");
6232
+ const skillMd = join14(dir, entry, "SKILL.md");
6055
6233
  if (!existsSync14(skillMd)) continue;
6056
6234
  try {
6057
6235
  if (!statSync3(skillMd).isFile()) continue;
@@ -6073,12 +6251,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
6073
6251
  return;
6074
6252
  }
6075
6253
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
6076
- const skill = readSkill(join13(dir, "SKILL.md"), "cli");
6254
+ const skill = readSkill(join14(dir, "SKILL.md"), "cli");
6077
6255
  if (skill) out.push(skill);
6078
6256
  }
6079
6257
  for (const entry of entries) {
6080
6258
  if (entry === "node_modules" || entry === ".git") continue;
6081
- const full = join13(dir, entry);
6259
+ const full = join14(dir, entry);
6082
6260
  try {
6083
6261
  if (!statSync3(full).isDirectory()) continue;
6084
6262
  } catch {
@@ -6098,17 +6276,17 @@ function discoverSkills(worktreePath) {
6098
6276
  const home = homedir();
6099
6277
  const collected = [];
6100
6278
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
6101
- scanSkillsDir(join13(worktreePath, rel), "workspace", collected);
6279
+ scanSkillsDir(join14(worktreePath, rel), "workspace", collected);
6102
6280
  }
6103
6281
  for (const abs of [
6104
- join13(home, ".claude/skills"),
6105
- join13(home, ".cursor/skills"),
6106
- join13(home, ".sideboard/skills"),
6107
- join13(home, ".brightsy/skills")
6282
+ join14(home, ".claude/skills"),
6283
+ join14(home, ".cursor/skills"),
6284
+ join14(home, ".sideboard/skills"),
6285
+ join14(home, ".brightsy/skills")
6108
6286
  ]) {
6109
6287
  scanSkillsDir(abs, "user", collected);
6110
6288
  }
6111
- scanClaudePluginSkills(join13(home, ".claude/plugins"), collected);
6289
+ scanClaudePluginSkills(join14(home, ".claude/plugins"), collected);
6112
6290
  collected.push(...bundledSkills());
6113
6291
  const rank = {
6114
6292
  workspace: 0,
@@ -6134,7 +6312,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
6134
6312
  \u2026(truncated)` : body;
6135
6313
  }
6136
6314
  }
6137
- const raw = readFileSync10(skillPath, "utf8");
6315
+ const raw = readFileSync11(skillPath, "utf8");
6138
6316
  if (raw.startsWith("---")) {
6139
6317
  const end = raw.indexOf("\n---", 3);
6140
6318
  if (end >= 0) {
@@ -6280,11 +6458,11 @@ function shiftQueuedItem(queue, queueAttachments) {
6280
6458
 
6281
6459
  // src/agents/instructions.ts
6282
6460
  import { existsSync as existsSync16 } from "fs";
6283
- import { join as join15 } from "path";
6461
+ import { join as join16 } from "path";
6284
6462
 
6285
6463
  // src/skills/detached-job-path.ts
6286
6464
  import { existsSync as existsSync15 } from "fs";
6287
- import { dirname as dirname5, join as join14 } from "path";
6465
+ import { dirname as dirname6, join as join15 } from "path";
6288
6466
  import { fileURLToPath } from "url";
6289
6467
  var DETACHED_JOB_FILENAMES = ["detached-job.cjs", "detached-job.js"];
6290
6468
  function compiledModuleDir() {
@@ -6293,7 +6471,7 @@ function compiledModuleDir() {
6293
6471
  try {
6294
6472
  const url = import.meta.url;
6295
6473
  if (typeof url === "string" && url.length > 0) {
6296
- return dirname5(fileURLToPath(url));
6474
+ return dirname6(fileURLToPath(url));
6297
6475
  }
6298
6476
  } catch {
6299
6477
  }
@@ -6301,7 +6479,7 @@ function compiledModuleDir() {
6301
6479
  }
6302
6480
  function detachedJobScriptIn(dir) {
6303
6481
  for (const name of DETACHED_JOB_FILENAMES) {
6304
- const script = join14(dir, "scripts", name);
6482
+ const script = join15(dir, "scripts", name);
6305
6483
  if (existsSync15(script)) return script;
6306
6484
  }
6307
6485
  return null;
@@ -6319,7 +6497,7 @@ function resolveDetachedJobScript() {
6319
6497
  for (let i = 0; i < 8; i++) {
6320
6498
  const candidate = detachedJobScriptIn(dir);
6321
6499
  if (candidate) return candidate;
6322
- const parent = dirname5(dir);
6500
+ const parent = dirname6(dir);
6323
6501
  if (parent === dir) break;
6324
6502
  dir = parent;
6325
6503
  }
@@ -6424,9 +6602,9 @@ function formatWorktreeDirective(thread, opts) {
6424
6602
  );
6425
6603
  } else {
6426
6604
  const slug = opts?.githubSlug ?? "<origin-owner/name>";
6427
- const resolve = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
6605
+ const resolve2 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
6428
6606
  lines.push(
6429
- `- 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\`.`
6607
+ `- 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\`.`
6430
6608
  );
6431
6609
  }
6432
6610
  lines.push(
@@ -6538,9 +6716,9 @@ function formatPrGateDirective() {
6538
6716
  }
6539
6717
  function formatProcessGuideDirective(opts) {
6540
6718
  const worktree = opts?.worktreePath?.trim();
6541
- const hasGraphSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/graph-engineering/SKILL.md"));
6542
- const hasReviewSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/review/SKILL.md"));
6543
- const hasInstallSkill = !!worktree && existsSync16(join15(worktree, ".claude/skills/worktree-install/SKILL.md"));
6719
+ const hasGraphSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/graph-engineering/SKILL.md"));
6720
+ const hasReviewSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/review/SKILL.md"));
6721
+ const hasInstallSkill = !!worktree && existsSync16(join16(worktree, ".claude/skills/worktree-install/SKILL.md"));
6544
6722
  return [
6545
6723
  "Process guides (recurring work only):",
6546
6724
  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,
@@ -6903,8 +7081,8 @@ async function syncThreadBranchFromGit(threadId) {
6903
7081
 
6904
7082
  // src/store/schedules.ts
6905
7083
  import { randomUUID as randomUUID4 } from "crypto";
6906
- import { existsSync as existsSync17, readFileSync as readFileSync11 } from "fs";
6907
- import { join as join16 } from "path";
7084
+ import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
7085
+ import { join as join17 } from "path";
6908
7086
  import { Cron } from "croner";
6909
7087
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
6910
7088
  var UNIT_MS = {
@@ -6914,7 +7092,7 @@ var UNIT_MS = {
6914
7092
  d: 864e5
6915
7093
  };
6916
7094
  function schedulesPath() {
6917
- return join16(appDataDir(), "schedules.json");
7095
+ return join17(appDataDir(), "schedules.json");
6918
7096
  }
6919
7097
  function parseDurationMs(every) {
6920
7098
  const m = every.trim().match(DURATION_RE);
@@ -7022,7 +7200,7 @@ function readAll() {
7022
7200
  const path = schedulesPath();
7023
7201
  if (!existsSync17(path)) return [];
7024
7202
  try {
7025
- const parsed = JSON.parse(readFileSync11(path, "utf8"));
7203
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
7026
7204
  if (!Array.isArray(parsed)) return [];
7027
7205
  return parsed.map((row) => {
7028
7206
  try {
@@ -7148,7 +7326,7 @@ function formatScheduledPrompt(name, prompt) {
7148
7326
  ${prompt}`;
7149
7327
  }
7150
7328
  async function defaultDeps() {
7151
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HPMDB2SG.js");
7329
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-R75VDUR2.js");
7152
7330
  const orch = getOrchestrator2();
7153
7331
  return {
7154
7332
  findThread: (id) => findThreadByRef(id),
@@ -7213,14 +7391,14 @@ function armOne(schedule) {
7213
7391
  if (!schedule.enabled) return;
7214
7392
  const at = new Date(schedule.nextRunAt);
7215
7393
  if (Number.isNaN(at.getTime())) return;
7216
- const delay = Math.max(0, at.getTime() - Date.now());
7217
- if (delay <= 250) {
7394
+ const delay2 = Math.max(0, at.getTime() - Date.now());
7395
+ if (delay2 <= 250) {
7218
7396
  void fireSchedule(schedule.id).then(() => {
7219
7397
  if (isThisProcessDesktopHost()) armSchedules();
7220
7398
  });
7221
7399
  return;
7222
7400
  }
7223
- const capped = Math.min(delay, SET_TIMEOUT_MAX);
7401
+ const capped = Math.min(delay2, SET_TIMEOUT_MAX);
7224
7402
  const timer = setTimeout(() => {
7225
7403
  timers.delete(schedule.id);
7226
7404
  const live = getSchedule(schedule.id);
@@ -7604,8 +7782,8 @@ var Orchestrator = class {
7604
7782
  scheduleQuotaResume(threadId, resumeAt) {
7605
7783
  this.clearQuotaResumeTimer(threadId);
7606
7784
  updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
7607
- const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
7608
- const capped = Math.min(delay, 2147483647);
7785
+ const delay2 = Math.max(5e3, resumeAt.getTime() - Date.now());
7786
+ const capped = Math.min(delay2, 2147483647);
7609
7787
  const timer = setTimeout(() => {
7610
7788
  this.quotaResumeTimers.delete(threadId);
7611
7789
  void this.resumeAfterQuotaWait(threadId);
@@ -8660,6 +8838,7 @@ var Orchestrator = class {
8660
8838
  return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
8661
8839
  }
8662
8840
  }
8841
+ const preferredPorts = active?.ports?.length ? active.ports : active?.port != null ? [active.port] : void 0;
8663
8842
  if (active && !existing) {
8664
8843
  killListenersOnPorts(collectActiveRunPorts([active]));
8665
8844
  this.syncWorktreeRuns(
@@ -8698,7 +8877,7 @@ var Orchestrator = class {
8698
8877
  thread.repoPath,
8699
8878
  thread.worktreePath,
8700
8879
  runOutput.push,
8701
- { scriptName: resolvedName }
8880
+ { scriptName: resolvedName, preferredPorts }
8702
8881
  );
8703
8882
  } catch (err) {
8704
8883
  runOutput.flush();
@@ -9147,7 +9326,7 @@ var Orchestrator = class {
9147
9326
  return thread;
9148
9327
  }
9149
9328
  const start = Date.now();
9150
- return new Promise((resolve, reject) => {
9329
+ return new Promise((resolve2, reject) => {
9151
9330
  let settled = false;
9152
9331
  let timer;
9153
9332
  const finish = (fn) => {
@@ -9165,7 +9344,7 @@ var Orchestrator = class {
9165
9344
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
9166
9345
  return;
9167
9346
  }
9168
- finish(() => resolve(latest));
9347
+ finish(() => resolve2(latest));
9169
9348
  return;
9170
9349
  }
9171
9350
  if (event.type === "status_changed") {
@@ -9175,7 +9354,7 @@ var Orchestrator = class {
9175
9354
  return;
9176
9355
  }
9177
9356
  if (!["running", "queued"].includes(latest.status)) {
9178
- finish(() => resolve(latest));
9357
+ finish(() => resolve2(latest));
9179
9358
  }
9180
9359
  }
9181
9360
  });
@@ -9187,12 +9366,12 @@ var Orchestrator = class {
9187
9366
  }
9188
9367
  const current = this.healStaleReportedActivity(raw);
9189
9368
  if (!this.threadLooksLive(current)) {
9190
- finish(() => resolve(current));
9369
+ finish(() => resolve2(current));
9191
9370
  return;
9192
9371
  }
9193
9372
  if (Date.now() - start > timeoutMs) {
9194
9373
  if (opts?.resolveIfStillRunning) {
9195
- finish(() => resolve(current));
9374
+ finish(() => resolve2(current));
9196
9375
  } else {
9197
9376
  finish(() => reject(new Error("wait_for_turn timed out")));
9198
9377
  }
@@ -10045,13 +10224,21 @@ export {
10045
10224
  buildReviewRequestAttachment,
10046
10225
  readExistingReviewRequestFile,
10047
10226
  requestReview,
10227
+ RUN_PORTS_REL,
10228
+ worktreeRunPortsPath,
10229
+ loadWorktreeRunPorts,
10230
+ saveWorktreeRunPorts,
10231
+ PORT_RANGE_SIZE,
10048
10232
  readWorktreeInclude,
10049
10233
  resolveFilesToCopy,
10050
10234
  copyConfiguredFiles,
10051
10235
  captureLoginEnv,
10052
10236
  buildWorkspaceScriptEnv,
10053
10237
  killableListenerPids,
10238
+ listenerCwd,
10239
+ isOtherWorktreeListenerCwd,
10054
10240
  killListenersOnPorts,
10241
+ killStaleWorktreeListeners,
10055
10242
  runSetupScript,
10056
10243
  runConventionSetup,
10057
10244
  runWorkspaceSetup,
@@ -10062,6 +10249,8 @@ export {
10062
10249
  getRunMode,
10063
10250
  reservePort,
10064
10251
  allocatePort,
10252
+ tryReservePort,
10253
+ tryReservePorts,
10065
10254
  reservePortRange,
10066
10255
  allocatePortRange,
10067
10256
  startDevServer,