@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/wd-bin.js
CHANGED
|
@@ -1573,11 +1573,12 @@ async function upsertSession(target, isGroup, branch, paths, jiraKey, baseBranch
|
|
|
1573
1573
|
saveHistory(sessions);
|
|
1574
1574
|
});
|
|
1575
1575
|
}
|
|
1576
|
-
async function upsertSessionWithPort(target, isGroup, branch, paths, config, jiraKey, baseBranch) {
|
|
1576
|
+
async function upsertSessionWithPort(target, isGroup, branch, paths, config, jiraKey, baseBranch, baseBranches) {
|
|
1577
1577
|
return withHistoryLock(async () => {
|
|
1578
1578
|
const sessions = loadHistory();
|
|
1579
1579
|
const existing = findSession(sessions, target, branch);
|
|
1580
1580
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1581
|
+
const hasPerRepo = baseBranches && Object.keys(baseBranches).length > 0;
|
|
1581
1582
|
let port = existing?.port;
|
|
1582
1583
|
if (port === void 0) {
|
|
1583
1584
|
const seedKey = sessionKey(target, branch);
|
|
@@ -1592,6 +1593,7 @@ async function upsertSessionWithPort(target, isGroup, branch, paths, config, jir
|
|
|
1592
1593
|
existing.lastAccessedAt = now;
|
|
1593
1594
|
if (jiraKey) existing.jiraKey = jiraKey;
|
|
1594
1595
|
if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;
|
|
1596
|
+
if (hasPerRepo && !existing.baseBranches) existing.baseBranches = baseBranches;
|
|
1595
1597
|
if (port !== void 0) existing.port = port;
|
|
1596
1598
|
} else {
|
|
1597
1599
|
const session = {
|
|
@@ -1604,6 +1606,7 @@ async function upsertSessionWithPort(target, isGroup, branch, paths, config, jir
|
|
|
1604
1606
|
};
|
|
1605
1607
|
if (jiraKey) session.jiraKey = jiraKey;
|
|
1606
1608
|
if (baseBranch) session.baseBranch = baseBranch;
|
|
1609
|
+
if (hasPerRepo) session.baseBranches = baseBranches;
|
|
1607
1610
|
if (port !== void 0) session.port = port;
|
|
1608
1611
|
sessions.push(session);
|
|
1609
1612
|
}
|
|
@@ -1719,6 +1722,62 @@ function copyConfigFiles(repoPath, worktreePath, patterns) {
|
|
|
1719
1722
|
}
|
|
1720
1723
|
}
|
|
1721
1724
|
|
|
1725
|
+
// src/core/base-spec.ts
|
|
1726
|
+
var BaseSpecError = class extends Error {
|
|
1727
|
+
constructor(message) {
|
|
1728
|
+
super(message);
|
|
1729
|
+
this.name = "BaseSpecError";
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
function parseBaseSpec(raw) {
|
|
1733
|
+
const spec = { perRepo: {} };
|
|
1734
|
+
if (raw === void 0) return spec;
|
|
1735
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
1736
|
+
for (const value of values) {
|
|
1737
|
+
const v = value.trim();
|
|
1738
|
+
if (!v) continue;
|
|
1739
|
+
const eq = v.indexOf("=");
|
|
1740
|
+
if (eq === -1) {
|
|
1741
|
+
if (spec.default !== void 0 && spec.default !== v) {
|
|
1742
|
+
throw new BaseSpecError(
|
|
1743
|
+
`Conflicting default --base values: '${spec.default}' and '${v}'`
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
spec.default = v;
|
|
1747
|
+
} else {
|
|
1748
|
+
const alias = v.slice(0, eq).trim();
|
|
1749
|
+
const branch = v.slice(eq + 1).trim();
|
|
1750
|
+
if (!alias || !branch) {
|
|
1751
|
+
throw new BaseSpecError(
|
|
1752
|
+
`Invalid --base '${value}'. Use 'alias=branch' or a bare 'branch'.`
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
const prior = spec.perRepo[alias];
|
|
1756
|
+
if (prior !== void 0 && prior !== branch) {
|
|
1757
|
+
throw new BaseSpecError(
|
|
1758
|
+
`Conflicting --base for '${alias}': '${prior}' and '${branch}'`
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
spec.perRepo[alias] = branch;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return spec;
|
|
1765
|
+
}
|
|
1766
|
+
function baseForAlias(spec, alias) {
|
|
1767
|
+
return spec.perRepo[alias] ?? spec.default;
|
|
1768
|
+
}
|
|
1769
|
+
function isEmptyBaseSpec(spec) {
|
|
1770
|
+
return spec.default === void 0 && Object.keys(spec.perRepo).length === 0;
|
|
1771
|
+
}
|
|
1772
|
+
function baseSpecOverrideAliases(spec) {
|
|
1773
|
+
return Object.keys(spec.perRepo);
|
|
1774
|
+
}
|
|
1775
|
+
function toBaseSpec(base) {
|
|
1776
|
+
if (base === void 0) return { perRepo: {} };
|
|
1777
|
+
if (typeof base === "string") return { default: base, perRepo: {} };
|
|
1778
|
+
return base;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1722
1781
|
// src/core/worktree.ts
|
|
1723
1782
|
function createSingleWorktree(repoPath, worktreePath, branchName, config, baseBranch) {
|
|
1724
1783
|
debug("createSingleWorktree", { repoPath, worktreePath, branchName, baseBranch });
|
|
@@ -1924,8 +1983,9 @@ function removeSingleWorktree(repoPath, worktreePath, branchName, force) {
|
|
|
1924
1983
|
return false;
|
|
1925
1984
|
}
|
|
1926
1985
|
}
|
|
1927
|
-
async function setupWorktree(targetName, branchName, config,
|
|
1928
|
-
|
|
1986
|
+
async function setupWorktree(targetName, branchName, config, base, jiraKey) {
|
|
1987
|
+
const spec = toBaseSpec(base);
|
|
1988
|
+
debug("setupWorktree", { targetName, branchName, spec, jiraKey });
|
|
1929
1989
|
const target = resolveProjectTarget(targetName, config);
|
|
1930
1990
|
if (!target) {
|
|
1931
1991
|
debug("setupWorktree: target not found", targetName);
|
|
@@ -1933,27 +1993,38 @@ async function setupWorktree(targetName, branchName, config, baseBranch, jiraKey
|
|
|
1933
1993
|
}
|
|
1934
1994
|
const workTreeDirName = branchName.replace(/\//g, "-");
|
|
1935
1995
|
if (target.isGroup) {
|
|
1936
|
-
return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config,
|
|
1996
|
+
return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config, spec, jiraKey);
|
|
1937
1997
|
} else {
|
|
1938
|
-
return setupSingleWorktree(targetName, branchName, workTreeDirName, config,
|
|
1998
|
+
return setupSingleWorktree(targetName, branchName, workTreeDirName, config, spec, jiraKey);
|
|
1939
1999
|
}
|
|
1940
2000
|
}
|
|
1941
|
-
async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDirName, config,
|
|
2001
|
+
async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDirName, config, spec, jiraKey) {
|
|
1942
2002
|
const groupWorktreePath = path12.join(config.worktreesRoot, groupName, workTreeDirName);
|
|
1943
|
-
if (
|
|
2003
|
+
if (!isEmptyBaseSpec(spec)) {
|
|
2004
|
+
const unknownAliases = baseSpecOverrideAliases(spec).filter(
|
|
2005
|
+
(a) => !repoAliases.includes(a)
|
|
2006
|
+
);
|
|
2007
|
+
if (unknownAliases.length > 0) {
|
|
2008
|
+
console.error(
|
|
2009
|
+
`--base names repo(s) not in group '${groupName}': ${unknownAliases.join(", ")}. Group repos: ${repoAliases.join(", ")}`
|
|
2010
|
+
);
|
|
2011
|
+
return null;
|
|
2012
|
+
}
|
|
1944
2013
|
const missingBase = [];
|
|
1945
2014
|
const branchExists = [];
|
|
1946
2015
|
for (const alias of repoAliases) {
|
|
1947
2016
|
const repoPath = config.repos[alias];
|
|
1948
|
-
|
|
1949
|
-
|
|
2017
|
+
const repoBase = baseForAlias(spec, alias);
|
|
2018
|
+
if (!repoBase) continue;
|
|
2019
|
+
if (!localBranchExists(repoBase, repoPath) && !remoteBranchExists(repoBase, repoPath)) {
|
|
2020
|
+
missingBase.push(`${alias} (${repoBase})`);
|
|
1950
2021
|
}
|
|
1951
2022
|
if (localBranchExists(branchName, repoPath) || remoteBranchExists(branchName, repoPath)) {
|
|
1952
2023
|
branchExists.push(alias);
|
|
1953
2024
|
}
|
|
1954
2025
|
}
|
|
1955
2026
|
if (missingBase.length > 0) {
|
|
1956
|
-
console.error(`Base branch
|
|
2027
|
+
console.error(`Base branch not found in: ${missingBase.join(", ")}`);
|
|
1957
2028
|
return null;
|
|
1958
2029
|
}
|
|
1959
2030
|
if (branchExists.length > 0) {
|
|
@@ -1966,14 +2037,17 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
1966
2037
|
console.log("");
|
|
1967
2038
|
fs13.mkdirSync(groupWorktreePath, { recursive: true });
|
|
1968
2039
|
const createdWorktrees = [];
|
|
2040
|
+
const baseBranches = {};
|
|
1969
2041
|
for (const alias of repoAliases) {
|
|
1970
2042
|
const repoPath = config.repos[alias];
|
|
1971
2043
|
const repoName = path12.basename(repoPath);
|
|
1972
2044
|
const subWorktreePath = path12.join(groupWorktreePath, repoName);
|
|
2045
|
+
const repoBase = baseForAlias(spec, alias);
|
|
1973
2046
|
console.log(chalk5.cyan(`[${alias}] (${repoName}):`));
|
|
1974
|
-
const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config,
|
|
2047
|
+
const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config, repoBase);
|
|
1975
2048
|
if (success) {
|
|
1976
2049
|
createdWorktrees.push({ repoPath, worktreePath: subWorktreePath });
|
|
2050
|
+
if (repoBase) baseBranches[subWorktreePath] = repoBase;
|
|
1977
2051
|
} else {
|
|
1978
2052
|
console.log("");
|
|
1979
2053
|
console.log(chalk5.yellow("Rolling back created worktrees due to failure..."));
|
|
@@ -2003,6 +2077,8 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
2003
2077
|
console.log(chalk5.yellow(`Run 'work config regengroup ${groupName}' to generate it.`));
|
|
2004
2078
|
}
|
|
2005
2079
|
const allPaths = createdWorktrees.map((wt) => wt.worktreePath);
|
|
2080
|
+
const distinctBases = [...new Set(Object.values(baseBranches))];
|
|
2081
|
+
const representativeBase = spec.default ?? (distinctBases.length === 1 ? distinctBases[0] : void 0);
|
|
2006
2082
|
const { port } = await upsertSessionWithPort(
|
|
2007
2083
|
groupName,
|
|
2008
2084
|
true,
|
|
@@ -2010,17 +2086,26 @@ async function setupGroupWorktree(groupName, repoAliases, branchName, workTreeDi
|
|
|
2010
2086
|
allPaths,
|
|
2011
2087
|
config,
|
|
2012
2088
|
jiraKey,
|
|
2013
|
-
|
|
2089
|
+
representativeBase,
|
|
2090
|
+
baseBranches
|
|
2014
2091
|
);
|
|
2015
2092
|
console.log("");
|
|
2016
2093
|
console.log(`Branch: ${branchName}`);
|
|
2017
2094
|
if (port !== void 0) console.log(chalk5.gray(`Dev-server port: ${port}`));
|
|
2018
2095
|
return { launchDir: groupWorktreePath, paths: allPaths, isGroup: true, port };
|
|
2019
2096
|
}
|
|
2020
|
-
async function setupSingleWorktree(targetName, branchName, workTreeDirName, config,
|
|
2097
|
+
async function setupSingleWorktree(targetName, branchName, workTreeDirName, config, spec, jiraKey) {
|
|
2021
2098
|
const repoPath = config.repos[targetName];
|
|
2022
2099
|
const repoName = path12.basename(repoPath);
|
|
2023
2100
|
let workTreePath = path12.join(config.worktreesRoot, repoName, workTreeDirName);
|
|
2101
|
+
const unknownAliases = baseSpecOverrideAliases(spec).filter((a) => a !== targetName);
|
|
2102
|
+
if (unknownAliases.length > 0) {
|
|
2103
|
+
console.error(
|
|
2104
|
+
`--base names repo(s) other than '${targetName}': ${unknownAliases.join(", ")}`
|
|
2105
|
+
);
|
|
2106
|
+
return null;
|
|
2107
|
+
}
|
|
2108
|
+
const baseBranch = baseForAlias(spec, targetName);
|
|
2024
2109
|
if (!fs13.existsSync(repoPath)) {
|
|
2025
2110
|
console.error(`Repository path does not exist: ${repoPath}`);
|
|
2026
2111
|
return null;
|
|
@@ -2047,7 +2132,8 @@ async function setupSingleWorktree(targetName, branchName, workTreeDirName, conf
|
|
|
2047
2132
|
[workTreePath],
|
|
2048
2133
|
config,
|
|
2049
2134
|
jiraKey,
|
|
2050
|
-
baseBranch
|
|
2135
|
+
baseBranch,
|
|
2136
|
+
baseBranch ? { [workTreePath]: baseBranch } : void 0
|
|
2051
2137
|
);
|
|
2052
2138
|
console.log(`Branch: ${branchName}`);
|
|
2053
2139
|
if (port !== void 0) console.log(chalk5.gray(`Dev-server port: ${port}`));
|
|
@@ -2119,7 +2205,7 @@ var treeCommand = {
|
|
|
2119
2205
|
type: "boolean",
|
|
2120
2206
|
default: false
|
|
2121
2207
|
}).option("base", {
|
|
2122
|
-
describe: "
|
|
2208
|
+
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.",
|
|
2123
2209
|
type: "string"
|
|
2124
2210
|
}).option("prompt", {
|
|
2125
2211
|
describe: "Initial prompt to send to the AI tool on startup",
|
|
@@ -2144,7 +2230,17 @@ var treeCommand = {
|
|
|
2144
2230
|
const open = argv.open;
|
|
2145
2231
|
const unsafe = argv.unsafe;
|
|
2146
2232
|
const setupOnly = argv["setup-only"];
|
|
2147
|
-
|
|
2233
|
+
let baseSpec;
|
|
2234
|
+
try {
|
|
2235
|
+
baseSpec = parseBaseSpec(argv.base);
|
|
2236
|
+
} catch (err) {
|
|
2237
|
+
if (err instanceof BaseSpecError) {
|
|
2238
|
+
console.error(err.message);
|
|
2239
|
+
process.exitCode = 1;
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
throw err;
|
|
2243
|
+
}
|
|
2148
2244
|
const jiraKey = argv["jira-key"];
|
|
2149
2245
|
const promptFile = argv["prompt-file"];
|
|
2150
2246
|
let initialPrompt = argv.prompt;
|
|
@@ -2182,12 +2278,10 @@ var treeCommand = {
|
|
|
2182
2278
|
process.exitCode = 1;
|
|
2183
2279
|
return;
|
|
2184
2280
|
}
|
|
2185
|
-
if (
|
|
2281
|
+
if (!isEmptyBaseSpec(baseSpec) && !branchName) {
|
|
2186
2282
|
console.error("--base requires a branch name");
|
|
2187
2283
|
console.log(
|
|
2188
|
-
chalk6.yellow(
|
|
2189
|
-
`Usage: work tree ${targetName} <branch> --base ${baseBranch}`
|
|
2190
|
-
)
|
|
2284
|
+
chalk6.yellow(`Usage: work tree ${targetName} <branch> --base <base>`)
|
|
2191
2285
|
);
|
|
2192
2286
|
process.exitCode = 1;
|
|
2193
2287
|
return;
|
|
@@ -2235,7 +2329,7 @@ var treeCommand = {
|
|
|
2235
2329
|
}
|
|
2236
2330
|
return;
|
|
2237
2331
|
}
|
|
2238
|
-
const result = await setupWorktree(targetName, branchName, config,
|
|
2332
|
+
const result = await setupWorktree(targetName, branchName, config, baseSpec, jiraKey);
|
|
2239
2333
|
if (!result) {
|
|
2240
2334
|
process.exitCode = 1;
|
|
2241
2335
|
return;
|
|
@@ -6335,9 +6429,9 @@ var hydrateCommand = {
|
|
|
6335
6429
|
};
|
|
6336
6430
|
|
|
6337
6431
|
// src/commands/diff.ts
|
|
6338
|
-
import
|
|
6432
|
+
import fs36 from "fs";
|
|
6339
6433
|
import os9 from "os";
|
|
6340
|
-
import
|
|
6434
|
+
import path33 from "path";
|
|
6341
6435
|
import { spawn as childSpawn } from "child_process";
|
|
6342
6436
|
import chalk21 from "chalk";
|
|
6343
6437
|
|
|
@@ -7039,11 +7133,12 @@ function resolveBase(scope, argv) {
|
|
|
7039
7133
|
}
|
|
7040
7134
|
return { base: "HEAD", source: "default" };
|
|
7041
7135
|
}
|
|
7042
|
-
function buildRepoSpecs(scope, base) {
|
|
7136
|
+
function buildRepoSpecs(scope, base, perRepoBase) {
|
|
7043
7137
|
return scope.repos.map((r) => {
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7138
|
+
const repoBase = perRepoBase?.[r.root] ?? base;
|
|
7139
|
+
let diffArg = repoBase;
|
|
7140
|
+
if (repoBase !== "HEAD") {
|
|
7141
|
+
const mb = git(["merge-base", repoBase, "HEAD"], r.root);
|
|
7047
7142
|
if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;
|
|
7048
7143
|
}
|
|
7049
7144
|
return { name: r.name, root: r.root, diffArg };
|
|
@@ -7060,6 +7155,17 @@ function resolveRepoDiff(root, base, sessionBaseBranch) {
|
|
|
7060
7155
|
if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;
|
|
7061
7156
|
return { resolvedBase: parent, diffArg };
|
|
7062
7157
|
}
|
|
7158
|
+
function sessionBaseForPath(root) {
|
|
7159
|
+
const norm = normPath(root);
|
|
7160
|
+
for (const s of loadHistory()) {
|
|
7161
|
+
for (const p of s.paths) {
|
|
7162
|
+
if (normPath(p) === norm) {
|
|
7163
|
+
return s.baseBranches?.[p] ?? s.baseBranch;
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
return void 0;
|
|
7168
|
+
}
|
|
7063
7169
|
|
|
7064
7170
|
// src/core/comment-server.ts
|
|
7065
7171
|
import { Hono as Hono2 } from "hono";
|
|
@@ -7147,9 +7253,56 @@ import { Hono } from "hono";
|
|
|
7147
7253
|
import { serve } from "@hono/node-server";
|
|
7148
7254
|
import { streamSSE } from "hono/streaming";
|
|
7149
7255
|
|
|
7150
|
-
// src/core/
|
|
7256
|
+
// src/core/file-context.ts
|
|
7151
7257
|
import fs31 from "fs";
|
|
7152
7258
|
import path28 from "path";
|
|
7259
|
+
import spawn10 from "cross-spawn";
|
|
7260
|
+
function readContextLines(opts) {
|
|
7261
|
+
const { root, relPath, ref } = opts;
|
|
7262
|
+
if (!relPath || !isInsideRoot(root, relPath)) return null;
|
|
7263
|
+
const start = Math.max(1, Math.floor(opts.start));
|
|
7264
|
+
const end = Math.max(start, Math.floor(opts.end));
|
|
7265
|
+
const content = !ref || ref === "working" ? readWorkingTree(root, relPath) : readAtRef(root, ref, relPath);
|
|
7266
|
+
if (content === null) return null;
|
|
7267
|
+
const all = content.split(/\r?\n/);
|
|
7268
|
+
if (all.length > 0 && all[all.length - 1] === "") all.pop();
|
|
7269
|
+
const totalLines = all.length;
|
|
7270
|
+
const slice = all.slice(start - 1, end);
|
|
7271
|
+
return {
|
|
7272
|
+
lines: slice,
|
|
7273
|
+
start,
|
|
7274
|
+
totalLines,
|
|
7275
|
+
eof: end >= totalLines
|
|
7276
|
+
};
|
|
7277
|
+
}
|
|
7278
|
+
function readWorkingTree(root, relPath) {
|
|
7279
|
+
try {
|
|
7280
|
+
const absPath = path28.join(root, relPath);
|
|
7281
|
+
const realRoot2 = fs31.realpathSync(path28.resolve(root));
|
|
7282
|
+
const realPath = fs31.realpathSync(absPath);
|
|
7283
|
+
const sep = path28.sep;
|
|
7284
|
+
if (realPath !== realRoot2 && !realPath.startsWith(realRoot2 + sep)) {
|
|
7285
|
+
return null;
|
|
7286
|
+
}
|
|
7287
|
+
return fs31.readFileSync(absPath, "utf-8");
|
|
7288
|
+
} catch {
|
|
7289
|
+
return null;
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
function readAtRef(root, ref, relPath) {
|
|
7293
|
+
const r = spawn10.sync("git", ["show", `${ref}:${relPath}`], {
|
|
7294
|
+
cwd: root,
|
|
7295
|
+
encoding: "utf-8",
|
|
7296
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
7297
|
+
windowsHide: true
|
|
7298
|
+
});
|
|
7299
|
+
if (r.status === 0 && typeof r.stdout === "string") return r.stdout;
|
|
7300
|
+
return null;
|
|
7301
|
+
}
|
|
7302
|
+
|
|
7303
|
+
// src/core/fs-watcher.ts
|
|
7304
|
+
import fs32 from "fs";
|
|
7305
|
+
import path29 from "path";
|
|
7153
7306
|
import chalk19 from "chalk";
|
|
7154
7307
|
import chokidar from "chokidar";
|
|
7155
7308
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -7175,7 +7328,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
7175
7328
|
]);
|
|
7176
7329
|
function isIgnoredWatchPath(roots, filePath) {
|
|
7177
7330
|
for (const root of roots) {
|
|
7178
|
-
const rel =
|
|
7331
|
+
const rel = path29.relative(root, filePath).replace(/\\/g, "/");
|
|
7179
7332
|
if (rel === "" || rel.startsWith("../")) continue;
|
|
7180
7333
|
if (rel.split("/").some((seg) => IGNORED_DIRS.has(seg))) return true;
|
|
7181
7334
|
}
|
|
@@ -7199,8 +7352,8 @@ function createFsWatcher(opts) {
|
|
|
7199
7352
|
};
|
|
7200
7353
|
if (SUPPORTS_RECURSIVE_WATCH) {
|
|
7201
7354
|
const watchers = opts.roots.map((root) => {
|
|
7202
|
-
const w =
|
|
7203
|
-
if (filename && isIgnoredWatchPath([root],
|
|
7355
|
+
const w = fs32.watch(root, { recursive: true }, (_event, filename) => {
|
|
7356
|
+
if (filename && isIgnoredWatchPath([root], path29.join(root, filename.toString()))) {
|
|
7204
7357
|
return;
|
|
7205
7358
|
}
|
|
7206
7359
|
fire();
|
|
@@ -7238,30 +7391,30 @@ function createFsWatcher(opts) {
|
|
|
7238
7391
|
}
|
|
7239
7392
|
|
|
7240
7393
|
// src/core/web-static.ts
|
|
7241
|
-
import
|
|
7242
|
-
import
|
|
7394
|
+
import fs33 from "fs";
|
|
7395
|
+
import path30 from "path";
|
|
7243
7396
|
import { fileURLToPath } from "url";
|
|
7244
7397
|
function resolveWebRoot() {
|
|
7245
|
-
const entryDir =
|
|
7246
|
-
const moduleDir =
|
|
7398
|
+
const entryDir = path30.dirname(process.argv[1] ?? "");
|
|
7399
|
+
const moduleDir = path30.dirname(fileURLToPath(import.meta.url));
|
|
7247
7400
|
const candidates = [
|
|
7248
|
-
|
|
7401
|
+
path30.join(entryDir, "web"),
|
|
7249
7402
|
// bundled: this module is inlined into dist/<bin>.js, so dist/web is a
|
|
7250
7403
|
// sibling of the bundle. Works even when argv[1] is an npm bin symlink
|
|
7251
7404
|
// (which is not realpath'd, so the entryDir candidate above misses).
|
|
7252
|
-
|
|
7405
|
+
path30.join(moduleDir, "web"),
|
|
7253
7406
|
// dev/tsx fallback: walk up from src/core to repo root then into dist/web.
|
|
7254
|
-
|
|
7407
|
+
path30.resolve(moduleDir, "../../dist/web")
|
|
7255
7408
|
];
|
|
7256
7409
|
for (const c of candidates) {
|
|
7257
|
-
if (
|
|
7410
|
+
if (fs33.existsSync(path30.join(c, "index.html"))) return c;
|
|
7258
7411
|
}
|
|
7259
7412
|
return null;
|
|
7260
7413
|
}
|
|
7261
7414
|
|
|
7262
7415
|
// src/core/spa-handler.ts
|
|
7263
|
-
import
|
|
7264
|
-
import
|
|
7416
|
+
import fs34 from "fs";
|
|
7417
|
+
import path31 from "path";
|
|
7265
7418
|
var MIME = {
|
|
7266
7419
|
".html": "text/html; charset=utf-8",
|
|
7267
7420
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -7275,18 +7428,18 @@ var MIME = {
|
|
|
7275
7428
|
function readFile(root, relPath) {
|
|
7276
7429
|
const clean = relPath.split("?")[0];
|
|
7277
7430
|
const requested = clean === "/" ? "/index.html" : clean;
|
|
7278
|
-
const filePath =
|
|
7279
|
-
const norm =
|
|
7280
|
-
if (!norm.startsWith(
|
|
7431
|
+
const filePath = path31.join(root, requested);
|
|
7432
|
+
const norm = path31.normalize(filePath);
|
|
7433
|
+
if (!norm.startsWith(path31.normalize(root))) return null;
|
|
7281
7434
|
let stat;
|
|
7282
7435
|
try {
|
|
7283
|
-
stat =
|
|
7436
|
+
stat = fs34.statSync(norm);
|
|
7284
7437
|
} catch {
|
|
7285
7438
|
return null;
|
|
7286
7439
|
}
|
|
7287
7440
|
if (!stat.isFile()) return null;
|
|
7288
|
-
const ext =
|
|
7289
|
-
return { body:
|
|
7441
|
+
const ext = path31.extname(norm).toLowerCase();
|
|
7442
|
+
return { body: fs34.readFileSync(norm), ext };
|
|
7290
7443
|
}
|
|
7291
7444
|
function serveSpa(c, webRoot) {
|
|
7292
7445
|
const url = new URL(c.req.url);
|
|
@@ -7342,7 +7495,11 @@ async function startDiffServer(opts) {
|
|
|
7342
7495
|
const base = c.req.query("base") === "branch" ? "branch" : "uncommitted";
|
|
7343
7496
|
try {
|
|
7344
7497
|
const resolved = opts.repos.map(
|
|
7345
|
-
(r) => base === "uncommitted" ? { resolvedBase: "HEAD", diffArg: r.diffArg } : resolveRepoDiff(
|
|
7498
|
+
(r) => base === "uncommitted" ? { resolvedBase: "HEAD", diffArg: r.diffArg } : resolveRepoDiff(
|
|
7499
|
+
r.root,
|
|
7500
|
+
"branch",
|
|
7501
|
+
opts.sessionBaseBranches?.[r.root] ?? opts.sessionBaseBranch
|
|
7502
|
+
)
|
|
7346
7503
|
);
|
|
7347
7504
|
const repos = opts.repos.map((r, i) => ({
|
|
7348
7505
|
name: r.name,
|
|
@@ -7356,6 +7513,21 @@ async function startDiffServer(opts) {
|
|
|
7356
7513
|
return c.json({ error: err.message }, 500);
|
|
7357
7514
|
}
|
|
7358
7515
|
});
|
|
7516
|
+
app.get("/api/file-lines", (c) => {
|
|
7517
|
+
const repoName = c.req.query("repo") ?? "";
|
|
7518
|
+
const relPath = c.req.query("path") ?? "";
|
|
7519
|
+
const start = Number(c.req.query("start"));
|
|
7520
|
+
const end = Number(c.req.query("end"));
|
|
7521
|
+
const ref = c.req.query("ref") || void 0;
|
|
7522
|
+
if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
7523
|
+
return c.json({ error: "bad path/start/end" }, 400);
|
|
7524
|
+
}
|
|
7525
|
+
const root = opts.repos.length === 1 ? opts.repos[0].root : opts.repos.find((r) => r.name === repoName)?.root;
|
|
7526
|
+
if (!root) return c.json({ error: "unknown repo" }, 404);
|
|
7527
|
+
const result = readContextLines({ root, relPath, start, end, ref });
|
|
7528
|
+
if (!result) return c.json({ error: "cannot read file" }, 400);
|
|
7529
|
+
return c.json(result);
|
|
7530
|
+
});
|
|
7359
7531
|
app.get(
|
|
7360
7532
|
"/events",
|
|
7361
7533
|
(c) => streamSSE(c, async (stream) => {
|
|
@@ -7518,6 +7690,7 @@ async function startCommentServer(opts) {
|
|
|
7518
7690
|
repos: opts.repos,
|
|
7519
7691
|
scopeLabel: opts.scopeLabel,
|
|
7520
7692
|
sessionBaseBranch: opts.sessionBaseBranch,
|
|
7693
|
+
sessionBaseBranches: opts.sessionBaseBranches,
|
|
7521
7694
|
watchDebounceMs: opts.watchDebounceMs,
|
|
7522
7695
|
attachRoutes
|
|
7523
7696
|
});
|
|
@@ -7559,8 +7732,8 @@ function diffReviewSnapshot(snapshot, seen) {
|
|
|
7559
7732
|
}
|
|
7560
7733
|
|
|
7561
7734
|
// src/core/static-renderer.ts
|
|
7562
|
-
import
|
|
7563
|
-
import
|
|
7735
|
+
import fs35 from "fs";
|
|
7736
|
+
import path32 from "path";
|
|
7564
7737
|
function buildDiff(specs, resolvedBase) {
|
|
7565
7738
|
return {
|
|
7566
7739
|
repos: specs.map((r) => ({
|
|
@@ -7576,8 +7749,8 @@ function renderStatic(opts) {
|
|
|
7576
7749
|
if (!webRoot) {
|
|
7577
7750
|
throw new Error("Could not find dist/web/. Run `npm run build` first.");
|
|
7578
7751
|
}
|
|
7579
|
-
const shellPath =
|
|
7580
|
-
let shell =
|
|
7752
|
+
const shellPath = path32.join(webRoot, "index.html");
|
|
7753
|
+
let shell = fs35.readFileSync(shellPath, "utf-8");
|
|
7581
7754
|
const uncommitted = buildDiff(opts.uncommitted, "HEAD");
|
|
7582
7755
|
const branch = opts.branch ? buildDiff(opts.branch.specs, opts.branch.resolvedBase) : void 0;
|
|
7583
7756
|
const initialBase = opts.initialBase ?? "uncommitted";
|
|
@@ -7626,10 +7799,10 @@ function escapeForScriptTag(json) {
|
|
|
7626
7799
|
}
|
|
7627
7800
|
function readAsset(webRoot, urlPath) {
|
|
7628
7801
|
const clean = urlPath.split("?")[0].replace(/^\//, "");
|
|
7629
|
-
const full =
|
|
7630
|
-
if (!
|
|
7802
|
+
const full = path32.join(webRoot, clean);
|
|
7803
|
+
if (!path32.normalize(full).startsWith(path32.normalize(webRoot))) return null;
|
|
7631
7804
|
try {
|
|
7632
|
-
return
|
|
7805
|
+
return fs35.readFileSync(full, "utf-8");
|
|
7633
7806
|
} catch {
|
|
7634
7807
|
return null;
|
|
7635
7808
|
}
|
|
@@ -7670,22 +7843,22 @@ async function runStop(repoSpecs) {
|
|
|
7670
7843
|
}
|
|
7671
7844
|
}
|
|
7672
7845
|
function webUrlFilePath() {
|
|
7673
|
-
return
|
|
7846
|
+
return path33.join(os9.homedir(), ".work", "web.url");
|
|
7674
7847
|
}
|
|
7675
7848
|
function resolveWorkBinPath(selfArgv1) {
|
|
7676
7849
|
let real = selfArgv1;
|
|
7677
7850
|
try {
|
|
7678
|
-
real =
|
|
7851
|
+
real = fs36.realpathSync(selfArgv1);
|
|
7679
7852
|
} catch {
|
|
7680
7853
|
}
|
|
7681
7854
|
if (real.endsWith("wd-bin.js")) {
|
|
7682
|
-
return
|
|
7855
|
+
return path33.join(path33.dirname(real), "bin.js");
|
|
7683
7856
|
}
|
|
7684
7857
|
return real;
|
|
7685
7858
|
}
|
|
7686
7859
|
function readWebUrl() {
|
|
7687
7860
|
try {
|
|
7688
|
-
const v =
|
|
7861
|
+
const v = fs36.readFileSync(webUrlFilePath(), "utf-8").trim();
|
|
7689
7862
|
return v || null;
|
|
7690
7863
|
} catch {
|
|
7691
7864
|
return null;
|
|
@@ -7695,8 +7868,8 @@ async function ensureWorkWebRunning() {
|
|
|
7695
7868
|
const existing = readWebUrl();
|
|
7696
7869
|
if (existing) return existing;
|
|
7697
7870
|
const workBin = resolveWorkBinPath(process.argv[1]);
|
|
7698
|
-
const out =
|
|
7699
|
-
|
|
7871
|
+
const out = fs36.openSync(
|
|
7872
|
+
path33.join(os9.homedir(), ".work", "web-autostart.log"),
|
|
7700
7873
|
"a"
|
|
7701
7874
|
);
|
|
7702
7875
|
const child = childSpawn(
|
|
@@ -7711,7 +7884,7 @@ async function ensureWorkWebRunning() {
|
|
|
7711
7884
|
}
|
|
7712
7885
|
);
|
|
7713
7886
|
child.unref();
|
|
7714
|
-
|
|
7887
|
+
fs36.closeSync(out);
|
|
7715
7888
|
const url = await waitForUrlFile(webUrlFilePath(), 5e3);
|
|
7716
7889
|
return url;
|
|
7717
7890
|
}
|
|
@@ -7755,9 +7928,10 @@ async function runLauncher(ctx) {
|
|
|
7755
7928
|
function runStatic(ctx, initialBranch) {
|
|
7756
7929
|
const uncommitted = buildRepoSpecs(ctx.scope, "HEAD");
|
|
7757
7930
|
const primaryRoot = ctx.scope.repos.find((r) => r.name === ctx.scope.activeRepoName)?.root ?? ctx.scope.repos[0].root;
|
|
7758
|
-
const
|
|
7931
|
+
const perRepoBase = ctx.scope.session?.baseBranches;
|
|
7932
|
+
const parent = perRepoBase?.[primaryRoot] ?? ctx.scope.session?.baseBranch ?? findAnyParentBranch(primaryRoot);
|
|
7759
7933
|
const branch = parent === null ? void 0 : {
|
|
7760
|
-
specs: buildRepoSpecs(ctx.scope, parent),
|
|
7934
|
+
specs: buildRepoSpecs(ctx.scope, parent, perRepoBase),
|
|
7761
7935
|
resolvedBase: parent
|
|
7762
7936
|
};
|
|
7763
7937
|
const uncommittedTotal = uncommitted.reduce(
|
|
@@ -7779,7 +7953,7 @@ function runStatic(ctx, initialBranch) {
|
|
|
7779
7953
|
initialBase: initialBranch && branch ? "branch" : "uncommitted"
|
|
7780
7954
|
});
|
|
7781
7955
|
const filePath = `${ctx.scopeStem}.html`;
|
|
7782
|
-
|
|
7956
|
+
fs36.writeFileSync(filePath, html, "utf-8");
|
|
7783
7957
|
info(chalk21.gray(`Wrote ${filePath}`));
|
|
7784
7958
|
openUrl(`file:///${filePath.replace(/\\/g, "/")}`);
|
|
7785
7959
|
}
|
|
@@ -7788,7 +7962,7 @@ function waitForUrlFile(filePath, timeoutMs) {
|
|
|
7788
7962
|
const start = Date.now();
|
|
7789
7963
|
const tick = () => {
|
|
7790
7964
|
try {
|
|
7791
|
-
const v =
|
|
7965
|
+
const v = fs36.readFileSync(filePath, "utf-8").trim();
|
|
7792
7966
|
if (v) return resolve(v);
|
|
7793
7967
|
} catch {
|
|
7794
7968
|
}
|
|
@@ -7928,6 +8102,7 @@ summary-id: ${info3.summary.id}` : ""}
|
|
|
7928
8102
|
repos: ctx.repoSpecs,
|
|
7929
8103
|
scopeLabel: ctx.scopeLabel,
|
|
7930
8104
|
sessionBaseBranch: ctx.scope.session?.baseBranch,
|
|
8105
|
+
sessionBaseBranches: ctx.scope.session?.baseBranches,
|
|
7931
8106
|
onComment,
|
|
7932
8107
|
onCommentDeleted,
|
|
7933
8108
|
onSubmitReviewStart,
|
|
@@ -8034,21 +8209,21 @@ var diffCommand = {
|
|
|
8034
8209
|
};
|
|
8035
8210
|
|
|
8036
8211
|
// src/commands/web.ts
|
|
8037
|
-
import
|
|
8212
|
+
import fs41 from "fs";
|
|
8038
8213
|
import os13 from "os";
|
|
8039
|
-
import
|
|
8214
|
+
import path42 from "path";
|
|
8040
8215
|
import chalk23 from "chalk";
|
|
8041
8216
|
|
|
8042
8217
|
// src/core/web-server.ts
|
|
8043
|
-
import
|
|
8218
|
+
import fs40 from "fs";
|
|
8044
8219
|
import os12 from "os";
|
|
8045
|
-
import
|
|
8220
|
+
import path41 from "path";
|
|
8046
8221
|
import chalk22 from "chalk";
|
|
8047
8222
|
import { Hono as Hono3 } from "hono";
|
|
8048
8223
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
8049
8224
|
|
|
8050
8225
|
// src/core/web-state.ts
|
|
8051
|
-
import
|
|
8226
|
+
import path34 from "path";
|
|
8052
8227
|
import crypto4 from "crypto";
|
|
8053
8228
|
import chokidar2 from "chokidar";
|
|
8054
8229
|
function sessionIdFor(s) {
|
|
@@ -8069,7 +8244,7 @@ function subscribeSession(sessionId, onChange) {
|
|
|
8069
8244
|
const watcher = chokidar2.watch(roots, {
|
|
8070
8245
|
ignored: (filePath) => {
|
|
8071
8246
|
for (const r of roots) {
|
|
8072
|
-
const rel =
|
|
8247
|
+
const rel = path34.relative(r, filePath).replace(/\\/g, "/");
|
|
8073
8248
|
if (rel === ".git" || rel.startsWith(".git/")) return true;
|
|
8074
8249
|
}
|
|
8075
8250
|
return false;
|
|
@@ -8183,24 +8358,24 @@ function disposeAllPtys() {
|
|
|
8183
8358
|
}
|
|
8184
8359
|
|
|
8185
8360
|
// src/core/comment-file-store.ts
|
|
8186
|
-
import
|
|
8187
|
-
import
|
|
8361
|
+
import fs37 from "fs";
|
|
8362
|
+
import path35 from "path";
|
|
8188
8363
|
import os10 from "os";
|
|
8189
8364
|
function commentsDir() {
|
|
8190
|
-
return
|
|
8365
|
+
return path35.join(os10.homedir(), ".work", "comments");
|
|
8191
8366
|
}
|
|
8192
8367
|
function ensureDir() {
|
|
8193
|
-
|
|
8368
|
+
fs37.mkdirSync(commentsDir(), { recursive: true });
|
|
8194
8369
|
}
|
|
8195
8370
|
function commentsFileFor(sessionId) {
|
|
8196
|
-
return
|
|
8371
|
+
return path35.join(commentsDir(), `${sessionId}.json`);
|
|
8197
8372
|
}
|
|
8198
8373
|
function pathFor(sessionId) {
|
|
8199
8374
|
return commentsFileFor(sessionId);
|
|
8200
8375
|
}
|
|
8201
8376
|
function readDisk(sessionId) {
|
|
8202
8377
|
try {
|
|
8203
|
-
const raw =
|
|
8378
|
+
const raw = fs37.readFileSync(pathFor(sessionId), "utf-8");
|
|
8204
8379
|
const parsed = JSON.parse(raw);
|
|
8205
8380
|
return Array.isArray(parsed) ? parsed : [];
|
|
8206
8381
|
} catch {
|
|
@@ -8272,29 +8447,29 @@ function clearCommentStoreCache() {
|
|
|
8272
8447
|
}
|
|
8273
8448
|
|
|
8274
8449
|
// src/core/pending-delivery.ts
|
|
8275
|
-
import
|
|
8276
|
-
import
|
|
8450
|
+
import fs38 from "fs";
|
|
8451
|
+
import path36 from "path";
|
|
8277
8452
|
function pathFor2(sessionId) {
|
|
8278
8453
|
return {
|
|
8279
8454
|
comments: commentsFileFor(sessionId),
|
|
8280
|
-
delivered:
|
|
8455
|
+
delivered: path36.join(commentsDir(), `${sessionId}.delivered.json`)
|
|
8281
8456
|
};
|
|
8282
8457
|
}
|
|
8283
8458
|
function readJson(filePath, fallback) {
|
|
8284
8459
|
try {
|
|
8285
|
-
return JSON.parse(
|
|
8460
|
+
return JSON.parse(fs38.readFileSync(filePath, "utf8"));
|
|
8286
8461
|
} catch {
|
|
8287
8462
|
return fallback;
|
|
8288
8463
|
}
|
|
8289
8464
|
}
|
|
8290
8465
|
function writeAtomic2(filePath, content) {
|
|
8291
|
-
|
|
8466
|
+
fs38.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
8292
8467
|
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
8293
|
-
|
|
8294
|
-
|
|
8468
|
+
fs38.writeFileSync(tmp, content, "utf-8");
|
|
8469
|
+
fs38.renameSync(tmp, filePath);
|
|
8295
8470
|
}
|
|
8296
8471
|
function normalize(p) {
|
|
8297
|
-
return
|
|
8472
|
+
return path36.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
8298
8473
|
}
|
|
8299
8474
|
function findSessionForCwd(cwd) {
|
|
8300
8475
|
const norm = normalize(cwd);
|
|
@@ -8325,7 +8500,7 @@ function readPendingForSession(sessionId) {
|
|
|
8325
8500
|
return comments.filter(isPendingFor(delivered));
|
|
8326
8501
|
}
|
|
8327
8502
|
function scopeStoreIdsForPaths(paths) {
|
|
8328
|
-
const resolved = paths.map((p) =>
|
|
8503
|
+
const resolved = paths.map((p) => path36.resolve(p));
|
|
8329
8504
|
const ids = /* @__PURE__ */ new Set();
|
|
8330
8505
|
ids.add(`scope-${scopeHashFor(resolved)}`);
|
|
8331
8506
|
for (const p of resolved) ids.add(`scope-${scopeHashFor([p])}`);
|
|
@@ -8648,8 +8823,8 @@ function mountPanesRoutes(app, opts) {
|
|
|
8648
8823
|
}
|
|
8649
8824
|
|
|
8650
8825
|
// src/core/worktree-routes.ts
|
|
8651
|
-
import
|
|
8652
|
-
import { spawn as
|
|
8826
|
+
import path37 from "path";
|
|
8827
|
+
import { spawn as spawn11 } from "child_process";
|
|
8653
8828
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
8654
8829
|
import { z as z3 } from "zod";
|
|
8655
8830
|
function mountWorktreeRoutes(app, opts) {
|
|
@@ -8772,10 +8947,10 @@ function mountWorktreeRoutes(app, opts) {
|
|
|
8772
8947
|
const id = c.req.param("id");
|
|
8773
8948
|
const session = findSession2(id);
|
|
8774
8949
|
if (!session) return c.json({ error: "unknown session" }, 404);
|
|
8775
|
-
const target = session.isGroup ?
|
|
8950
|
+
const target = session.isGroup ? path37.dirname(session.paths[0]) : session.paths[0];
|
|
8776
8951
|
try {
|
|
8777
8952
|
const cmd = process.platform === "win32" ? "code.cmd" : "code";
|
|
8778
|
-
const child =
|
|
8953
|
+
const child = spawn11(cmd, [target], {
|
|
8779
8954
|
detached: true,
|
|
8780
8955
|
stdio: "ignore",
|
|
8781
8956
|
shell: false
|
|
@@ -8792,27 +8967,27 @@ function mountWorktreeRoutes(app, opts) {
|
|
|
8792
8967
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
8793
8968
|
import { z as z4 } from "zod";
|
|
8794
8969
|
import { EventEmitter } from "events";
|
|
8795
|
-
import
|
|
8796
|
-
import
|
|
8970
|
+
import path40 from "path";
|
|
8971
|
+
import spawn13 from "cross-spawn";
|
|
8797
8972
|
|
|
8798
8973
|
// src/core/checkpoint.ts
|
|
8799
|
-
import
|
|
8974
|
+
import fs39 from "fs";
|
|
8800
8975
|
import os11 from "os";
|
|
8801
|
-
import
|
|
8802
|
-
import
|
|
8976
|
+
import path38 from "path";
|
|
8977
|
+
import spawn12 from "cross-spawn";
|
|
8803
8978
|
function manifestPath(scopeHash) {
|
|
8804
|
-
const dir =
|
|
8805
|
-
|
|
8806
|
-
return
|
|
8979
|
+
const dir = path38.join(os11.homedir(), ".work", "diffs");
|
|
8980
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
8981
|
+
return path38.join(dir, `${scopeHash}.checkpoints.json`);
|
|
8807
8982
|
}
|
|
8808
8983
|
function emptyManifest(scopeHash) {
|
|
8809
8984
|
return { version: 1, scopeHash, entries: [] };
|
|
8810
8985
|
}
|
|
8811
8986
|
function loadManifest(scopeHash) {
|
|
8812
8987
|
const file = manifestPath(scopeHash);
|
|
8813
|
-
if (!
|
|
8988
|
+
if (!fs39.existsSync(file)) return emptyManifest(scopeHash);
|
|
8814
8989
|
try {
|
|
8815
|
-
const raw =
|
|
8990
|
+
const raw = fs39.readFileSync(file, "utf-8");
|
|
8816
8991
|
const parsed = JSON.parse(raw);
|
|
8817
8992
|
if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
|
|
8818
8993
|
return emptyManifest(scopeHash);
|
|
@@ -8841,7 +9016,7 @@ function snapshotRepo(repoRoot, scopeHash, id, includeWorkingTree = true) {
|
|
|
8841
9016
|
GIT_COMMITTER_EMAIL: "wd@local",
|
|
8842
9017
|
GIT_COMMITTER_DATE: "2000-01-01T00:00:00Z"
|
|
8843
9018
|
};
|
|
8844
|
-
const commit =
|
|
9019
|
+
const commit = spawn12.sync("git", commitArgs, {
|
|
8845
9020
|
cwd: repoRoot,
|
|
8846
9021
|
encoding: "utf-8",
|
|
8847
9022
|
env: commitEnv,
|
|
@@ -8850,7 +9025,7 @@ function snapshotRepo(repoRoot, scopeHash, id, includeWorkingTree = true) {
|
|
|
8850
9025
|
if (commit.status !== 0 || !commit.stdout) return null;
|
|
8851
9026
|
const commitSha = commit.stdout.trim();
|
|
8852
9027
|
const refName = `refs/wd/${scopeHash}/${id}`;
|
|
8853
|
-
const updateRef =
|
|
9028
|
+
const updateRef = spawn12.sync("git", ["update-ref", refName, commitSha], {
|
|
8854
9029
|
cwd: repoRoot,
|
|
8855
9030
|
encoding: "utf-8",
|
|
8856
9031
|
windowsHide: true
|
|
@@ -8877,7 +9052,7 @@ async function takeCheckpoint(scopeHash, repos, opts = {}) {
|
|
|
8877
9052
|
const rollbackRefs = () => {
|
|
8878
9053
|
const refName = `refs/wd/${scopeHash}/${nextId}`;
|
|
8879
9054
|
for (const repo of repos) {
|
|
8880
|
-
|
|
9055
|
+
spawn12.sync("git", ["update-ref", "-d", refName], {
|
|
8881
9056
|
cwd: repo.root,
|
|
8882
9057
|
encoding: "utf-8",
|
|
8883
9058
|
windowsHide: true
|
|
@@ -8892,7 +9067,7 @@ async function takeCheckpoint(scopeHash, repos, opts = {}) {
|
|
|
8892
9067
|
const prev = manifest.entries[manifest.entries.length - 1];
|
|
8893
9068
|
const treeOf = (root, commitSha) => {
|
|
8894
9069
|
if (!commitSha) return null;
|
|
8895
|
-
const r =
|
|
9070
|
+
const r = spawn12.sync(
|
|
8896
9071
|
"git",
|
|
8897
9072
|
["rev-parse", `${commitSha}^{tree}`],
|
|
8898
9073
|
{ cwd: root, encoding: "utf-8", windowsHide: true }
|
|
@@ -8927,7 +9102,7 @@ function clearCheckpoints(scopeHash, repoRoots) {
|
|
|
8927
9102
|
for (const root of repoRoots) {
|
|
8928
9103
|
for (const entry of manifest.entries) {
|
|
8929
9104
|
const refName = `refs/wd/${scopeHash}/${entry.id}`;
|
|
8930
|
-
|
|
9105
|
+
spawn12.sync("git", ["update-ref", "-d", refName], {
|
|
8931
9106
|
cwd: root,
|
|
8932
9107
|
encoding: "utf-8",
|
|
8933
9108
|
windowsHide: true
|
|
@@ -8935,16 +9110,16 @@ function clearCheckpoints(scopeHash, repoRoots) {
|
|
|
8935
9110
|
}
|
|
8936
9111
|
}
|
|
8937
9112
|
const file = manifestPath(scopeHash);
|
|
8938
|
-
if (
|
|
9113
|
+
if (fs39.existsSync(file)) {
|
|
8939
9114
|
try {
|
|
8940
|
-
|
|
9115
|
+
fs39.unlinkSync(file);
|
|
8941
9116
|
} catch {
|
|
8942
9117
|
}
|
|
8943
9118
|
}
|
|
8944
9119
|
}
|
|
8945
9120
|
|
|
8946
9121
|
// src/core/scope-manager.ts
|
|
8947
|
-
import
|
|
9122
|
+
import path39 from "path";
|
|
8948
9123
|
import crypto5 from "crypto";
|
|
8949
9124
|
var scopes = /* @__PURE__ */ new Map();
|
|
8950
9125
|
function hashFor(paths) {
|
|
@@ -8961,7 +9136,7 @@ var ScopePathRejectedError = class extends Error {
|
|
|
8961
9136
|
}
|
|
8962
9137
|
};
|
|
8963
9138
|
function normaliseForCompare(p) {
|
|
8964
|
-
return
|
|
9139
|
+
return path39.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
8965
9140
|
}
|
|
8966
9141
|
function rejectedPaths(normalised) {
|
|
8967
9142
|
const config = loadConfig();
|
|
@@ -8979,7 +9154,7 @@ function rejectedPaths(normalised) {
|
|
|
8979
9154
|
});
|
|
8980
9155
|
}
|
|
8981
9156
|
function registerScope(paths, label2) {
|
|
8982
|
-
const normalised = paths.map((p) =>
|
|
9157
|
+
const normalised = paths.map((p) => path39.resolve(p));
|
|
8983
9158
|
const rejected = rejectedPaths(normalised);
|
|
8984
9159
|
if (rejected.length > 0) throw new ScopePathRejectedError(rejected);
|
|
8985
9160
|
const hash = hashFor(normalised);
|
|
@@ -8993,7 +9168,7 @@ function registerScope(paths, label2) {
|
|
|
8993
9168
|
const scope = {
|
|
8994
9169
|
hash,
|
|
8995
9170
|
paths: normalised,
|
|
8996
|
-
label: label2 ??
|
|
9171
|
+
label: label2 ?? path39.basename(normalised[0]),
|
|
8997
9172
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8998
9173
|
ended: false
|
|
8999
9174
|
};
|
|
@@ -9066,7 +9241,7 @@ function mountScopeRoutes(app, opts) {
|
|
|
9066
9241
|
function workingTreeFingerprint(paths) {
|
|
9067
9242
|
const parts = [];
|
|
9068
9243
|
for (const p of paths) {
|
|
9069
|
-
const r =
|
|
9244
|
+
const r = spawn13.sync(
|
|
9070
9245
|
"git",
|
|
9071
9246
|
["status", "--porcelain", "--no-renames", "-z"],
|
|
9072
9247
|
{ cwd: p, encoding: "utf-8", windowsHide: true }
|
|
@@ -9190,7 +9365,7 @@ function mountScopeRoutes(app, opts) {
|
|
|
9190
9365
|
const fromSha = fromEntry.repos[p] ?? "HEAD";
|
|
9191
9366
|
const toSha = toEntry === void 0 ? "working" : toEntry.repos[p] ?? "HEAD";
|
|
9192
9367
|
return {
|
|
9193
|
-
name:
|
|
9368
|
+
name: path40.basename(p),
|
|
9194
9369
|
root: p,
|
|
9195
9370
|
files: computeRangeDiff({ root: p, fromRef: fromSha, toRef: toSha })
|
|
9196
9371
|
};
|
|
@@ -9204,9 +9379,11 @@ function mountScopeRoutes(app, opts) {
|
|
|
9204
9379
|
repos: repos2
|
|
9205
9380
|
});
|
|
9206
9381
|
}
|
|
9207
|
-
const resolved = scope.paths.map(
|
|
9382
|
+
const resolved = scope.paths.map(
|
|
9383
|
+
(p) => resolveRepoDiff(p, base, sessionBaseForPath(p))
|
|
9384
|
+
);
|
|
9208
9385
|
const repos = scope.paths.map((p, i) => ({
|
|
9209
|
-
name:
|
|
9386
|
+
name: path40.basename(p),
|
|
9210
9387
|
root: p,
|
|
9211
9388
|
resolvedBase: resolved[i].resolvedBase,
|
|
9212
9389
|
files: computeDiff({ root: p, diffArg: resolved[i].diffArg })
|
|
@@ -9225,6 +9402,23 @@ function mountScopeRoutes(app, opts) {
|
|
|
9225
9402
|
return c.json({ error: err.message }, 500);
|
|
9226
9403
|
}
|
|
9227
9404
|
});
|
|
9405
|
+
app.get("/api/scopes/:hash/file-lines", (c) => {
|
|
9406
|
+
const scope = getScope(c.req.param("hash"));
|
|
9407
|
+
if (!scope) return c.json({ error: "unknown scope" }, 404);
|
|
9408
|
+
const repoName = c.req.query("repo") ?? "";
|
|
9409
|
+
const relPath = c.req.query("path") ?? "";
|
|
9410
|
+
const start = Number(c.req.query("start"));
|
|
9411
|
+
const end = Number(c.req.query("end"));
|
|
9412
|
+
const ref = c.req.query("ref") || void 0;
|
|
9413
|
+
if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
9414
|
+
return c.json({ error: "bad path/start/end" }, 400);
|
|
9415
|
+
}
|
|
9416
|
+
const root = scope.paths.length === 1 ? scope.paths[0] : scope.paths.find((p) => path40.basename(p) === repoName);
|
|
9417
|
+
if (!root) return c.json({ error: "unknown repo" }, 404);
|
|
9418
|
+
const result = readContextLines({ root, relPath, start, end, ref });
|
|
9419
|
+
if (!result) return c.json({ error: "cannot read file" }, 400);
|
|
9420
|
+
return c.json(result);
|
|
9421
|
+
});
|
|
9228
9422
|
app.get("/api/scopes/:hash/checkpoints", (c) => {
|
|
9229
9423
|
const scope = getScope(c.req.param("hash"));
|
|
9230
9424
|
if (!scope) return c.json({ error: "unknown scope" }, 404);
|
|
@@ -9453,9 +9647,11 @@ function sessionToWire(s) {
|
|
|
9453
9647
|
};
|
|
9454
9648
|
}
|
|
9455
9649
|
function computeSessionDiff(s, base) {
|
|
9456
|
-
const resolved = s.paths.map(
|
|
9650
|
+
const resolved = s.paths.map(
|
|
9651
|
+
(p) => resolveRepoDiff(p, base, s.baseBranches?.[p] ?? s.baseBranch)
|
|
9652
|
+
);
|
|
9457
9653
|
const repos = s.paths.map((p, i) => ({
|
|
9458
|
-
name:
|
|
9654
|
+
name: path41.basename(p),
|
|
9459
9655
|
root: p,
|
|
9460
9656
|
resolvedBase: resolved[i].resolvedBase,
|
|
9461
9657
|
files: computeDiff({ root: p, diffArg: resolved[i].diffArg })
|
|
@@ -9475,17 +9671,17 @@ async function startWebServer(opts = {}) {
|
|
|
9475
9671
|
for (const cb of sseListeners) cb({ event, data });
|
|
9476
9672
|
};
|
|
9477
9673
|
const home = os12.homedir();
|
|
9478
|
-
const historyPath =
|
|
9674
|
+
const historyPath = path41.join(home, ".work", "history.json");
|
|
9479
9675
|
const onHistoryChange = () => broadcast("sessions-changed", { ts: Date.now() });
|
|
9480
|
-
|
|
9481
|
-
const tasksPath =
|
|
9676
|
+
fs40.watchFile(historyPath, { interval: 1e3 }, onHistoryChange);
|
|
9677
|
+
const tasksPath = path41.join(home, ".work", "tasks.json");
|
|
9482
9678
|
const onTasksChange = () => broadcast("tasks-changed", { ts: Date.now() });
|
|
9483
|
-
|
|
9679
|
+
fs40.watchFile(tasksPath, { interval: 1e3 }, onTasksChange);
|
|
9484
9680
|
const projectsRoot = claudeProjectsRoot();
|
|
9485
9681
|
let activityWatcher = null;
|
|
9486
9682
|
if (!lean) {
|
|
9487
9683
|
try {
|
|
9488
|
-
if (
|
|
9684
|
+
if (fs40.existsSync(projectsRoot)) {
|
|
9489
9685
|
activityWatcher = createFsWatcher({
|
|
9490
9686
|
roots: [projectsRoot],
|
|
9491
9687
|
debounceMs: 250,
|
|
@@ -9560,8 +9756,8 @@ async function startWebServer(opts = {}) {
|
|
|
9560
9756
|
url: handle.url,
|
|
9561
9757
|
port: handle.port,
|
|
9562
9758
|
stop: async () => {
|
|
9563
|
-
|
|
9564
|
-
|
|
9759
|
+
fs40.unwatchFile(historyPath, onHistoryChange);
|
|
9760
|
+
fs40.unwatchFile(tasksPath, onTasksChange);
|
|
9565
9761
|
if (decayTick) clearInterval(decayTick);
|
|
9566
9762
|
activityWatcher?.stop();
|
|
9567
9763
|
disposeAllWatchers();
|
|
@@ -9623,10 +9819,10 @@ function info2(message) {
|
|
|
9623
9819
|
process.stderr.write(message + "\n");
|
|
9624
9820
|
}
|
|
9625
9821
|
function urlFilePath() {
|
|
9626
|
-
return
|
|
9822
|
+
return path42.join(os13.homedir(), ".work", "web.url");
|
|
9627
9823
|
}
|
|
9628
9824
|
function pidFilePath() {
|
|
9629
|
-
return
|
|
9825
|
+
return path42.join(os13.homedir(), ".work", "web.pid");
|
|
9630
9826
|
}
|
|
9631
9827
|
function isPidAlive(pid) {
|
|
9632
9828
|
try {
|
|
@@ -9638,7 +9834,7 @@ function isPidAlive(pid) {
|
|
|
9638
9834
|
}
|
|
9639
9835
|
function readPid() {
|
|
9640
9836
|
try {
|
|
9641
|
-
const raw =
|
|
9837
|
+
const raw = fs41.readFileSync(pidFilePath(), "utf-8").trim();
|
|
9642
9838
|
const n = Number(raw);
|
|
9643
9839
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
9644
9840
|
} catch {
|
|
@@ -9647,7 +9843,7 @@ function readPid() {
|
|
|
9647
9843
|
}
|
|
9648
9844
|
function readUrl() {
|
|
9649
9845
|
try {
|
|
9650
|
-
const v =
|
|
9846
|
+
const v = fs41.readFileSync(urlFilePath(), "utf-8").trim();
|
|
9651
9847
|
return v || null;
|
|
9652
9848
|
} catch {
|
|
9653
9849
|
return null;
|
|
@@ -9673,11 +9869,11 @@ function stopExisting() {
|
|
|
9673
9869
|
if (!isPidAlive(pid)) {
|
|
9674
9870
|
info2(chalk23.gray(`Stale PID ${pid} \u2014 cleaning up.`));
|
|
9675
9871
|
try {
|
|
9676
|
-
|
|
9872
|
+
fs41.unlinkSync(pidFilePath());
|
|
9677
9873
|
} catch {
|
|
9678
9874
|
}
|
|
9679
9875
|
try {
|
|
9680
|
-
|
|
9876
|
+
fs41.unlinkSync(urlFilePath());
|
|
9681
9877
|
} catch {
|
|
9682
9878
|
}
|
|
9683
9879
|
return false;
|
|
@@ -9686,11 +9882,11 @@ function stopExisting() {
|
|
|
9686
9882
|
process.kill(pid);
|
|
9687
9883
|
info2(chalk23.gray(`Stopped work web (PID ${pid}).`));
|
|
9688
9884
|
try {
|
|
9689
|
-
|
|
9885
|
+
fs41.unlinkSync(pidFilePath());
|
|
9690
9886
|
} catch {
|
|
9691
9887
|
}
|
|
9692
9888
|
try {
|
|
9693
|
-
|
|
9889
|
+
fs41.unlinkSync(urlFilePath());
|
|
9694
9890
|
} catch {
|
|
9695
9891
|
}
|
|
9696
9892
|
return true;
|
|
@@ -9741,19 +9937,19 @@ var webCommand = {
|
|
|
9741
9937
|
process.exit(1);
|
|
9742
9938
|
}
|
|
9743
9939
|
try {
|
|
9744
|
-
|
|
9940
|
+
fs41.unlinkSync(pidFilePath());
|
|
9745
9941
|
} catch {
|
|
9746
9942
|
}
|
|
9747
9943
|
try {
|
|
9748
|
-
|
|
9944
|
+
fs41.unlinkSync(urlFilePath());
|
|
9749
9945
|
} catch {
|
|
9750
9946
|
}
|
|
9751
9947
|
const lean = !!argv.lean || process.env.WORK_WEB_LEAN === "1";
|
|
9752
9948
|
const handle = await startWebServer({ lean });
|
|
9753
9949
|
try {
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9950
|
+
fs41.mkdirSync(path42.dirname(urlFilePath()), { recursive: true });
|
|
9951
|
+
fs41.writeFileSync(urlFilePath(), handle.url);
|
|
9952
|
+
fs41.writeFileSync(pidFilePath(), String(process.pid));
|
|
9757
9953
|
} catch {
|
|
9758
9954
|
}
|
|
9759
9955
|
info2(
|
|
@@ -9783,11 +9979,11 @@ var webCommand = {
|
|
|
9783
9979
|
const shutdown = () => {
|
|
9784
9980
|
info2(chalk23.gray("\nStopping work web."));
|
|
9785
9981
|
try {
|
|
9786
|
-
|
|
9982
|
+
fs41.unlinkSync(urlFilePath());
|
|
9787
9983
|
} catch {
|
|
9788
9984
|
}
|
|
9789
9985
|
try {
|
|
9790
|
-
|
|
9986
|
+
fs41.unlinkSync(pidFilePath());
|
|
9791
9987
|
} catch {
|
|
9792
9988
|
}
|
|
9793
9989
|
if (!lean) {
|
|
@@ -9807,11 +10003,11 @@ var webCommand = {
|
|
|
9807
10003
|
process.on("SIGTERM", shutdown);
|
|
9808
10004
|
process.on("exit", () => {
|
|
9809
10005
|
try {
|
|
9810
|
-
|
|
10006
|
+
fs41.unlinkSync(pidFilePath());
|
|
9811
10007
|
} catch {
|
|
9812
10008
|
}
|
|
9813
10009
|
try {
|
|
9814
|
-
|
|
10010
|
+
fs41.unlinkSync(urlFilePath());
|
|
9815
10011
|
} catch {
|
|
9816
10012
|
}
|
|
9817
10013
|
});
|
|
@@ -9886,7 +10082,7 @@ var hookCommand = {
|
|
|
9886
10082
|
|
|
9887
10083
|
// src/commands/run.ts
|
|
9888
10084
|
import chalk24 from "chalk";
|
|
9889
|
-
import
|
|
10085
|
+
import spawn14 from "cross-spawn";
|
|
9890
10086
|
|
|
9891
10087
|
// src/core/fleet.ts
|
|
9892
10088
|
function selectSessions(sessions, filter) {
|
|
@@ -9928,7 +10124,7 @@ function killAllChildren(signal = "SIGTERM") {
|
|
|
9928
10124
|
function runInPath(unit, cmd, prefix) {
|
|
9929
10125
|
const { bin, args } = shellInvocation(cmd);
|
|
9930
10126
|
return new Promise((resolve) => {
|
|
9931
|
-
const child =
|
|
10127
|
+
const child = spawn14(bin, args, {
|
|
9932
10128
|
cwd: unit.path,
|
|
9933
10129
|
stdio: prefix ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
9934
10130
|
shell: false
|
|
@@ -10160,15 +10356,15 @@ var runCommand = {
|
|
|
10160
10356
|
};
|
|
10161
10357
|
|
|
10162
10358
|
// src/commands/broadcast.ts
|
|
10163
|
-
import
|
|
10359
|
+
import fs43 from "fs";
|
|
10164
10360
|
import chalk25 from "chalk";
|
|
10165
10361
|
|
|
10166
10362
|
// src/core/broadcast.ts
|
|
10167
10363
|
import crypto6 from "crypto";
|
|
10168
|
-
import
|
|
10364
|
+
import fs42 from "fs";
|
|
10169
10365
|
function readComments(file) {
|
|
10170
10366
|
try {
|
|
10171
|
-
const parsed = JSON.parse(
|
|
10367
|
+
const parsed = JSON.parse(fs42.readFileSync(file, "utf-8"));
|
|
10172
10368
|
return Array.isArray(parsed) ? parsed : [];
|
|
10173
10369
|
} catch {
|
|
10174
10370
|
return [];
|
|
@@ -10176,7 +10372,7 @@ function readComments(file) {
|
|
|
10176
10372
|
}
|
|
10177
10373
|
async function appendLocked(sessionId, body) {
|
|
10178
10374
|
const file = commentsFileFor(sessionId);
|
|
10179
|
-
|
|
10375
|
+
fs42.mkdirSync(commentsDir(), { recursive: true });
|
|
10180
10376
|
ensureFile(file, "[]");
|
|
10181
10377
|
const comment = {
|
|
10182
10378
|
id: crypto6.randomBytes(8).toString("hex"),
|
|
@@ -10213,7 +10409,7 @@ async function broadcastPrompt(sessions, filter, prompt) {
|
|
|
10213
10409
|
// src/commands/broadcast.ts
|
|
10214
10410
|
function readStdin() {
|
|
10215
10411
|
try {
|
|
10216
|
-
return
|
|
10412
|
+
return fs43.readFileSync(0, "utf-8");
|
|
10217
10413
|
} catch {
|
|
10218
10414
|
return "";
|
|
10219
10415
|
}
|
|
@@ -10288,8 +10484,8 @@ var broadcastCommand = {
|
|
|
10288
10484
|
};
|
|
10289
10485
|
|
|
10290
10486
|
// src/completions/index.ts
|
|
10291
|
-
import
|
|
10292
|
-
import
|
|
10487
|
+
import fs44 from "fs";
|
|
10488
|
+
import path43 from "path";
|
|
10293
10489
|
function completionHandler(current, argv, done) {
|
|
10294
10490
|
const rawArgs = argv._;
|
|
10295
10491
|
const args = rawArgs.slice(1, -1);
|
|
@@ -10393,7 +10589,7 @@ function completeTreeRemoveList(command, args, current, config, done) {
|
|
|
10393
10589
|
}
|
|
10394
10590
|
function completeRepoBranches(alias, current, config, done) {
|
|
10395
10591
|
const repoPath = config.repos[alias];
|
|
10396
|
-
if (!repoPath || !
|
|
10592
|
+
if (!repoPath || !fs44.existsSync(repoPath)) {
|
|
10397
10593
|
done([]);
|
|
10398
10594
|
return;
|
|
10399
10595
|
}
|
|
@@ -10402,19 +10598,19 @@ function completeRepoBranches(alias, current, config, done) {
|
|
|
10402
10598
|
done(branches);
|
|
10403
10599
|
}
|
|
10404
10600
|
function completeGroupBranches(groupName, repoAliases, current, config, done) {
|
|
10405
|
-
const groupDir =
|
|
10406
|
-
if (!
|
|
10601
|
+
const groupDir = path43.join(config.worktreesRoot, groupName);
|
|
10602
|
+
if (!fs44.existsSync(groupDir)) {
|
|
10407
10603
|
done([]);
|
|
10408
10604
|
return;
|
|
10409
10605
|
}
|
|
10410
10606
|
try {
|
|
10411
|
-
const branchDirs =
|
|
10412
|
-
const bdPath =
|
|
10607
|
+
const branchDirs = fs44.readdirSync(groupDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
10608
|
+
const bdPath = path43.join(groupDir, d.name);
|
|
10413
10609
|
for (const alias of repoAliases) {
|
|
10414
10610
|
const repoPath = config.repos[alias];
|
|
10415
10611
|
if (!repoPath) continue;
|
|
10416
|
-
const subPath =
|
|
10417
|
-
if (
|
|
10612
|
+
const subPath = path43.join(bdPath, path43.basename(repoPath));
|
|
10613
|
+
if (fs44.existsSync(subPath)) {
|
|
10418
10614
|
const branch = getCurrentBranch(subPath);
|
|
10419
10615
|
if (branch) return branch;
|
|
10420
10616
|
}
|
|
@@ -10428,7 +10624,7 @@ function completeGroupBranches(groupName, repoAliases, current, config, done) {
|
|
|
10428
10624
|
}
|
|
10429
10625
|
|
|
10430
10626
|
// src/version.ts
|
|
10431
|
-
var VERSION = true ? "1.
|
|
10627
|
+
var VERSION = true ? "1.7.0" : "dev";
|
|
10432
10628
|
|
|
10433
10629
|
// src/cli.ts
|
|
10434
10630
|
function showHelp() {
|