@youtyan/code-viewer 0.8.5 → 0.8.7

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.
@@ -996,11 +996,13 @@ function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MA
996
996
  cache.delete(oldest);
997
997
  }
998
998
  }
999
+ function fileSignatureFromStats(stats) {
1000
+ const inode = stats.ino ?? 0;
1001
+ return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
1002
+ }
999
1003
  function worktreeFileSignature(path, cwd) {
1000
1004
  try {
1001
- const stats = lstatSync(join3(cwd, path));
1002
- const inode = "ino" in stats ? stats.ino : 0;
1003
- return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
1005
+ return fileSignatureFromStats(lstatSync(join3(cwd, path)));
1004
1006
  } catch {
1005
1007
  return "state:missing";
1006
1008
  }
@@ -1407,17 +1409,15 @@ var init_runtime = () => {};
1407
1409
 
1408
1410
  // web-src/server/git.ts
1409
1411
  import {
1410
- closeSync,
1411
1412
  existsSync,
1412
1413
  lstatSync as lstatSync2,
1413
- openSync,
1414
1414
  readdirSync,
1415
1415
  readFileSync,
1416
1416
  readlinkSync,
1417
- readSync,
1418
1417
  realpathSync as realpathSync2,
1419
1418
  statSync as statSync2
1420
1419
  } from "node:fs";
1420
+ import { open, stat } from "node:fs/promises";
1421
1421
  import { dirname as dirname3, join as join4, posix, relative as relative2 } from "node:path";
1422
1422
  function normalizeBlameRef(ref, base) {
1423
1423
  const rawRef = ref || "worktree";
@@ -1449,18 +1449,13 @@ function resolveGitArgs(args) {
1449
1449
  return [commandForExternal("git"), ...args.slice(1)];
1450
1450
  }
1451
1451
  function gitFailureMessage(res, fallback) {
1452
- if (isCommandNotFoundResult("git", res))
1453
- return commandNotFoundDetail("git");
1454
- return res.stderr?.trim() || fallback;
1452
+ const message = isCommandNotFoundResult("git", res) ? commandNotFoundDetail("git") : res.stderr?.trim() || fallback;
1453
+ console.error(`[code-viewer] ${fallback} (git exit ${res.code}): ${message}`);
1454
+ return message;
1455
1455
  }
1456
1456
  function gitFailureResult(res, fallback) {
1457
- if (!isCommandNotFoundResult("git", res)) {
1458
- return { error: fallback };
1459
- }
1460
- return {
1461
- error: commandNotFoundDetail("git"),
1462
- status: 503
1463
- };
1457
+ const error = gitFailureMessage(res, fallback);
1458
+ return isCommandNotFoundResult("git", res) ? { error, status: 503 } : { error };
1464
1459
  }
1465
1460
  function runGitRefLookup(args, cwd) {
1466
1461
  const res = run(args, cwd);
@@ -1485,9 +1480,6 @@ function repoRootResult(cwd) {
1485
1480
  return { kind: "outside" };
1486
1481
  return { kind: "error", error: stderr || "git rev-parse failed" };
1487
1482
  }
1488
- function currentBranch(cwd) {
1489
- return runGitRefLookup(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
1490
- }
1491
1483
  function currentBranchAsync(cwd) {
1492
1484
  return runGitRefLookupAsync(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
1493
1485
  }
@@ -1555,9 +1547,6 @@ async function repoStatusMapAsync(cwd, now = Date.now()) {
1555
1547
  setTimedCacheEntry(repoStatusMapCache, cwd, { map }, now);
1556
1548
  return map;
1557
1549
  }
1558
- function show(ref, path, cwd) {
1559
- return run(["git", "show", `${ref}:${path}`], cwd);
1560
- }
1561
1550
  function showAsync(ref, path, cwd) {
1562
1551
  return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
1563
1552
  }
@@ -1849,12 +1838,6 @@ function parseRemoteWebUrl(remote) {
1849
1838
  return `https://${httpUrl[1]}/${httpUrl[2]}`;
1850
1839
  return null;
1851
1840
  }
1852
- function remoteWebUrl(cwd) {
1853
- const res = run(["git", "remote", "get-url", "origin"], cwd);
1854
- if (res.code !== 0)
1855
- return null;
1856
- return parseRemoteWebUrl(res.stdout.trim());
1857
- }
1858
1841
  async function remoteWebUrlAsync(cwd) {
1859
1842
  const res = await runGitAsync(["git", "remote", "get-url", "origin"], cwd);
1860
1843
  if (res.code !== 0)
@@ -1923,60 +1906,6 @@ function historyQueryArgs(query) {
1923
1906
  shaTerm: /^[0-9a-f]{4,40}$/i.test(trimmed) ? trimmed : ""
1924
1907
  };
1925
1908
  }
1926
- function commitHistory(cwd, options) {
1927
- const ref = (options.ref || "HEAD").trim();
1928
- if (!ref || ref.startsWith("-") || ref.includes("\x00"))
1929
- return { commits: [], hasMore: false, error: "invalid ref" };
1930
- const verified = run(["git", "rev-parse", "--verify", `${ref}^{commit}`], cwd);
1931
- if (verified.code !== 0)
1932
- return {
1933
- commits: [],
1934
- hasMore: false,
1935
- ...gitFailureResult(verified, "unknown ref")
1936
- };
1937
- const skip = Math.max(0, Math.floor(options.skip) || 0);
1938
- const limit = Math.max(1, Math.min(Math.floor(options.limit) || 1, MAX_HISTORY_LIMIT));
1939
- const { filterArgs, pathspec, shaTerm } = historyQueryArgs(options.query || "");
1940
- const pathFilter = (options.path || "").trim();
1941
- const pathArgs = [];
1942
- if (pathFilter && !pathFilter.includes("\x00") && !pathFilter.startsWith("-")) {
1943
- if (!pathFilter.endsWith("/"))
1944
- pathArgs.push("--follow");
1945
- pathArgs.push("--", pathFilter);
1946
- }
1947
- const res = run([
1948
- "git",
1949
- "log",
1950
- "-z",
1951
- `--skip=${skip}`,
1952
- `--max-count=${limit + 1}`,
1953
- `--format=${HISTORY_FORMAT}`,
1954
- ...filterArgs,
1955
- verified.stdout.trim(),
1956
- ...pathspec,
1957
- ...pathArgs
1958
- ], cwd);
1959
- if (res.code !== 0)
1960
- return {
1961
- commits: [],
1962
- hasMore: false,
1963
- ...gitFailureResult(res, "git log failed")
1964
- };
1965
- let parsed = parseHistoryLog(res.stdout);
1966
- if (shaTerm && skip === 0) {
1967
- const bySha = run(["git", "rev-parse", "--verify", `${shaTerm}^{commit}`], cwd);
1968
- const sha = bySha.code === 0 ? bySha.stdout.trim() : "";
1969
- if (sha) {
1970
- const single = run(["git", "log", "-z", "-1", `--format=${HISTORY_FORMAT}`, sha], cwd);
1971
- if (single.code === 0) {
1972
- const hit = parseHistoryLog(single.stdout);
1973
- parsed = [...hit, ...parsed.filter((c) => c.sha !== sha)];
1974
- }
1975
- }
1976
- }
1977
- const hasMore = parsed.length > limit;
1978
- return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
1979
- }
1980
1909
  async function commitHistoryAsync(cwd, options) {
1981
1910
  const ref = (options.ref || "HEAD").trim();
1982
1911
  if (!ref || ref.startsWith("-") || ref.includes("\x00"))
@@ -2031,50 +1960,6 @@ async function commitHistoryAsync(cwd, options) {
2031
1960
  const hasMore = parsed.length > limit;
2032
1961
  return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
2033
1962
  }
2034
- function nameStatusResult(args, cwd) {
2035
- const res = run([
2036
- "git",
2037
- "-c",
2038
- "core.quotepath=false",
2039
- "diff",
2040
- "--no-color",
2041
- "--no-ext-diff",
2042
- "--find-renames",
2043
- "--name-status",
2044
- "-z",
2045
- ...args
2046
- ], cwd);
2047
- if (res.code !== 0) {
2048
- return {
2049
- files: [],
2050
- error: gitFailureMessage(res, "git diff --name-status failed")
2051
- };
2052
- }
2053
- const parts = res.stdout.split("\x00");
2054
- const files = [];
2055
- for (let i = 0;i < parts.length; ) {
2056
- const status = parts[i++];
2057
- if (!status)
2058
- break;
2059
- const kind = status[0];
2060
- if (kind === "R" || kind === "C") {
2061
- const oldPath = parts[i++] || "";
2062
- const path = parts[i++] || "";
2063
- if (path)
2064
- files.push({
2065
- status: kind,
2066
- old_path: oldPath,
2067
- path,
2068
- similarity: Number(status.slice(1)) || undefined
2069
- });
2070
- } else {
2071
- const path = parts[i++] || "";
2072
- if (path)
2073
- files.push({ status: kind, path });
2074
- }
2075
- }
2076
- return { files };
2077
- }
2078
1963
  async function nameStatusResultAsync(args, cwd) {
2079
1964
  const res = await runGitAsync([
2080
1965
  "git",
@@ -2119,49 +2004,6 @@ async function nameStatusResultAsync(args, cwd) {
2119
2004
  }
2120
2005
  return { files };
2121
2006
  }
2122
- function numstatZResult(args, cwd) {
2123
- const res = run([
2124
- "git",
2125
- "-c",
2126
- "core.quotepath=false",
2127
- "diff",
2128
- "--no-color",
2129
- "--no-ext-diff",
2130
- "--find-renames",
2131
- "--numstat",
2132
- "-z",
2133
- ...args
2134
- ], cwd);
2135
- if (res.code !== 0) {
2136
- return {
2137
- files: [],
2138
- error: gitFailureMessage(res, "git diff --numstat failed")
2139
- };
2140
- }
2141
- const parts = res.stdout.split("\x00");
2142
- const files = [];
2143
- for (let i = 0;i < parts.length; ) {
2144
- const rec = parts[i++];
2145
- if (!rec)
2146
- break;
2147
- const match = rec.match(/^(\S+)\t(\S+)\t(.*)$/);
2148
- if (!match)
2149
- break;
2150
- const [, add, del, rest] = match;
2151
- const binary = add === "-" && del === "-";
2152
- const additions = binary ? 0 : Number(add) || 0;
2153
- const deletions = binary ? 0 : Number(del) || 0;
2154
- if (rest === "") {
2155
- const oldPath = parts[i++] || "";
2156
- const path = parts[i++] || "";
2157
- if (path)
2158
- files.push({ old_path: oldPath, path, additions, deletions, binary });
2159
- } else {
2160
- files.push({ path: rest, additions, deletions, binary });
2161
- }
2162
- }
2163
- return { files };
2164
- }
2165
2007
  async function numstatZResultAsync(args, cwd) {
2166
2008
  const res = await runGitAsync([
2167
2009
  "git",
@@ -2218,8 +2060,8 @@ function isGitInternalPath(path) {
2218
2060
  function syntheticUncommittedBlameFromWorktree(cwd, path) {
2219
2061
  const filePath = join4(cwd, path);
2220
2062
  try {
2221
- const stat = statSync2(filePath);
2222
- if (!stat.isFile())
2063
+ const stat2 = statSync2(filePath);
2064
+ if (!stat2.isFile())
2223
2065
  return { lines: [], commits: {}, error: "not a file" };
2224
2066
  const text = readFileSync(filePath, "utf8");
2225
2067
  const normalized = text.replace(/\r\n/g, `
@@ -2255,98 +2097,6 @@ function syntheticUncommittedBlameFromWorktree(cwd, path) {
2255
2097
  return { lines: [], commits: {}, error: "file not readable" };
2256
2098
  }
2257
2099
  }
2258
- function blame(cwd, options) {
2259
- const path = options.path;
2260
- if (!path || path.includes("\x00") || path.startsWith("-")) {
2261
- return { lines: [], commits: {}, error: "invalid path" };
2262
- }
2263
- const normalized = normalizeBlameRef(options.ref, options.base);
2264
- const args = ["git", "blame", "--porcelain"];
2265
- if (normalized.base === "HEAD") {
2266
- if (normalized.ref.startsWith("-") || normalized.ref.includes("\x00"))
2267
- return { lines: [], commits: {}, error: "invalid ref" };
2268
- args.push(normalized.ref);
2269
- }
2270
- args.push("--", path);
2271
- const res = run(args, cwd);
2272
- if (res.code !== 0) {
2273
- if (isCommandNotFoundResult("git", res)) {
2274
- return {
2275
- lines: [],
2276
- commits: {},
2277
- error: commandNotFoundDetail("git"),
2278
- status: 503
2279
- };
2280
- }
2281
- if (normalized.base === "worktree") {
2282
- return syntheticUncommittedBlameFromWorktree(cwd, path);
2283
- }
2284
- return {
2285
- lines: [],
2286
- commits: {},
2287
- error: res.stderr.trim() || "blame failed"
2288
- };
2289
- }
2290
- const lines = [];
2291
- const commits = {};
2292
- const rawLines = res.stdout.split(`
2293
- `);
2294
- let i = 0;
2295
- while (i < rawLines.length) {
2296
- const headerLine = rawLines[i];
2297
- if (!headerLine) {
2298
- i++;
2299
- continue;
2300
- }
2301
- const headerMatch = /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(headerLine);
2302
- if (!headerMatch) {
2303
- i++;
2304
- continue;
2305
- }
2306
- const sha = headerMatch[1];
2307
- const finalLine = Number(headerMatch[3]);
2308
- i++;
2309
- let commit = commits[sha];
2310
- if (!commit) {
2311
- commit = {
2312
- sha,
2313
- author: "",
2314
- authorMail: "",
2315
- authorTime: 0,
2316
- summary: "",
2317
- isUncommitted: sha === BLAME_ZERO_SHA
2318
- };
2319
- commits[sha] = commit;
2320
- }
2321
- while (i < rawLines.length && !rawLines[i].startsWith("\t")) {
2322
- const metaLine = rawLines[i++];
2323
- if (!metaLine)
2324
- continue;
2325
- const sp = metaLine.indexOf(" ");
2326
- const key = sp >= 0 ? metaLine.slice(0, sp) : metaLine;
2327
- const val = sp >= 0 ? metaLine.slice(sp + 1) : "";
2328
- if (key === "author" && !commit.author)
2329
- commit.author = val;
2330
- else if (key === "author-mail" && !commit.authorMail)
2331
- commit.authorMail = val.replace(/^</, "").replace(/>$/, "");
2332
- else if (key === "author-time" && !commit.authorTime)
2333
- commit.authorTime = Number(val) || 0;
2334
- else if (key === "summary" && !commit.summary)
2335
- commit.summary = val;
2336
- }
2337
- if (i < rawLines.length && rawLines[i].startsWith("\t"))
2338
- i++;
2339
- if (Number.isFinite(finalLine) && finalLine > 0) {
2340
- lines.push({
2341
- lineNo: finalLine,
2342
- sha,
2343
- isUncommitted: sha === BLAME_ZERO_SHA
2344
- });
2345
- }
2346
- }
2347
- lines.sort((a, b) => a.lineNo - b.lineNo);
2348
- return { lines, commits };
2349
- }
2350
2100
  async function blameAsync(cwd, options) {
2351
2101
  const path = options.path;
2352
2102
  if (!path || path.includes("\x00") || path.startsWith("-")) {
@@ -2439,16 +2189,6 @@ async function blameAsync(cwd, options) {
2439
2189
  lines.sort((a, b) => a.lineNo - b.lineNo);
2440
2190
  return { lines, commits };
2441
2191
  }
2442
- function untracked(cwd, path = "") {
2443
- const args = ["git", "ls-files", "--others", "--exclude-standard"];
2444
- if (path)
2445
- args.push("--", `${path}/`);
2446
- const res = run(args, cwd);
2447
- if (res.code !== 0)
2448
- return [];
2449
- return res.stdout.split(`
2450
- `).filter(Boolean).filter((entry) => !isToolInternalPath(entry));
2451
- }
2452
2192
  async function untrackedAsync(cwd, path = "") {
2453
2193
  const args = ["git", "ls-files", "--others", "--exclude-standard"];
2454
2194
  if (path)
@@ -2474,18 +2214,6 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
2474
2214
  return "internal";
2475
2215
  return omitDirNames.matches(name) ? "heavy" : undefined;
2476
2216
  }
2477
- function worktreeSubmodulePaths(cwd) {
2478
- if (!existsSync(join4(cwd, ".gitmodules")))
2479
- return new Set;
2480
- const res = run(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
2481
- if (res.code !== 0)
2482
- return new Set;
2483
- return new Set(res.stdout.split(`
2484
- `).map((line) => {
2485
- const split = line.indexOf(" ");
2486
- return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
2487
- }).filter(Boolean));
2488
- }
2489
2217
  async function worktreeSubmodulePathsAsync(cwd) {
2490
2218
  if (!existsSync(join4(cwd, ".gitmodules")))
2491
2219
  return new Set;
@@ -2522,8 +2250,8 @@ function resolveWorktreeSymlinkTarget(cwd, full) {
2522
2250
  let symlink_target_type = "missing";
2523
2251
  if (realpathWithinRepo(cwd, full, false) !== null) {
2524
2252
  try {
2525
- const stat = statSync2(full);
2526
- symlink_target_type = stat.isDirectory() ? "tree" : stat.isFile() ? "blob" : "missing";
2253
+ const stat2 = statSync2(full);
2254
+ symlink_target_type = stat2.isDirectory() ? "tree" : stat2.isFile() ? "blob" : "missing";
2527
2255
  } catch {
2528
2256
  symlink_target_type = "missing";
2529
2257
  }
@@ -2565,83 +2293,6 @@ function worktreeEntryFromDirent(cwd, base, dir, name, isDirectory, isSymlink, o
2565
2293
  children_omitted_reason: omittedReason
2566
2294
  } : baseEntry;
2567
2295
  }
2568
- function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
2569
- const base = normalizeTreePath(path);
2570
- const root = join4(cwd, base);
2571
- if (realpathWithinRepo(cwd, root, true) === null)
2572
- return [];
2573
- const omitDirNameSet = compileNamePatterns(omitDirNames);
2574
- const excludeNameSet = compileNamePatterns(excludeNames);
2575
- const submodulePaths = worktreeSubmodulePaths(cwd);
2576
- let directEntries;
2577
- try {
2578
- const dirents = readdirSync(root, { withFileTypes: true });
2579
- directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
2580
- } catch {
2581
- return [];
2582
- }
2583
- if (!recursive)
2584
- return directEntries;
2585
- const fileEntries = [];
2586
- let truncated = false;
2587
- const pushRecursiveEntry = (entry) => {
2588
- if (fileEntries.length >= WORKTREE_RECURSIVE_ENTRY_LIMIT) {
2589
- if (!truncated) {
2590
- fileEntries.push({
2591
- name: "more...",
2592
- path: "__code_viewer_truncated__",
2593
- type: "tree",
2594
- children_omitted: true,
2595
- children_omitted_reason: "truncated"
2596
- });
2597
- truncated = true;
2598
- }
2599
- return false;
2600
- }
2601
- fileEntries.push(entry);
2602
- return true;
2603
- };
2604
- const walk = (dir, prefix, depth) => {
2605
- if (truncated)
2606
- return;
2607
- if (depth >= WORKTREE_RECURSIVE_DEPTH_LIMIT)
2608
- return;
2609
- let entries;
2610
- try {
2611
- entries = readdirSync(dir, { withFileTypes: true });
2612
- } catch {
2613
- return;
2614
- }
2615
- for (const entry of entries) {
2616
- if (excludeNameSet.matches(entry.name))
2617
- continue;
2618
- const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
2619
- const full = join4(dir, entry.name);
2620
- if (entry.isDirectory()) {
2621
- const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
2622
- if (omittedReason) {
2623
- if (!pushRecursiveEntry({
2624
- name: entry.name,
2625
- path: entryPath,
2626
- type: "tree",
2627
- children_omitted: true,
2628
- children_omitted_reason: omittedReason
2629
- }))
2630
- return;
2631
- continue;
2632
- }
2633
- if (hasDotGitEntry(full))
2634
- continue;
2635
- walk(full, entryPath, depth + 1);
2636
- } else if (entry.isFile() || entry.isSymbolicLink()) {
2637
- if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
2638
- return;
2639
- }
2640
- }
2641
- };
2642
- walk(root, base, 0);
2643
- return combineDirectAndRecursiveFiles(directEntries, fileEntries.sort((a, b) => a.path.localeCompare(b.path)));
2644
- }
2645
2296
  async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
2646
2297
  const base = normalizeTreePath(path);
2647
2298
  const root = join4(cwd, base);
@@ -2747,25 +2398,6 @@ function parseLsTreeRecord(rec, allowedTypes) {
2747
2398
  ...mode === LS_TREE_SYMLINK_MODE ? { is_symlink: true } : {}
2748
2399
  };
2749
2400
  }
2750
- function gitTreeEntries(ref, path, cwd, recursive) {
2751
- const base = normalizeTreePath(path);
2752
- const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
2753
- if (recursive)
2754
- args.push("-r");
2755
- args.push("-z", "--full-tree", ref, "--");
2756
- if (base)
2757
- args.push(`${base}/`);
2758
- const res = run(args, cwd);
2759
- if (res.code !== 0)
2760
- return { code: res.code, entries: [], stderr: res.stderr };
2761
- const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
2762
- let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
2763
- if (recursive)
2764
- entries.sort((a, b) => a.path.localeCompare(b.path));
2765
- else
2766
- entries = sortTreeEntries(entries);
2767
- return { code: 0, entries, stderr: "" };
2768
- }
2769
2401
  async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
2770
2402
  const base = normalizeTreePath(path);
2771
2403
  const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
@@ -2778,40 +2410,19 @@ async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
2778
2410
  if (res.code !== 0)
2779
2411
  return { code: res.code, entries: [], stderr: res.stderr };
2780
2412
  const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
2781
- let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
2782
- if (recursive)
2783
- entries.sort((a, b) => a.path.localeCompare(b.path));
2784
- else
2785
- entries = sortTreeEntries(entries);
2786
- return { code: 0, entries, stderr: "" };
2787
- }
2788
- function combineDirectAndRecursiveFiles(directEntries, fileEntries) {
2789
- const seen = new Set(directEntries.map((entry) => entry.path));
2790
- return [
2791
- ...directEntries,
2792
- ...fileEntries.filter((entry) => !seen.has(entry.path))
2793
- ];
2794
- }
2795
- function listTree(ref, path, cwd, options = {}) {
2796
- const base = normalizeTreePath(path);
2797
- if (ref === "worktree") {
2798
- return {
2799
- code: 0,
2800
- entries: worktreeFilesystemEntries(cwd, base, !!options.recursive, options.omitDirNames, options.excludeNames),
2801
- stderr: ""
2802
- };
2803
- }
2804
- const direct = gitTreeEntries(ref, base, cwd, false);
2805
- if (direct.code !== 0 || !options.recursive)
2806
- return direct;
2807
- const recursive = gitTreeEntries(ref, base, cwd, true);
2808
- if (recursive.code !== 0)
2809
- return recursive;
2810
- return {
2811
- code: 0,
2812
- entries: combineDirectAndRecursiveFiles(direct.entries, recursive.entries),
2813
- stderr: ""
2814
- };
2413
+ let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
2414
+ if (recursive)
2415
+ entries.sort((a, b) => a.path.localeCompare(b.path));
2416
+ else
2417
+ entries = sortTreeEntries(entries);
2418
+ return { code: 0, entries, stderr: "" };
2419
+ }
2420
+ function combineDirectAndRecursiveFiles(directEntries, fileEntries) {
2421
+ const seen = new Set(directEntries.map((entry) => entry.path));
2422
+ return [
2423
+ ...directEntries,
2424
+ ...fileEntries.filter((entry) => !seen.has(entry.path))
2425
+ ];
2815
2426
  }
2816
2427
  async function listTreeAsync(ref, path, cwd, options = {}) {
2817
2428
  const base = normalizeTreePath(path);
@@ -2840,121 +2451,97 @@ async function listTreeResultAsync(ref, path, cwd, options = {}) {
2840
2451
  return { entries: result.entries };
2841
2452
  return { entries: [], ...gitFailureResult(result, "git ls-tree failed") };
2842
2453
  }
2843
- function untrackedMeta(cwd) {
2844
- return untracked(cwd).flatMap((path) => {
2845
- const full = join4(cwd, path);
2846
- let fileExists = false;
2847
- try {
2848
- fileExists = existsSync(full) && statSync2(full).isFile();
2849
- } catch {
2850
- fileExists = false;
2851
- }
2852
- let scan;
2853
- if (fileExists) {
2854
- try {
2855
- scan = scanFileBinaryAndNewlines(full);
2856
- } catch {
2857
- return [];
2858
- }
2859
- } else {
2860
- return [];
2861
- }
2862
- return [
2863
- {
2864
- path,
2865
- status: "A",
2866
- additions: scan.binary ? 0 : scan.newlines,
2867
- deletions: 0,
2868
- binary: scan.binary,
2869
- untracked: true
2870
- }
2871
- ];
2872
- });
2873
- }
2874
- async function untrackedMetaAsync(cwd) {
2875
- const paths = await untrackedAsync(cwd);
2876
- return paths.flatMap((path) => {
2877
- const full = join4(cwd, path);
2878
- let fileExists = false;
2879
- try {
2880
- fileExists = existsSync(full) && statSync2(full).isFile();
2881
- } catch {
2882
- fileExists = false;
2883
- }
2884
- let scan;
2885
- if (fileExists) {
2886
- try {
2887
- scan = scanFileBinaryAndNewlines(full);
2888
- } catch {
2889
- return [];
2890
- }
2891
- } else {
2892
- return [];
2893
- }
2894
- return [
2895
- {
2896
- path,
2897
- status: "A",
2898
- additions: scan.binary ? 0 : scan.newlines,
2899
- deletions: 0,
2900
- binary: scan.binary,
2901
- untracked: true
2902
- }
2903
- ];
2904
- });
2454
+ function untrackedFileMeta(path, scan) {
2455
+ return {
2456
+ path,
2457
+ status: "A",
2458
+ additions: scan.binary ? 0 : scan.newlines,
2459
+ deletions: 0,
2460
+ binary: scan.binary,
2461
+ untracked: true
2462
+ };
2905
2463
  }
2906
- function scanFileBinaryAndNewlines(full) {
2907
- const fd = openSync(full, "r");
2464
+ async function scanFileBinaryAndNewlinesAsync(full) {
2465
+ const handle = await open(full, "r");
2908
2466
  const buffer = Buffer.allocUnsafe(64 * 1024);
2909
2467
  let newlines = 0;
2910
2468
  let inspected = 0;
2911
2469
  try {
2912
2470
  while (true) {
2913
- const read = readSync(fd, buffer, 0, buffer.length, null);
2914
- if (read <= 0)
2471
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
2472
+ if (bytesRead <= 0)
2915
2473
  break;
2916
- const binaryProbeBytes = Math.min(read, Math.max(0, 8192 - inspected));
2474
+ const binaryProbeBytes = Math.min(bytesRead, Math.max(0, 8192 - inspected));
2917
2475
  for (let i = 0;i < binaryProbeBytes; i++) {
2918
2476
  if (buffer[i] === 0)
2919
2477
  return { binary: true, newlines: 0 };
2920
2478
  }
2921
- inspected += read;
2922
- for (let i = 0;i < read; i++) {
2479
+ inspected += bytesRead;
2480
+ for (let i = 0;i < bytesRead; i++) {
2923
2481
  if (buffer[i] === 10)
2924
2482
  newlines++;
2925
2483
  }
2926
2484
  }
2927
2485
  } finally {
2928
- closeSync(fd);
2486
+ await handle.close();
2929
2487
  }
2930
2488
  return { binary: false, newlines };
2931
2489
  }
2932
- function fileMetaResult(args, cwd, includeUntracked = false) {
2933
- const ns = nameStatusResult(args, cwd);
2934
- if (ns.error)
2935
- return { files: [], error: ns.error };
2936
- const nm = numstatZResult(args, cwd);
2937
- if (nm.error)
2938
- return { files: [], error: nm.error };
2939
- const byPath = new Map(nm.files.map((file) => [file.path, file]));
2940
- const files = ns.files.map((file) => {
2941
- const stats = byPath.get(file.path);
2942
- return {
2943
- ...file,
2944
- additions: stats?.additions || 0,
2945
- deletions: stats?.deletions || 0,
2946
- binary: stats?.binary || false
2947
- };
2948
- });
2949
- return {
2950
- files: includeUntracked ? files.concat(untrackedMeta(cwd)) : files
2490
+ async function untrackedMetaAsync(cwd) {
2491
+ const paths = await untrackedAsync(cwd);
2492
+ const previous = untrackedScanCache.get(cwd);
2493
+ const next = new Map;
2494
+ const results = new Array(paths.length).fill(null);
2495
+ let cursor = 0;
2496
+ const worker = async () => {
2497
+ while (cursor < paths.length) {
2498
+ const index = cursor++;
2499
+ const path = paths[index];
2500
+ const full = join4(cwd, path);
2501
+ let stats;
2502
+ try {
2503
+ stats = await stat(full);
2504
+ } catch {
2505
+ continue;
2506
+ }
2507
+ if (!stats.isFile())
2508
+ continue;
2509
+ const signature = fileSignatureFromStats(stats);
2510
+ const cached = previous?.get(path);
2511
+ let scan;
2512
+ if (cached && cached.signature === signature) {
2513
+ scan = cached.scan;
2514
+ } else {
2515
+ try {
2516
+ scan = await scanFileBinaryAndNewlinesAsync(full);
2517
+ } catch {
2518
+ continue;
2519
+ }
2520
+ }
2521
+ next.set(path, { signature, scan });
2522
+ results[index] = untrackedFileMeta(path, scan);
2523
+ }
2951
2524
  };
2525
+ await Promise.all(Array.from({
2526
+ length: Math.min(UNTRACKED_SCAN_CONCURRENCY, Math.max(1, paths.length))
2527
+ }, () => worker()));
2528
+ untrackedScanCache.set(cwd, next);
2529
+ return results.filter((meta) => meta !== null);
2952
2530
  }
2953
2531
  async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
2954
- const ns = await nameStatusResultAsync(args, cwd);
2532
+ let ns;
2533
+ let nm;
2534
+ if (args.includes("--")) {
2535
+ [ns, nm] = await Promise.all([
2536
+ nameStatusResultAsync(args, cwd),
2537
+ numstatZResultAsync(args, cwd)
2538
+ ]);
2539
+ } else {
2540
+ ns = await nameStatusResultAsync(args, cwd);
2541
+ nm = await numstatZResultAsync(args, cwd);
2542
+ }
2955
2543
  if (ns.error)
2956
2544
  return { files: [], error: ns.error };
2957
- const nm = await numstatZResultAsync(args, cwd);
2958
2545
  if (nm.error)
2959
2546
  return { files: [], error: nm.error };
2960
2547
  const byPath = new Map(nm.files.map((file) => [file.path, file]));
@@ -2971,25 +2558,6 @@ async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
2971
2558
  files: includeUntracked ? files.concat(await untrackedMetaAsync(cwd)) : files
2972
2559
  };
2973
2560
  }
2974
- function fileDiffText(args, path, cwd) {
2975
- const paths = Array.isArray(path) ? path : [path];
2976
- const res = run([
2977
- "git",
2978
- "-c",
2979
- "core.quotepath=false",
2980
- "diff",
2981
- "--no-color",
2982
- "--no-ext-diff",
2983
- "--find-renames",
2984
- ...args,
2985
- "--",
2986
- ...paths
2987
- ], cwd);
2988
- if (isCommandNotFoundResult("git", res)) {
2989
- return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
2990
- }
2991
- return res;
2992
- }
2993
2561
  async function fileDiffTextAsync(args, path, cwd) {
2994
2562
  const paths = Array.isArray(path) ? path : [path];
2995
2563
  const res = await runGitAsync([
@@ -3009,24 +2577,6 @@ async function fileDiffTextAsync(args, path, cwd) {
3009
2577
  }
3010
2578
  return res;
3011
2579
  }
3012
- function untrackedFileDiff(extras, path, cwd) {
3013
- const res = run([
3014
- "git",
3015
- "-c",
3016
- "core.quotepath=false",
3017
- "diff",
3018
- "--no-color",
3019
- "--no-ext-diff",
3020
- "--no-index",
3021
- ...extras,
3022
- "/dev/null",
3023
- path
3024
- ], cwd);
3025
- if (isCommandNotFoundResult("git", res)) {
3026
- return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
3027
- }
3028
- return res;
3029
- }
3030
2580
  async function untrackedFileDiffAsync(extras, path, cwd) {
3031
2581
  const res = await runGitAsync([
3032
2582
  "git",
@@ -3122,7 +2672,7 @@ function truncateToNHunks(diffText, n, maxLines = Number.POSITIVE_INFINITY) {
3122
2672
  lineTruncated
3123
2673
  };
3124
2674
  }
3125
- var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000";
2675
+ var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000", untrackedScanCache, UNTRACKED_SCAN_CONCURRENCY = 8;
3126
2676
  var init_git = __esm(() => {
3127
2677
  init_cache();
3128
2678
  init_command_resolver();
@@ -3174,6 +2724,7 @@ var init_git = __esm(() => {
3174
2724
  "obj"
3175
2725
  ];
3176
2726
  repoStatusMapCache = new Map;
2727
+ untrackedScanCache = new Map;
3177
2728
  });
3178
2729
 
3179
2730
  // web-src/server/server-registry.ts
@@ -4417,16 +3968,11 @@ __export(exports_file_cli, {
4417
3968
  safeWorktreePathFromRoot: () => safeWorktreePathFromRoot,
4418
3969
  runFileCli: () => runFileCli,
4419
3970
  readShowTextAsync: () => readShowTextAsync,
4420
- readShowText: () => readShowText,
4421
3971
  parseFileArgs: () => parseFileArgs,
4422
3972
  buildFileShowReportAsync: () => buildFileShowReportAsync,
4423
- buildFileShowReport: () => buildFileShowReport,
4424
3973
  buildFileHistoryReportAsync: () => buildFileHistoryReportAsync,
4425
- buildFileHistoryReport: () => buildFileHistoryReport,
4426
3974
  buildFileDiffReportAsync: () => buildFileDiffReportAsync,
4427
- buildFileDiffReport: () => buildFileDiffReport,
4428
3975
  buildFileBlameReportAsync: () => buildFileBlameReportAsync,
4429
- buildFileBlameReport: () => buildFileBlameReport,
4430
3976
  FILE_HISTORY_HARD_CAP: () => FILE_HISTORY_HARD_CAP,
4431
3977
  FILE_HELP: () => FILE_HELP,
4432
3978
  FILE_DIFF_LINE_HARD_CAP: () => FILE_DIFF_LINE_HARD_CAP,
@@ -4643,14 +4189,14 @@ function parseFileArgs(argv) {
4643
4189
  if (oldPathError)
4644
4190
  return { ok: false, error: oldPathError };
4645
4191
  }
4646
- const untracked2 = flags.has("--untracked");
4647
- if (untracked2 && rawFrom !== undefined) {
4192
+ const untracked = flags.has("--untracked");
4193
+ if (untracked && rawFrom !== undefined) {
4648
4194
  return {
4649
4195
  ok: false,
4650
4196
  error: "--untracked cannot be combined with --from"
4651
4197
  };
4652
4198
  }
4653
- if (untracked2 && to !== "worktree") {
4199
+ if (untracked && to !== "worktree") {
4654
4200
  return {
4655
4201
  ok: false,
4656
4202
  error: "--untracked requires --to worktree (or --to omitted)"
@@ -4694,7 +4240,7 @@ function parseFileArgs(argv) {
4694
4240
  ...oldPathRaw !== undefined ? { oldPath: oldPathRaw } : {},
4695
4241
  from,
4696
4242
  to,
4697
- untracked: untracked2,
4243
+ untracked,
4698
4244
  ignoreWs,
4699
4245
  ignoreBlank,
4700
4246
  mode: full ? "full" : "preview",
@@ -4726,19 +4272,6 @@ function formatBlameText(result) {
4726
4272
  function formatHistoryText(commits) {
4727
4273
  return commits.map((c) => `${c.sha.slice(0, 8)} ${c.when} ${c.author} ${c.subject}`);
4728
4274
  }
4729
- function buildFileBlameReport(root, command) {
4730
- const result = blame(root, {
4731
- path: command.path,
4732
- ref: command.ref,
4733
- base: command.base
4734
- });
4735
- return {
4736
- path: command.path,
4737
- ref: command.ref,
4738
- base: command.base,
4739
- result
4740
- };
4741
- }
4742
4275
  async function buildFileBlameReportAsync(root, command) {
4743
4276
  const result = await blameAsync(root, {
4744
4277
  path: command.path,
@@ -4752,23 +4285,6 @@ async function buildFileBlameReportAsync(root, command) {
4752
4285
  result
4753
4286
  };
4754
4287
  }
4755
- function buildFileHistoryReport(root, command) {
4756
- const result = commitHistory(root, {
4757
- ref: command.ref,
4758
- skip: command.skip,
4759
- limit: command.limit,
4760
- query: command.query,
4761
- path: command.path
4762
- });
4763
- return {
4764
- path: command.path,
4765
- ref: command.ref,
4766
- limit: command.limit,
4767
- skip: command.skip,
4768
- ...command.query !== undefined ? { query: command.query } : {},
4769
- result
4770
- };
4771
- }
4772
4288
  async function buildFileHistoryReportAsync(root, command) {
4773
4289
  const result = await commitHistoryAsync(root, {
4774
4290
  ref: command.ref,
@@ -4786,8 +4302,8 @@ async function buildFileHistoryReportAsync(root, command) {
4786
4302
  result
4787
4303
  };
4788
4304
  }
4789
- function runBlame(root, command) {
4790
- const report = buildFileBlameReport(root, command);
4305
+ async function runBlame(root, command) {
4306
+ const report = await buildFileBlameReportAsync(root, command);
4791
4307
  if (command.json) {
4792
4308
  console.log(JSON.stringify(report, null, 2));
4793
4309
  } else {
@@ -4799,8 +4315,8 @@ function runBlame(root, command) {
4799
4315
  process.exit(1);
4800
4316
  }
4801
4317
  }
4802
- function runHistory(root, command) {
4803
- const report = buildFileHistoryReport(root, command);
4318
+ async function runHistory(root, command) {
4319
+ const report = await buildFileHistoryReportAsync(root, command);
4804
4320
  if (command.json) {
4805
4321
  console.log(JSON.stringify(report, null, 2));
4806
4322
  } else if (report.result.commits.length === 0) {
@@ -4852,28 +4368,6 @@ function safeWorktreePathFromRoot(root, path) {
4852
4368
  return null;
4853
4369
  }
4854
4370
  }
4855
- function readShowText(root, command) {
4856
- if (command.ref !== "worktree" && command.ref !== "") {
4857
- return show(command.ref, command.path, root);
4858
- }
4859
- const full = safeWorktreePathFromRoot(root, command.path);
4860
- if (!full) {
4861
- return {
4862
- code: 1,
4863
- stdout: "",
4864
- stderr: "file not found or forbidden"
4865
- };
4866
- }
4867
- try {
4868
- const stat = statSync3(full);
4869
- if (!stat.isFile()) {
4870
- return { code: 1, stdout: "", stderr: "not a file" };
4871
- }
4872
- return { code: 0, stdout: readFileSync4(full, "utf8"), stderr: "" };
4873
- } catch {
4874
- return { code: 1, stdout: "", stderr: "file not readable" };
4875
- }
4876
- }
4877
4371
  async function readShowTextAsync(root, command) {
4878
4372
  if (command.ref !== "worktree" && command.ref !== "") {
4879
4373
  return showAsync(command.ref, command.path, root);
@@ -4887,8 +4381,8 @@ async function readShowTextAsync(root, command) {
4887
4381
  };
4888
4382
  }
4889
4383
  try {
4890
- const stat = statSync3(full);
4891
- if (!stat.isFile()) {
4384
+ const stat2 = statSync3(full);
4385
+ if (!stat2.isFile()) {
4892
4386
  return { code: 1, stdout: "", stderr: "not a file" };
4893
4387
  }
4894
4388
  return { code: 0, stdout: readFileSync4(full, "utf8"), stderr: "" };
@@ -4896,33 +4390,6 @@ async function readShowTextAsync(root, command) {
4896
4390
  return { code: 1, stdout: "", stderr: "file not readable" };
4897
4391
  }
4898
4392
  }
4899
- function buildFileShowReport(root, command) {
4900
- const res = readShowText(root, command);
4901
- if (res.code !== 0) {
4902
- const detail = res.stderr.trim() || `git show exited with code ${res.code}`;
4903
- return {
4904
- path: command.path,
4905
- ref: command.ref,
4906
- ...command.start !== undefined ? { start: command.start } : {},
4907
- ...command.end !== undefined ? { end: command.end } : {},
4908
- totalLines: 0,
4909
- complete: false,
4910
- text: "",
4911
- error: detail
4912
- };
4913
- }
4914
- const sliced = sliceLines(res.stdout, command.start, command.end);
4915
- return {
4916
- path: command.path,
4917
- ref: command.ref,
4918
- ...command.start !== undefined ? { start: command.start } : {},
4919
- ...command.end !== undefined ? { end: command.end } : {},
4920
- totalLines: sliced.total,
4921
- complete: sliced.complete,
4922
- text: sliced.lines.join(`
4923
- `)
4924
- };
4925
- }
4926
4393
  async function buildFileShowReportAsync(root, command) {
4927
4394
  const res = await readShowTextAsync(root, command);
4928
4395
  if (res.code !== 0) {
@@ -4950,8 +4417,8 @@ async function buildFileShowReportAsync(root, command) {
4950
4417
  `)
4951
4418
  };
4952
4419
  }
4953
- function runShow(root, command) {
4954
- const report = buildFileShowReport(root, command);
4420
+ async function runShow(root, command) {
4421
+ const report = await buildFileShowReportAsync(root, command);
4955
4422
  if (report.error !== undefined) {
4956
4423
  if (command.json) {
4957
4424
  console.log(JSON.stringify(report, null, 2));
@@ -4974,64 +4441,6 @@ function buildFileDiffRangeArgs(from, to) {
4974
4441
  refs.push(to);
4975
4442
  return refs;
4976
4443
  }
4977
- function buildFileDiffReport(root, command) {
4978
- const base = {
4979
- path: command.path,
4980
- ...command.oldPath !== undefined ? { old_path: command.oldPath } : {},
4981
- from: command.untracked ? "/dev/null" : command.from,
4982
- to: command.to,
4983
- untracked: command.untracked,
4984
- ignore_ws: command.ignoreWs,
4985
- ignore_blank: command.ignoreBlank,
4986
- mode: command.mode,
4987
- max_hunks: command.mode === "preview" ? command.maxHunks : null,
4988
- max_lines: command.mode === "preview" ? command.maxLines : null,
4989
- diff: "",
4990
- hunk_count: 0,
4991
- rendered_hunk_count: 0,
4992
- line_count: 0,
4993
- truncated: false,
4994
- binary: false
4995
- };
4996
- if (!command.untracked && isSameWorktreeRange({ from: command.from, to: command.to })) {
4997
- return base;
4998
- }
4999
- const extras = [];
5000
- if (command.ignoreWs)
5001
- extras.push("-w");
5002
- if (command.ignoreBlank)
5003
- extras.push("--ignore-blank-lines");
5004
- let diffText = "";
5005
- let errText = "";
5006
- if (command.untracked) {
5007
- const res = untrackedFileDiff(extras, command.path, root);
5008
- diffText = res.stdout || "";
5009
- if (res.code !== 0)
5010
- errText = res.stderr.trim();
5011
- } else {
5012
- const args = [
5013
- ...extras,
5014
- ...buildFileDiffRangeArgs(command.from, command.to)
5015
- ];
5016
- const paths = command.oldPath !== undefined ? [command.oldPath, command.path] : command.path;
5017
- const res = fileDiffText(args, paths, root);
5018
- diffText = res.stdout || "";
5019
- if (res.code !== 0)
5020
- errText = res.stderr.trim();
5021
- }
5022
- const truncated = command.mode === "preview" ? truncateToNHunks(diffText, command.maxHunks, command.maxLines) : truncateToNHunks(diffText, 1e9);
5023
- const previewTruncated = command.mode === "preview" && (truncated.totalHunks > truncated.renderedHunks || truncated.lineTruncated);
5024
- return {
5025
- ...base,
5026
- diff: truncated.text,
5027
- hunk_count: truncated.totalHunks,
5028
- rendered_hunk_count: truncated.renderedHunks,
5029
- line_count: truncated.lineCount,
5030
- truncated: previewTruncated,
5031
- binary: diffText.includes("Binary files"),
5032
- ...errText ? { error: errText } : {}
5033
- };
5034
- }
5035
4444
  async function buildFileDiffReportAsync(root, command) {
5036
4445
  const base = {
5037
4446
  path: command.path,
@@ -5090,8 +4499,8 @@ async function buildFileDiffReportAsync(root, command) {
5090
4499
  ...errText ? { error: errText } : {}
5091
4500
  };
5092
4501
  }
5093
- function runDiff(root, command) {
5094
- const report = buildFileDiffReport(root, command);
4502
+ async function runDiff(root, command) {
4503
+ const report = await buildFileDiffReportAsync(root, command);
5095
4504
  if (command.json) {
5096
4505
  console.log(JSON.stringify(report, null, 2));
5097
4506
  } else if (report.diff.length > 0) {
@@ -10049,18 +9458,18 @@ function formatRecentText(commits) {
10049
9458
  }
10050
9459
  return lines;
10051
9460
  }
10052
- function buildStatusReport(opts) {
9461
+ async function buildStatusReport(opts) {
10053
9462
  const { root, ref, limit } = opts;
10054
- const stagedResult = fileMetaResult(["--cached"], root, false);
10055
- const changedResult = fileMetaResult(["HEAD"], root, true);
10056
- const worktreeFallbackResult = isMissingDiffBaseError(changedResult.error) ? fileMetaResult([], root, true) : null;
9463
+ const stagedResult = await fileMetaResultAsync(["--cached"], root, false);
9464
+ const changedResult = await fileMetaResultAsync(["HEAD"], root, true);
9465
+ const worktreeFallbackResult = isMissingDiffBaseError(changedResult.error) ? await fileMetaResultAsync([], root, true) : null;
10057
9466
  const stagedFiles = stagedResult.files;
10058
9467
  const changedFiles = changedResult.error ? worktreeFallbackResult ? mergeMissingByPath(worktreeFallbackResult.files, stagedFiles) : [] : mergeMissingByPath(changedResult.files, stagedFiles);
10059
9468
  const changed = buildGroup(changedFiles, worktreeFallbackResult ? worktreeFallbackResult.error || stagedResult.error : changedResult.error || stagedResult.error);
10060
9469
  const staged = buildGroup(stagedFiles, stagedResult.error);
10061
- const history = commitHistory(root, { ref, skip: 0, limit });
10062
- const branch = currentBranch(root);
10063
- const remote = remoteWebUrl(root);
9470
+ const history = await commitHistoryAsync(root, { ref, skip: 0, limit });
9471
+ const branch = await currentBranchAsync(root);
9472
+ const remote = await remoteWebUrlAsync(root);
10064
9473
  const registry = readServerRegistry(root);
10065
9474
  const serverUrl = registry?.url ?? null;
10066
9475
  const nextCommands = buildNextCommands(changed, staged, serverUrl);
@@ -10098,7 +9507,7 @@ function formatStatusReportText(report) {
10098
9507
  return lines.join(`
10099
9508
  `);
10100
9509
  }
10101
- function runStatusCli(argv) {
9510
+ async function runStatusCli(argv) {
10102
9511
  const parsed = parseStatusArgs(argv);
10103
9512
  if (parsed.ok === false) {
10104
9513
  console.error(parsed.error);
@@ -10124,7 +9533,7 @@ function runStatusCli(argv) {
10124
9533
  process.exit(1);
10125
9534
  }
10126
9535
  const root = resolveRepoRoot(cwd);
10127
- const report = buildStatusReport({
9536
+ const report = await buildStatusReport({
10128
9537
  root,
10129
9538
  ref: command.ref,
10130
9539
  limit: command.limit
@@ -14805,26 +14214,26 @@ function hasControlCharacter(value) {
14805
14214
 
14806
14215
  // web-src/server/database/discovery.ts
14807
14216
  import {
14808
- closeSync as closeSync2,
14217
+ closeSync,
14809
14218
  existsSync as existsSync6,
14810
- openSync as openSync2,
14811
- readSync as readSync2,
14219
+ openSync,
14220
+ readSync,
14812
14221
  realpathSync as realpathSync5,
14813
14222
  statSync as statSync4
14814
14223
  } from "node:fs";
14815
- import { lstat, open, readdir, readFile as readFile2, stat } from "node:fs/promises";
14224
+ import { lstat, open as open2, readdir, readFile as readFile2, stat as stat2 } from "node:fs/promises";
14816
14225
  import { basename, join as join9, relative as relative4 } from "node:path";
14817
14226
  function isSqliteFile(fullPath) {
14818
14227
  try {
14819
- const stat2 = statSync4(fullPath);
14820
- if (!stat2.isFile() || stat2.size < 16)
14228
+ const stat3 = statSync4(fullPath);
14229
+ if (!stat3.isFile() || stat3.size < 16)
14821
14230
  return false;
14822
14231
  const buf = Buffer.alloc(16);
14823
- const fd = openSync2(fullPath, "r");
14232
+ const fd = openSync(fullPath, "r");
14824
14233
  try {
14825
- readSync2(fd, buf, 0, 16, 0);
14234
+ readSync(fd, buf, 0, 16, 0);
14826
14235
  } finally {
14827
- closeSync2(fd);
14236
+ closeSync(fd);
14828
14237
  }
14829
14238
  return buf.toString("utf8", 0, 16) === SQLITE_MAGIC;
14830
14239
  } catch {
@@ -14833,10 +14242,10 @@ function isSqliteFile(fullPath) {
14833
14242
  }
14834
14243
  async function isSqliteFileAsync(fullPath) {
14835
14244
  try {
14836
- const fileStat = await stat(fullPath);
14245
+ const fileStat = await stat2(fullPath);
14837
14246
  if (!fileStat.isFile() || fileStat.size < 16)
14838
14247
  return false;
14839
- const file = await open(fullPath, "r");
14248
+ const file = await open2(fullPath, "r");
14840
14249
  try {
14841
14250
  const buf = Buffer.alloc(16);
14842
14251
  await file.read(buf, 0, 16, 0);
@@ -15300,7 +14709,7 @@ function cloneDockerDiscoveryResult(result) {
15300
14709
  }
15301
14710
  async function pathExistsAsync(path) {
15302
14711
  try {
15303
- await stat(path);
14712
+ await stat2(path);
15304
14713
  return true;
15305
14714
  } catch {
15306
14715
  return false;
@@ -15649,6 +15058,7 @@ function startWorktreeUpdateWatch(options) {
15649
15058
  const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
15650
15059
  const initialScanQueue = [];
15651
15060
  let initialScanTimer = null;
15061
+ let processingInitialScan = false;
15652
15062
  const pendingPathInspections = new Map;
15653
15063
  let pathInspectionTimer = null;
15654
15064
  let timer = null;
@@ -15736,8 +15146,14 @@ function startWorktreeUpdateWatch(options) {
15736
15146
  return;
15737
15147
  }
15738
15148
  const next = initialScanQueue.shift();
15739
- if (next)
15740
- watchDirectory(next, true);
15149
+ if (next) {
15150
+ processingInitialScan = true;
15151
+ try {
15152
+ watchDirectory(next, true);
15153
+ } finally {
15154
+ processingInitialScan = false;
15155
+ }
15156
+ }
15741
15157
  if (watchers.size >= maxWatchedDirectories) {
15742
15158
  reportWatchLimit();
15743
15159
  initialScanQueue.length = 0;
@@ -15755,7 +15171,7 @@ function startWorktreeUpdateWatch(options) {
15755
15171
  if (children.length > remaining)
15756
15172
  reportWatchLimit();
15757
15173
  initialScanQueue.push(...children.slice(0, remaining));
15758
- if (!initialScanTimer)
15174
+ if (!initialScanTimer && !processingInitialScan)
15759
15175
  initialScanTimer = setTimer(processInitialScanQueue, 5000);
15760
15176
  };
15761
15177
  const processChangedPath = (changed, fullChangedPath) => {
@@ -17334,7 +16750,7 @@ function createDockerAdapterCache(maxEntries = DEFAULT_MAX_DOCKER_ADAPTER_CACHE,
17334
16750
  }
17335
16751
  }
17336
16752
  return {
17337
- async getOrOpenAsync(key, open2) {
16753
+ async getOrOpenAsync(key, open3) {
17338
16754
  prune();
17339
16755
  const cached = cache.get(key);
17340
16756
  if (cached) {
@@ -17348,7 +16764,7 @@ function createDockerAdapterCache(maxEntries = DEFAULT_MAX_DOCKER_ADAPTER_CACHE,
17348
16764
  closed: false,
17349
16765
  promise: undefined
17350
16766
  };
17351
- pendingEntry.promise = Promise.resolve().then(open2).then((adapter) => {
16767
+ pendingEntry.promise = Promise.resolve().then(open3).then((adapter) => {
17352
16768
  if (pendingEntry.closed) {
17353
16769
  adapter.close();
17354
16770
  } else {
@@ -21646,27 +21062,27 @@ async function checkSqlite(cwd) {
21646
21062
  const status = await describeSqliteDriver();
21647
21063
  const rows = [sqliteStatusToRow(status)];
21648
21064
  if (status.kind === "ok") {
21649
- const open2 = await trySnapshotDbOpen(cwd);
21650
- if (open2.kind === "skipped") {
21065
+ const open3 = await trySnapshotDbOpen(cwd);
21066
+ if (open3.kind === "skipped") {
21651
21067
  rows.push({
21652
21068
  id: "sqlite.snapshot-open",
21653
21069
  title: "Snapshot DB open smoke test",
21654
21070
  status: "ok",
21655
21071
  detail: "Skipped (snapshot DB will be created on first use)"
21656
21072
  });
21657
- } else if (open2.kind === "ok") {
21073
+ } else if (open3.kind === "ok") {
21658
21074
  rows.push({
21659
21075
  id: "sqlite.snapshot-open",
21660
21076
  title: "Snapshot DB open smoke test",
21661
21077
  status: "ok",
21662
- detail: `Opened ${open2.path}`
21078
+ detail: `Opened ${open3.path}`
21663
21079
  });
21664
21080
  } else {
21665
21081
  rows.push({
21666
21082
  id: "sqlite.snapshot-open",
21667
21083
  title: "Snapshot DB open failed",
21668
21084
  status: "error",
21669
- detail: open2.message,
21085
+ detail: open3.message,
21670
21086
  hint: "The snapshot DB exists but could not be opened. Inspect file permissions, possible corruption, or another process holding the file."
21671
21087
  });
21672
21088
  }
@@ -21715,8 +21131,8 @@ function checkSnapshotStore(cwd) {
21715
21131
  }
21716
21132
  let dbDetail = dbPath;
21717
21133
  try {
21718
- const stat2 = statSync5(dbPath);
21719
- dbDetail = `${dbPath} (${stat2.size.toLocaleString()} bytes)`;
21134
+ const stat3 = statSync5(dbPath);
21135
+ dbDetail = `${dbPath} (${stat3.size.toLocaleString()} bytes)`;
21720
21136
  } catch {
21721
21137
  dbDetail = `${dbPath} (not created yet — created on first snapshot)`;
21722
21138
  }
@@ -23330,7 +22746,8 @@ var init_journal2 = __esm(() => {
23330
22746
  });
23331
22747
 
23332
22748
  // web-src/server/search-service.ts
23333
- import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as realpathSync6 } from "node:fs";
22749
+ import { existsSync as existsSync7, realpathSync as realpathSync6 } from "node:fs";
22750
+ import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
23334
22751
  import { join as join18, relative as relative7 } from "node:path";
23335
22752
  async function rgAvailableAsync(cwd) {
23336
22753
  if (rgAvailableCache !== null)
@@ -23382,12 +22799,12 @@ function safeWorktreePath(env, path) {
23382
22799
  function filterCallerPaths(env, paths) {
23383
22800
  return paths.filter((path) => isSafePath(path) && !isGitInternalPath(path) && !isSkippableSearchPath(path, env.omitDirNames, env.excludeNames));
23384
22801
  }
23385
- function grepWorktreeFallback(env, query, max, paths) {
23386
- const candidates = paths.length ? paths : listTree("worktree", "", env.cwd, {
22802
+ async function grepWorktreeFallback(env, query, max, paths) {
22803
+ const candidates = paths.length ? paths : (await listTreeAsync("worktree", "", env.cwd, {
23387
22804
  recursive: true,
23388
22805
  omitDirNames: env.omitDirNames,
23389
22806
  excludeNames: env.excludeNames
23390
- }).entries.map((entry) => entry.path);
22807
+ })).entries.map((entry) => entry.path);
23391
22808
  const matches = [];
23392
22809
  for (const path of candidates) {
23393
22810
  if (matches.length >= max)
@@ -23397,17 +22814,17 @@ function grepWorktreeFallback(env, query, max, paths) {
23397
22814
  const full = safeWorktreePath(env, path);
23398
22815
  if (!full)
23399
22816
  continue;
23400
- let stat2;
22817
+ let stat3;
23401
22818
  try {
23402
- stat2 = lstatSync4(full);
22819
+ stat3 = await lstat2(full);
23403
22820
  } catch {
23404
22821
  continue;
23405
22822
  }
23406
- if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.size > GREP_MAX_FILE_BYTES)
22823
+ if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > GREP_MAX_FILE_BYTES)
23407
22824
  continue;
23408
22825
  let data;
23409
22826
  try {
23410
- data = readFileSync7(full);
22827
+ data = await readFile3(full);
23411
22828
  } catch {
23412
22829
  continue;
23413
22830
  }
@@ -23447,7 +22864,7 @@ async function grepWorktreeAsync(env, req) {
23447
22864
  matches: []
23448
22865
  };
23449
22866
  }
23450
- const matches = grepWorktreeFallback(env, req.query, req.max, paths);
22867
+ const matches = await grepWorktreeFallback(env, req.query, req.max, paths);
23451
22868
  return {
23452
22869
  ref: "worktree",
23453
22870
  engine: "fallback",
@@ -23553,7 +22970,7 @@ var init_search_service = __esm(() => {
23553
22970
  });
23554
22971
 
23555
22972
  // web-src/server/mcp.ts
23556
- import { readFileSync as readFileSync8 } from "node:fs";
22973
+ import { readFileSync as readFileSync7 } from "node:fs";
23557
22974
  import { join as join19 } from "node:path";
23558
22975
  function defaultMcpTools(options = {}) {
23559
22976
  return [
@@ -24051,7 +23468,7 @@ function defaultMcpTools(options = {}) {
24051
23468
  }
24052
23469
  ];
24053
23470
  }
24054
- function runStatusTool(input, defaultCwd) {
23471
+ async function runStatusTool(input, defaultCwd) {
24055
23472
  const params = isPlainObject(input) ? input : {};
24056
23473
  const cwdRaw = params.cwd;
24057
23474
  const refRaw = params.ref;
@@ -24092,7 +23509,7 @@ function runStatusTool(input, defaultCwd) {
24092
23509
  return { text: resolved.error, isError: true };
24093
23510
  }
24094
23511
  try {
24095
- const report = buildStatusReport({ root: resolved.root, ref, limit });
23512
+ const report = await buildStatusReport({ root: resolved.root, ref, limit });
24096
23513
  return {
24097
23514
  text: JSON.stringify(report, null, 2),
24098
23515
  isError: !!(report.changed.error || report.staged.error)
@@ -24365,14 +23782,14 @@ async function runFileDiffTool(input, defaultCwd) {
24365
23782
  if (untrackedRaw !== undefined && typeof untrackedRaw !== "boolean") {
24366
23783
  return { text: "untracked must be a boolean", isError: true };
24367
23784
  }
24368
- const untracked2 = untrackedRaw === true;
24369
- if (untracked2 && params.from !== undefined) {
23785
+ const untracked = untrackedRaw === true;
23786
+ if (untracked && params.from !== undefined) {
24370
23787
  return {
24371
23788
  text: "untracked cannot be combined with from",
24372
23789
  isError: true
24373
23790
  };
24374
23791
  }
24375
- if (untracked2 && toParsed.ref !== "worktree") {
23792
+ if (untracked && toParsed.ref !== "worktree") {
24376
23793
  return {
24377
23794
  text: "untracked requires to='worktree' (or omitted)",
24378
23795
  isError: true
@@ -24420,7 +23837,7 @@ async function runFileDiffTool(input, defaultCwd) {
24420
23837
  ...oldPath !== undefined ? { oldPath } : {},
24421
23838
  from: fromParsed.ref,
24422
23839
  to: toParsed.ref,
24423
- untracked: untracked2,
23840
+ untracked,
24424
23841
  ignoreWs,
24425
23842
  ignoreBlank,
24426
23843
  mode,
@@ -25028,7 +24445,7 @@ var init_mcp = __esm(() => {
25028
24445
  init_search_cli();
25029
24446
  init_search_service();
25030
24447
  init_status_cli();
25031
- PACKAGE_VERSION = JSON.parse(readFileSync8(join19(ROOT, "package.json"), "utf8")).version;
24448
+ PACKAGE_VERSION = JSON.parse(readFileSync7(join19(ROOT, "package.json"), "utf8")).version;
25032
24449
  MCP_SERVER_INFO = {
25033
24450
  name: "code-viewer",
25034
24451
  title: "code-viewer",
@@ -25109,13 +24526,13 @@ var init_state_route = __esm(() => {
25109
24526
  // web-src/server/preview.ts
25110
24527
  var exports_preview = {};
25111
24528
  import {
25112
- closeSync as closeSync3,
24529
+ closeSync as closeSync2,
25113
24530
  constants as constants3,
25114
24531
  existsSync as existsSync8,
25115
- lstatSync as lstatSync5,
24532
+ lstatSync as lstatSync4,
25116
24533
  mkdirSync as mkdirSync4,
25117
- openSync as openSync3,
25118
- readFileSync as readFileSync9,
24534
+ openSync as openSync2,
24535
+ readFileSync as readFileSync8,
25119
24536
  realpathSync as realpathSync7,
25120
24537
  renameSync,
25121
24538
  statSync as statSync6,
@@ -25356,7 +24773,7 @@ function staticFile(pathname) {
25356
24773
  const full = join20(WEB_ROOT, spec[0]);
25357
24774
  if (!existsSync8(full))
25358
24775
  return text("not found", 404);
25359
- return new Response(readFileSync9(full), {
24776
+ return new Response(readFileSync8(full), {
25360
24777
  headers: { "Content-Type": spec[1], "Cache-Control": "no-store" }
25361
24778
  });
25362
24779
  }
@@ -25413,7 +24830,7 @@ function buildQuery(params) {
25413
24830
  const s = q.toString();
25414
24831
  return s ? `?${s}` : "";
25415
24832
  }
25416
- function fileToMeta(file, range, extraQs) {
24833
+ function fileToMeta(file, range, extraQs, responseGeneration) {
25417
24834
  const sizeClass = classify(file);
25418
24835
  const q = {
25419
24836
  path: file.path,
@@ -25421,6 +24838,7 @@ function fileToMeta(file, range, extraQs) {
25421
24838
  status: file.status,
25422
24839
  from: range.from,
25423
24840
  to: range.to,
24841
+ generation: responseGeneration,
25424
24842
  ...extraQs
25425
24843
  };
25426
24844
  if (file.untracked)
@@ -25459,7 +24877,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
25459
24877
  };
25460
24878
  }
25461
24879
  const { args, refs } = buildRangeArgs(range);
25462
- const fullArgs = [...extras, ...args];
24880
+ const fullArgs = pathFilter ? [...extras, ...args, "--", pathFilter] : [...extras, ...args];
25463
24881
  const metaResult = await fileMetaResultAsync(fullArgs, cwd, false);
25464
24882
  const files = metaResult.files;
25465
24883
  if (!metaResult.error && includeUntracked(range, refs)) {
@@ -25477,7 +24895,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
25477
24895
  if (e === "--ignore-blank-lines")
25478
24896
  extraQs.ignore_blank = "1";
25479
24897
  }
25480
- const meta = filteredFiles.map((file) => fileToMeta(file, range, extraQs));
24898
+ const meta = filteredFiles.map((file) => fileToMeta(file, range, extraQs, responseGeneration));
25481
24899
  const totals = meta.reduce((acc, file) => {
25482
24900
  acc.additions += file.additions || 0;
25483
24901
  acc.deletions += file.deletions || 0;
@@ -25496,7 +24914,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
25496
24914
  };
25497
24915
  }
25498
24916
  async function handleDiffJson(url) {
25499
- const responseGeneration = generation;
24917
+ let responseGeneration = generation;
25500
24918
  const extras = [];
25501
24919
  if (url.searchParams.get("ignore_ws") === "1")
25502
24920
  extras.push("-w");
@@ -25522,9 +24940,15 @@ async function handleDiffJson(url) {
25522
24940
  const requestSequence = ++diffMetaRequestSequence;
25523
24941
  latestDiffMetaRequest.set(key, requestSequence);
25524
24942
  try {
25525
- const payload = await computePayload(extras, range, path, responseGeneration);
25526
- if (latestDiffMetaRequest.get(key) !== requestSequence || responseGeneration !== generation)
24943
+ let payload = await computePayload(extras, range, path, responseGeneration);
24944
+ if (latestDiffMetaRequest.get(key) !== requestSequence)
25527
24945
  return json2(payload);
24946
+ if (responseGeneration !== generation) {
24947
+ responseGeneration = generation;
24948
+ payload = await computePayload(extras, range, path, responseGeneration);
24949
+ if (latestDiffMetaRequest.get(key) !== requestSequence || responseGeneration !== generation)
24950
+ return json2(payload);
24951
+ }
25528
24952
  const sig = JSON.stringify({ ...payload, generation: undefined });
25529
24953
  if (noCache && (!cached || cached.sig !== sig)) {
25530
24954
  generation++;
@@ -25631,11 +25055,11 @@ function worktreeFileMetadata(path, knownSize) {
25631
25055
  if (!full)
25632
25056
  return {};
25633
25057
  try {
25634
- const stat2 = statSync6(full);
25058
+ const stat3 = statSync6(full);
25635
25059
  return {
25636
- size: knownSize ?? stat2.size,
25637
- created_at: isoDate(stat2.birthtimeMs),
25638
- updated_at: isoDate(stat2.mtimeMs)
25060
+ size: knownSize ?? stat3.size,
25061
+ created_at: isoDate(stat3.birthtimeMs),
25062
+ updated_at: isoDate(stat3.mtimeMs)
25639
25063
  };
25640
25064
  } catch {
25641
25065
  return {};
@@ -25656,10 +25080,10 @@ async function directoryMetadata(target, path) {
25656
25080
  if (!full)
25657
25081
  return {};
25658
25082
  try {
25659
- const stat2 = statSync6(full);
25083
+ const stat3 = statSync6(full);
25660
25084
  return {
25661
- created_at: isoDate(stat2.birthtimeMs),
25662
- updated_at: isoDate(stat2.mtimeMs)
25085
+ created_at: isoDate(stat3.birthtimeMs),
25086
+ updated_at: isoDate(stat3.mtimeMs)
25663
25087
  };
25664
25088
  } catch {
25665
25089
  return {};
@@ -25704,7 +25128,7 @@ async function readReadme(target, dirPath) {
25704
25128
  if (!full)
25705
25129
  continue;
25706
25130
  try {
25707
- return { path, text: readFileSync9(full, "utf8") };
25131
+ return { path, text: readFileSync8(full, "utf8") };
25708
25132
  } catch {
25709
25133
  continue;
25710
25134
  }
@@ -25993,7 +25417,7 @@ async function handleFileBlame(url) {
25993
25417
  return json2({ ...result, base, ref, generation: responseGeneration });
25994
25418
  }
25995
25419
  async function handleFileDiff(url) {
25996
- const responseGeneration = generation;
25420
+ const serverGenerationAtRequest = generation;
25997
25421
  const path = url.searchParams.get("path") || "";
25998
25422
  if (!safePath(path))
25999
25423
  return text("invalid path", 400);
@@ -26007,6 +25431,9 @@ async function handleFileDiff(url) {
26007
25431
  from: url.searchParams.get("from") || "",
26008
25432
  to: url.searchParams.get("to") || ""
26009
25433
  };
25434
+ const requestedGeneration = Number(url.searchParams.get("generation"));
25435
+ const usesWorktree = !range.from || range.from === "worktree" || !range.to || range.to === "worktree";
25436
+ const responseGeneration = !usesWorktree && Number.isSafeInteger(requestedGeneration) && requestedGeneration > 0 ? requestedGeneration : serverGenerationAtRequest;
26010
25437
  if (isSameWorktreeRange(range)) {
26011
25438
  return json2({
26012
25439
  path,
@@ -26060,7 +25487,7 @@ async function handleFileDiff(url) {
26060
25487
  errStatus = res.status ?? 500;
26061
25488
  }
26062
25489
  }
26063
- if (!errText && responseGeneration === generation)
25490
+ if (!errText && serverGenerationAtRequest === generation)
26064
25491
  setTimedCacheEntry(fileCache, cacheKey, { diffText });
26065
25492
  }
26066
25493
  if (errStatus)
@@ -26085,8 +25512,8 @@ async function handleFileDiff(url) {
26085
25512
  }
26086
25513
  function worktreeLineIndexSignature(full) {
26087
25514
  try {
26088
- const stat2 = statSync6(full);
26089
- return `size:${stat2.size}|mtime:${stat2.mtimeMs}|ctime:${stat2.ctimeMs}|ino:${stat2.ino || 0}`;
25515
+ const stat3 = statSync6(full);
25516
+ return `size:${stat3.size}|mtime:${stat3.mtimeMs}|ctime:${stat3.ctimeMs}|ino:${stat3.ino || 0}`;
26090
25517
  } catch {
26091
25518
  return null;
26092
25519
  }
@@ -26101,10 +25528,10 @@ async function getWorktreeLineIndex(full) {
26101
25528
  lineIndexCache.set(full, cached);
26102
25529
  return cached.index;
26103
25530
  }
26104
- const stat2 = statSync6(full);
26105
- if (stat2.size > LINE_INDEX_MAX_FILE_BYTES)
25531
+ const stat3 = statSync6(full);
25532
+ if (stat3.size > LINE_INDEX_MAX_FILE_BYTES)
26106
25533
  return null;
26107
- const index = await buildLineOffsetIndexFromStream(fileReadableStream(full), stat2.size);
25534
+ const index = await buildLineOffsetIndexFromStream(fileReadableStream(full), stat3.size);
26108
25535
  lineIndexCache.delete(full);
26109
25536
  lineIndexCache.set(full, { signature, index });
26110
25537
  while (lineIndexCache.size > 32) {
@@ -26476,11 +25903,11 @@ async function handleUploadFiles(req) {
26476
25903
  const written = [];
26477
25904
  try {
26478
25905
  for (const upload of uploads) {
26479
- const fd = openSync3(upload.target, uploadOpenFlags(), 420);
25906
+ const fd = openSync2(upload.target, uploadOpenFlags(), 420);
26480
25907
  try {
26481
25908
  writeFileSync2(fd, new Uint8Array(await upload.file.arrayBuffer()));
26482
25909
  } finally {
26483
- closeSync3(fd);
25910
+ closeSync2(fd);
26484
25911
  }
26485
25912
  written.push(upload.target);
26486
25913
  }
@@ -26600,7 +26027,7 @@ function moveMacPathIntoTrash(path) {
26600
26027
  }
26601
26028
  }
26602
26029
  async function movePathToTrash(path) {
26603
- lstatSync5(path);
26030
+ lstatSync4(path);
26604
26031
  if (process.platform === "darwin") {
26605
26032
  return moveMacPathIntoTrash(path);
26606
26033
  }
@@ -27393,7 +26820,7 @@ var init_preview = __esm(async () => {
27393
26820
  init_state_store();
27394
26821
  init_worktree_watcher();
27395
26822
  WEB_ROOT = join20(ROOT, "web");
27396
- VERSION = JSON.parse(readFileSync9(join20(ROOT, "package.json"), "utf8")).version;
26823
+ VERSION = JSON.parse(readFileSync8(join20(ROOT, "package.json"), "utf8")).version;
27397
26824
  DEFAULT_ARGS = ["HEAD"];
27398
26825
  WATCHED_ASSET_FILES = ["index.html", "style.css", "app.js"];
27399
26826
  LINE_INDEX_MAX_FILE_BYTES = 256 * 1024 * 1024;
@@ -27679,7 +27106,7 @@ if (process.argv[2] === "agent-help") {
27679
27106
  await runFileCli2(process.argv.slice(3));
27680
27107
  } else if (process.argv[2] === "status") {
27681
27108
  const { runStatusCli: runStatusCli2 } = await Promise.resolve().then(() => (init_status_cli(), exports_status_cli));
27682
- runStatusCli2(process.argv.slice(3));
27109
+ await runStatusCli2(process.argv.slice(3));
27683
27110
  } else if (process.argv[2] === "skill") {
27684
27111
  const { runSkillCli: runSkillCli2 } = await Promise.resolve().then(() => (init_skill_cli(), exports_skill_cli));
27685
27112
  runSkillCli2(process.argv.slice(3));