glassbox 0.4.3 → 0.5.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/dist/cli.js CHANGED
@@ -395,7 +395,7 @@ init_queries();
395
395
  init_connection();
396
396
  import { mkdirSync as mkdirSync7 } from "fs";
397
397
  import { tmpdir } from "os";
398
- import { join as join10, resolve as resolve4 } from "path";
398
+ import { join as join10, resolve as resolve5 } from "path";
399
399
 
400
400
  // src/debug.ts
401
401
  var debugEnabled = false;
@@ -1723,7 +1723,7 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
1723
1723
  // src/server.ts
1724
1724
  import { serve } from "@hono/node-server";
1725
1725
  import { exec } from "child_process";
1726
- import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
1726
+ import { existsSync as existsSync6, readFileSync as readFileSync9 } from "fs";
1727
1727
  import { Hono as Hono4 } from "hono";
1728
1728
  import { dirname, join as join7 } from "path";
1729
1729
  import { fileURLToPath } from "url";
@@ -2445,8 +2445,8 @@ function isRetriable(err) {
2445
2445
  return msg.includes("429") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("rate_limit");
2446
2446
  }
2447
2447
  function sleep(ms) {
2448
- return new Promise((resolve5) => {
2449
- setTimeout(resolve5, ms);
2448
+ return new Promise((resolve6) => {
2449
+ setTimeout(resolve6, ms);
2450
2450
  });
2451
2451
  }
2452
2452
 
@@ -2490,8 +2490,8 @@ function randomLines(count) {
2490
2490
  return lines.sort((a, b) => a.line - b.line);
2491
2491
  }
2492
2492
  function sleep2(ms) {
2493
- return new Promise((resolve5) => {
2494
- setTimeout(resolve5, ms);
2493
+ return new Promise((resolve6) => {
2494
+ setTimeout(resolve6, ms);
2495
2495
  });
2496
2496
  }
2497
2497
  async function mockRiskAnalysisBatch(files) {
@@ -4059,6 +4059,71 @@ apiRoutes.get("/outline/:fileId", async (c) => {
4059
4059
  const symbols = parseOutline(content, file.file_path);
4060
4060
  return c.json({ symbols });
4061
4061
  });
4062
+ apiRoutes.get("/symbol-definition", async (c) => {
4063
+ const name = c.req.query("name");
4064
+ const currentFileId = c.req.query("currentFileId");
4065
+ if (!name) return c.json({ definitions: [] });
4066
+ const reviewId = resolveReviewId(c);
4067
+ const repoRoot = c.get("repoRoot");
4068
+ const definitions = [];
4069
+ const searchedPaths = /* @__PURE__ */ new Set();
4070
+ const reviewFiles = await getReviewFiles(reviewId);
4071
+ for (const file of reviewFiles) {
4072
+ searchedPaths.add(file.file_path);
4073
+ const diff = JSON.parse(file.diff_data ?? "{}");
4074
+ const isDeleted = diff.status === "deleted";
4075
+ let content = "";
4076
+ try {
4077
+ content = isDeleted ? getFileContent(file.file_path, "HEAD", repoRoot) : getFileContent(file.file_path, "working", repoRoot);
4078
+ } catch {
4079
+ continue;
4080
+ }
4081
+ if (!content) continue;
4082
+ const symbols = parseOutline(content, file.file_path);
4083
+ collectDefinitions(symbols, name, file.id, file.file_path, definitions);
4084
+ }
4085
+ if (definitions.length === 0) {
4086
+ try {
4087
+ const allFiles = execSync5("git ls-files", { cwd: repoRoot, encoding: "utf-8" }).trim().split("\n").filter(Boolean);
4088
+ for (const filePath of allFiles) {
4089
+ if (searchedPaths.has(filePath)) continue;
4090
+ const ext = filePath.slice(filePath.lastIndexOf("."));
4091
+ if (!/\.(js|mjs|cjs|jsx|ts|tsx|mts|cts|java|go|rs|c|h|cpp|cc|cxx|hpp|cs|swift|php|kt|kts|scala|dart|groovy|py|rb)$/i.test(ext)) continue;
4092
+ let content = "";
4093
+ try {
4094
+ content = readFileSync7(resolve3(repoRoot, filePath), "utf-8");
4095
+ } catch {
4096
+ continue;
4097
+ }
4098
+ if (!content) continue;
4099
+ const symbols = parseOutline(content, filePath);
4100
+ collectDefinitions(symbols, name, null, filePath, definitions);
4101
+ if (definitions.length > 0) break;
4102
+ }
4103
+ } catch {
4104
+ }
4105
+ }
4106
+ definitions.sort((a, b) => {
4107
+ if (a.fileId === currentFileId && b.fileId !== currentFileId) return -1;
4108
+ if (b.fileId === currentFileId && a.fileId !== currentFileId) return 1;
4109
+ if (a.fileId !== null && b.fileId === null) return -1;
4110
+ if (b.fileId !== null && a.fileId === null) return 1;
4111
+ if (a.kind === "class" && b.kind !== "class") return -1;
4112
+ if (b.kind === "class" && a.kind !== "class") return 1;
4113
+ return 0;
4114
+ });
4115
+ return c.json({ definitions });
4116
+ });
4117
+ function collectDefinitions(symbols, targetName, fileId, filePath, out) {
4118
+ for (const sym of symbols) {
4119
+ if (sym.name === targetName) {
4120
+ out.push({ fileId, filePath, name: sym.name, kind: sym.kind, line: sym.line });
4121
+ }
4122
+ if (sym.children?.length > 0) {
4123
+ collectDefinitions(sym.children, targetName, fileId, filePath, out);
4124
+ }
4125
+ }
4126
+ }
4062
4127
  apiRoutes.get("/context/:fileId", async (c) => {
4063
4128
  const repoRoot = c.get("repoRoot");
4064
4129
  const file = await getReviewFile(c.req.param("fileId"));
@@ -4155,7 +4220,9 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
4155
4220
  });
4156
4221
 
4157
4222
  // src/routes/pages.tsx
4223
+ import { readFileSync as readFileSync8 } from "fs";
4158
4224
  import { Hono as Hono3 } from "hono";
4225
+ import { resolve as resolve4 } from "path";
4159
4226
 
4160
4227
  // src/utils/escapeHtml.ts
4161
4228
  function escapeHtml(str) {
@@ -4332,6 +4399,9 @@ function jsx(tag, props) {
4332
4399
  const childStr = children != null ? renderChildren(children) : "";
4333
4400
  return new SafeHtml(`<${tag}${attrStr}>${childStr}</${tag}>`);
4334
4401
  }
4402
+ function Fragment({ children }) {
4403
+ return new SafeHtml(children != null ? renderChildren(children) : "");
4404
+ }
4335
4405
 
4336
4406
  // src/icons.tsx
4337
4407
  var S14 = { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" };
@@ -4392,6 +4462,64 @@ function IconActualSize() {
4392
4462
  ] });
4393
4463
  }
4394
4464
 
4465
+ // src/utils/charDiff.ts
4466
+ function charDiff(oldStr, newStr) {
4467
+ if (!oldStr && !newStr) return null;
4468
+ if (oldStr === newStr) return null;
4469
+ const lcs = lcsTable(oldStr, newStr);
4470
+ const lcsLen = lcs[oldStr.length][newStr.length];
4471
+ const maxLen = Math.max(oldStr.length, newStr.length);
4472
+ if (maxLen === 0) return null;
4473
+ if (lcsLen / maxLen < 0.2) return null;
4474
+ const oldCommon = /* @__PURE__ */ new Set();
4475
+ const newCommon = /* @__PURE__ */ new Set();
4476
+ let i = oldStr.length, j = newStr.length;
4477
+ while (i > 0 && j > 0) {
4478
+ if (oldStr[i - 1] === newStr[j - 1]) {
4479
+ oldCommon.add(i - 1);
4480
+ newCommon.add(j - 1);
4481
+ i--;
4482
+ j--;
4483
+ } else if (lcs[i - 1][j] > lcs[i][j - 1]) {
4484
+ i--;
4485
+ } else {
4486
+ j--;
4487
+ }
4488
+ }
4489
+ return {
4490
+ oldSegments: buildSegments(oldStr, oldCommon),
4491
+ newSegments: buildSegments(newStr, newCommon)
4492
+ };
4493
+ }
4494
+ function lcsTable(a, b) {
4495
+ const m = a.length, n = b.length;
4496
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
4497
+ for (let i = 1; i <= m; i++) {
4498
+ for (let j = 1; j <= n; j++) {
4499
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
4500
+ }
4501
+ }
4502
+ return dp;
4503
+ }
4504
+ function buildSegments(str, commonPositions) {
4505
+ const segments = [];
4506
+ let current = "";
4507
+ let currentChanged = false;
4508
+ for (let i = 0; i < str.length; i++) {
4509
+ const changed = !commonPositions.has(i);
4510
+ if (changed !== currentChanged && current.length > 0) {
4511
+ segments.push({ text: current, changed: currentChanged });
4512
+ current = "";
4513
+ }
4514
+ currentChanged = changed;
4515
+ current += str[i];
4516
+ }
4517
+ if (current.length > 0) {
4518
+ segments.push({ text: current, changed: currentChanged });
4519
+ }
4520
+ return segments;
4521
+ }
4522
+
4395
4523
  // src/components/imageDiff.tsx
4396
4524
  function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
4397
4525
  const fileId = file.id;
@@ -4519,7 +4647,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4519
4647
  "data-new-line": group.pair.left?.newNum ?? group.pair.right?.newNum ?? "",
4520
4648
  children: [
4521
4649
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": group.pair.left?.oldNum ?? "" }),
4522
- /* @__PURE__ */ jsx("span", { className: "code", children: group.pair.left?.content ?? "" })
4650
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(group.pair, "left") })
4523
4651
  ]
4524
4652
  }
4525
4653
  ),
