@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.
- package/dist/{chunk-E6UJGLPM.js → chunk-CDG4CLWQ.js} +326 -147
- package/dist/{chunk-32DG3DA2.js → chunk-D5SDZSEA.js} +337 -148
- package/dist/index.cjs +501 -295
- package/dist/index.d.cts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +22 -2
- package/dist/mcp/run-stdio.cjs +482 -296
- package/dist/mcp/run-stdio.js +2 -2
- package/dist/{orchestrator-AAYKR4AI.js → orchestrator-3ZP4WTQ4.js} +1 -1
- package/dist/{orchestrator-HPMDB2SG.js → orchestrator-R75VDUR2.js} +1 -1
- package/package.json +1 -1
|
@@ -792,13 +792,14 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
792
792
|
import {
|
|
793
793
|
copyFileSync,
|
|
794
794
|
existsSync as existsSync3,
|
|
795
|
-
mkdirSync as
|
|
795
|
+
mkdirSync as mkdirSync4,
|
|
796
796
|
readdirSync,
|
|
797
|
-
readFileSync as
|
|
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
|
|
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 =
|
|
1726
|
+
const path = join5(repoPath, ".worktreeinclude");
|
|
1669
1727
|
if (!existsSync3(path)) return [];
|
|
1670
|
-
return
|
|
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 =
|
|
1767
|
+
const src = join5(repoPath, rel);
|
|
1710
1768
|
if (!existsSync3(src)) continue;
|
|
1711
|
-
const dest =
|
|
1712
|
-
|
|
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
|
-
|
|
1807
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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", () =>
|
|
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
|
-
|
|
2145
|
+
resolve2({ port, release });
|
|
2024
2146
|
});
|
|
2025
2147
|
});
|
|
2026
2148
|
}
|
|
2027
|
-
async function
|
|
2028
|
-
const held = [
|
|
2029
|
-
const
|
|
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 =
|
|
2032
|
-
const candidate = base + i;
|
|
2033
|
-
const next = await tryReservePort(candidate);
|
|
2034
|
-
if (next)
|
|
2035
|
-
|
|
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
|
|
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
|
|
2077
|
-
import { join as
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
2221
|
-
import { join as
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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 =
|
|
2594
|
+
const path = join8(root, entry.name).replace(/\/$/, "");
|
|
2417
2595
|
if (known.has(path) || seen.has(path)) continue;
|
|
2418
|
-
if (!existsSync6(
|
|
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
|
|
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 =
|
|
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
|
|
2658
|
-
import { join as
|
|
2835
|
+
import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
2836
|
+
import { join as join10 } from "path";
|
|
2659
2837
|
function desktopHostPidPath() {
|
|
2660
|
-
return
|
|
2838
|
+
return join10(appDataDir(), "desktop-host.pid");
|
|
2661
2839
|
}
|
|
2662
2840
|
function readDesktopHostPid() {
|
|
2663
2841
|
try {
|
|
2664
|
-
const pid = Number.parseInt(
|
|
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
|
|
2915
|
+
mkdirSync as mkdirSync5,
|
|
2738
2916
|
readdirSync as readdirSync3,
|
|
2739
|
-
readFileSync as
|
|
2740
|
-
writeFileSync as
|
|
2917
|
+
readFileSync as readFileSync7,
|
|
2918
|
+
writeFileSync as writeFileSync6
|
|
2741
2919
|
} from "fs";
|
|
2742
|
-
import { dirname as
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
2956
|
-
log:
|
|
2957
|
-
cursor:
|
|
2958
|
-
progress: tailProgress(
|
|
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
|
-
|
|
2968
|
-
|
|
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((
|
|
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
|
-
|
|
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 =
|
|
3270
|
+
const exitFile = join11(dir, "exit");
|
|
3093
3271
|
if (!existsSync8(exitFile)) {
|
|
3094
3272
|
try {
|
|
3095
|
-
|
|
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
|
|
3903
|
-
readFileSync as
|
|
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
|
-
|
|
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(
|
|
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
|
|
4395
|
+
readFileSync as readFileSync9,
|
|
4218
4396
|
rmSync
|
|
4219
4397
|
} from "fs";
|
|
4220
4398
|
import { tmpdir } from "os";
|
|
4221
|
-
import { join as
|
|
4399
|
+
import { join as join12 } from "path";
|
|
4222
4400
|
import { createRequire } from "module";
|
|
4223
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
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 =
|
|
4230
|
-
var 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] ||
|
|
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 =
|
|
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 =
|
|
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(
|
|
4319
|
-
const snapshot =
|
|
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(
|
|
4410
|
-
const snapshot =
|
|
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
|
|
4791
|
-
import { dirname as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5520
|
-
|
|
5521
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
6135
|
+
scanSkillsDir(join14(worktreePath, rel), "workspace", collected);
|
|
5958
6136
|
}
|
|
5959
6137
|
for (const abs of [
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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.${
|
|
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(
|
|
6376
|
-
const hasReviewSkill = !!worktree && existsSync16(
|
|
6377
|
-
const hasInstallSkill = !!worktree && existsSync16(
|
|
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
|
|
6579
|
-
import { join as
|
|
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
|
|
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(
|
|
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-
|
|
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
|
|
6880
|
-
if (
|
|
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(
|
|
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
|
|
7271
|
-
const capped = Math.min(
|
|
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((
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
9032
|
+
finish(() => resolve2(current));
|
|
8854
9033
|
return;
|
|
8855
9034
|
}
|
|
8856
9035
|
if (Date.now() - start > timeoutMs) {
|
|
8857
9036
|
if (opts?.resolveIfStillRunning) {
|
|
8858
|
-
finish(() =>
|
|
9037
|
+
finish(() => resolve2(current));
|
|
8859
9038
|
} else {
|
|
8860
9039
|
finish(() => reject(new Error("wait_for_turn timed out")));
|
|
8861
9040
|
}
|