@moberg_hr/work-tree 1.5.1 → 1.7.0
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/README.md +2 -2
- package/dist/bin.js +361 -165
- package/dist/bin.js.map +1 -1
- package/dist/wd-bin.js +355 -159
- package/dist/wd-bin.js.map +1 -1
- package/dist/web/assets/index-BV5-pKcI.js +106 -0
- package/dist/web/assets/index-BV5-pKcI.js.map +1 -0
- package/dist/web/assets/{index-8WP9kHU6.css → index-uJQw1Itb.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +3 -3
- package/dist/web/assets/index-C5ddmssO.js +0 -106
- package/dist/web/assets/index-C5ddmssO.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs45 from "fs";
|
|
5
|
+
import path44 from "path";
|
|
6
6
|
import chalk27 from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
@@ -1575,11 +1575,12 @@ async function upsertSession(target, isGroup, branch, paths, jiraKey, baseBranch
|
|
|
1575
1575
|
saveHistory(sessions);
|
|
1576
1576
|
});
|
|
1577
1577
|
}
|
|
1578
|
-
async function upsertSessionWithPort(target, isGroup, branch, paths, config, jiraKey, baseBranch) {
|
|
1578
|
+
async function upsertSessionWithPort(target, isGroup, branch, paths, config, jiraKey, baseBranch, baseBranches) {
|
|
1579
1579
|
return withHistoryLock(async () => {
|
|
1580
1580
|
const sessions = loadHistory();
|
|
1581
1581
|
const existing = findSession(sessions, target, branch);
|
|
1582
1582
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1583
|
+
const hasPerRepo = baseBranches && Object.keys(baseBranches).length > 0;
|
|
1583
1584
|
let port = existing?.port;
|
|
1584
1585
|
if (port === void 0) {
|
|
1585
1586
|
const seedKey = sessionKey(target, branch);
|
|
@@ -1594,6 +1595,7 @@ async function upsertSessionWithPort(target, isGroup, branch, paths, config, jir
|
|
|
1594
1595
|
existing.lastAccessedAt = now;
|
|
1595
1596
|
if (jiraKey) existing.jiraKey = jiraKey;
|
|
1596
1597
|
if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;
|
|
1598
|
+
if (hasPerRepo && !existing.baseBranches) existing.baseBranches = baseBranches;
|
|
1597
1599
|
if (port !== void 0) existing.port = port;
|
|
1598
1600
|
} else {
|
|
1599
1601
|
const session = {
|
|
@@ -1606,6 +1608,7 @@ async function upsertSessionWithPort(target, isGroup, branch, paths, config, jir
|
|
|
1606
1608
|
};
|
|
1607
1609
|
if (jiraKey) session.jiraKey = jiraKey;
|
|
1608
1610
|
if (baseBranch) session.baseBranch = baseBranch;
|
|
1611
|
+
if (hasPerRepo) session.baseBranches = baseBranches;
|
|
1609
1612
|
if (port !== void 0) session.port = port;
|
|
1610
1613
|
sessions.push(session);
|
|
1611
1614
|
}
|
|
@@ -1721,6 +1724,62 @@ function copyConfigFiles(repoPath, worktreePath, patterns) {
|
|
|
1721
1724
|
}
|
|
1722
1725
|
}
|
|
1723
1726
|
|
|
1727
|
+
// src/core/base-spec.ts
|
|
1728
|
+
var BaseSpecError = class extends Error {
|
|
1729
|
+
constructor(message) {
|
|
1730
|
+
super(message);
|
|
1731
|
+
this.name = "BaseSpecError";
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
function parseBaseSpec(raw) {
|
|
1735
|
+
const spec = { perRepo: {} };
|
|
1736
|
+
if (raw === void 0) return spec;
|
|
1737
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
1738
|
+
for (const value of values) {
|
|
1739
|
+
const v = value.trim();
|
|
1740
|
+
if (!v) continue;
|
|
1741
|
+
const eq = v.indexOf("=");
|
|
1742
|
+
if (eq === -1) {
|
|
1743
|
+
if (spec.default !== void 0 && spec.default !== v) {
|
|
1744
|
+
throw new BaseSpecError(
|
|
1745
|
+
`Conflicting default --base values: '${spec.default}' and '${v}'`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
spec.default = v;
|
|
1749
|
+
} else {
|
|
1750
|
+
const alias = v.slice(0, eq).trim();
|
|
1751
|
+
const branch = v.slice(eq + 1).trim();
|
|
1752
|
+
if (!alias || !branch) {
|
|
1753
|
+
throw new BaseSpecError(
|
|
1754
|
+
`Invalid --base '${value}'. Use 'alias=branch' or a bare 'branch'.`
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
const prior = spec.perRepo[alias];
|
|
1758
|
+
if (prior !== void 0 && prior !== branch) {
|
|
1759
|
+
throw new BaseSpecError(
|
|
1760
|
+
`Conflicting --base for '${alias}': '${prior}' and '${branch}'`
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
spec.perRepo[alias] = branch;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
return spec;
|
|
1767
|
+
}
|
|
1768
|
+
function baseForAlias(spec, alias) {
|
|
1769
|
+
return spec.perRepo[alias] ?? spec.default;
|
|
1770
|
+
}
|
|
1771
|
+
function isEmptyBaseSpec(spec) {
|
|
1772
|
+
return spec.default === void 0 && Object.keys(spec.perRepo).length === 0;
|
|
1773
|
+
}
|
|
1774
|
+
function baseSpecOverrideAliases(spec) {
|
|
1775
|
+
return Object.keys(spec.perRepo);
|
|
1776
|
+
}
|
|
1777
|
+
function toBaseSpec(base) {
|
|
1778
|
+
if (base === void 0) return { perRepo: {} };
|
|
1779
|
+
if (typeof base === "string") return { default: base, perRepo: {} };
|
|
1780
|
+
return base;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1724
1783
|
// src/core/worktree.ts
|
|
1725
1784
|
function createSingleWorktree(repoPath, worktreePath, branchName, config, baseBranch) {
|
|
1726
1785
|
debug("createSingleWorktree", { repoPath, worktreePath, branchName, baseBranch });
|
|
@@ -1926,8 +1985,9 @@ function removeSingleWorktree(repoPath, worktreePath, branchName, force) {
|
|
|
1926
1985
|
return false;
|
|
1927
1986
|
}
|
|
1928
1987
|
}
|
|
1929
|
-
async function setupWorktree(targetName, branchName, config,
|
|
1930
|
-
|
|
1988
|
+
async function setupWorktree(targetName, branchName, config, base, jiraKey) {
|
|
1989
|
+
const spec = toBaseSpec(base);
|
|
1990
|
+
debug("setupWorktree", { targetName, branchName, spec, jiraKey });
|
|
1931
1991
|
const target = resolveProjectTarget(targetName, config);
|
|
1932
1992
|
if (!target) {
|
|
1933
1993
|
debug("setupWorktree: target not found", targetName);
|
|
@@ -1935,27 +1995,38 @@ async function setupWorktree(targetName, branchName, config, baseBranch, jiraKey
|
|
|
1935
1995
|
}
|
|
1936
1996
|
const workTreeDirName = branchName.replace(/\//g, "-");
|
|
1937
1997
|
if (target.isGroup) {
|
|
1938
|
-
return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config,
|
|
1998
|
+
return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config, spec, jiraKey);
|
|
1939
1999
|
} else {
|
|
1940
|
-
return setupSingleWorktree(targetName, branchName, workTreeDirName, config,
|
|
2000
|
+
return setupSingleWorktree(targetName, branchName, workTreeDirName, config, spec, jiraKey);
|
|
1941
2001
|
}
|
|
1942
2002
|
}
|
|
1943
|
-
async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDirName, config,
|
|
2003
|
+
async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDirName, config, spec, jiraKey) {
|
|
1944
2004
|
const groupWorktreePath = path12.join(config.worktreesRoot, groupName, workTreeDirName);
|
|
1945
|
-
if (
|
|
2005
|
+
if (!isEmptyBaseSpec(spec)) {
|
|
2006
|
+
const unknownAliases = baseSpecOverrideAliases(spec).filter(
|
|
2007
|
+
(a) => !repoAliases.includes(a)
|
|
2008
|
+
);
|
|
2009
|
+
if (unknownAliases.length > 0) {
|
|
2010
|
+
console.error(
|
|
2011
|
+
`--base names repo(s) not in group '${groupName}': ${unknownAliases.join(", ")}. Group repos: ${repoAliases.join(", ")}`
|
|
2012
|
+
);
|
|
2013
|
+
return null;
|
|
2014
|
+
}
|
|
1946
2015
|
const missingBase = [];
|
|
1947
2016
|
const branchExists = [];
|
|
1948
2017
|
for (const alias of repoAliases) {
|
|
1949
2018
|
const repoPath = config.repos[alias];
|
|
1950
|
-
|
|
1951
|
-
|
|
2019
|
+
const repoBase = baseForAlias(spec, alias);
|
|
2020
|
+
if (!repoBase) continue;
|
|
2021
|
+
if (!localBranchExists(repoBase, repoPath) && !remoteBranchExists(repoBase, repoPath)) {
|
|
2022
|
+
missingBase.push(`${alias} (${repoBase})`);
|
|
1952
2023
|
}
|
|
1953
2024
|
if (localBranchExists(branchName, repoPath) || remoteBranchExists(branchName, repoPath)) {
|
|
1954
2025
|
branchExists.push(alias);
|
|
1955
2026
|
}
|
|
1956
2027
|
}
|
|
1957
2028
|
if (missingBase.length > 0) {
|
|
1958
|
-
console.error(`Base branch
|
|
2029
|
+
console.error(`Base branch not found in: ${missingBase.join(", ")}`);
|
|
1959
2030
|
return null;
|
|
1960
2031
|
}
|
|
1961
2032
|
if (branchExists.length > 0) {
|
|
@@ -1968,14 +2039,17 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
1968
2039
|
console.log("");
|
|
1969
2040
|
fs13.mkdirSync(groupWorktreePath, { recursive: true });
|
|
1970
2041
|
const createdWorktrees = [];
|
|
2042
|
+
const baseBranches = {};
|
|
1971
2043
|
for (const alias of repoAliases) {
|
|
1972
2044
|
const repoPath = config.repos[alias];
|
|
1973
2045
|
const repoName = path12.basename(repoPath);
|
|
1974
2046
|
const subWorktreePath = path12.join(groupWorktreePath, repoName);
|
|
2047
|
+
const repoBase = baseForAlias(spec, alias);
|
|
1975
2048
|
console.log(chalk5.cyan(`[${alias}] (${repoName}):`));
|
|
1976
|
-
const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config,
|
|
2049
|
+
const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config, repoBase);
|
|
1977
2050
|
if (success) {
|
|
1978
2051
|
createdWorktrees.push({ repoPath, worktreePath: subWorktreePath });
|
|
2052
|
+
if (repoBase) baseBranches[subWorktreePath] = repoBase;
|
|
1979
2053
|
} else {
|
|
1980
2054
|
console.log("");
|
|
1981
2055
|
console.log(chalk5.yellow("Rolling back created worktrees due to failure..."));
|
|
@@ -2005,6 +2079,8 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
2005
2079
|
console.log(chalk5.yellow(`Run 'work config regengroup ${groupName}' to generate it.`));
|
|
2006
2080
|
}
|
|
2007
2081
|
const allPaths = createdWorktrees.map((wt) => wt.worktreePath);
|
|
2082
|
+
const distinctBases = [...new Set(Object.values(baseBranches))];
|
|
2083
|
+
const representativeBase = spec.default ?? (distinctBases.length === 1 ? distinctBases[0] : void 0);
|
|
2008
2084
|
const { port } = await upsertSessionWithPort(
|
|
2009
2085
|
groupName,
|
|
2010
2086
|
true,
|
|
@@ -2012,17 +2088,26 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
2012
2088
|
allPaths,
|
|
2013
2089
|
config,
|
|
2014
2090
|
jiraKey,
|
|
2015
|
-
|
|
2091
|
+
representativeBase,
|
|
2092
|
+
baseBranches
|
|
2016
2093
|
);
|
|
2017
2094
|
console.log("");
|
|
2018
2095
|
console.log(`Branch: ${branchName}`);
|
|
2019
2096
|
if (port !== void 0) console.log(chalk5.gray(`Dev-server port: ${port}`));
|
|
2020
2097
|
return { launchDir: groupWorktreePath, paths: allPaths, isGroup: true, port };
|
|
2021
2098
|
}
|
|
2022
|
-
async function setupSingleWorktree(targetName, branchName, workTreeDirName, config,
|
|
2099
|
+
async function setupSingleWorktree(targetName, branchName, workTreeDirName, config, spec, jiraKey) {
|
|
2023
2100
|
const repoPath = config.repos[targetName];
|
|
2024
2101
|
const repoName = path12.basename(repoPath);
|
|
2025
2102
|
let workTreePath = path12.join(config.worktreesRoot, repoName, workTreeDirName);
|
|
2103
|
+
const unknownAliases = baseSpecOverrideAliases(spec).filter((a) => a !== targetName);
|
|
2104
|
+
if (unknownAliases.length > 0) {
|
|
2105
|
+
console.error(
|
|
2106
|
+
`--base names repo(s) other than '${targetName}': ${unknownAliases.join(", ")}`
|
|
2107
|
+
);
|
|
2108
|
+
return null;
|
|
2109
|
+
}
|
|
2110
|
+
const baseBranch = baseForAlias(spec, targetName);
|
|
2026
2111
|
if (!fs13.existsSync(repoPath)) {
|
|
2027
2112
|
console.error(`Repository path does not exist: ${repoPath}`);
|
|
2028
2113
|
return null;
|
|
@@ -2049,7 +2134,8 @@ async function setupSingleWorktree(targetName, branchName, workTreeDirName, conf
|
|
|
2049
2134
|
[workTreePath],
|
|
2050
2135
|
config,
|
|
2051
2136
|
jiraKey,
|
|
2052
|
-
baseBranch
|
|
2137
|
+
baseBranch,
|
|
2138
|
+
baseBranch ? { [workTreePath]: baseBranch } : void 0
|
|
2053
2139
|
);
|
|
2054
2140
|
console.log(`Branch: ${branchName}`);
|
|
2055
2141
|
if (port !== void 0) console.log(chalk5.gray(`Dev-server port: ${port}`));
|
|
@@ -2121,7 +2207,7 @@ var treeCommand = {
|
|
|
2121
2207
|
type: "boolean",
|
|
2122
2208
|
default: false
|
|
2123
2209
|
}).option("base", {
|
|
2124
|
-
describe: "
|
|
2210
|
+
describe: "Base branch to fork from instead of HEAD. Repeatable. Use a bare branch (--base dev) for all repos, or alias=branch (--base backend=dev --base frontend=feat/x) for per-repo bases in a group.",
|
|
2125
2211
|
type: "string"
|
|
2126
2212
|
}).option("prompt", {
|
|
2127
2213
|
describe: "Initial prompt to send to the AI tool on startup",
|
|
@@ -2146,7 +2232,17 @@ var treeCommand = {
|
|
|
2146
2232
|
const open = argv.open;
|
|
2147
2233
|
const unsafe = argv.unsafe;
|
|
2148
2234
|
const setupOnly = argv["setup-only"];
|
|
2149
|
-
|
|
2235
|
+
let baseSpec;
|
|
2236
|
+
try {
|
|
2237
|
+
baseSpec = parseBaseSpec(argv.base);
|
|
2238
|
+
} catch (err) {
|
|
2239
|
+
if (err instanceof BaseSpecError) {
|
|
2240
|
+
console.error(err.message);
|
|
2241
|
+
process.exitCode = 1;
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
throw err;
|
|
2245
|
+
}
|
|
2150
2246
|
const jiraKey = argv["jira-key"];
|
|
2151
2247
|
const promptFile = argv["prompt-file"];
|
|
2152
2248
|
let initialPrompt = argv.prompt;
|
|
@@ -2184,12 +2280,10 @@ var treeCommand = {
|
|
|
2184
2280
|
process.exitCode = 1;
|
|
2185
2281
|
return;
|
|
2186
2282
|
}
|
|
2187
|
-
if (
|
|
2283
|
+
if (!isEmptyBaseSpec(baseSpec) && !branchName) {
|
|
2188
2284
|
console.error("--base requires a branch name");
|
|
2189
2285
|
console.log(
|
|
2190
|
-
chalk6.yellow(
|
|
2191
|
-
`Usage: work tree ${targetName} <branch> --base ${baseBranch}`
|
|
2192
|
-
)
|
|
2286
|
+
chalk6.yellow(`Usage: work tree ${targetName} <branch> --base <base>`)
|
|
2193
2287
|
);
|
|
2194
2288
|
process.exitCode = 1;
|
|
2195
2289
|
return;
|
|
@@ -2237,7 +2331,7 @@ var treeCommand = {
|
|
|
2237
2331
|
}
|
|
2238
2332
|
return;
|
|
2239
2333
|
}
|
|
2240
|
-
const result = await setupWorktree(targetName, branchName, config,
|
|
2334
|
+
const result = await setupWorktree(targetName, branchName, config, baseSpec, jiraKey);
|
|
2241
2335
|
if (!result) {
|
|
2242
2336
|
process.exitCode = 1;
|
|
2243
2337
|
return;
|
|
@@ -6337,9 +6431,9 @@ var hydrateCommand = {
|
|
|
6337
6431
|
};
|
|
6338
6432
|
|
|
6339
6433
|
// src/commands/diff.ts
|
|
6340
|
-
import
|
|
6434
|
+
import fs36 from "fs";
|
|
6341
6435
|
import os9 from "os";
|
|
6342
|
-
import
|
|
6436
|
+
import path33 from "path";
|
|
6343
6437
|
import { spawn as childSpawn } from "child_process";
|
|
6344
6438
|
import chalk21 from "chalk";
|
|
6345
6439
|
|
|
@@ -7041,11 +7135,12 @@ function resolveBase(scope, argv) {
|
|
|
7041
7135
|
}
|
|
7042
7136
|
return { base: "HEAD", source: "default" };
|
|
7043
7137
|
}
|
|
7044
|
-
function buildRepoSpecs(scope, base) {
|
|
7138
|
+
function buildRepoSpecs(scope, base, perRepoBase) {
|
|
7045
7139
|
return scope.repos.map((r) => {
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7140
|
+
const repoBase = perRepoBase?.[r.root] ?? base;
|
|
7141
|
+
let diffArg = repoBase;
|
|
7142
|
+
if (repoBase !== "HEAD") {
|
|
7143
|
+
const mb = git(["merge-base", repoBase, "HEAD"], r.root);
|
|
7049
7144
|
if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;
|
|
7050
7145
|
}
|
|
7051
7146
|
return { name: r.name, root: r.root, diffArg };
|
|
@@ -7062,6 +7157,17 @@ function resolveRepoDiff(root, base, sessionBaseBranch) {
|
|
|
7062
7157
|
if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;
|
|
7063
7158
|
return { resolvedBase: parent, diffArg };
|
|
7064
7159
|
}
|
|
7160
|
+
function sessionBaseForPath(root) {
|
|
7161
|
+
const norm = normPath(root);
|
|
7162
|
+
for (const s of loadHistory()) {
|
|
7163
|
+
for (const p of s.paths) {
|
|
7164
|
+
if (normPath(p) === norm) {
|
|
7165
|
+
return s.baseBranches?.[p] ?? s.baseBranch;
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7169
|
+
return void 0;
|
|
7170
|
+
}
|
|
7065
7171
|
|
|
7066
7172
|
// src/core/comment-server.ts
|
|
7067
7173
|
import { Hono as Hono2 } from "hono";
|
|
@@ -7149,9 +7255,56 @@ import { Hono } from "hono";
|
|
|
7149
7255
|
import { serve } from "@hono/node-server";
|
|
7150
7256
|
import { streamSSE } from "hono/streaming";
|
|
7151
7257
|
|
|
7152
|
-
// src/core/
|
|
7258
|
+
// src/core/file-context.ts
|
|
7153
7259
|
import fs31 from "fs";
|
|
7154
7260
|
import path28 from "path";
|
|
7261
|
+
import spawn10 from "cross-spawn";
|
|
7262
|
+
function readContextLines(opts) {
|
|
7263
|
+
const { root, relPath, ref } = opts;
|
|
7264
|
+
if (!relPath || !isInsideRoot(root, relPath)) return null;
|
|
7265
|
+
const start = Math.max(1, Math.floor(opts.start));
|
|
7266
|
+
const end = Math.max(start, Math.floor(opts.end));
|
|
7267
|
+
const content = !ref || ref === "working" ? readWorkingTree(root, relPath) : readAtRef(root, ref, relPath);
|
|
7268
|
+
if (content === null) return null;
|
|
7269
|
+
const all = content.split(/\r?\n/);
|
|
7270
|
+
if (all.length > 0 && all[all.length - 1] === "") all.pop();
|
|
7271
|
+
const totalLines = all.length;
|
|
7272
|
+
const slice = all.slice(start - 1, end);
|
|
7273
|
+
return {
|
|
7274
|
+
lines: slice,
|
|
7275
|
+
start,
|
|
7276
|
+
totalLines,
|
|
7277
|
+
eof: end >= totalLines
|
|
7278
|
+
};
|
|
7279
|
+
}
|
|
7280
|
+
function readWorkingTree(root, relPath) {
|
|
7281
|
+
try {
|
|
7282
|
+
const absPath = path28.join(root, relPath);
|
|
7283
|
+
const realRoot2 = fs31.realpathSync(path28.resolve(root));
|
|
7284
|
+
const realPath = fs31.realpathSync(absPath);
|
|
7285
|
+
const sep = path28.sep;
|
|
7286
|
+
if (realPath !== realRoot2 && !realPath.startsWith(realRoot2 + sep)) {
|
|
7287
|
+
return null;
|
|
7288
|
+
}
|
|
7289
|
+
return fs31.readFileSync(absPath, "utf-8");
|
|
7290
|
+
} catch {
|
|
7291
|
+
return null;
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7294
|
+
function readAtRef(root, ref, relPath) {
|
|
7295
|
+
const r = spawn10.sync("git", ["show", `${ref}:${relPath}`], {
|
|
7296
|
+
cwd: root,
|
|
7297
|
+
encoding: "utf-8",
|
|
7298
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
7299
|
+
windowsHide: true
|
|
7300
|
+
});
|
|
7301
|
+
if (r.status === 0 && typeof r.stdout === "string") return r.stdout;
|
|
7302
|
+
return null;
|
|
7303
|
+
}
|
|
7304
|
+
|
|
7305
|
+
// src/core/fs-watcher.ts
|
|
7306
|
+
import fs32 from "fs";
|
|
7307
|
+
import path29 from "path";
|
|
7155
7308
|
import chalk19 from "chalk";
|
|
7156
7309
|
import chokidar from "chokidar";
|
|
7157
7310
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -7177,7 +7330,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
7177
7330
|
]);
|
|
7178
7331
|
function isIgnoredWatchPath(roots, filePath) {
|
|
7179
7332
|
for (const root of roots) {
|
|
7180
|
-
const rel =
|
|
7333
|
+
const rel = path29.relative(root, filePath).replace(/\\/g, "/");
|
|
7181
7334
|
if (rel === "" || rel.startsWith("../")) continue;
|
|
7182
7335
|
if (rel.split("/").some((seg) => IGNORED_DIRS.has(seg))) return true;
|
|
7183
7336
|
}
|
|
@@ -7201,8 +7354,8 @@ function createFsWatcher(opts) {
|
|
|
7201
7354
|
};
|
|
7202
7355
|
if (SUPPORTS_RECURSIVE_WATCH) {
|
|
7203
7356
|
const watchers = opts.roots.map((root) => {
|
|
7204
|
-
const w =
|
|
7205
|
-
if (filename && isIgnoredWatchPath([root],
|
|
7357
|
+
const w = fs32.watch(root, { recursive: true }, (_event, filename) => {
|
|
7358
|
+
if (filename && isIgnoredWatchPath([root], path29.join(root, filename.toString()))) {
|
|
7206
7359
|
return;
|
|
7207
7360
|
}
|
|
7208
7361
|
fire();
|
|
@@ -7240,30 +7393,30 @@ function createFsWatcher(opts) {
|
|
|
7240
7393
|
}
|
|
7241
7394
|
|
|
7242
7395
|
// src/core/web-static.ts
|
|
7243
|
-
import
|
|
7244
|
-
import
|
|
7396
|
+
import fs33 from "fs";
|
|
7397
|
+
import path30 from "path";
|
|
7245
7398
|
import { fileURLToPath } from "url";
|
|
7246
7399
|
function resolveWebRoot() {
|
|
7247
|
-
const entryDir =
|
|
7248
|
-
const moduleDir =
|
|
7400
|
+
const entryDir = path30.dirname(process.argv[1] ?? "");
|
|
7401
|
+
const moduleDir = path30.dirname(fileURLToPath(import.meta.url));
|
|
7249
7402
|
const candidates = [
|
|
7250
|
-
|
|
7403
|
+
path30.join(entryDir, "web"),
|
|
7251
7404
|
// bundled: this module is inlined into dist/<bin>.js, so dist/web is a
|
|
7252
7405
|
// sibling of the bundle. Works even when argv[1] is an npm bin symlink
|
|
7253
7406
|
// (which is not realpath'd, so the entryDir candidate above misses).
|
|
7254
|
-
|
|
7407
|
+
path30.join(moduleDir, "web"),
|
|
7255
7408
|
// dev/tsx fallback: walk up from src/core to repo root then into dist/web.
|
|
7256
|
-
|
|
7409
|
+
path30.resolve(moduleDir, "../../dist/web")
|
|
7257
7410
|
];
|
|
7258
7411
|
for (const c of candidates) {
|
|
7259
|
-
if (
|
|
7412
|
+
if (fs33.existsSync(path30.join(c, "index.html"))) return c;
|
|
7260
7413
|
}
|
|
7261
7414
|
return null;
|
|
7262
7415
|
}
|
|
7263
7416
|
|
|
7264
7417
|
// src/core/spa-handler.ts
|
|
7265
|
-
import
|
|
7266
|
-
import
|
|
7418
|
+
import fs34 from "fs";
|
|
7419
|
+
import path31 from "path";
|
|
7267
7420
|
var MIME = {
|
|
7268
7421
|
".html": "text/html; charset=utf-8",
|
|
7269
7422
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -7277,18 +7430,18 @@ var MIME = {
|
|
|
7277
7430
|
function readFile(root, relPath) {
|
|
7278
7431
|
const clean = relPath.split("?")[0];
|
|
7279
7432
|
const requested = clean === "/" ? "/index.html" : clean;
|
|
7280
|
-
const filePath =
|
|
7281
|
-
const norm =
|
|
7282
|
-
if (!norm.startsWith(
|
|
7433
|
+
const filePath = path31.join(root, requested);
|
|
7434
|
+
const norm = path31.normalize(filePath);
|
|
7435
|
+
if (!norm.startsWith(path31.normalize(root))) return null;
|
|
7283
7436
|
let stat;
|
|
7284
7437
|
try {
|
|
7285
|
-
stat =
|
|
7438
|
+
stat = fs34.statSync(norm);
|
|
7286
7439
|
} catch {
|
|
7287
7440
|
return null;
|
|
7288
7441
|
}
|
|
7289
7442
|
if (!stat.isFile()) return null;
|
|
7290
|
-
const ext =
|
|
7291
|
-
return { body:
|
|
7443
|
+
const ext = path31.extname(norm).toLowerCase();
|
|
7444
|
+
return { body: fs34.readFileSync(norm), ext };
|
|
7292
7445
|
}
|
|
7293
7446
|
function serveSpa(c, webRoot) {
|
|
7294
7447
|
const url = new URL(c.req.url);
|
|
@@ -7344,7 +7497,11 @@ async function startDiffServer(opts) {
|
|
|
7344
7497
|
const base = c.req.query("base") === "branch" ? "branch" : "uncommitted";
|
|
7345
7498
|
try {
|
|
7346
7499
|
const resolved = opts.repos.map(
|
|
7347
|
-
(r) => base === "uncommitted" ? { resolvedBase: "HEAD", diffArg: r.diffArg } : resolveRepoDiff(
|
|
7500
|
+
(r) => base === "uncommitted" ? { resolvedBase: "HEAD", diffArg: r.diffArg } : resolveRepoDiff(
|
|
7501
|
+
r.root,
|
|
7502
|
+
"branch",
|
|
7503
|
+
opts.sessionBaseBranches?.[r.root] ?? opts.sessionBaseBranch
|
|
7504
|
+
)
|
|
7348
7505
|
);
|
|
7349
7506
|
const repos = opts.repos.map((r, i) => ({
|
|
7350
7507
|
name: r.name,
|
|
@@ -7358,6 +7515,21 @@ async function startDiffServer(opts) {
|
|
|
7358
7515
|
return c.json({ error: err.message }, 500);
|
|
7359
7516
|
}
|
|
7360
7517
|
});
|
|
7518
|
+
app.get("/api/file-lines", (c) => {
|
|
7519
|
+
const repoName = c.req.query("repo") ?? "";
|
|
7520
|
+
const relPath = c.req.query("path") ?? "";
|
|
7521
|
+
const start = Number(c.req.query("start"));
|
|
7522
|
+
const end = Number(c.req.query("end"));
|
|
7523
|
+
const ref = c.req.query("ref") || void 0;
|
|
7524
|
+
if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
7525
|
+
return c.json({ error: "bad path/start/end" }, 400);
|
|
7526
|
+
}
|
|
7527
|
+
const root = opts.repos.length === 1 ? opts.repos[0].root : opts.repos.find((r) => r.name === repoName)?.root;
|
|
7528
|
+
if (!root) return c.json({ error: "unknown repo" }, 404);
|
|
7529
|
+
const result = readContextLines({ root, relPath, start, end, ref });
|
|
7530
|
+
if (!result) return c.json({ error: "cannot read file" }, 400);
|
|
7531
|
+
return c.json(result);
|
|
7532
|
+
});
|
|
7361
7533
|
app.get(
|
|
7362
7534
|
"/events",
|
|
7363
7535
|
(c) => streamSSE(c, async (stream) => {
|
|
@@ -7520,6 +7692,7 @@ async function startCommentServer(opts) {
|
|
|
7520
7692
|
repos: opts.repos,
|
|
7521
7693
|
scopeLabel: opts.scopeLabel,
|
|
7522
7694
|
sessionBaseBranch: opts.sessionBaseBranch,
|
|
7695
|
+
sessionBaseBranches: opts.sessionBaseBranches,
|
|
7523
7696
|
watchDebounceMs: opts.watchDebounceMs,
|
|
7524
7697
|
attachRoutes
|
|
7525
7698
|
});
|
|
@@ -7561,8 +7734,8 @@ function diffReviewSnapshot(snapshot, seen) {
|
|
|
7561
7734
|
}
|
|
7562
7735
|
|
|
7563
7736
|
// src/core/static-renderer.ts
|
|
7564
|
-
import
|
|
7565
|
-
import
|
|
7737
|
+
import fs35 from "fs";
|
|
7738
|
+
import path32 from "path";
|
|
7566
7739
|
function buildDiff(specs, resolvedBase) {
|
|
7567
7740
|
return {
|
|
7568
7741
|
repos: specs.map((r) => ({
|
|
@@ -7578,8 +7751,8 @@ function renderStatic(opts) {
|
|
|
7578
7751
|
if (!webRoot) {
|
|
7579
7752
|
throw new Error("Could not find dist/web/. Run `npm run build` first.");
|
|
7580
7753
|
}
|
|
7581
|
-
const shellPath =
|
|
7582
|
-
let shell =
|
|
7754
|
+
const shellPath = path32.join(webRoot, "index.html");
|
|
7755
|
+
let shell = fs35.readFileSync(shellPath, "utf-8");
|
|
7583
7756
|
const uncommitted = buildDiff(opts.uncommitted, "HEAD");
|
|
7584
7757
|
const branch = opts.branch ? buildDiff(opts.branch.specs, opts.branch.resolvedBase) : void 0;
|
|
7585
7758
|
const initialBase = opts.initialBase ?? "uncommitted";
|
|
@@ -7628,10 +7801,10 @@ function escapeForScriptTag(json) {
|
|
|
7628
7801
|
}
|
|
7629
7802
|
function readAsset(webRoot, urlPath) {
|
|
7630
7803
|
const clean = urlPath.split("?")[0].replace(/^\//, "");
|
|
7631
|
-
const full =
|
|
7632
|
-
if (!
|
|
7804
|
+
const full = path32.join(webRoot, clean);
|
|
7805
|
+
if (!path32.normalize(full).startsWith(path32.normalize(webRoot))) return null;
|
|
7633
7806
|
try {
|
|
7634
|
-
return
|
|
7807
|
+
return fs35.readFileSync(full, "utf-8");
|
|
7635
7808
|
} catch {
|
|
7636
7809
|
return null;
|
|
7637
7810
|
}
|
|
@@ -7672,22 +7845,22 @@ async function runStop(repoSpecs) {
|
|
|
7672
7845
|
}
|
|
7673
7846
|
}
|
|
7674
7847
|
function webUrlFilePath() {
|
|
7675
|
-
return
|
|
7848
|
+
return path33.join(os9.homedir(), ".work", "web.url");
|
|
7676
7849
|
}
|
|
7677
7850
|
function resolveWorkBinPath(selfArgv1) {
|
|
7678
7851
|
let real = selfArgv1;
|
|
7679
7852
|
try {
|
|
7680
|
-
real =
|
|
7853
|
+
real = fs36.realpathSync(selfArgv1);
|
|
7681
7854
|
} catch {
|
|
7682
7855
|
}
|
|
7683
7856
|
if (real.endsWith("wd-bin.js")) {
|
|
7684
|
-
return
|
|
7857
|
+
return path33.join(path33.dirname(real), "bin.js");
|
|
7685
7858
|
}
|
|
7686
7859
|
return real;
|
|
7687
7860
|
}
|
|
7688
7861
|
function readWebUrl() {
|
|
7689
7862
|
try {
|
|
7690
|
-
const v =
|
|
7863
|
+
const v = fs36.readFileSync(webUrlFilePath(), "utf-8").trim();
|
|
7691
7864
|
return v || null;
|
|
7692
7865
|
} catch {
|
|
7693
7866
|
return null;
|
|
@@ -7697,8 +7870,8 @@ async function ensureWorkWebRunning() {
|
|
|
7697
7870
|
const existing = readWebUrl();
|
|
7698
7871
|
if (existing) return existing;
|
|
7699
7872
|
const workBin = resolveWorkBinPath(process.argv[1]);
|
|
7700
|
-
const out =
|
|
7701
|
-
|
|
7873
|
+
const out = fs36.openSync(
|
|
7874
|
+
path33.join(os9.homedir(), ".work", "web-autostart.log"),
|
|
7702
7875
|
"a"
|
|
7703
7876
|
);
|
|
7704
7877
|
const child = childSpawn(
|
|
@@ -7713,7 +7886,7 @@ async function ensureWorkWebRunning() {
|
|
|
7713
7886
|
}
|
|
7714
7887
|
);
|
|
7715
7888
|
child.unref();
|
|
7716
|
-
|
|
7889
|
+
fs36.closeSync(out);
|
|
7717
7890
|
const url = await waitForUrlFile(webUrlFilePath(), 5e3);
|
|
7718
7891
|
return url;
|
|
7719
7892
|
}
|
|
@@ -7757,9 +7930,10 @@ async function runLauncher(ctx) {
|
|
|
7757
7930
|
function runStatic(ctx, initialBranch) {
|
|
7758
7931
|
const uncommitted = buildRepoSpecs(ctx.scope, "HEAD");
|
|
7759
7932
|
const primaryRoot = ctx.scope.repos.find((r) => r.name === ctx.scope.activeRepoName)?.root ?? ctx.scope.repos[0].root;
|
|
7760
|
-
const
|
|
7933
|
+
const perRepoBase = ctx.scope.session?.baseBranches;
|
|
7934
|
+
const parent = perRepoBase?.[primaryRoot] ?? ctx.scope.session?.baseBranch ?? findAnyParentBranch(primaryRoot);
|
|
7761
7935
|
const branch = parent === null ? void 0 : {
|
|
7762
|
-
specs: buildRepoSpecs(ctx.scope, parent),
|
|
7936
|
+
specs: buildRepoSpecs(ctx.scope, parent, perRepoBase),
|
|
7763
7937
|
resolvedBase: parent
|
|
7764
7938
|
};
|
|
7765
7939
|
const uncommittedTotal = uncommitted.reduce(
|
|
@@ -7781,7 +7955,7 @@ function runStatic(ctx, initialBranch) {
|
|
|
7781
7955
|
initialBase: initialBranch && branch ? "branch" : "uncommitted"
|
|
7782
7956
|
});
|
|
7783
7957
|
const filePath = `${ctx.scopeStem}.html`;
|
|
7784
|
-
|
|
7958
|
+
fs36.writeFileSync(filePath, html, "utf-8");
|
|
7785
7959
|
info(chalk21.gray(`Wrote ${filePath}`));
|
|
7786
7960
|
openUrl(`file:///${filePath.replace(/\\/g, "/")}`);
|
|
7787
7961
|
}
|
|
@@ -7790,7 +7964,7 @@ function waitForUrlFile(filePath, timeoutMs) {
|
|
|
7790
7964
|
const start = Date.now();
|
|
7791
7965
|
const tick = () => {
|
|
7792
7966
|
try {
|
|
7793
|
-
const v =
|
|
7967
|
+
const v = fs36.readFileSync(filePath, "utf-8").trim();
|
|
7794
7968
|
if (v) return resolve(v);
|
|
7795
7969
|
} catch {
|
|
7796
7970
|
}
|
|
@@ -7930,6 +8104,7 @@ summary-id: ${info3.summary.id}` : ""}
|
|
|
7930
8104
|
repos: ctx.repoSpecs,
|
|
7931
8105
|
scopeLabel: ctx.scopeLabel,
|
|
7932
8106
|
sessionBaseBranch: ctx.scope.session?.baseBranch,
|
|
8107
|
+
sessionBaseBranches: ctx.scope.session?.baseBranches,
|
|
7933
8108
|
onComment,
|
|
7934
8109
|
onCommentDeleted,
|
|
7935
8110
|
onSubmitReviewStart,
|
|
@@ -8036,21 +8211,21 @@ var diffCommand = {
|
|
|
8036
8211
|
};
|
|
8037
8212
|
|
|
8038
8213
|
// src/commands/web.ts
|
|
8039
|
-
import
|
|
8214
|
+
import fs41 from "fs";
|
|
8040
8215
|
import os13 from "os";
|
|
8041
|
-
import
|
|
8216
|
+
import path42 from "path";
|
|
8042
8217
|
import chalk23 from "chalk";
|
|
8043
8218
|
|
|
8044
8219
|
// src/core/web-server.ts
|
|
8045
|
-
import
|
|
8220
|
+
import fs40 from "fs";
|
|
8046
8221
|
import os12 from "os";
|
|
8047
|
-
import
|
|
8222
|
+
import path41 from "path";
|
|
8048
8223
|
import chalk22 from "chalk";
|
|
8049
8224
|
import { Hono as Hono3 } from "hono";
|
|
8050
8225
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
8051
8226
|
|
|
8052
8227
|
// src/core/web-state.ts
|
|
8053
|
-
import
|
|
8228
|
+
import path34 from "path";
|
|
8054
8229
|
import crypto4 from "crypto";
|
|
8055
8230
|
import chokidar2 from "chokidar";
|
|
8056
8231
|
function sessionIdFor(s) {
|
|
@@ -8071,7 +8246,7 @@ function subscribeSession(sessionId, onChange) {
|
|
|
8071
8246
|
const watcher = chokidar2.watch(roots, {
|
|
8072
8247
|
ignored: (filePath) => {
|
|
8073
8248
|
for (const r of roots) {
|
|
8074
|
-
const rel =
|
|
8249
|
+
const rel = path34.relative(r, filePath).replace(/\\/g, "/");
|
|
8075
8250
|
if (rel === ".git" || rel.startsWith(".git/")) return true;
|
|
8076
8251
|
}
|
|
8077
8252
|
return false;
|
|
@@ -8185,24 +8360,24 @@ function disposeAllPtys() {
|
|
|
8185
8360
|
}
|
|
8186
8361
|
|
|
8187
8362
|
// src/core/comment-file-store.ts
|
|
8188
|
-
import
|
|
8189
|
-
import
|
|
8363
|
+
import fs37 from "fs";
|
|
8364
|
+
import path35 from "path";
|
|
8190
8365
|
import os10 from "os";
|
|
8191
8366
|
function commentsDir() {
|
|
8192
|
-
return
|
|
8367
|
+
return path35.join(os10.homedir(), ".work", "comments");
|
|
8193
8368
|
}
|
|
8194
8369
|
function ensureDir() {
|
|
8195
|
-
|
|
8370
|
+
fs37.mkdirSync(commentsDir(), { recursive: true });
|
|
8196
8371
|
}
|
|
8197
8372
|
function commentsFileFor(sessionId) {
|
|
8198
|
-
return
|
|
8373
|
+
return path35.join(commentsDir(), `${sessionId}.json`);
|
|
8199
8374
|
}
|
|
8200
8375
|
function pathFor(sessionId) {
|
|
8201
8376
|
return commentsFileFor(sessionId);
|
|
8202
8377
|
}
|
|
8203
8378
|
function readDisk(sessionId) {
|
|
8204
8379
|
try {
|
|
8205
|
-
const raw =
|
|
8380
|
+
const raw = fs37.readFileSync(pathFor(sessionId), "utf-8");
|
|
8206
8381
|
const parsed = JSON.parse(raw);
|
|
8207
8382
|
return Array.isArray(parsed) ? parsed : [];
|
|
8208
8383
|
} catch {
|
|
@@ -8274,29 +8449,29 @@ function clearCommentStoreCache() {
|
|
|
8274
8449
|
}
|
|
8275
8450
|
|
|
8276
8451
|
// src/core/pending-delivery.ts
|
|
8277
|
-
import
|
|
8278
|
-
import
|
|
8452
|
+
import fs38 from "fs";
|
|
8453
|
+
import path36 from "path";
|
|
8279
8454
|
function pathFor2(sessionId) {
|
|
8280
8455
|
return {
|
|
8281
8456
|
comments: commentsFileFor(sessionId),
|
|
8282
|
-
delivered:
|
|
8457
|
+
delivered: path36.join(commentsDir(), `${sessionId}.delivered.json`)
|
|
8283
8458
|
};
|
|
8284
8459
|
}
|
|
8285
8460
|
function readJson(filePath, fallback) {
|
|
8286
8461
|
try {
|
|
8287
|
-
return JSON.parse(
|
|
8462
|
+
return JSON.parse(fs38.readFileSync(filePath, "utf8"));
|
|
8288
8463
|
} catch {
|
|
8289
8464
|
return fallback;
|
|
8290
8465
|
}
|
|
8291
8466
|
}
|
|
8292
8467
|
function writeAtomic2(filePath, content) {
|
|
8293
|
-
|
|
8468
|
+
fs38.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
8294
8469
|
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
8295
|
-
|
|
8296
|
-
|
|
8470
|
+
fs38.writeFileSync(tmp, content, "utf-8");
|
|
8471
|
+
fs38.renameSync(tmp, filePath);
|
|
8297
8472
|
}
|
|
8298
8473
|
function normalize(p) {
|
|
8299
|
-
return
|
|
8474
|
+
return path36.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
8300
8475
|
}
|
|
8301
8476
|
function findSessionForCwd(cwd) {
|
|
8302
8477
|
const norm = normalize(cwd);
|
|
@@ -8327,7 +8502,7 @@ function readPendingForSession(sessionId) {
|
|
|
8327
8502
|
return comments.filter(isPendingFor(delivered));
|
|
8328
8503
|
}
|
|
8329
8504
|
function scopeStoreIdsForPaths(paths) {
|
|
8330
|
-
const resolved = paths.map((p) =>
|
|
8505
|
+
const resolved = paths.map((p) => path36.resolve(p));
|
|
8331
8506
|
const ids = /* @__PURE__ */ new Set();
|
|
8332
8507
|
ids.add(`scope-${scopeHashFor(resolved)}`);
|
|
8333
8508
|
for (const p of resolved) ids.add(`scope-${scopeHashFor([p])}`);
|
|
@@ -8650,8 +8825,8 @@ function mountPanesRoutes(app, opts) {
|
|
|
8650
8825
|
}
|
|
8651
8826
|
|
|
8652
8827
|
// src/core/worktree-routes.ts
|
|
8653
|
-
import
|
|
8654
|
-
import { spawn as
|
|
8828
|
+
import path37 from "path";
|
|
8829
|
+
import { spawn as spawn11 } from "child_process";
|
|
8655
8830
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
8656
8831
|
import { z as z3 } from "zod";
|
|
8657
8832
|
function mountWorktreeRoutes(app, opts) {
|
|
@@ -8774,10 +8949,10 @@ function mountWorktreeRoutes(app, opts) {
|
|
|
8774
8949
|
const id = c.req.param("id");
|
|
8775
8950
|
const session = findSession2(id);
|
|
8776
8951
|
if (!session) return c.json({ error: "unknown session" }, 404);
|
|
8777
|
-
const target = session.isGroup ?
|
|
8952
|
+
const target = session.isGroup ? path37.dirname(session.paths[0]) : session.paths[0];
|
|
8778
8953
|
try {
|
|
8779
8954
|
const cmd = process.platform === "win32" ? "code.cmd" : "code";
|
|
8780
|
-
const child =
|
|
8955
|
+
const child = spawn11(cmd, [target], {
|
|
8781
8956
|
detached: true,
|
|
8782
8957
|
stdio: "ignore",
|
|
8783
8958
|
shell: false
|
|
@@ -8794,27 +8969,27 @@ function mountWorktreeRoutes(app, opts) {
|
|
|
8794
8969
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
8795
8970
|
import { z as z4 } from "zod";
|
|
8796
8971
|
import { EventEmitter } from "events";
|
|
8797
|
-
import
|
|
8798
|
-
import
|
|
8972
|
+
import path40 from "path";
|
|
8973
|
+
import spawn13 from "cross-spawn";
|
|
8799
8974
|
|
|
8800
8975
|
// src/core/checkpoint.ts
|
|
8801
|
-
import
|
|
8976
|
+
import fs39 from "fs";
|
|
8802
8977
|
import os11 from "os";
|
|
8803
|
-
import
|
|
8804
|
-
import
|
|
8978
|
+
import path38 from "path";
|
|
8979
|
+
import spawn12 from "cross-spawn";
|
|
8805
8980
|
function manifestPath(scopeHash) {
|
|
8806
|
-
const dir =
|
|
8807
|
-
|
|
8808
|
-
return
|
|
8981
|
+
const dir = path38.join(os11.homedir(), ".work", "diffs");
|
|
8982
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
8983
|
+
return path38.join(dir, `${scopeHash}.checkpoints.json`);
|
|
8809
8984
|
}
|
|
8810
8985
|
function emptyManifest(scopeHash) {
|
|
8811
8986
|
return { version: 1, scopeHash, entries: [] };
|
|
8812
8987
|
}
|
|
8813
8988
|
function loadManifest(scopeHash) {
|
|
8814
8989
|
const file = manifestPath(scopeHash);
|
|
8815
|
-
if (!
|
|
8990
|
+
if (!fs39.existsSync(file)) return emptyManifest(scopeHash);
|
|
8816
8991
|
try {
|
|
8817
|
-
const raw =
|
|
8992
|
+
const raw = fs39.readFileSync(file, "utf-8");
|
|
8818
8993
|
const parsed = JSON.parse(raw);
|
|
8819
8994
|
if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
|
|
8820
8995
|
return emptyManifest(scopeHash);
|
|
@@ -8843,7 +9018,7 @@ function snapshotRepo(repoRoot, scopeHash, id, includeWorkingTree = true) {
|
|
|
8843
9018
|
GIT_COMMITTER_EMAIL: "wd@local",
|
|
8844
9019
|
GIT_COMMITTER_DATE: "2000-01-01T00:00:00Z"
|
|
8845
9020
|
};
|
|
8846
|
-
const commit =
|
|
9021
|
+
const commit = spawn12.sync("git", commitArgs, {
|
|
8847
9022
|
cwd: repoRoot,
|
|
8848
9023
|
encoding: "utf-8",
|
|
8849
9024
|
env: commitEnv,
|
|
@@ -8852,7 +9027,7 @@ function snapshotRepo(repoRoot, scopeHash, id, includeWorkingTree = true) {
|
|
|
8852
9027
|
if (commit.status !== 0 || !commit.stdout) return null;
|
|
8853
9028
|
const commitSha = commit.stdout.trim();
|
|
8854
9029
|
const refName = `refs/wd/${scopeHash}/${id}`;
|
|
8855
|
-
const updateRef =
|
|
9030
|
+
const updateRef = spawn12.sync("git", ["update-ref", refName, commitSha], {
|
|
8856
9031
|
cwd: repoRoot,
|
|
8857
9032
|
encoding: "utf-8",
|
|
8858
9033
|
windowsHide: true
|
|
@@ -8879,7 +9054,7 @@ async function takeCheckpoint(scopeHash, repos, opts = {}) {
|
|
|
8879
9054
|
const rollbackRefs = () => {
|
|
8880
9055
|
const refName = `refs/wd/${scopeHash}/${nextId}`;
|
|
8881
9056
|
for (const repo of repos) {
|
|
8882
|
-
|
|
9057
|
+
spawn12.sync("git", ["update-ref", "-d", refName], {
|
|
8883
9058
|
cwd: repo.root,
|
|
8884
9059
|
encoding: "utf-8",
|
|
8885
9060
|
windowsHide: true
|
|
@@ -8894,7 +9069,7 @@ async function takeCheckpoint(scopeHash, repos, opts = {}) {
|
|
|
8894
9069
|
const prev = manifest.entries[manifest.entries.length - 1];
|
|
8895
9070
|
const treeOf = (root, commitSha) => {
|
|
8896
9071
|
if (!commitSha) return null;
|
|
8897
|
-
const r =
|
|
9072
|
+
const r = spawn12.sync(
|
|
8898
9073
|
"git",
|
|
8899
9074
|
["rev-parse", `${commitSha}^{tree}`],
|
|
8900
9075
|
{ cwd: root, encoding: "utf-8", windowsHide: true }
|
|
@@ -8929,7 +9104,7 @@ function clearCheckpoints(scopeHash, repoRoots) {
|
|
|
8929
9104
|
for (const root of repoRoots) {
|
|
8930
9105
|
for (const entry of manifest.entries) {
|
|
8931
9106
|
const refName = `refs/wd/${scopeHash}/${entry.id}`;
|
|
8932
|
-
|
|
9107
|
+
spawn12.sync("git", ["update-ref", "-d", refName], {
|
|
8933
9108
|
cwd: root,
|
|
8934
9109
|
encoding: "utf-8",
|
|
8935
9110
|
windowsHide: true
|
|
@@ -8937,16 +9112,16 @@ function clearCheckpoints(scopeHash, repoRoots) {
|
|
|
8937
9112
|
}
|
|
8938
9113
|
}
|
|
8939
9114
|
const file = manifestPath(scopeHash);
|
|
8940
|
-
if (
|
|
9115
|
+
if (fs39.existsSync(file)) {
|
|
8941
9116
|
try {
|
|
8942
|
-
|
|
9117
|
+
fs39.unlinkSync(file);
|
|
8943
9118
|
} catch {
|
|
8944
9119
|
}
|
|
8945
9120
|
}
|
|
8946
9121
|
}
|
|
8947
9122
|
|
|
8948
9123
|
// src/core/scope-manager.ts
|
|
8949
|
-
import
|
|
9124
|
+
import path39 from "path";
|
|
8950
9125
|
import crypto5 from "crypto";
|
|
8951
9126
|
var scopes = /* @__PURE__ */ new Map();
|
|
8952
9127
|
function hashFor(paths) {
|
|
@@ -8963,7 +9138,7 @@ var ScopePathRejectedError = class extends Error {
|
|
|
8963
9138
|
}
|
|
8964
9139
|
};
|
|
8965
9140
|
function normaliseForCompare(p) {
|
|
8966
|
-
return
|
|
9141
|
+
return path39.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
8967
9142
|
}
|
|
8968
9143
|
function rejectedPaths(normalised) {
|
|
8969
9144
|
const config = loadConfig();
|
|
@@ -8981,7 +9156,7 @@ function rejectedPaths(normalised) {
|
|
|
8981
9156
|
});
|
|
8982
9157
|
}
|
|
8983
9158
|
function registerScope(paths, label2) {
|
|
8984
|
-
const normalised = paths.map((p) =>
|
|
9159
|
+
const normalised = paths.map((p) => path39.resolve(p));
|
|
8985
9160
|
const rejected = rejectedPaths(normalised);
|
|
8986
9161
|
if (rejected.length > 0) throw new ScopePathRejectedError(rejected);
|
|
8987
9162
|
const hash = hashFor(normalised);
|
|
@@ -8995,7 +9170,7 @@ function registerScope(paths, label2) {
|
|
|
8995
9170
|
const scope = {
|
|
8996
9171
|
hash,
|
|
8997
9172
|
paths: normalised,
|
|
8998
|
-
label: label2 ??
|
|
9173
|
+
label: label2 ?? path39.basename(normalised[0]),
|
|
8999
9174
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9000
9175
|
ended: false
|
|
9001
9176
|
};
|
|
@@ -9068,7 +9243,7 @@ function mountScopeRoutes(app, opts) {
|
|
|
9068
9243
|
function workingTreeFingerprint(paths) {
|
|
9069
9244
|
const parts = [];
|
|
9070
9245
|
for (const p of paths) {
|
|
9071
|
-
const r =
|
|
9246
|
+
const r = spawn13.sync(
|
|
9072
9247
|
"git",
|
|
9073
9248
|
["status", "--porcelain", "--no-renames", "-z"],
|
|
9074
9249
|
{ cwd: p, encoding: "utf-8", windowsHide: true }
|
|
@@ -9192,7 +9367,7 @@ function mountScopeRoutes(app, opts) {
|
|
|
9192
9367
|
const fromSha = fromEntry.repos[p] ?? "HEAD";
|
|
9193
9368
|
const toSha = toEntry === void 0 ? "working" : toEntry.repos[p] ?? "HEAD";
|
|
9194
9369
|
return {
|
|
9195
|
-
name:
|
|
9370
|
+
name: path40.basename(p),
|
|
9196
9371
|
root: p,
|
|
9197
9372
|
files: computeRangeDiff({ root: p, fromRef: fromSha, toRef: toSha })
|
|
9198
9373
|
};
|
|
@@ -9206,9 +9381,11 @@ function mountScopeRoutes(app, opts) {
|
|
|
9206
9381
|
repos: repos2
|
|
9207
9382
|
});
|
|
9208
9383
|
}
|
|
9209
|
-
const resolved = scope.paths.map(
|
|
9384
|
+
const resolved = scope.paths.map(
|
|
9385
|
+
(p) => resolveRepoDiff(p, base, sessionBaseForPath(p))
|
|
9386
|
+
);
|
|
9210
9387
|
const repos = scope.paths.map((p, i) => ({
|
|
9211
|
-
name:
|
|
9388
|
+
name: path40.basename(p),
|
|
9212
9389
|
root: p,
|
|
9213
9390
|
resolvedBase: resolved[i].resolvedBase,
|
|
9214
9391
|
files: computeDiff({ root: p, diffArg: resolved[i].diffArg })
|
|
@@ -9227,6 +9404,23 @@ function mountScopeRoutes(app, opts) {
|
|
|
9227
9404
|
return c.json({ error: err.message }, 500);
|
|
9228
9405
|
}
|
|
9229
9406
|
});
|
|
9407
|
+
app.get("/api/scopes/:hash/file-lines", (c) => {
|
|
9408
|
+
const scope = getScope(c.req.param("hash"));
|
|
9409
|
+
if (!scope) return c.json({ error: "unknown scope" }, 404);
|
|
9410
|
+
const repoName = c.req.query("repo") ?? "";
|
|
9411
|
+
const relPath = c.req.query("path") ?? "";
|
|
9412
|
+
const start = Number(c.req.query("start"));
|
|
9413
|
+
const end = Number(c.req.query("end"));
|
|
9414
|
+
const ref = c.req.query("ref") || void 0;
|
|
9415
|
+
if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
9416
|
+
return c.json({ error: "bad path/start/end" }, 400);
|
|
9417
|
+
}
|
|
9418
|
+
const root = scope.paths.length === 1 ? scope.paths[0] : scope.paths.find((p) => path40.basename(p) === repoName);
|
|
9419
|
+
if (!root) return c.json({ error: "unknown repo" }, 404);
|
|
9420
|
+
const result = readContextLines({ root, relPath, start, end, ref });
|
|
9421
|
+
if (!result) return c.json({ error: "cannot read file" }, 400);
|
|
9422
|
+
return c.json(result);
|
|
9423
|
+
});
|
|
9230
9424
|
app.get("/api/scopes/:hash/checkpoints", (c) => {
|
|
9231
9425
|
const scope = getScope(c.req.param("hash"));
|
|
9232
9426
|
if (!scope) return c.json({ error: "unknown scope" }, 404);
|
|
@@ -9455,9 +9649,11 @@ function sessionToWire(s) {
|
|
|
9455
9649
|
};
|
|
9456
9650
|
}
|
|
9457
9651
|
function computeSessionDiff(s, base) {
|
|
9458
|
-
const resolved = s.paths.map(
|
|
9652
|
+
const resolved = s.paths.map(
|
|
9653
|
+
(p) => resolveRepoDiff(p, base, s.baseBranches?.[p] ?? s.baseBranch)
|
|
9654
|
+
);
|
|
9459
9655
|
const repos = s.paths.map((p, i) => ({
|
|
9460
|
-
name:
|
|
9656
|
+
name: path41.basename(p),
|
|
9461
9657
|
root: p,
|
|
9462
9658
|
resolvedBase: resolved[i].resolvedBase,
|
|
9463
9659
|
files: computeDiff({ root: p, diffArg: resolved[i].diffArg })
|
|
@@ -9477,17 +9673,17 @@ async function startWebServer(opts = {}) {
|
|
|
9477
9673
|
for (const cb of sseListeners) cb({ event, data });
|
|
9478
9674
|
};
|
|
9479
9675
|
const home = os12.homedir();
|
|
9480
|
-
const historyPath =
|
|
9676
|
+
const historyPath = path41.join(home, ".work", "history.json");
|
|
9481
9677
|
const onHistoryChange = () => broadcast("sessions-changed", { ts: Date.now() });
|
|
9482
|
-
|
|
9483
|
-
const tasksPath =
|
|
9678
|
+
fs40.watchFile(historyPath, { interval: 1e3 }, onHistoryChange);
|
|
9679
|
+
const tasksPath = path41.join(home, ".work", "tasks.json");
|
|
9484
9680
|
const onTasksChange = () => broadcast("tasks-changed", { ts: Date.now() });
|
|
9485
|
-
|
|
9681
|
+
fs40.watchFile(tasksPath, { interval: 1e3 }, onTasksChange);
|
|
9486
9682
|
const projectsRoot = claudeProjectsRoot();
|
|
9487
9683
|
let activityWatcher = null;
|
|
9488
9684
|
if (!lean) {
|
|
9489
9685
|
try {
|
|
9490
|
-
if (
|
|
9686
|
+
if (fs40.existsSync(projectsRoot)) {
|
|
9491
9687
|
activityWatcher = createFsWatcher({
|
|
9492
9688
|
roots: [projectsRoot],
|
|
9493
9689
|
debounceMs: 250,
|
|
@@ -9562,8 +9758,8 @@ async function startWebServer(opts = {}) {
|
|
|
9562
9758
|
url: handle.url,
|
|
9563
9759
|
port: handle.port,
|
|
9564
9760
|
stop: async () => {
|
|
9565
|
-
|
|
9566
|
-
|
|
9761
|
+
fs40.unwatchFile(historyPath, onHistoryChange);
|
|
9762
|
+
fs40.unwatchFile(tasksPath, onTasksChange);
|
|
9567
9763
|
if (decayTick) clearInterval(decayTick);
|
|
9568
9764
|
activityWatcher?.stop();
|
|
9569
9765
|
disposeAllWatchers();
|
|
@@ -9625,10 +9821,10 @@ function info2(message) {
|
|
|
9625
9821
|
process.stderr.write(message + "\n");
|
|
9626
9822
|
}
|
|
9627
9823
|
function urlFilePath() {
|
|
9628
|
-
return
|
|
9824
|
+
return path42.join(os13.homedir(), ".work", "web.url");
|
|
9629
9825
|
}
|
|
9630
9826
|
function pidFilePath() {
|
|
9631
|
-
return
|
|
9827
|
+
return path42.join(os13.homedir(), ".work", "web.pid");
|
|
9632
9828
|
}
|
|
9633
9829
|
function isPidAlive(pid) {
|
|
9634
9830
|
try {
|
|
@@ -9640,7 +9836,7 @@ function isPidAlive(pid) {
|
|
|
9640
9836
|
}
|
|
9641
9837
|
function readPid() {
|
|
9642
9838
|
try {
|
|
9643
|
-
const raw =
|
|
9839
|
+
const raw = fs41.readFileSync(pidFilePath(), "utf-8").trim();
|
|
9644
9840
|
const n = Number(raw);
|
|
9645
9841
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
9646
9842
|
} catch {
|
|
@@ -9649,7 +9845,7 @@ function readPid() {
|
|
|
9649
9845
|
}
|
|
9650
9846
|
function readUrl() {
|
|
9651
9847
|
try {
|
|
9652
|
-
const v =
|
|
9848
|
+
const v = fs41.readFileSync(urlFilePath(), "utf-8").trim();
|
|
9653
9849
|
return v || null;
|
|
9654
9850
|
} catch {
|
|
9655
9851
|
return null;
|
|
@@ -9675,11 +9871,11 @@ function stopExisting() {
|
|
|
9675
9871
|
if (!isPidAlive(pid)) {
|
|
9676
9872
|
info2(chalk23.gray(`Stale PID ${pid} \u2014 cleaning up.`));
|
|
9677
9873
|
try {
|
|
9678
|
-
|
|
9874
|
+
fs41.unlinkSync(pidFilePath());
|
|
9679
9875
|
} catch {
|
|
9680
9876
|
}
|
|
9681
9877
|
try {
|
|
9682
|
-
|
|
9878
|
+
fs41.unlinkSync(urlFilePath());
|
|
9683
9879
|
} catch {
|
|
9684
9880
|
}
|
|
9685
9881
|
return false;
|
|
@@ -9688,11 +9884,11 @@ function stopExisting() {
|
|
|
9688
9884
|
process.kill(pid);
|
|
9689
9885
|
info2(chalk23.gray(`Stopped work web (PID ${pid}).`));
|
|
9690
9886
|
try {
|
|
9691
|
-
|
|
9887
|
+
fs41.unlinkSync(pidFilePath());
|
|
9692
9888
|
} catch {
|
|
9693
9889
|
}
|
|
9694
9890
|
try {
|
|
9695
|
-
|
|
9891
|
+
fs41.unlinkSync(urlFilePath());
|
|
9696
9892
|
} catch {
|
|
9697
9893
|
}
|
|
9698
9894
|
return true;
|
|
@@ -9743,19 +9939,19 @@ var webCommand = {
|
|
|
9743
9939
|
process.exit(1);
|
|
9744
9940
|
}
|
|
9745
9941
|
try {
|
|
9746
|
-
|
|
9942
|
+
fs41.unlinkSync(pidFilePath());
|
|
9747
9943
|
} catch {
|
|
9748
9944
|
}
|
|
9749
9945
|
try {
|
|
9750
|
-
|
|
9946
|
+
fs41.unlinkSync(urlFilePath());
|
|
9751
9947
|
} catch {
|
|
9752
9948
|
}
|
|
9753
9949
|
const lean = !!argv.lean || process.env.WORK_WEB_LEAN === "1";
|
|
9754
9950
|
const handle = await startWebServer({ lean });
|
|
9755
9951
|
try {
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9952
|
+
fs41.mkdirSync(path42.dirname(urlFilePath()), { recursive: true });
|
|
9953
|
+
fs41.writeFileSync(urlFilePath(), handle.url);
|
|
9954
|
+
fs41.writeFileSync(pidFilePath(), String(process.pid));
|
|
9759
9955
|
} catch {
|
|
9760
9956
|
}
|
|
9761
9957
|
info2(
|
|
@@ -9785,11 +9981,11 @@ var webCommand = {
|
|
|
9785
9981
|
const shutdown = () => {
|
|
9786
9982
|
info2(chalk23.gray("\nStopping work web."));
|
|
9787
9983
|
try {
|
|
9788
|
-
|
|
9984
|
+
fs41.unlinkSync(urlFilePath());
|
|
9789
9985
|
} catch {
|
|
9790
9986
|
}
|
|
9791
9987
|
try {
|
|
9792
|
-
|
|
9988
|
+
fs41.unlinkSync(pidFilePath());
|
|
9793
9989
|
} catch {
|
|
9794
9990
|
}
|
|
9795
9991
|
if (!lean) {
|
|
@@ -9809,11 +10005,11 @@ var webCommand = {
|
|
|
9809
10005
|
process.on("SIGTERM", shutdown);
|
|
9810
10006
|
process.on("exit", () => {
|
|
9811
10007
|
try {
|
|
9812
|
-
|
|
10008
|
+
fs41.unlinkSync(pidFilePath());
|
|
9813
10009
|
} catch {
|
|
9814
10010
|
}
|
|
9815
10011
|
try {
|
|
9816
|
-
|
|
10012
|
+
fs41.unlinkSync(urlFilePath());
|
|
9817
10013
|
} catch {
|
|
9818
10014
|
}
|
|
9819
10015
|
});
|
|
@@ -9888,7 +10084,7 @@ var hookCommand = {
|
|
|
9888
10084
|
|
|
9889
10085
|
// src/commands/run.ts
|
|
9890
10086
|
import chalk24 from "chalk";
|
|
9891
|
-
import
|
|
10087
|
+
import spawn14 from "cross-spawn";
|
|
9892
10088
|
|
|
9893
10089
|
// src/core/fleet.ts
|
|
9894
10090
|
function selectSessions(sessions, filter) {
|
|
@@ -9930,7 +10126,7 @@ function killAllChildren(signal = "SIGTERM") {
|
|
|
9930
10126
|
function runInPath(unit, cmd, prefix) {
|
|
9931
10127
|
const { bin, args } = shellInvocation(cmd);
|
|
9932
10128
|
return new Promise((resolve) => {
|
|
9933
|
-
const child =
|
|
10129
|
+
const child = spawn14(bin, args, {
|
|
9934
10130
|
cwd: unit.path,
|
|
9935
10131
|
stdio: prefix ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
9936
10132
|
shell: false
|
|
@@ -10162,15 +10358,15 @@ var runCommand = {
|
|
|
10162
10358
|
};
|
|
10163
10359
|
|
|
10164
10360
|
// src/commands/broadcast.ts
|
|
10165
|
-
import
|
|
10361
|
+
import fs43 from "fs";
|
|
10166
10362
|
import chalk25 from "chalk";
|
|
10167
10363
|
|
|
10168
10364
|
// src/core/broadcast.ts
|
|
10169
10365
|
import crypto6 from "crypto";
|
|
10170
|
-
import
|
|
10366
|
+
import fs42 from "fs";
|
|
10171
10367
|
function readComments(file) {
|
|
10172
10368
|
try {
|
|
10173
|
-
const parsed = JSON.parse(
|
|
10369
|
+
const parsed = JSON.parse(fs42.readFileSync(file, "utf-8"));
|
|
10174
10370
|
return Array.isArray(parsed) ? parsed : [];
|
|
10175
10371
|
} catch {
|
|
10176
10372
|
return [];
|
|
@@ -10178,7 +10374,7 @@ function readComments(file) {
|
|
|
10178
10374
|
}
|
|
10179
10375
|
async function appendLocked(sessionId, body) {
|
|
10180
10376
|
const file = commentsFileFor(sessionId);
|
|
10181
|
-
|
|
10377
|
+
fs42.mkdirSync(commentsDir(), { recursive: true });
|
|
10182
10378
|
ensureFile(file, "[]");
|
|
10183
10379
|
const comment = {
|
|
10184
10380
|
id: crypto6.randomBytes(8).toString("hex"),
|
|
@@ -10215,7 +10411,7 @@ async function broadcastPrompt(sessions, filter, prompt) {
|
|
|
10215
10411
|
// src/commands/broadcast.ts
|
|
10216
10412
|
function readStdin() {
|
|
10217
10413
|
try {
|
|
10218
|
-
return
|
|
10414
|
+
return fs43.readFileSync(0, "utf-8");
|
|
10219
10415
|
} catch {
|
|
10220
10416
|
return "";
|
|
10221
10417
|
}
|
|
@@ -10290,8 +10486,8 @@ var broadcastCommand = {
|
|
|
10290
10486
|
};
|
|
10291
10487
|
|
|
10292
10488
|
// src/completions/index.ts
|
|
10293
|
-
import
|
|
10294
|
-
import
|
|
10489
|
+
import fs44 from "fs";
|
|
10490
|
+
import path43 from "path";
|
|
10295
10491
|
function completionHandler(current, argv, done) {
|
|
10296
10492
|
const rawArgs = argv._;
|
|
10297
10493
|
const args = rawArgs.slice(1, -1);
|
|
@@ -10395,7 +10591,7 @@ function completeTreeRemoveList(command, args, current, config, done) {
|
|
|
10395
10591
|
}
|
|
10396
10592
|
function completeRepoBranches(alias, current, config, done) {
|
|
10397
10593
|
const repoPath = config.repos[alias];
|
|
10398
|
-
if (!repoPath || !
|
|
10594
|
+
if (!repoPath || !fs44.existsSync(repoPath)) {
|
|
10399
10595
|
done([]);
|
|
10400
10596
|
return;
|
|
10401
10597
|
}
|
|
@@ -10404,19 +10600,19 @@ function completeRepoBranches(alias, current, config, done) {
|
|
|
10404
10600
|
done(branches);
|
|
10405
10601
|
}
|
|
10406
10602
|
function completeGroupBranches(groupName, repoAliases, current, config, done) {
|
|
10407
|
-
const groupDir =
|
|
10408
|
-
if (!
|
|
10603
|
+
const groupDir = path43.join(config.worktreesRoot, groupName);
|
|
10604
|
+
if (!fs44.existsSync(groupDir)) {
|
|
10409
10605
|
done([]);
|
|
10410
10606
|
return;
|
|
10411
10607
|
}
|
|
10412
10608
|
try {
|
|
10413
|
-
const branchDirs =
|
|
10414
|
-
const bdPath =
|
|
10609
|
+
const branchDirs = fs44.readdirSync(groupDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
10610
|
+
const bdPath = path43.join(groupDir, d.name);
|
|
10415
10611
|
for (const alias of repoAliases) {
|
|
10416
10612
|
const repoPath = config.repos[alias];
|
|
10417
10613
|
if (!repoPath) continue;
|
|
10418
|
-
const subPath =
|
|
10419
|
-
if (
|
|
10614
|
+
const subPath = path43.join(bdPath, path43.basename(repoPath));
|
|
10615
|
+
if (fs44.existsSync(subPath)) {
|
|
10420
10616
|
const branch = getCurrentBranch(subPath);
|
|
10421
10617
|
if (branch) return branch;
|
|
10422
10618
|
}
|
|
@@ -10430,7 +10626,7 @@ function completeGroupBranches(groupName, repoAliases, current, config, done) {
|
|
|
10430
10626
|
}
|
|
10431
10627
|
|
|
10432
10628
|
// src/version.ts
|
|
10433
|
-
var VERSION = true ? "1.
|
|
10629
|
+
var VERSION = true ? "1.7.0" : "dev";
|
|
10434
10630
|
|
|
10435
10631
|
// src/cli.ts
|
|
10436
10632
|
function showHelp() {
|
|
@@ -10527,8 +10723,8 @@ function handleFatalError(err) {
|
|
|
10527
10723
|
}
|
|
10528
10724
|
if (err instanceof Error && err.message?.includes("pty that has already exited")) {
|
|
10529
10725
|
try {
|
|
10530
|
-
|
|
10531
|
-
|
|
10726
|
+
fs45.appendFileSync(
|
|
10727
|
+
path44.join(getConfigDir(), "debug.log"),
|
|
10532
10728
|
`${(/* @__PURE__ */ new Date()).toISOString()} [WARN] Ignored async node-pty error: ${err.message}
|
|
10533
10729
|
`
|
|
10534
10730
|
);
|
|
@@ -10538,8 +10734,8 @@ function handleFatalError(err) {
|
|
|
10538
10734
|
}
|
|
10539
10735
|
try {
|
|
10540
10736
|
const msg = err instanceof Error ? err.stack || err.message : String(err);
|
|
10541
|
-
|
|
10542
|
-
|
|
10737
|
+
fs45.appendFileSync(
|
|
10738
|
+
path44.join(getConfigDir(), "debug.log"),
|
|
10543
10739
|
`${(/* @__PURE__ */ new Date()).toISOString()} [FATAL] handleFatalError: ${msg}
|
|
10544
10740
|
`
|
|
10545
10741
|
);
|