@@ -4531,7 +4659,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4531
4659
  "data-side": "new",
4532
4660
  children: [
4533
4661
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": group.pair.right?.newNum ?? "" }),
4534
- /* @__PURE__ */ jsx("span", { className: "code", children: group.pair.right?.content ?? "" })
4662
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(group.pair, "right") })
4535
4663
  ]
4536
4664
  }
4537
4665
  )
@@ -4581,7 +4709,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4581
4709
  "data-new-line": pair.left?.newNum ?? pair.right?.newNum ?? "",
4582
4710
  children: [
4583
4711
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": pair.left?.oldNum ?? "" }),
4584
- /* @__PURE__ */ jsx("span", { className: "code", children: pair.left?.content ?? "" })
4712
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(pair, "left") })
4585
4713
  ]
4586
4714
  }
4587
4715
  );
@@ -4626,7 +4754,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4626
4754
  "data-side": "new",
4627
4755
  children: [
4628
4756
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": pair.right?.newNum ?? "" }),
4629
- /* @__PURE__ */ jsx("span", { className: "code", children: pair.right?.content ?? "" })
4757
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(pair, "right") })
4630
4758
  ]
4631
4759
  }
4632
4760
  );
@@ -4634,6 +4762,20 @@ function SplitDiff({ hunks, annotationsByLine }) {
4634
4762
  ] });
