@kenkaiiii/ggcoder 4.14.3 → 5.0.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.
Files changed (234) hide show
  1. package/dist/cli/command-routing.d.ts +1 -1
  2. package/dist/cli/command-routing.d.ts.map +1 -1
  3. package/dist/cli/command-routing.js +1 -3
  4. package/dist/cli/command-routing.js.map +1 -1
  5. package/dist/cli/command-routing.test.js +0 -1
  6. package/dist/cli/command-routing.test.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +1 -29
  9. package/dist/cli.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +6 -3
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/code-retrieval.d.ts +13 -0
  14. package/dist/core/code-retrieval.d.ts.map +1 -0
  15. package/dist/core/code-retrieval.js +87 -0
  16. package/dist/core/code-retrieval.js.map +1 -0
  17. package/dist/core/hashline-edit-benchmark.d.ts +55 -0
  18. package/dist/core/hashline-edit-benchmark.d.ts.map +1 -0
  19. package/dist/core/hashline-edit-benchmark.js +342 -0
  20. package/dist/core/hashline-edit-benchmark.js.map +1 -0
  21. package/dist/core/hashline-edit-benchmark.test.d.ts +2 -0
  22. package/dist/core/hashline-edit-benchmark.test.d.ts.map +1 -0
  23. package/dist/core/hashline-edit-benchmark.test.js +141 -0
  24. package/dist/core/hashline-edit-benchmark.test.js.map +1 -0
  25. package/dist/core/hashline.d.ts +50 -0
  26. package/dist/core/hashline.d.ts.map +1 -0
  27. package/dist/core/hashline.js +76 -0
  28. package/dist/core/hashline.js.map +1 -0
  29. package/dist/core/ideal-review.d.ts +12 -1
  30. package/dist/core/ideal-review.d.ts.map +1 -1
  31. package/dist/core/ideal-review.js +52 -2
  32. package/dist/core/ideal-review.js.map +1 -1
  33. package/dist/core/ideal-review.test.js +41 -1
  34. package/dist/core/ideal-review.test.js.map +1 -1
  35. package/dist/core/semantic-search-benchmark.d.ts +37 -0
  36. package/dist/core/semantic-search-benchmark.d.ts.map +1 -0
  37. package/dist/core/semantic-search-benchmark.js +211 -0
  38. package/dist/core/semantic-search-benchmark.js.map +1 -0
  39. package/dist/core/semantic-search-benchmark.test.d.ts +2 -0
  40. package/dist/core/semantic-search-benchmark.test.d.ts.map +1 -0
  41. package/dist/core/semantic-search-benchmark.test.js +89 -0
  42. package/dist/core/semantic-search-benchmark.test.js.map +1 -0
  43. package/dist/tools/edit.d.ts +6 -0
  44. package/dist/tools/edit.d.ts.map +1 -1
  45. package/dist/tools/edit.js +34 -2
  46. package/dist/tools/edit.js.map +1 -1
  47. package/dist/tools/edit.test.js +112 -0
  48. package/dist/tools/edit.test.js.map +1 -1
  49. package/dist/tools/index.d.ts +1 -0
  50. package/dist/tools/index.d.ts.map +1 -1
  51. package/dist/tools/index.js +3 -0
  52. package/dist/tools/index.js.map +1 -1
  53. package/dist/tools/prompt-hints.d.ts.map +1 -1
  54. package/dist/tools/prompt-hints.js +8 -0
  55. package/dist/tools/prompt-hints.js.map +1 -1
  56. package/dist/tools/read.d.ts +1 -0
  57. package/dist/tools/read.d.ts.map +1 -1
  58. package/dist/tools/read.js +14 -3
  59. package/dist/tools/read.js.map +1 -1
  60. package/dist/tools/read.test.js +40 -0
  61. package/dist/tools/read.test.js.map +1 -1
  62. package/dist/tools/search-code.d.ts +11 -0
  63. package/dist/tools/search-code.d.ts.map +1 -0
  64. package/dist/tools/search-code.js +95 -0
  65. package/dist/tools/search-code.js.map +1 -0
  66. package/dist/tools/search-code.test.d.ts +2 -0
  67. package/dist/tools/search-code.test.d.ts.map +1 -0
  68. package/dist/tools/search-code.test.js +77 -0
  69. package/dist/tools/search-code.test.js.map +1 -0
  70. package/dist/ui/App.d.ts +3 -7
  71. package/dist/ui/App.d.ts.map +1 -1
  72. package/dist/ui/App.js +17 -105
  73. package/dist/ui/App.js.map +1 -1
  74. package/dist/ui/components/ActivityIndicator.d.ts +1 -1
  75. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  76. package/dist/ui/components/AnimationContext.d.ts +1 -1
  77. package/dist/ui/components/AnimationContext.d.ts.map +1 -1
  78. package/dist/ui/components/AssistantMessage.d.ts +1 -1
  79. package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
  80. package/dist/ui/components/BackgroundTasksBar.d.ts +1 -2
  81. package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -1
  82. package/dist/ui/components/Banner.d.ts +1 -2
  83. package/dist/ui/components/Banner.d.ts.map +1 -1
  84. package/dist/ui/components/ChatFooterPane.d.ts +1 -2
  85. package/dist/ui/components/ChatFooterPane.d.ts.map +1 -1
  86. package/dist/ui/components/ChatInputStack.d.ts +1 -1
  87. package/dist/ui/components/ChatInputStack.d.ts.map +1 -1
  88. package/dist/ui/components/ChatLayout.d.ts +4 -4
  89. package/dist/ui/components/ChatLayout.d.ts.map +1 -1
  90. package/dist/ui/components/ChatLivePane.d.ts +1 -1
  91. package/dist/ui/components/ChatLivePane.d.ts.map +1 -1
  92. package/dist/ui/components/ChatScreen.d.ts +1 -2
  93. package/dist/ui/components/ChatScreen.d.ts.map +1 -1
  94. package/dist/ui/components/ChatScreen.js +1 -1
  95. package/dist/ui/components/ChatScreen.js.map +1 -1
  96. package/dist/ui/components/ChatStatusRow.d.ts +1 -1
  97. package/dist/ui/components/ChatStatusRow.d.ts.map +1 -1
  98. package/dist/ui/components/CompactionNotice.d.ts +2 -3
  99. package/dist/ui/components/CompactionNotice.d.ts.map +1 -1
  100. package/dist/ui/components/DiffFrame.d.ts +1 -1
  101. package/dist/ui/components/DiffFrame.d.ts.map +1 -1
  102. package/dist/ui/components/DiffView.d.ts +1 -2
  103. package/dist/ui/components/DiffView.d.ts.map +1 -1
  104. package/dist/ui/components/Footer.d.ts +1 -2
  105. package/dist/ui/components/Footer.d.ts.map +1 -1
  106. package/dist/ui/components/FooterStatusRow.d.ts +1 -2
  107. package/dist/ui/components/FooterStatusRow.d.ts.map +1 -1
  108. package/dist/ui/components/FullScreenOverlayRouter.d.ts +2 -9
  109. package/dist/ui/components/FullScreenOverlayRouter.d.ts.map +1 -1
  110. package/dist/ui/components/FullScreenOverlayRouter.js +1 -5
  111. package/dist/ui/components/FullScreenOverlayRouter.js.map +1 -1
  112. package/dist/ui/components/IdealHookMessage.d.ts +1 -1
  113. package/dist/ui/components/IdealHookMessage.d.ts.map +1 -1
  114. package/dist/ui/components/InputArea.d.ts +1 -2
  115. package/dist/ui/components/InputArea.d.ts.map +1 -1
  116. package/dist/ui/components/InputArea.js +1 -6
  117. package/dist/ui/components/InputArea.js.map +1 -1
  118. package/dist/ui/components/LiveToolPanel.d.ts +1 -2
  119. package/dist/ui/components/LiveToolPanel.d.ts.map +1 -1
  120. package/dist/ui/components/Markdown.d.ts +2 -2
  121. package/dist/ui/components/Markdown.d.ts.map +1 -1
  122. package/dist/ui/components/ModelSelector.d.ts +1 -2
  123. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  124. package/dist/ui/components/Overlay.d.ts +1 -1
  125. package/dist/ui/components/Overlay.d.ts.map +1 -1
  126. package/dist/ui/components/PlanApproval.d.ts +1 -2
  127. package/dist/ui/components/PlanApproval.d.ts.map +1 -1
  128. package/dist/ui/components/PlanBanner.d.ts +1 -2
  129. package/dist/ui/components/PlanBanner.d.ts.map +1 -1
  130. package/dist/ui/components/PlanModeLogo.d.ts +1 -2
  131. package/dist/ui/components/PlanModeLogo.d.ts.map +1 -1
  132. package/dist/ui/components/PlanOverlay.d.ts +2 -3
  133. package/dist/ui/components/PlanOverlay.d.ts.map +1 -1
  134. package/dist/ui/components/PlanProgress.d.ts +1 -2
  135. package/dist/ui/components/PlanProgress.d.ts.map +1 -1
  136. package/dist/ui/components/QueueIndicator.d.ts +1 -2
  137. package/dist/ui/components/QueueIndicator.d.ts.map +1 -1
  138. package/dist/ui/components/RewindOverlay.d.ts +1 -2
  139. package/dist/ui/components/RewindOverlay.d.ts.map +1 -1
  140. package/dist/ui/components/SelectList.d.ts +1 -2
  141. package/dist/ui/components/SelectList.d.ts.map +1 -1
  142. package/dist/ui/components/ServerToolExecution.d.ts +1 -2
  143. package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
  144. package/dist/ui/components/SessionSelector.d.ts +1 -2
  145. package/dist/ui/components/SessionSelector.d.ts.map +1 -1
  146. package/dist/ui/components/SessionSummary.d.ts +1 -2
  147. package/dist/ui/components/SessionSummary.d.ts.map +1 -1
  148. package/dist/ui/components/SettingsSelector.d.ts +1 -2
  149. package/dist/ui/components/SettingsSelector.d.ts.map +1 -1
  150. package/dist/ui/components/SkillsOverlay.d.ts +1 -2
  151. package/dist/ui/components/SkillsOverlay.d.ts.map +1 -1
  152. package/dist/ui/components/SlashCommandMenu.d.ts +1 -2
  153. package/dist/ui/components/SlashCommandMenu.d.ts.map +1 -1
  154. package/dist/ui/components/SlashStyledSelectList.d.ts +1 -2
  155. package/dist/ui/components/SlashStyledSelectList.d.ts.map +1 -1
  156. package/dist/ui/components/Spinner.d.ts +1 -2
  157. package/dist/ui/components/Spinner.d.ts.map +1 -1
  158. package/dist/ui/components/StreamingArea.d.ts +1 -1
  159. package/dist/ui/components/StreamingArea.d.ts.map +1 -1
  160. package/dist/ui/components/SubAgentPanel.d.ts +1 -2
  161. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
  162. package/dist/ui/components/TaskPickerMenu.d.ts +1 -2
  163. package/dist/ui/components/TaskPickerMenu.d.ts.map +1 -1
  164. package/dist/ui/components/ThemeSelector.d.ts +1 -2
  165. package/dist/ui/components/ThemeSelector.d.ts.map +1 -1
  166. package/dist/ui/components/ThinkingBlock.d.ts +1 -1
  167. package/dist/ui/components/ThinkingBlock.d.ts.map +1 -1
  168. package/dist/ui/components/ToolExecution.d.ts +1 -2
  169. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  170. package/dist/ui/components/ToolGroupExecution.d.ts +1 -2
  171. package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
  172. package/dist/ui/components/TranscriptViewport.d.ts +1 -1
  173. package/dist/ui/components/TranscriptViewport.d.ts.map +1 -1
  174. package/dist/ui/components/UserMessage.d.ts +1 -2
  175. package/dist/ui/components/UserMessage.d.ts.map +1 -1
  176. package/dist/ui/hooks/useAgentLoop.d.ts +1 -1
  177. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  178. package/dist/ui/hooks/useAgentLoop.js +1 -3
  179. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  180. package/dist/ui/layout-decisions.d.ts +1 -1
  181. package/dist/ui/layout-decisions.d.ts.map +1 -1
  182. package/dist/ui/render.d.ts +4 -12
  183. package/dist/ui/render.d.ts.map +1 -1
  184. package/dist/ui/render.js +1 -3
  185. package/dist/ui/render.js.map +1 -1
  186. package/dist/ui/transcript/MiscRows.d.ts +7 -8
  187. package/dist/ui/transcript/MiscRows.d.ts.map +1 -1
  188. package/dist/ui/transcript/StatusRow.d.ts +1 -1
  189. package/dist/ui/transcript/StatusRow.d.ts.map +1 -1
  190. package/dist/ui/transcript/ToolRows.d.ts +6 -7
  191. package/dist/ui/transcript/ToolRows.d.ts.map +1 -1
  192. package/dist/ui/transcript/TranscriptItemFrame.d.ts +1 -1
  193. package/dist/ui/transcript/TranscriptItemFrame.d.ts.map +1 -1
  194. package/package.json +6 -5
  195. package/dist/cli/pixel.d.ts +0 -27
  196. package/dist/cli/pixel.d.ts.map +0 -1
  197. package/dist/cli/pixel.js +0 -103
  198. package/dist/cli/pixel.js.map +0 -1
  199. package/dist/core/pixel-fix-agent.d.ts +0 -24
  200. package/dist/core/pixel-fix-agent.d.ts.map +0 -1
  201. package/dist/core/pixel-fix-agent.js +0 -49
  202. package/dist/core/pixel-fix-agent.js.map +0 -1
  203. package/dist/core/pixel-fix.d.ts +0 -70
  204. package/dist/core/pixel-fix.d.ts.map +0 -1
  205. package/dist/core/pixel-fix.js +0 -338
  206. package/dist/core/pixel-fix.js.map +0 -1
  207. package/dist/core/pixel-fix.test.d.ts +0 -2
  208. package/dist/core/pixel-fix.test.d.ts.map +0 -1
  209. package/dist/core/pixel-fix.test.js +0 -310
  210. package/dist/core/pixel-fix.test.js.map +0 -1
  211. package/dist/core/pixel.d.ts +0 -37
  212. package/dist/core/pixel.d.ts.map +0 -1
  213. package/dist/core/pixel.js +0 -378
  214. package/dist/core/pixel.js.map +0 -1
  215. package/dist/core/pixel.test.d.ts +0 -2
  216. package/dist/core/pixel.test.d.ts.map +0 -1
  217. package/dist/core/pixel.test.js +0 -269
  218. package/dist/core/pixel.test.js.map +0 -1
  219. package/dist/ui/components/PixelOverlay.d.ts +0 -12
  220. package/dist/ui/components/PixelOverlay.d.ts.map +0 -1
  221. package/dist/ui/components/PixelOverlay.js +0 -117
  222. package/dist/ui/components/PixelOverlay.js.map +0 -1
  223. package/dist/ui/hooks/usePixelFixFlow.d.ts +0 -57
  224. package/dist/ui/hooks/usePixelFixFlow.d.ts.map +0 -1
  225. package/dist/ui/hooks/usePixelFixFlow.js +0 -102
  226. package/dist/ui/hooks/usePixelFixFlow.js.map +0 -1
  227. package/dist/ui/pixel.d.ts +0 -23
  228. package/dist/ui/pixel.d.ts.map +0 -1
  229. package/dist/ui/pixel.js +0 -393
  230. package/dist/ui/pixel.js.map +0 -1
  231. package/dist/ui/pixel.test.d.ts +0 -2
  232. package/dist/ui/pixel.test.d.ts.map +0 -1
  233. package/dist/ui/pixel.test.js +0 -138
  234. package/dist/ui/pixel.test.js.map +0 -1
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { lineHash, anchorFile } from "./hashline.js";
3
+ import { applyBaseline, applyHashline, checkAnchors, stripFence, genFile, buildTasks, } from "./hashline-edit-benchmark.js";
4
+ /**
5
+ * These tests prove the DETERMINISTIC half of the hashline benchmark — the
6
+ * apply + grading logic that decides OK / FAIL / corruption. The live-model
7
+ * numbers are only trustworthy if this logic is correct, so we exercise it from
8
+ * every angle: anchor uniqueness, corruption-avoidance, multi-edit ordering,
9
+ * malformed input, and the string-match baseline's ambiguity handling.
10
+ */
11
+ /** Build a minimal EditTask around a hand-written file for precise assertions. */
12
+ function taskFor(file, mustContain, mustPreserve) {
13
+ return {
14
+ name: "t",
15
+ approxLines: file.split("\n").length,
16
+ file,
17
+ instruction: "",
18
+ mustContain,
19
+ mustPreserve,
20
+ };
21
+ }
22
+ const FN_FILE = [
23
+ "export function computeTimeout(cfg: Config): number {",
24
+ " return cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT;",
25
+ "}",
26
+ ].join("\n");
27
+ /** The anchor for a given 0-based line index of a file. */
28
+ function anchorAt(file, index) {
29
+ return lineHash(file.split("\n")[index], index);
30
+ }
31
+ describe("hashline anchoring", () => {
32
+ it("1. folds line position into the hash so identical lines get distinct anchors", () => {
33
+ // Two identical lines at different positions must NOT collide.
34
+ expect(lineHash(" return x;", 3)).not.toBe(lineHash(" return x;", 9));
35
+ // Same content + same position is stable (deterministic).
36
+ expect(lineHash(" return x;", 3)).toBe(lineHash(" return x;", 3));
37
+ });
38
+ it("2. gives every line a unique resolvable anchor — even repeated blank lines", () => {
39
+ const file = ["a", "", "b", "", "c"].join("\n"); // two blank lines
40
+ const a = anchorFile(file);
41
+ expect(a.ambiguous.size).toBe(0);
42
+ expect(a.anchorToIndex.size).toBe(5);
43
+ expect([...a.anchorToIndex.values()].sort((x, y) => x - y)).toEqual([0, 1, 2, 3, 4]);
44
+ });
45
+ it("3. renders as `anchor│line` and the line column reconstructs the file", () => {
46
+ const a = anchorFile(FN_FILE);
47
+ const stripped = a.rendered
48
+ .split("\n")
49
+ .map((l) => l.slice(l.indexOf("│") + 1))
50
+ .join("\n");
51
+ expect(stripped).toBe(FN_FILE);
52
+ });
53
+ });
54
+ describe("applyHashline (proposed)", () => {
55
+ it("4. applies a valid single-line edit and grades it correct", () => {
56
+ const anchored = anchorFile(FN_FILE);
57
+ const anchor = anchorAt(FN_FILE, 1); // the `return ...` line
58
+ const raw = JSON.stringify({
59
+ edits: [
60
+ {
61
+ from: anchor,
62
+ to: anchor,
63
+ lines: [
64
+ " return cfg.retries > 0 ? Math.min(cfg.timeoutMs * cfg.retries, 30000) : (cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT);",
65
+ ],
66
+ },
67
+ ],
68
+ });
69
+ const task = taskFor(FN_FILE, ["cfg.timeoutMs * cfg.retries", "30000"], ["computeTimeout", "DEFAULT_TIMEOUT"]);
70
+ const out = applyHashline(raw, task, anchored);
71
+ expect(out.applied).toBe(true);
72
+ expect(out.correct).toBe(true);
73
+ expect(out.ambiguousEdits).toBe(0);
74
+ });
75
+ it("5. rejects an edit whose anchor is not in the file — never corrupts", () => {
76
+ const anchored = anchorFile(FN_FILE);
77
+ const raw = JSON.stringify({ edits: [{ from: "0000", to: "0000", lines: ["// hijacked"] }] });
78
+ const task = taskFor(FN_FILE, [], ["computeTimeout"]);
79
+ const out = applyHashline(raw, task, anchored);
80
+ expect(out.applied).toBe(false);
81
+ expect(out.correct).toBe(false);
82
+ expect(out.ambiguousEdits).toBe(1);
83
+ });
84
+ it("6. rejects a reversed range (from after to)", () => {
85
+ const anchored = anchorFile(FN_FILE);
86
+ const raw = JSON.stringify({
87
+ edits: [{ from: anchorAt(FN_FILE, 2), to: anchorAt(FN_FILE, 0), lines: ["x"] }],
88
+ });
89
+ const out = applyHashline(raw, taskFor(FN_FILE, [], []), anchored);
90
+ expect(out.applied).toBe(false);
91
+ });
92
+ it("7. applies multiple edits bottom-up so earlier indices stay valid", () => {
93
+ const file = ["const a = 1;", "const b = 2;", "const c = 3;"].join("\n");
94
+ const anchored = anchorFile(file);
95
+ const raw = JSON.stringify({
96
+ edits: [
97
+ { from: anchorAt(file, 0), to: anchorAt(file, 0), lines: ["const a = 10;"] },
98
+ { from: anchorAt(file, 2), to: anchorAt(file, 2), lines: ["const c = 30;"] },
99
+ ],
100
+ });
101
+ const out = applyHashline(raw, taskFor(file, ["const a = 10;", "const c = 30;"], ["const b = 2;"]), anchored);
102
+ expect(out.applied).toBe(true);
103
+ expect(out.correct).toBe(true);
104
+ });
105
+ it("8. returns a clean failure on malformed JSON instead of throwing", () => {
106
+ const anchored = anchorFile(FN_FILE);
107
+ expect(() => applyHashline("not json {", taskFor(FN_FILE, [], []), anchored)).not.toThrow();
108
+ const out = applyHashline("not json {", taskFor(FN_FILE, [], []), anchored);
109
+ expect(out.applied).toBe(false);
110
+ expect(out.parsedEdits).toBe(0);
111
+ });
112
+ });
113
+ describe("applyBaseline (current string-match) + helpers", () => {
114
+ it("9. applies a unique old_text but rejects a non-unique one", () => {
115
+ const unique = taskFor("const x = 1;\nconst y = 2;\n", ["const x = 100;"], ["const y = 2;"]);
116
+ const okRaw = JSON.stringify({
117
+ edits: [{ old_text: "const x = 1;", new_text: "const x = 100;" }],
118
+ });
119
+ expect(applyBaseline(okRaw, unique).applied).toBe(true);
120
+ // Same literal appears twice → the real edit tool refuses; so must we.
121
+ const dup = taskFor("const x = 1;\nconst y = 2;\nconst x = 1;\n", [], []);
122
+ const dupRaw = JSON.stringify({
123
+ edits: [{ old_text: "const x = 1;", new_text: "const x = 9;" }],
124
+ });
125
+ const out = applyBaseline(dupRaw, dup);
126
+ expect(out.applied).toBe(false);
127
+ expect(out.ambiguousEdits).toBe(1);
128
+ });
129
+ it("10. flags not-found old_text, strips code fences, and grades generated tasks", () => {
130
+ // Paraphrased / drifted old_text that isn't in the file → rejected.
131
+ const notFound = JSON.stringify({ edits: [{ old_text: "const z = 0;", new_text: "x" }] });
132
+ expect(applyBaseline(notFound, taskFor("const a = 1;\n", [], [])).applied).toBe(false);
133
+ // stripFence removes ```ts ... ``` wrappers the model sometimes adds.
134
+ expect(stripFence('```ts\n{"edits":[]}\n```')).toBe('{"edits":[]}');
135
+ // The generator + grader agree: a freshly generated file preserves its anchors.
136
+ const task = buildTasks()[0];
137
+ expect(genFile(task.approxLines)).toContain("computeTimeout");
138
+ expect(checkAnchors(task.file, taskFor(task.file, [], task.mustPreserve))).toBe(true);
139
+ });
140
+ });
141
+ //# sourceMappingURL=hashline-edit-benchmark.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline-edit-benchmark.test.js","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 4-hex-char anchor for a line. Position is folded into the hash so anchors are
3
+ * UNIQUE by construction (blank lines and repeated lines no longer collide).
4
+ * Resolution stays O(1) via a lookup map. `index` is the 0-based line index.
5
+ */
6
+ export declare function lineHash(line: string, index: number): string;
7
+ /** File rendered with `anchor│line` prefixes for the model to read. */
8
+ export declare function renderWithAnchors(file: string): string;
9
+ export interface AnchoredFile {
10
+ /** File rendered with `anchor│line` prefixes for the model to read. */
11
+ rendered: string;
12
+ /** anchor → line index (0-based). Only UNIQUE anchors are resolvable. */
13
+ anchorToIndex: Map<string, number>;
14
+ /** anchors that collided (ambiguous → unresolvable, like a stale-file reject). */
15
+ ambiguous: Set<string>;
16
+ lines: string[];
17
+ }
18
+ export declare function anchorFile(file: string): AnchoredFile;
19
+ /**
20
+ * True when the line at `index` (0-based) still hashes to `hash`. Out-of-range
21
+ * indices return false. This is the staleness gate the edit tool uses.
22
+ */
23
+ export declare function verifyAnchor(lines: string[], index: number, hash: string): boolean;
24
+ /** Optional per-edit anchor guard: pin both endpoints of an edit by line+hash. */
25
+ export interface EditAnchor {
26
+ /** 1-based line number of the first line of the edited span. */
27
+ start_line: number;
28
+ /** Content anchor of the first line (from a `read` with `anchors: true`). */
29
+ start_hash: string;
30
+ /** 1-based line number of the last line of the edited span. */
31
+ end_line: number;
32
+ /** Content anchor of the last line. */
33
+ end_hash: string;
34
+ }
35
+ export type AnchorFailure = "out_of_range" | "hash_mismatch" | "reversed";
36
+ export interface AnchorResolution {
37
+ ok: boolean;
38
+ /** 0-based index of the first line (only when ok). */
39
+ startIndex?: number;
40
+ /** 0-based index of the last line (only when ok). */
41
+ endIndex?: number;
42
+ reason?: AnchorFailure;
43
+ }
44
+ /**
45
+ * Resolve an anchor against the current file lines (0-based). Rejects the edit
46
+ * if either endpoint is out of range, the range is reversed, or either hash no
47
+ * longer matches — the corruption-avoidance property.
48
+ */
49
+ export declare function resolveAnchoredEdit(lines: string[], anchor: EditAnchor): AnchorResolution;
50
+ //# sourceMappingURL=hashline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline.d.ts","sourceRoot":"","sources":["../../src/core/hashline.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,kFAAkF;IAClF,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAiBrD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlF;AAED,kFAAkF;AAClF,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,UAAU,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAgBzF"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Hash-anchored line addressing — the pure, UI-free core shared by the hashline
3
+ * benchmark and the opt-in anchor guard in the read/edit tools.
4
+ *
5
+ * Every line gets a short content+position hash. Anchors are UNIQUE by
6
+ * construction (the line's index is folded into the hash, so blank/repeated
7
+ * lines never collide), which is what lets an edit either resolve to exactly one
8
+ * location or be rejected — never silently corrupt a file that drifted since the
9
+ * model last read it.
10
+ */
11
+ import { createHash } from "node:crypto";
12
+ /**
13
+ * 4-hex-char anchor for a line. Position is folded into the hash so anchors are
14
+ * UNIQUE by construction (blank lines and repeated lines no longer collide).
15
+ * Resolution stays O(1) via a lookup map. `index` is the 0-based line index.
16
+ */
17
+ export function lineHash(line, index) {
18
+ return createHash("sha1").update(`${index}:${line.trim()}`).digest("hex").slice(0, 4);
19
+ }
20
+ /** File rendered with `anchor│line` prefixes for the model to read. */
21
+ export function renderWithAnchors(file) {
22
+ return file
23
+ .split("\n")
24
+ .map((l, i) => `${lineHash(l, i)}│${l}`)
25
+ .join("\n");
26
+ }
27
+ export function anchorFile(file) {
28
+ const lines = file.split("\n");
29
+ const counts = new Map();
30
+ lines.forEach((l, i) => {
31
+ const h = lineHash(l, i);
32
+ const arr = counts.get(h) ?? [];
33
+ arr.push(i);
34
+ counts.set(h, arr);
35
+ });
36
+ const anchorToIndex = new Map();
37
+ const ambiguous = new Set();
38
+ for (const [h, idxs] of counts) {
39
+ // Position-folded anchors are unique unless sha1 itself collides in 16 bits.
40
+ if (idxs.length === 1)
41
+ anchorToIndex.set(h, idxs[0]);
42
+ else
43
+ ambiguous.add(h);
44
+ }
45
+ return { rendered: renderWithAnchors(file), anchorToIndex, ambiguous, lines };
46
+ }
47
+ /**
48
+ * True when the line at `index` (0-based) still hashes to `hash`. Out-of-range
49
+ * indices return false. This is the staleness gate the edit tool uses.
50
+ */
51
+ export function verifyAnchor(lines, index, hash) {
52
+ if (index < 0 || index >= lines.length)
53
+ return false;
54
+ return lineHash(lines[index], index) === hash;
55
+ }
56
+ /**
57
+ * Resolve an anchor against the current file lines (0-based). Rejects the edit
58
+ * if either endpoint is out of range, the range is reversed, or either hash no
59
+ * longer matches — the corruption-avoidance property.
60
+ */
61
+ export function resolveAnchoredEdit(lines, anchor) {
62
+ const startIndex = anchor.start_line - 1;
63
+ const endIndex = anchor.end_line - 1;
64
+ if (startIndex < 0 || endIndex < 0 || startIndex >= lines.length || endIndex >= lines.length) {
65
+ return { ok: false, reason: "out_of_range" };
66
+ }
67
+ if (startIndex > endIndex) {
68
+ return { ok: false, reason: "reversed" };
69
+ }
70
+ if (!verifyAnchor(lines, startIndex, anchor.start_hash) ||
71
+ !verifyAnchor(lines, endIndex, anchor.end_hash)) {
72
+ return { ok: false, reason: "hash_mismatch" };
73
+ }
74
+ return { ok: true, startIndex, endIndex };
75
+ }
76
+ //# sourceMappingURL=hashline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline.js","sourceRoot":"","sources":["../../src/core/hashline.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,17 @@ export interface IdealReviewDecision {
15
15
  }