4635
4763
  }) });
4636
4764
  }
4765
+ function renderSegments(segments) {
4766
+ return /* @__PURE__ */ jsx(Fragment, { children: segments.map((s) => s.changed ? /* @__PURE__ */ jsx("span", { className: "char-change", children: s.text }) : /* @__PURE__ */ jsx(Fragment, { children: s.text })) });
4767
+ }
4768
+ function renderPairContent(pair, side) {
4769
+ const line = side === "left" ? pair.left : pair.right;
4770
+ if (!line) return "";
4771
+ if (pair.left && pair.right && pair.left.type === "remove" && pair.right.type === "add") {
4772
+ const diff = charDiff(pair.left.content, pair.right.content);
4773
+ if (diff) {
4774
+ return renderSegments(side === "left" ? diff.oldSegments : diff.newSegments);
4775
+ }
4776
+ }
4777
+ return line.content;
4778
+ }
4637
4779
  function pairLines(lines) {
4638
4780
  const pairs = [];
4639
4781
  let i = 0;
@@ -4667,55 +4809,88 @@ function pairLines(lines) {
4667
4809
  }
4668
4810
  return pairs;
4669
4811
  }
4812
+ function buildUnifiedCharDiffs(lines) {
4813
+ const result = /* @__PURE__ */ new Map();
4814
+ let i = 0;
4815
+ while (i < lines.length) {
4816
+ if (lines[i].type === "remove") {
4817
+ const removes = [];
4818
+ while (i < lines.length && lines[i].type === "remove") {
4819
+ removes.push(lines[i]);
4820
+ i++;
4821
+ }
4822
+ const adds = [];
4823
+ while (i < lines.length && lines[i].type === "add") {
4824
+ adds.push(lines[i]);
4825
+ i++;
4826
+ }
4827
+ const pairCount = Math.min(removes.length, adds.length);
4828
+ for (let j = 0; j < pairCount; j++) {
4829
+ const diff = charDiff(removes[j].content, adds[j].content);
4830
+ if (diff) {
4831
+ result.set(removes[j], diff.oldSegments);
4832
+ result.set(adds[j], diff.newSegments);
4833
+ }
4834
+ }
4835
+ } else {
4836
+ i++;
4837
+ }
4838
+ }
4839
+ return result;
4840
+ }
4670
4841
  function UnifiedDiff({ hunks, annotationsByLine }) {
4671
4842
  const lastHunk = hunks[hunks.length - 1];
4672
4843
  const tailStart = lastHunk ? lastHunk.newStart + lastHunk.newCount : 1;
4673
4844
  return /* @__PURE__ */ jsx("div", { className: "diff-table-unified", children: [
4674
- hunks.map((hunk, hunkIdx) => /* @__PURE__ */ jsx("div", { className: "hunk-block", children: [
4675
- /* @__PURE__ */ jsx(
4676
- "div",
4677
- {
4678
- className: "hunk-separator",
4679
- "data-hunk-idx": hunkIdx,
4680
- "data-old-start": hunk.oldStart,
4681
- "data-old-count": hunk.oldCount,
4682
- "data-new-start": hunk.newStart,
4683
- "data-new-count": hunk.newCount,
4684
- children: [
4685
- "@@ -",
4686
- hunk.oldStart,
4687
- ",",
4688
- hunk.oldCount,
4689
- " +",
4690
- hunk.newStart,
4691
- ",",
4692
- hunk.newCount,
4693
- " @@"
4694
- ]
4695
- }
4696
- ),
4697
- hunk.lines.map((line) => {
4698
- const lineNum = line.type === "remove" ? line.oldNum : line.newNum;
4699
- const side = line.type === "remove" ? "old" : "new";
4700
- const anns = annotationsByLine[`${lineNum}:${side}`] ?? [];
4701
- return /* @__PURE__ */ jsx("div", { children: [
4702
- /* @__PURE__ */ jsx(
4703
- "div",
4704
- {
4705
- className: `diff-line ${line.type}${anns.length ? " has-annotation" : ""}`,
4706
- "data-line": lineNum,
4707
- "data-side": side,
4708
- children: [
4709
- /* @__PURE__ */ jsx("span", { className: "gutter-old", "data-line-number": line.oldNum ?? "" }),
4710
- /* @__PURE__ */ jsx("span", { className: "gutter-new", "data-line-number": line.newNum ?? "" }),
4711
- /* @__PURE__ */ jsx("span", { className: "code", children: line.content })
4712
- ]
4713
- }
4714
- ),
4715
- anns.length > 0 ? /* @__PURE__ */ jsx(AnnotationRows, { annotations: anns }) : null
4716
- ] });
4717
- })
4718
- ] })),
4845
+ hunks.map((hunk, hunkIdx) => {
4846
+ const charDiffs = buildUnifiedCharDiffs(hunk.lines);
4847
+ return /* @__PURE__ */ jsx("div", { className: "hunk-block", children: [
4848
+ /* @__PURE__ */ jsx(
4849
+ "div",
4850
+ {
4851
+ className: "hunk-separator",
4852
+ "data-hunk-idx": hunkIdx,
4853
+ "data-old-start": hunk.oldStart,
4854
+ "data-old-count": hunk.oldCount,
4855
+ "data-new-start": hunk.newStart,
4856
+ "data-new-count": hunk.newCount,
4857
+ children: [
4858
+ "@@ -",
4859
+ hunk.oldStart,
4860
+ ",",
4861
+ hunk.oldCount,
4862
+ " +",
4863
+ hunk.newStart,
4864
+ ",",
4865
+ hunk.newCount,
4866
+ " @@"
4867
+ ]
4868
+ }
4869
+ ),
4870
+ hunk.lines.map((line) => {
4871
+ const lineNum = line.type === "remove" ? line.oldNum : line.newNum;
4872
+ const side = line.type === "remove" ? "old" : "new";
4873
+ const anns = annotationsByLine[`${lineNum}:${side}`] ?? [];
4874
+ const segments = charDiffs.get(line);
4875
+ return /* @__PURE__ */ jsx("div", { children: [
4876
+ /* @__PURE__ */ jsx(
4877
+ "div",
4878
+ {
4879
+ className: `diff-line ${line.type}${anns.length ? " has-annotation" : ""}`,
4880
+ "data-line": lineNum,
4881
+ "data-side": side,
4882
+ children: [
4883
+ /* @__PURE__ */ jsx("span", { className: "gutter-old", "data-line-number": line.oldNum ?? "" }),
4884
+ /* @__PURE__ */ jsx("span", { className: "gutter-new", "data-line-number": line.newNum ?? "" }),
4885
+ /* @__PURE__ */ jsx("span", { className: "code", children: segments ? renderSegments(segments) : line.content })
4886
+ ]
4887
+ }
4888
+ ),
4889
+ anns.length > 0 ? /* @__PURE__ */ jsx(AnnotationRows, { annotations: anns }) : null
4890
+ ] });
4891
+ })
4892
+ ] });
4893
+ }),
4719
4894
  /* @__PURE__ */ jsx("div", { className: "hunk-separator hunk-expander-tail", "data-start": tailStart, children: "\u2195 Show remaining lines" })
4720
4895
  ] });
4721
4896
  }