16
16
  export declare const IDEAL_REVIEW_PROMPT: string;
17
17
  export declare function evaluateIdealReview(stats: IdealReviewStats): IdealReviewDecision;
18
- export declare function buildIdealReviewMessage(reasons: readonly string[]): Message;
18
+ export declare function buildIdealReviewMessage(reasons: readonly string[], driftedFiles?: readonly string[]): Message;
19
+ /**
20
+ * Test-drift detector \u2014 the one stranding signal a typechecker is blind to.
21
+ * Given the set of files the run mutated, return the source files whose sibling
22
+ * test exists on disk but was NOT touched this run (a green-but-stale test).
23
+ *
24
+ * Pure structural check: no sibling test on disk \u2192 no signal, so it stays
25
+ * silent on projects (or files) without co-located tests. `fileExists` is
26
+ * injectable for tests; paths are resolved against `cwd` so relative tool paths
27
+ * and absolute ones compare consistently.
28
+ */
29
+ export declare function detectTestDrift(touchedFiles: Iterable<string>, cwd: string, fileExists?: (p: string) => boolean): string[];
19
30
  export declare function shouldCountAsRiskyTool(toolName: string): boolean;
20
31
  //# sourceMappingURL=ideal-review.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ideal-review.d.ts","sourceRoot":"","sources":["../../src/core/ideal-review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,QAM6C,CAAC;AAI9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,mBAAmB,CAyChF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM3E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEhE"}
1
+ {"version":3,"file":"ideal-review.d.ts","sourceRoot":"","sources":["../../src/core/ideal-review.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,QAM6C,CAAC;AAI9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,mBAAmB,CAyChF;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,YAAY,GAAE,SAAS,MAAM,EAAO,GACnC,OAAO,CAYT;AAOD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC9B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAoB,GAC9C,MAAM,EAAE,CAwBV;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEhE"}
@@ -1,3 +1,5 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
1
3
  export const IDEAL_REVIEW_PROMPT = "Ideal? Review the actual work against the user's request before the final response. " +
2
4
  "Is it simple, focused, correct, and aligned? Did you over-edit, leave TODOs, miss an obvious " +
3
5
  "case the request called for, or introduce risk? Judge this by reading the code you changed \u2014 " +
@@ -42,13 +44,61 @@ export function evaluateIdealReview(stats) {
42
44
  }
43
45
  return { shouldReview: score >= 4, score, reasons };
44
46
  }
45
- export function buildIdealReviewMessage(reasons) {
47
+ export function buildIdealReviewMessage(reasons, driftedFiles = []) {
46
48
  const reasonText = reasons.length > 0 ? ` Triggered because: ${reasons.join(", ")}.` : "";
49
+ const driftText = driftedFiles.length > 0
50
+ ? ` Also: you changed ${driftedFiles.join(", ")} but the matching test file was not updated. ` +
51
+ `Update the test to match the new behavior, or state plainly why the existing test is still valid. ` +
52
+ `Edit the test only \u2014 do not run the suite now.`
53
+ : "";
47
54
  return {
48
55
  role: "user",
49
- content: `${IDEAL_REVIEW_PROMPT}${reasonText}`,
56
+ content: `${IDEAL_REVIEW_PROMPT}${reasonText}${driftText}`,
50
57
  };
51
58
  }
59
+ // A test file: foo.test.ts, foo.spec.tsx, foo.test.mjs, etc.
60
+ const TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
61
+ // A source code file we can pair with a sibling test.
62
+ const CODE_EXT_RE = /\.([cm]?[jt]sx?)$/;
63
+ /**
64
+ * Test-drift detector \u2014 the one stranding signal a typechecker is blind to.
65
+ * Given the set of files the run mutated, return the source files whose sibling
66
+ * test exists on disk but was NOT touched this run (a green-but-stale test).
67
+ *
68
+ * Pure structural check: no sibling test on disk \u2192 no signal, so it stays
69
+ * silent on projects (or files) without co-located tests. `fileExists` is
70
+ * injectable for tests; paths are resolved against `cwd` so relative tool paths
71
+ * and absolute ones compare consistently.
72
+ */
73
+ export function detectTestDrift(touchedFiles, cwd, fileExists = existsSync) {
74
+ const resolved = new Map(); // absolute -> original (as the model wrote it)
75
+ for (const f of touchedFiles)
76
+ resolved.set(path.resolve(cwd, f), f);
77
+ const touchedSet = new Set(resolved.keys());
78
+ const drifted = [];
79
+ for (const [abs, original] of resolved) {
80
+ const base = path.basename(abs);
81
+ if (TEST_FILE_RE.test(base))
82
+ continue; // the file itself is a test
83
+ const match = base.match(CODE_EXT_RE);
84
+ if (!match)
85
+ continue; // not a code file
86
+ const ext = match[1];
87
+ const dir = path.dirname(abs);
88
+ const stem = base.slice(0, base.length - ext.length - 1);
89
+ // Tests commonly drop the JSX `x` (Button.tsx -> Button.test.ts), so try the
90
+ // source ext and its non-JSX variant against both .test and .spec.
91
+ const testExts = ext.endsWith("x") ? [ext, ext.slice(0, -1)] : [ext];
92
+ const candidates = testExts
93
+ .flatMap((e) => [`${stem}.test.${e}`, `${stem}.spec.${e}`])
94
+ .map((c) => path.join(dir, c));
95
+ if (candidates.some((c) => touchedSet.has(c)))
96
+ continue; // sibling test was updated
97
+ if (candidates.some((c) => fileExists(c)))
98
+ drifted.push(original);
99
+ }
100
+ return drifted;
101
+ }
52
102
  export function shouldCountAsRiskyTool(toolName) {
53
103
  return RISKY_TOOL_NAMES.has(toolName);
54
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ideal-review.js","sourceRoot":"","sources":["../../src/core/ideal-review.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,mBAAmB,GAC9B,sFAAsF;IACtF,+FAA+F;IAC/F,oGAAoG;IACpG,8FAA8F;IAC9F,2FAA2F;IAC3F,2EAA2E,CAAC;AAE9E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,MAAM,UAAU,mBAAmB,CAAC,KAAuB;IACzD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,KAAK,CAAC,YAAY,IAAI,GAAG,EAAE,CAAC;QAC9B,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,gBAAgB,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,KAAK,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,gBAAgB,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,aAAa,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QAC5C,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,sBAAsB,CAAC,CAAC;IAC5E,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QACnD,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,sBAAsB,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;QAClE,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,YAAY,oBAAoB,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACrB,KAAK,IAAI,CAAC,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,cAAc,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAA0B;IAChE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1F,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,GAAG,mBAAmB,GAAG,UAAU,EAAE;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,OAAO,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"ideal-review.js","sourceRoot":"","sources":["../../src/core/ideal-review.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { buildIdealReviewMessage, evaluateIdealReview } from "./ideal-review.js";
2
+ import { buildIdealReviewMessage, detectTestDrift, evaluateIdealReview } from "./ideal-review.js";
3
3
  describe("evaluateIdealReview", () => {
4
4
  it("skips tiny text-only changes", () => {
5
5
  const decision = evaluateIdealReview({
@@ -55,5 +55,45 @@ describe("buildIdealReviewMessage", () => {
55
55
  expect(message.content).toContain("do NOT run builds, typechecks, linters, or test suites now");
56
56
  expect(message.content).toContain("/commit");
57
57
  });
58
+ it("calls out drifted files and their stale tests", () => {
59
+ const message = buildIdealReviewMessage([], ["src/foo.ts"]);
60
+ expect(message.content).toContain("src/foo.ts");
61
+ expect(message.content).toContain("matching test file was not updated");
62
+ });
63
+ });
64
+ describe("detectTestDrift", () => {
65
+ const cwd = "/proj";
66
+ const exists = (files) => {
67
+ const set = new Set(files);
68
+ return (p) => set.has(p);
69
+ };
70
+ it("flags a changed source whose sibling test exists but was not touched", () => {
71
+ const drift = detectTestDrift(["src/foo.ts"], cwd, exists(["/proj/src/foo.test.ts"]));
72
+ expect(drift).toEqual(["src/foo.ts"]);
73
+ });
74
+ it("stays silent when the sibling test was updated in the same run", () => {
75
+ const drift = detectTestDrift(["src/foo.ts", "src/foo.test.ts"], cwd, exists(["/proj/src/foo.test.ts"]));
76
+ expect(drift).toEqual([]);
77
+ });
78
+ it("stays silent when no sibling test exists on disk", () => {
79
+ const drift = detectTestDrift(["src/foo.ts"], cwd, exists([]));
80
+ expect(drift).toEqual([]);
81
+ });
82
+ it("ignores test files that are themselves the change", () => {
83
+ const drift = detectTestDrift(["src/foo.test.ts"], cwd, exists(["/proj/src/foo.test.ts"]));
84
+ expect(drift).toEqual([]);
85
+ });
86
+ it("ignores non-code files", () => {
87
+ const drift = detectTestDrift(["README.md"], cwd, exists(["/proj/README.test.md"]));
88
+ expect(drift).toEqual([]);
89
+ });
90
+ it("matches .spec siblings and resolves absolute paths", () => {
91
+ const drift = detectTestDrift(["/proj/src/bar.tsx"], cwd, exists(["/proj/src/bar.spec.tsx"]));
92
+ expect(drift).toEqual(["/proj/src/bar.tsx"]);
93
+ });
94
+ it("matches a .test.ts sibling for a .tsx source (test drops the x)", () => {
95
+ const drift = detectTestDrift(["src/Button.tsx"], cwd, exists(["/proj/src/Button.test.ts"]));
96
+ expect(drift).toEqual(["src/Button.tsx"]);
97
+ });
58
98
  });
59
99
  //# sourceMappingURL=ideal-review.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ideal-review.test.js","sourceRoot":"","sources":["../../src/core/ideal-review.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ideal-review.test.js","sourceRoot":"","sources":["../../src/core/ideal-review.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Semantic AST-chunk retrieval vs whole-file reads — real-API measurement of
3
+ * whether Feature #3 is worth building.
4
+ *
5
+ * The claim (cocoindex-code / oh-my-pi): replacing "grep then read whole files"
6
+ * with "retrieve only the relevant AST chunks" cuts ~70% of the tokens an agent
7
+ * spends locating code, with no loss of answer quality. We test that directly on
8
+ * OUR OWN repo against a live model.
9
+ *
10
+ * For a set of natural-language questions about real files in this repo, we build
11
+ * three context strategies and ask the model the same question with each:
12
+ *
13
+ * BASELINE (whole-file): deliver the FULL text of the top files a lexical
14
+ * grep would surface — this is what the agent reads today (read + grep).
15
+ *
16
+ * SEMANTIC (AST chunks): parse every file into top-level declarations
17
+ * (functions / classes / interfaces / consts), rank chunks with a real BM25
18
+ * retriever, and deliver only the top-k chunks. No embedding model needed; a
19
+ * learned embedding retriever would land between BM25 and ORACLE.
20
+ *
21
+ * ORACLE (upper bound): deliver only the hand-labelled answer chunk(s) — the
22
+ * best case any retriever could achieve.
23
+ *
24
+ * We measure, per question and strategy: INPUT tokens delivered (the headline
25
+ * cost) and whether the model's answer was correct (deterministic keyword grade).
26
+ * The verdict: does SEMANTIC reach BASELINE-level correctness at a fraction of
27
+ * the input tokens?
28
+ *
29
+ * Usage:
30
+ * npx tsx src/core/semantic-search-benchmark.ts
31
+ *
32
+ * Env overrides:
33
+ * GG_SS_PROVIDER / GG_SS_MODEL (default openai / gpt-5.5)
34
+ * GG_SS_TOPK (chunks/files delivered, default 3)
35
+ */
36
+ export declare function grade(answer: string, mustInclude: string[]): boolean;
37
+ //# sourceMappingURL=semantic-search-benchmark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-search-benchmark.d.ts","sourceRoot":"","sources":["../../src/core/semantic-search-benchmark.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAwHH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAGpE"}