@@ -4951,6 +5126,11 @@ pageRoutes.get("/", async (c) => {
4951
5126
  ] }),
4952
5127
  /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
4953
5128
  ] }),
5129
+ /* @__PURE__ */ jsx("div", { className: "diff-nav-bar", id: "diff-nav-bar", style: "display:none", children: [
5130
+ /* @__PURE__ */ jsx("button", { className: "nav-btn disabled", id: "nav-back-btn", disabled: true, title: "Back", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
5131
+ /* @__PURE__ */ jsx("button", { className: "nav-btn disabled", id: "nav-forward-btn", disabled: true, title: "Forward", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) }),
5132
+ /* @__PURE__ */ jsx("span", { className: "nav-file-path", id: "nav-file-path" })
5133
+ ] }),
4954
5134
  /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
4955
5135
  /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
4956
5136
  /* @__PURE__ */ jsx("div", { className: "diff-toolbar-svg-toggle", style: "display:none", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
@@ -5053,6 +5233,39 @@ pageRoutes.get("/file/:fileId", async (c) => {
5053
5233
  const html = /* @__PURE__ */ jsx(DiffView, { file, diff: finalDiff, annotations, mode });
5054
5234
  return c.html(html.toString());
5055
5235
  });
5236
+ pageRoutes.get("/file-raw", (c) => {
5237
+ const filePath = c.req.query("path");
5238
+ if (!filePath) return c.text("Missing path", 400);
5239
+ const repoRoot = c.get("repoRoot");
5240
+ let content;
5241
+ try {
5242
+ content = readFileSync8(resolve4(repoRoot, filePath), "utf-8");
5243
+ } catch {
5244
+ return c.text("File not found", 404);
5245
+ }
5246
+ const lines = content.split("\n");
5247
+ const diff = {
5248
+ filePath,
5249
+ oldPath: null,
5250
+ status: "added",
5251
+ isBinary: false,
5252
+ hunks: [{
5253
+ oldStart: 0,
5254
+ oldCount: 0,
5255
+ newStart: 1,
5256
+ newCount: lines.length,
5257
+ lines: lines.map((line, i) => ({
5258
+ type: "context",
5259
+ oldNum: i + 1,
5260
+ newNum: i + 1,
5261
+ content: line
5262
+ }))
5263
+ }]
5264
+ };
5265
+ const fakeFile = { id: "", review_id: "", file_path: filePath, status: "reviewed", diff_data: null };
5266
+ const html = /* @__PURE__ */ jsx(DiffView, { file: fakeFile, diff, annotations: [], mode: "unified" });
5267
+ return c.html(html.toString());
5268
+ });
5056
5269
  pageRoutes.get("/review/:reviewId", async (c) => {
5057
5270
  const reviewId = c.req.param("reviewId");
5058
5271
  const currentReviewId = c.get("reviewId");
@@ -5102,6 +5315,11 @@ pageRoutes.get("/review/:reviewId", async (c) => {
5102
5315
  ] }),
5103
5316
  /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
5104
5317
  ] }),
5318
+ /* @__PURE__ */ jsx("div", { className: "diff-nav-bar", id: "diff-nav-bar", style: "display:none", children: [
5319
+ /* @__PURE__ */ jsx("button", { className: "nav-btn disabled", id: "nav-back-btn", disabled: true, title: "Back", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
5320
+ /* @__PURE__ */ jsx("button", { className: "nav-btn disabled", id: "nav-forward-btn", disabled: true, title: "Forward", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) }),
5321
+ /* @__PURE__ */ jsx("span", { className: "nav-file-path", id: "nav-file-path" })
5322
+ ] }),
5105
5323
  /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
5106
5324
  /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
5107
5325
  /* @__PURE__ */ jsx("div", { className: "diff-toolbar-svg-toggle", style: "display:none", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
@@ -5149,10 +5367,10 @@ pageRoutes.get("/history", async (c) => {
5149
5367
 
5150
5368
  // src/server.ts
5151
5369
  function tryServe(fetch2, port) {
5152
- return new Promise((resolve5, reject) => {
5370
+ return new Promise((resolve6, reject) => {
5153
5371
  const server = serve({ fetch: fetch2, port });
5154
5372
  server.on("listening", () => {
5155
- resolve5(port);
5373
+ resolve6(port);
5156
5374
  });
5157
5375
  server.on("error", (err) => {
5158
5376
  if (err.code === "EADDRINUSE") {
@@ -5174,15 +5392,15 @@ async function startServer(port, reviewId, repoRoot, options) {
5174
5392
  const selfDir = dirname(fileURLToPath(import.meta.url));
5175
5393
  const distDir = existsSync6(join7(selfDir, "client", "styles.css")) ? join7(selfDir, "client") : join7(selfDir, "..", "dist", "client");
5176
5394
  app.get("/static/styles.css", (c) => {
5177
- const css = readFileSync8(join7(distDir, "styles.css"), "utf-8");
5395
+ const css = readFileSync9(join7(distDir, "styles.css"), "utf-8");
5178
5396
  return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
5179
5397
  });
5180
5398
  app.get("/static/app.js", (c) => {
5181
- const js = readFileSync8(join7(distDir, "app.global.js"), "utf-8");
5399
+ const js = readFileSync9(join7(distDir, "app.global.js"), "utf-8");
5182
5400
  return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
5183
5401
  });
5184
5402
  app.get("/static/history.js", (c) => {
5185
- const js = readFileSync8(join7(distDir, "history.global.js"), "utf-8");
5403
+ const js = readFileSync9(join7(distDir, "history.global.js"), "utf-8");
5186
5404
  return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
5187
5405
  });
5188
5406
  app.route("/api", apiRoutes);
@@ -5218,7 +5436,7 @@ async function startServer(port, reviewId, repoRoot, options) {
5218
5436
  }
5219
5437
 
5220
5438
  // src/skills.ts
5221
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
5439
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
5222
5440
  import { join as join8 } from "path";
5223
5441
  var SKILL_VERSION = 1;
5224
5442
  function versionHeader() {
@@ -5231,7 +5449,7 @@ function parseVersionHeader(content) {
5231
5449
  }
5232
5450
  function updateFile(path, content) {
5233
5451
  if (existsSync7(path)) {
5234
- const existing = readFileSync9(path, "utf-8");
5452
+ const existing = readFileSync10(path, "utf-8");
5235
5453
  const version = parseVersionHeader(existing);
5236
5454
  if (version !== null && version >= SKILL_VERSION) {
5237
5455
  return false;
@@ -5335,7 +5553,7 @@ function ensureSkills() {
5335
5553
  }
5336
5554
 
5337
5555
  // src/update-check.ts
5338
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
5556
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
5339
5557
  import { get } from "https";
5340
5558
  import { homedir as homedir3 } from "os";
5341
5559
  import { dirname as dirname2, join as join9 } from "path";
@@ -5346,7 +5564,7 @@ var PACKAGE_NAME = "glassbox";
5346
5564
  function getCurrentVersion() {
5347
5565
  try {
5348
5566
  const dir = dirname2(fileURLToPath2(import.meta.url));
5349
- const pkg = JSON.parse(readFileSync10(join9(dir, "..", "package.json"), "utf-8"));
5567
+ const pkg = JSON.parse(readFileSync11(join9(dir, "..", "package.json"), "utf-8"));
5350
5568
  return pkg.version;
5351
5569
  } catch {
5352
5570
  return "0.0.0";
@@ -5355,7 +5573,7 @@ function getCurrentVersion() {
5355
5573
  function getLastCheckDate() {
5356
5574
  try {
5357
5575
  if (existsSync8(CHECK_FILE)) {
5358
- return readFileSync10(CHECK_FILE, "utf-8").trim();
5576
+ return readFileSync11(CHECK_FILE, "utf-8").trim();
5359
5577
  }
5360
5578
  } catch {
5361
5579
  }
@@ -5372,10 +5590,10 @@ function isFirstUseToday() {
5372
5590
  return last !== today;
5373
5591
  }
5374
5592
  function fetchLatestVersion() {
5375
- return new Promise((resolve5) => {
5593
+ return new Promise((resolve6) => {
5376
5594
  const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
5377
5595
  if (res.statusCode !== 200) {
5378
- resolve5(null);
5596
+ resolve6(null);
5379
5597
  return;
5380
5598
  }
5381
5599
  let data = "";
@@ -5384,18 +5602,18 @@ function fetchLatestVersion() {
5384
5602
  });
5385
5603
  res.on("end", () => {
5386
5604
  try {
5387
- resolve5(JSON.parse(data).version);
5605
+ resolve6(JSON.parse(data).version);
5388
5606
  } catch {
5389
- resolve5(null);
5607
+ resolve6(null);
5390
5608
  }
5391
5609
  });
5392
5610
  });
5393
5611
  req.on("error", () => {
5394
- resolve5(null);
5612
+ resolve6(null);
5395
5613
  });
5396
5614
  req.on("timeout", () => {
5397
5615
  req.destroy();
5398
- resolve5(null);
5616
+ resolve6(null);
5399
5617
  });
5400
5618
  });
5401
5619
  }
@@ -5533,7 +5751,7 @@ function parseArgs(argv) {
5533
5751
  port = parseInt(args[++i], 10);
5534
5752
  break;
5535
5753
  case "--data-dir":
5536
- dataDir = resolve4(args[++i]);
5754
+ dataDir = resolve5(args[++i]);
5537
5755
  break;
5538
5756
  case "--resume":
5539
5757
  resume = true;
@@ -5589,7 +5807,7 @@ async function main() {
5589
5807
  console.log("AI service test mode enabled \u2014 using mock AI responses");
5590
5808
  }
5591
5809
  if (debug) {
5592
- console.log(`[debug] Build timestamp: ${"2026-03-21T03:42:45.606Z"}`);
5810
+ console.log(`[debug] Build timestamp: ${"2026-03-23T04:00:15.529Z"}`);
5593
5811
  }
5594
5812
  if (projectDir) {
5595
5813
  process.chdir(projectDir);