codesearch-mcp 0.2.3-alpha → 0.4.0-alpha

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 (317) hide show
  1. package/CHANGELOG.md +88 -5
  2. package/LICENSE +661 -21
  3. package/README.md +76 -40
  4. package/dist/app.js +17 -38
  5. package/dist/src/ast/CallExtractor.d.ts +8 -0
  6. package/dist/src/ast/CallExtractor.js +68 -0
  7. package/dist/src/ast/GraphqlNameExtractor.d.ts +22 -0
  8. package/dist/src/ast/GraphqlNameExtractor.js +240 -0
  9. package/dist/src/ast/ImportEdgeExtractor.d.ts +11 -0
  10. package/dist/src/ast/ImportEdgeExtractor.js +129 -0
  11. package/dist/src/ast/IndexedNodeResolver.d.ts +14 -0
  12. package/dist/src/ast/IndexedNodeResolver.js +217 -0
  13. package/dist/src/ast/MemberExtractor.d.ts +8 -0
  14. package/dist/src/ast/MemberExtractor.js +129 -0
  15. package/dist/src/ast/TopLevelSymbolExtractor.d.ts +5 -0
  16. package/dist/src/ast/TopLevelSymbolExtractor.js +86 -0
  17. package/dist/src/ast/graphql-grammar.d.ts +24 -0
  18. package/dist/src/ast/graphql-grammar.js +93 -0
  19. package/dist/src/ast/node-helpers.d.ts +11 -0
  20. package/dist/src/ast/node-helpers.js +55 -0
  21. package/dist/src/chunking/AstChunker.d.ts +12 -0
  22. package/dist/src/chunking/AstChunker.js +30 -0
  23. package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
  24. package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
  25. package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
  26. package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
  27. package/dist/src/chunking/FragmentMerger.d.ts +11 -0
  28. package/dist/src/chunking/FragmentMerger.js +61 -0
  29. package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
  30. package/dist/src/chunking/ImportOnlyFilter.js +14 -0
  31. package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
  32. package/dist/src/chunking/LangchainTextSplitter.js +133 -0
  33. package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
  34. package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
  35. package/dist/src/chunking/RunPacker.d.ts +12 -0
  36. package/dist/src/chunking/RunPacker.js +84 -0
  37. package/dist/src/chunking/create-chunker.d.ts +3 -0
  38. package/dist/src/chunking/create-chunker.js +15 -0
  39. package/dist/src/chunking/interfaces.d.ts +27 -0
  40. package/dist/src/commands/ConfigCommand.js +1 -9
  41. package/dist/src/commands/EvalCommand.d.ts +10 -5
  42. package/dist/src/commands/EvalCommand.js +60 -149
  43. package/dist/src/commands/IndexCommand.d.ts +10 -4
  44. package/dist/src/commands/IndexCommand.js +52 -146
  45. package/dist/src/commands/McpCommand.d.ts +6 -3
  46. package/dist/src/commands/McpCommand.js +19 -20
  47. package/dist/src/commands/WebCommand.d.ts +9 -3
  48. package/dist/src/commands/WebCommand.js +17 -11
  49. package/dist/src/commands/eval/baseline-comparison.d.ts +6 -0
  50. package/dist/src/commands/eval/baseline-comparison.js +54 -0
  51. package/dist/src/commands/eval/eval-args.d.ts +6 -0
  52. package/dist/src/commands/eval/eval-args.js +71 -0
  53. package/dist/src/commands/eval/eval-corpora.d.ts +6 -0
  54. package/dist/src/commands/eval/eval-corpora.js +76 -0
  55. package/dist/src/commands/eval/eval-metadata.d.ts +17 -0
  56. package/dist/src/commands/eval/eval-metadata.js +75 -0
  57. package/dist/src/commands/eval/index-inspector.d.ts +3 -0
  58. package/dist/src/commands/eval/index-inspector.js +21 -0
  59. package/dist/src/commands/eval/staged-search.d.ts +3 -0
  60. package/dist/src/commands/eval/staged-search.js +20 -0
  61. package/dist/src/commands/eval/suite-loader.d.ts +8 -0
  62. package/dist/src/commands/eval/suite-loader.js +52 -0
  63. package/dist/src/commands/index/backend-probe.d.ts +2 -0
  64. package/dist/src/commands/index/backend-probe.js +15 -0
  65. package/dist/src/commands/index/embedding-setup.d.ts +3 -0
  66. package/dist/src/commands/index/embedding-setup.js +16 -0
  67. package/dist/src/commands/index/index-summary.d.ts +2 -0
  68. package/dist/src/commands/index/index-summary.js +22 -0
  69. package/dist/src/commands/index/legacy-ollama-guard.d.ts +3 -0
  70. package/dist/src/commands/index/legacy-ollama-guard.js +23 -0
  71. package/dist/src/commands/index/prune-step.d.ts +3 -0
  72. package/dist/src/commands/index/prune-step.js +22 -0
  73. package/dist/src/commands/index/scan-targets.d.ts +8 -0
  74. package/dist/src/commands/index/scan-targets.js +107 -0
  75. package/dist/src/config-error.d.ts +0 -4
  76. package/dist/src/config-error.js +0 -4
  77. package/dist/src/config-store.d.ts +1 -55
  78. package/dist/src/config-store.js +11 -73
  79. package/dist/src/config.d.ts +9 -18
  80. package/dist/src/config.js +36 -35
  81. package/dist/src/constants.d.ts +17 -28
  82. package/dist/src/constants.js +35 -29
  83. package/dist/src/container.d.ts +10 -2
  84. package/dist/src/container.js +19 -8
  85. package/dist/src/db/drizzle-types.d.ts +7 -0
  86. package/dist/src/db/drizzle-types.js +2 -0
  87. package/dist/src/db/index.d.ts +1 -0
  88. package/dist/src/db/schema-steps.d.ts +8 -0
  89. package/dist/src/db/schema-steps.js +105 -0
  90. package/dist/src/db/schema.d.ts +17 -0
  91. package/dist/src/db/schema.js +1 -6
  92. package/dist/src/declarations/DeclarationTree.d.ts +19 -0
  93. package/dist/src/declarations/DeclarationTree.js +66 -0
  94. package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
  95. package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
  96. package/dist/src/declarations/SourceLines.d.ts +37 -0
  97. package/dist/src/declarations/SourceLines.js +415 -0
  98. package/dist/src/dependency-traversal.d.ts +0 -6
  99. package/dist/src/dependency-traversal.js +0 -6
  100. package/dist/src/enriched-prompt.d.ts +0 -6
  101. package/dist/src/enriched-prompt.js +0 -6
  102. package/dist/src/error-record.d.ts +12 -0
  103. package/dist/src/error-record.js +248 -0
  104. package/dist/src/eval-corpus.d.ts +16 -0
  105. package/dist/src/eval-corpus.js +169 -0
  106. package/dist/src/eval-results.d.ts +6 -3
  107. package/dist/src/eval-results.js +323 -26
  108. package/dist/src/eval-vector-index.d.ts +5 -0
  109. package/dist/src/eval-vector-index.js +60 -0
  110. package/dist/src/file-utils.d.ts +0 -5
  111. package/dist/src/file-utils.js +0 -5
  112. package/dist/src/graph.d.ts +0 -25
  113. package/dist/src/graph.js +0 -28
  114. package/dist/src/help.js +9 -1
  115. package/dist/src/hnsw-settings.d.ts +3 -0
  116. package/dist/src/hnsw-settings.js +24 -0
  117. package/dist/src/index-args.d.ts +13 -0
  118. package/dist/src/index-args.js +48 -0
  119. package/dist/src/indexing/AnalysisBatcher.d.ts +12 -0
  120. package/dist/src/indexing/AnalysisBatcher.js +28 -0
  121. package/dist/src/indexing/ChunkEmbedder.d.ts +24 -0
  122. package/dist/src/indexing/ChunkEmbedder.js +37 -0
  123. package/dist/src/indexing/ConsoleScanReporter.d.ts +10 -0
  124. package/dist/src/indexing/ConsoleScanReporter.js +73 -0
  125. package/dist/src/indexing/EmbeddingSchemaPreparer.d.ts +14 -0
  126. package/dist/src/indexing/EmbeddingSchemaPreparer.js +66 -0
  127. package/dist/src/indexing/FileIngestor.d.ts +22 -0
  128. package/dist/src/indexing/FileIngestor.js +116 -0
  129. package/dist/src/indexing/IndexStore.d.ts +24 -0
  130. package/dist/src/indexing/IndexStore.js +69 -0
  131. package/dist/src/indexing/StaleFilePruner.d.ts +8 -0
  132. package/dist/src/indexing/StaleFilePruner.js +55 -0
  133. package/dist/src/indexing/analysis-rows.d.ts +4 -0
  134. package/dist/src/indexing/analysis-rows.js +65 -0
  135. package/dist/src/indexing/project-notices.d.ts +2 -0
  136. package/dist/src/indexing/project-notices.js +63 -0
  137. package/dist/src/interfaces/IAstParser.d.ts +4 -40
  138. package/dist/src/interfaces/ICallGraph.d.ts +28 -0
  139. package/dist/src/interfaces/ICallGraph.js +2 -0
  140. package/dist/src/interfaces/IChunkSearch.d.ts +42 -0
  141. package/dist/src/interfaces/IChunkSearch.js +2 -0
  142. package/dist/src/interfaces/IChunker.d.ts +3 -12
  143. package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
  144. package/dist/src/interfaces/ICorpusInspector.js +2 -0
  145. package/dist/src/interfaces/IDatabase.d.ts +8 -24
  146. package/dist/src/interfaces/IDependencyGraph.d.ts +24 -0
  147. package/dist/src/interfaces/IDependencyGraph.js +2 -0
  148. package/dist/src/interfaces/IEmbedder.d.ts +0 -9
  149. package/dist/src/interfaces/IIndexIdentityGuard.d.ts +3 -0
  150. package/dist/src/interfaces/IIndexIdentityGuard.js +2 -0
  151. package/dist/src/interfaces/IIndexedNodeResolver.d.ts +10 -0
  152. package/dist/src/interfaces/IIndexedNodeResolver.js +2 -0
  153. package/dist/src/interfaces/ILogger.d.ts +1 -11
  154. package/dist/src/interfaces/IMcpReadiness.d.ts +12 -0
  155. package/dist/src/interfaces/IMcpReadiness.js +2 -0
  156. package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
  157. package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
  158. package/dist/src/interfaces/IProjectCatalog.d.ts +13 -0
  159. package/dist/src/interfaces/IProjectCatalog.js +2 -0
  160. package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
  161. package/dist/src/interfaces/IProjectRoots.d.ts +3 -0
  162. package/dist/src/interfaces/IProjectRoots.js +2 -0
  163. package/dist/src/interfaces/IRetrievalEvaluator.d.ts +153 -12
  164. package/dist/src/interfaces/IScanReporter.d.ts +13 -0
  165. package/dist/src/interfaces/IScanReporter.js +2 -0
  166. package/dist/src/interfaces/ISymbolLookup.d.ts +49 -0
  167. package/dist/src/interfaces/ISymbolLookup.js +2 -0
  168. package/dist/src/interfaces/IToolPaths.d.ts +62 -0
  169. package/dist/src/interfaces/IToolPaths.js +2 -0
  170. package/dist/src/interfaces/IWorkspaceFiles.d.ts +28 -0
  171. package/dist/src/interfaces/IWorkspaceFiles.js +2 -0
  172. package/dist/src/interfaces/SymbolKind.d.ts +1 -0
  173. package/dist/src/interfaces/SymbolKind.js +2 -0
  174. package/dist/src/interfaces/index.d.ts +12 -1
  175. package/dist/src/interrupt.js +0 -2
  176. package/dist/src/owner-path.d.ts +9 -10
  177. package/dist/src/owner-path.js +11 -23
  178. package/dist/src/project-scope.d.ts +1 -12
  179. package/dist/src/project-scope.js +5 -12
  180. package/dist/src/project-selector.d.ts +0 -5
  181. package/dist/src/project-selector.js +0 -11
  182. package/dist/src/retrieval-eval.d.ts +15 -6
  183. package/dist/src/retrieval-eval.js +388 -59
  184. package/dist/src/search/CallGraph.d.ts +9 -0
  185. package/dist/src/search/CallGraph.js +84 -0
  186. package/dist/src/search/ChunkSearch.d.ts +23 -0
  187. package/dist/src/search/ChunkSearch.js +115 -0
  188. package/dist/src/search/DependencyGraph.d.ts +19 -0
  189. package/dist/src/search/DependencyGraph.js +180 -0
  190. package/dist/src/search/IndexIdentityGuard.d.ts +11 -0
  191. package/dist/src/search/IndexIdentityGuard.js +37 -0
  192. package/dist/src/search/ProjectCatalog.d.ts +11 -0
  193. package/dist/src/search/ProjectCatalog.js +38 -0
  194. package/dist/src/search/SymbolLookup.d.ts +24 -0
  195. package/dist/src/search/SymbolLookup.js +97 -0
  196. package/dist/src/search/chunks/CandidateFetcher.d.ts +19 -0
  197. package/dist/src/search/chunks/CandidateFetcher.js +40 -0
  198. package/dist/src/search/chunks/IterativeScanGate.d.ts +10 -0
  199. package/dist/src/search/chunks/IterativeScanGate.js +42 -0
  200. package/dist/src/search/chunks/RerankStage.d.ts +15 -0
  201. package/dist/src/search/chunks/RerankStage.js +42 -0
  202. package/dist/src/search/chunks/search-metrics.d.ts +18 -0
  203. package/dist/src/search/chunks/search-metrics.js +32 -0
  204. package/dist/src/search/chunks/search-plan.d.ts +18 -0
  205. package/dist/src/search/chunks/search-plan.js +19 -0
  206. package/dist/src/search/create-search-services.d.ts +26 -0
  207. package/dist/src/search/create-search-services.js +29 -0
  208. package/dist/src/search/index-paths.d.ts +1 -0
  209. package/dist/src/search/index-paths.js +5 -0
  210. package/dist/src/search/result-shaping.d.ts +4 -0
  211. package/dist/src/search/result-shaping.js +19 -0
  212. package/dist/src/search/symbol-sql.d.ts +13 -0
  213. package/dist/src/search/symbol-sql.js +66 -0
  214. package/dist/src/search/symbols/SymbolGrepFallback.d.ts +19 -0
  215. package/dist/src/search/symbols/SymbolGrepFallback.js +106 -0
  216. package/dist/src/search/symbols/SymbolIndexQuery.d.ts +14 -0
  217. package/dist/src/search/symbols/SymbolIndexQuery.js +52 -0
  218. package/dist/src/search/symbols/SymbolTextSearch.d.ts +13 -0
  219. package/dist/src/search/symbols/SymbolTextSearch.js +84 -0
  220. package/dist/src/search/symbols/TargetColumnGate.d.ts +7 -0
  221. package/dist/src/search/symbols/TargetColumnGate.js +23 -0
  222. package/dist/src/search/symbols/find-symbol-constants.d.ts +4 -0
  223. package/dist/src/search/symbols/find-symbol-constants.js +8 -0
  224. package/dist/src/search/symbols/grep-args.d.ts +3 -0
  225. package/dist/src/search/symbols/grep-args.js +7 -0
  226. package/dist/src/search/symbols/grep-patterns.d.ts +4 -0
  227. package/dist/src/search/symbols/grep-patterns.js +60 -0
  228. package/dist/src/services/AstParserService.d.ts +10 -106
  229. package/dist/src/services/AstParserService.js +37 -1068
  230. package/dist/src/services/DatabaseService.d.ts +10 -17
  231. package/dist/src/services/DatabaseService.js +71 -115
  232. package/dist/src/services/EmbedderFactory.d.ts +0 -4
  233. package/dist/src/services/EmbedderFactory.js +0 -5
  234. package/dist/src/services/IndexerService.d.ts +7 -26
  235. package/dist/src/services/IndexerService.js +38 -286
  236. package/dist/src/services/InfinityEmbedder.d.ts +0 -21
  237. package/dist/src/services/InfinityEmbedder.js +0 -15
  238. package/dist/src/services/LoggerService.d.ts +7 -5
  239. package/dist/src/services/LoggerService.js +37 -41
  240. package/dist/src/services/McpReadiness.d.ts +18 -0
  241. package/dist/src/services/McpReadiness.js +73 -0
  242. package/dist/src/services/McpServer.d.ts +50 -19
  243. package/dist/src/services/McpServer.js +132 -62
  244. package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
  245. package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
  246. package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
  247. package/dist/src/services/NxMonorepoResolver.js +5 -56
  248. package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
  249. package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
  250. package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
  251. package/dist/src/services/ProjectDiscoveryService.js +0 -4
  252. package/dist/src/services/VoyageEmbedder.d.ts +0 -6
  253. package/dist/src/services/VoyageEmbedder.js +0 -6
  254. package/dist/src/services/WebServerService.d.ts +5 -56
  255. package/dist/src/services/WebServerService.js +25 -63
  256. package/dist/src/services/http-retry.d.ts +0 -7
  257. package/dist/src/services/http-retry.js +1 -10
  258. package/dist/src/services/infinity-provisioning.d.ts +0 -74
  259. package/dist/src/services/infinity-provisioning.js +5 -88
  260. package/dist/src/services/legacy-ollama-index.d.ts +0 -16
  261. package/dist/src/services/legacy-ollama-index.js +0 -23
  262. package/dist/src/services/reranker.d.ts +0 -9
  263. package/dist/src/services/reranker.js +46 -12
  264. package/dist/src/setup.d.ts +24 -24
  265. package/dist/src/setup.js +205 -103
  266. package/dist/src/symbol-name.d.ts +1 -12
  267. package/dist/src/symbol-name.js +16 -25
  268. package/dist/src/symbol-rows.d.ts +0 -14
  269. package/dist/src/symbol-rows.js +0 -14
  270. package/dist/src/tool-call-log.d.ts +2 -0
  271. package/dist/src/tool-call-log.js +24 -0
  272. package/dist/src/tools/FindSymbolTool.d.ts +17 -7
  273. package/dist/src/tools/FindSymbolTool.js +54 -35
  274. package/dist/src/tools/GetCallersTool.d.ts +16 -8
  275. package/dist/src/tools/GetCallersTool.js +65 -35
  276. package/dist/src/tools/GetDependenciesTool.d.ts +16 -8
  277. package/dist/src/tools/GetDependenciesTool.js +36 -29
  278. package/dist/src/tools/GetDependentsTool.d.ts +20 -8
  279. package/dist/src/tools/GetDependentsTool.js +48 -34
  280. package/dist/src/tools/ListFilesTool.d.ts +16 -8
  281. package/dist/src/tools/ListFilesTool.js +20 -26
  282. package/dist/src/tools/ListProjectsTool.d.ts +11 -7
  283. package/dist/src/tools/ListProjectsTool.js +13 -11
  284. package/dist/src/tools/McpTool.d.ts +17 -0
  285. package/dist/src/tools/McpTool.js +28 -0
  286. package/dist/src/tools/ReadFileRangeTool.d.ts +16 -8
  287. package/dist/src/tools/ReadFileRangeTool.js +23 -25
  288. package/dist/src/tools/ReadFileTool.d.ts +15 -8
  289. package/dist/src/tools/ReadFileTool.js +19 -24
  290. package/dist/src/tools/SearchCodebaseTool.d.ts +22 -10
  291. package/dist/src/tools/SearchCodebaseTool.js +71 -50
  292. package/dist/src/tools/format-helpers.d.ts +9 -9
  293. package/dist/src/tools/format-helpers.js +146 -28
  294. package/dist/src/tools/tool-args.d.ts +38 -0
  295. package/dist/src/tools/tool-args.js +95 -0
  296. package/dist/src/web/dashboard.d.ts +0 -8
  297. package/dist/src/web/dashboard.js +7 -10
  298. package/dist/src/workspace/GrepRunner.d.ts +15 -0
  299. package/dist/src/workspace/GrepRunner.js +104 -0
  300. package/dist/src/workspace/ProjectRoots.d.ts +22 -0
  301. package/dist/src/workspace/ProjectRoots.js +102 -0
  302. package/dist/src/workspace/ToolPaths.d.ts +31 -0
  303. package/dist/src/workspace/ToolPaths.js +312 -0
  304. package/dist/src/workspace/WorkspaceFiles.d.ts +19 -0
  305. package/dist/src/workspace/WorkspaceFiles.js +285 -0
  306. package/dist/src/workspace/fs-access.d.ts +9 -0
  307. package/dist/src/workspace/fs-access.js +47 -0
  308. package/dist/src/workspace/path-helpers.d.ts +3 -0
  309. package/dist/src/workspace/path-helpers.js +43 -0
  310. package/package.json +5 -4
  311. package/dist/src/interfaces/ISearcher.d.ts +0 -35
  312. package/dist/src/services/AstChunker.d.ts +0 -42
  313. package/dist/src/services/AstChunker.js +0 -158
  314. package/dist/src/services/SearcherService.d.ts +0 -342
  315. package/dist/src/services/SearcherService.js +0 -1199
  316. package/docs/web-dashboard.md +0 -206
  317. /package/dist/src/{interfaces/ISearcher.js → chunking/interfaces.js} +0 -0
@@ -33,20 +33,111 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.degradedNotices = degradedNotices;
37
+ exports.renderSummary = renderSummary;
38
+ exports.renderComparison = renderComparison;
39
+ exports.renderProgress = renderProgress;
36
40
  exports.timestampSlug = timestampSlug;
37
41
  exports.writeResultFiles = writeResultFiles;
42
+ exports.normaliseResultFile = normaliseResultFile;
38
43
  exports.readResultFile = readResultFile;
39
44
  const fs = __importStar(require("fs"));
40
45
  const path = __importStar(require("path"));
46
+ const retrieval_eval_1 = require("./retrieval-eval");
47
+ const eval_vector_index_1 = require("./eval-vector-index");
48
+ const CATEGORIES = ['identifier', 'behaviour', 'concept'];
49
+ const STAGE_LABEL = {
50
+ vector: 'vector (reranker input order, not a rerank-off search)',
51
+ reranked: 'reranked',
52
+ };
41
53
  function pct(n) {
42
54
  return `${(n * 100).toFixed(1)}%`;
43
55
  }
56
+ function signed(n, digits = 4) {
57
+ return (n >= 0 ? '+' : '') + n.toFixed(digits);
58
+ }
59
+ function oneCase(count) {
60
+ return count > 0 ? pct(1 / count) : '—';
61
+ }
44
62
  function metricsRow(label, m) {
45
- return `| ${label} | ${m.count} | ${pct(m.hit1)} | ${pct(m.hit5)} | ${m.mrr10.toFixed(4)} |`;
63
+ return `| ${label} | ${m.count} | ${oneCase(m.count)} | ${pct(m.hit1)} | ${pct(m.hit5)} | ${m.mrr10.toFixed(4)} |`;
46
64
  }
47
65
  function deltaRow(label, d) {
48
- const sign = (n) => (n >= 0 ? '+' : '') + n.toFixed(4);
49
- return `| ${label} | ${d.count.current} | ${pct(d.hit1.current)} (${sign(d.hit1.delta)}) | ${pct(d.hit5.current)} (${sign(d.hit5.delta)}) | ${d.mrr10.current.toFixed(4)} (${sign(d.mrr10.delta)}) |`;
66
+ return `| ${label} | ${d.count.current} | ${pct(d.hit1.current)} (${signed(d.hit1.delta)}) | ${pct(d.hit5.current)} (${signed(d.hit5.delta)}) | ${d.mrr10.current.toFixed(4)} (${signed(d.mrr10.delta)}) |`;
67
+ }
68
+ function poolRow(label, p) {
69
+ return `| ${label} | ${p.count} | ${oneCase(p.count)} | ${pct(p.recall)} | ${p.short} |`;
70
+ }
71
+ function pairRow(label, p, count) {
72
+ return `| ${label} | ${p.improved} | ${p.worsened} | ${p.unchanged} | ${p.signTestP.toFixed(4)} | ${oneCase(count)} |`;
73
+ }
74
+ function poolValue(v, format, digits) {
75
+ if (v.baseline === null || v.delta === null)
76
+ return `no pool data → ${format(v.current)}`;
77
+ return `${format(v.baseline)} → ${format(v.current)} (${signed(v.delta, digits)})`;
78
+ }
79
+ function poolDeltaRow(label, d) {
80
+ return `| ${label} | ${poolValue(d.recall, pct, 4)} | ${poolValue(d.short, String, 0)} |`;
81
+ }
82
+ function degradedNotices(run) {
83
+ if (run.rerankFailures === 0 || !run.stages.includes('reranked'))
84
+ return [];
85
+ return [`reranked stage degraded: ${run.rerankFailures} rerank failure(s) scored on the vector-order fallback`];
86
+ }
87
+ function caseCount(change) {
88
+ const o = change.overall;
89
+ return o.improved + o.worsened + o.unchanged;
90
+ }
91
+ function categoryCount(change, category) {
92
+ const p = change.byCategory[category];
93
+ return p ? p.improved + p.worsened + p.unchanged : 0;
94
+ }
95
+ function pairTable(change) {
96
+ const lines = ['| Category | Improved | Worsened | Unchanged | Sign-test p | 1 case |', '|---|---|---|---|---|---|'];
97
+ lines.push(pairRow('Overall', change.overall, caseCount(change)));
98
+ for (const category of CATEGORIES) {
99
+ const p = change.byCategory[category];
100
+ if (p)
101
+ lines.push(pairRow(category, p, categoryCount(change, category)));
102
+ }
103
+ if (change.hit5Lost.length > 0)
104
+ lines.push('', `**Lost hit@5:** ${change.hit5Lost.join(', ')}`);
105
+ if (change.hit5Gained.length > 0)
106
+ lines.push('', `**Gained hit@5:** ${change.hit5Gained.join(', ')}`);
107
+ return lines;
108
+ }
109
+ function formatList(values) {
110
+ return values.map(v => (v === null ? 'not sent' : String(v))).join(', ') || '—';
111
+ }
112
+ function corpusLines(metadata) {
113
+ const lines = metadata.corpora.map(c => `${c.root} @ ${c.commit} — ${c.projects.length} project(s), ${c.chunks} chunks, fingerprint ${c.fingerprint}`);
114
+ if (metadata.liveCases > 0)
115
+ lines.push(`${metadata.liveCases} live-corpus case(s) — not comparable over time`);
116
+ return lines.length > 0 ? lines : ['none recorded'];
117
+ }
118
+ function groupLabel(key, group) {
119
+ return `${key === 'live' ? 'live cases' : key} (${group.cases} case(s))`;
120
+ }
121
+ function corpusTable(stages, group) {
122
+ const lines = ['| Stage | Category | Cases | 1 case | hit@5 | MRR@10 |', '|---|---|---|---|---|---|'];
123
+ for (const stage of stages) {
124
+ const m = group.metrics[stage];
125
+ if (!m)
126
+ continue;
127
+ const row = (label, x) => `| ${stage} | ${label} | ${x.count} | ${oneCase(x.count)} | ${pct(x.hit5)} | ${x.mrr10.toFixed(4)} |`;
128
+ lines.push(row('Overall', m.overall));
129
+ for (const category of CATEGORIES) {
130
+ const cm = m.byCategory[category];
131
+ if (cm)
132
+ lines.push(row(category, cm));
133
+ }
134
+ }
135
+ lines.push('', `Pool recall ${pct(group.pool.overall.recall)}, short pools ${group.pool.overall.short}.`);
136
+ for (const sc of group.stageChanges) {
137
+ const o = sc.change.overall;
138
+ lines.push('', `${sc.from} → ${sc.to}: improved ${o.improved}, worsened ${o.worsened}, unchanged ${o.unchanged}, p=${o.signTestP.toFixed(4)}.`);
139
+ }
140
+ return lines;
50
141
  }
51
142
  function renderMarkdown(result, comparison) {
52
143
  const { metadata, run } = result;
@@ -58,17 +149,51 @@ function renderMarkdown(result, comparison) {
58
149
  if (metadata.indexMeta) {
59
150
  lines.push(`**Index:** ${metadata.indexMeta.provider}/${metadata.indexMeta.model} (${metadata.indexMeta.dimensions} dims)`);
60
151
  }
61
- lines.push('', '## Metrics', '', '| Category | Cases | hit@1 | hit@5 | MRR@10 |', '|---|---|---|---|---|');
62
- lines.push(metricsRow('Overall', run.metrics.overall));
63
- for (const category of ['identifier', 'behaviour', 'concept']) {
64
- const m = run.metrics.byCategory[category];
65
- if (m)
66
- lines.push(metricsRow(category, m));
152
+ lines.push(`**Vector index:** ${(0, eval_vector_index_1.formatVectorIndex)(metadata.vectorIndex)}`);
153
+ const corpora = corpusLines(metadata);
154
+ lines.push(corpora.length === 1 ? `**Corpus:** ${corpora[0]}` : `**Corpora:** ${corpora.join('; ')}`);
155
+ const c = metadata.conditions;
156
+ if (c) {
157
+ lines.push(`**Search conditions:** threshold ${(0, retrieval_eval_1.formatThresholdConditions)(c.thresholds)} | overfetch ${c.overfetch} | candidateFloor ${c.candidateFloor} | fetchLimit ${formatList(c.fetchLimit)} | ef_search ${formatList(c.efSearch)} | iterative scan ${formatList(c.iterativeScan)}`);
158
+ }
159
+ else {
160
+ lines.push('**Search conditions:** not recorded');
161
+ }
162
+ for (const stage of run.stages) {
163
+ const m = run.metrics[stage];
164
+ if (!m)
165
+ continue;
166
+ lines.push('', `## Metrics — ${STAGE_LABEL[stage]}`, '', '| Category | Cases | 1 case | hit@1 | hit@5 | MRR@10 |', '|---|---|---|---|---|---|');
167
+ lines.push(metricsRow('Overall', m.overall));
168
+ for (const category of CATEGORIES) {
169
+ const cm = m.byCategory[category];
170
+ if (cm)
171
+ lines.push(metricsRow(category, cm));
172
+ }
173
+ }
174
+ lines.push('', '## Candidate pool', '', '| Category | Cases | 1 case | Pool recall | Short pools |', '|---|---|---|---|---|');
175
+ lines.push(poolRow('Overall', run.pool.overall));
176
+ for (const category of CATEGORIES) {
177
+ const p = run.pool.byCategory[category];
178
+ if (p)
179
+ lines.push(poolRow(category, p));
180
+ }
181
+ for (const sc of run.stageChanges) {
182
+ lines.push('', `## Stage change — ${sc.from} → ${sc.to}`, '', ...pairTable(sc.change));
67
183
  }
184
+ const groups = Object.entries(run.byCorpus);
185
+ if (groups.length > 0) {
186
+ lines.push('', '## Per corpus');
187
+ for (const [key, group] of groups)
188
+ lines.push('', `### ${groupLabel(key, group)}`, '', ...corpusTable(run.stages, group));
189
+ }
190
+ const degraded = degradedNotices(run);
191
+ if (degraded.length > 0)
192
+ lines.push('', ...degraded.map(d => `> ⚠ ${d}`));
68
193
  if (run.invalid.length > 0) {
69
194
  lines.push('', '## Invalid cases', '', '| Case | Reason |', '|---|---|');
70
- for (const c of run.invalid)
71
- lines.push(`| ${c.id} | ${c.reason}: ${c.invalidExpects.map(e => e.file).join(', ')} |`);
195
+ for (const ic of run.invalid)
196
+ lines.push(`| ${ic.id} | ${ic.reason}: ${ic.invalidExpects.map(e => e.file).join(', ')} |`);
72
197
  }
73
198
  if (run.warnings.length > 0) {
74
199
  lines.push('', '## Warnings (partially invalid)', '', '| Case | Dropped |', '|---|---|');
@@ -76,39 +201,211 @@ function renderMarkdown(result, comparison) {
76
201
  lines.push(`| ${w.id} | ${w.droppedExpects.map(e => e.file).join(', ')} |`);
77
202
  }
78
203
  if (comparison) {
204
+ const cmp = comparison.result;
79
205
  lines.push('', `## Comparison against ${comparison.baselinePath}`, '');
80
- if (comparison.result.conditionWarnings.length > 0) {
81
- lines.push('> ⚠ ' + comparison.result.conditionWarnings.join('; '), '');
206
+ if (cmp.conditionWarnings.length > 0) {
207
+ lines.push('> ⚠ ' + cmp.conditionWarnings.join('; '), '');
82
208
  }
83
- lines.push('| Category | Cases | hit@1 (Δ) | hit@5 (Δ) | MRR@10 (Δ) |', '|---|---|---|---|---|');
84
- lines.push(deltaRow('Overall', comparison.result.overall));
85
- for (const category of ['identifier', 'behaviour', 'concept']) {
86
- const d = comparison.result.byCategory[category];
209
+ for (const sc of cmp.stages) {
210
+ lines.push(`### ${sc.stage}`, '', '| Category | Cases | hit@1 (Δ) | hit@5 (Δ) | MRR@10 (Δ) |', '|---|---|---|---|---|');
211
+ lines.push(deltaRow('Overall', sc.overall));
212
+ for (const category of CATEGORIES) {
213
+ const d = sc.byCategory[category];
214
+ if (d)
215
+ lines.push(deltaRow(category, d));
216
+ }
217
+ lines.push('', `Paired change, baseline → current (${sc.stage}):`, '', ...pairTable(sc.change), '');
218
+ }
219
+ for (const u of cmp.unmatchedStages) {
220
+ lines.push(`**Stage \`${u.stage}\`** is present only in the ${u.side === 'baseline' ? 'baseline' : 'current run'}; not compared.`, '');
221
+ }
222
+ lines.push('### Candidate pool', '', '| Category | Pool recall | Short pools |', '|---|---|---|');
223
+ lines.push(poolDeltaRow('Overall', cmp.pool.overall));
224
+ for (const category of CATEGORIES) {
225
+ const d = cmp.pool.byCategory[category];
87
226
  if (d)
88
- lines.push(deltaRow(category, d));
227
+ lines.push(poolDeltaRow(category, d));
89
228
  }
90
- if (comparison.result.hit5Lost.length > 0)
91
- lines.push('', `**Lost hit@5:** ${comparison.result.hit5Lost.join(', ')}`);
92
- if (comparison.result.hit5Gained.length > 0)
93
- lines.push('', `**Gained hit@5:** ${comparison.result.hit5Gained.join(', ')}`);
94
229
  }
95
230
  return lines.join('\n') + '\n';
96
231
  }
97
- /** `yyyy-mm-dd_hh-mm` in local time, matching the existing `benchmarks/results/` naming. */
232
+ function consoleMetrics(label, m) {
233
+ return ` ${label.padEnd(10)} — hit@1: ${pct(m.hit1)} hit@5: ${pct(m.hit5)} MRR@10: ${m.mrr10.toFixed(4)} (${m.count}, 1 case = ${oneCase(m.count)})`;
234
+ }
235
+ function consolePairs(label, p) {
236
+ return ` ${label.padEnd(10)} — improved ${p.improved} worsened ${p.worsened} unchanged ${p.unchanged} p=${p.signTestP.toFixed(4)}`;
237
+ }
238
+ function renderSummary(result) {
239
+ const { run, metadata } = result;
240
+ const lines = [];
241
+ lines.push('', `Evaluated ${run.cases.length} case(s) (${run.invalid.length} invalid) against ${metadata.embeddingProvider}/${metadata.embeddingModel}`);
242
+ for (const line of corpusLines(metadata))
243
+ lines.push(` corpus: ${line}`);
244
+ lines.push(` vector index: ${(0, eval_vector_index_1.formatVectorIndex)(metadata.vectorIndex)}`);
245
+ for (const stage of run.stages) {
246
+ const m = run.metrics[stage];
247
+ if (!m)
248
+ continue;
249
+ lines.push(`\n[${STAGE_LABEL[stage]}]`);
250
+ lines.push(consoleMetrics('Overall', m.overall));
251
+ for (const category of CATEGORIES) {
252
+ const cm = m.byCategory[category];
253
+ if (cm)
254
+ lines.push(consoleMetrics(category, cm));
255
+ }
256
+ }
257
+ lines.push('\n[candidate pool]');
258
+ const poolLine = (label, p) => ` ${label.padEnd(10)} — recall: ${pct(p.recall)} short: ${p.short} (${p.count})`;
259
+ lines.push(poolLine('Overall', run.pool.overall));
260
+ for (const category of CATEGORIES) {
261
+ const p = run.pool.byCategory[category];
262
+ if (p)
263
+ lines.push(poolLine(category, p));
264
+ }
265
+ for (const sc of run.stageChanges) {
266
+ lines.push(`\n[${sc.from} → ${sc.to}]`);
267
+ lines.push(consolePairs('Overall', sc.change.overall));
268
+ for (const category of CATEGORIES) {
269
+ const p = sc.change.byCategory[category];
270
+ if (p)
271
+ lines.push(consolePairs(category, p));
272
+ }
273
+ if (sc.change.hit5Lost.length > 0)
274
+ lines.push(` Lost hit@5: ${sc.change.hit5Lost.join(', ')}`);
275
+ if (sc.change.hit5Gained.length > 0)
276
+ lines.push(` Gained hit@5: ${sc.change.hit5Gained.join(', ')}`);
277
+ }
278
+ for (const [key, group] of Object.entries(run.byCorpus)) {
279
+ lines.push(`\n[${groupLabel(key, group)}]`);
280
+ for (const stage of run.stages) {
281
+ const m = group.metrics[stage];
282
+ if (!m)
283
+ continue;
284
+ const cats = CATEGORIES.filter(cat => m.byCategory[cat]).map(cat => `${cat} ${m.byCategory[cat].mrr10.toFixed(4)}`).join(' ');
285
+ lines.push(` ${stage.padEnd(10)} — MRR@10: ${m.overall.mrr10.toFixed(4)} hit@5: ${pct(m.overall.hit5)} (${cats})`);
286
+ }
287
+ lines.push(` ${'pool'.padEnd(10)} — recall: ${pct(group.pool.overall.recall)} short: ${group.pool.overall.short}`);
288
+ for (const sc of group.stageChanges)
289
+ lines.push(consolePairs(`${sc.from}→${sc.to}`, sc.change.overall));
290
+ }
291
+ for (const d of degradedNotices(run))
292
+ lines.push(`\n⚠ ${d}`);
293
+ if (run.invalid.length > 0) {
294
+ lines.push('\nInvalid cases:');
295
+ for (const c of run.invalid)
296
+ lines.push(` ${c.id}: ${c.reason} — ${c.invalidExpects.map(e => e.file).join(', ')}`);
297
+ }
298
+ if (run.warnings.length > 0) {
299
+ lines.push('\nWarnings (scored on remaining valid answers):');
300
+ for (const w of run.warnings)
301
+ lines.push(` ${w.id}: dropped ${w.droppedExpects.map(e => e.file).join(', ')}`);
302
+ }
303
+ return lines;
304
+ }
305
+ function renderComparison(baselinePath, cmp) {
306
+ const lines = [`\nComparison against ${baselinePath}:`];
307
+ for (const w of cmp.conditionWarnings)
308
+ lines.push(` ⚠ ${w}`);
309
+ for (const sc of cmp.stages) {
310
+ lines.push(` [${sc.stage}]`);
311
+ lines.push(` MRR@10: ${sc.overall.mrr10.baseline.toFixed(4)} → ${sc.overall.mrr10.current.toFixed(4)} (${signed(sc.overall.mrr10.delta)})`);
312
+ lines.push(` hit@5: ${pct(sc.overall.hit5.baseline)} → ${pct(sc.overall.hit5.current)}`);
313
+ const o = sc.change.overall;
314
+ lines.push(` paired: improved ${o.improved} worsened ${o.worsened} unchanged ${o.unchanged} p=${o.signTestP.toFixed(4)}`);
315
+ if (sc.change.hit5Lost.length > 0)
316
+ lines.push(` Lost hit@5: ${sc.change.hit5Lost.join(', ')}`);
317
+ if (sc.change.hit5Gained.length > 0)
318
+ lines.push(` Gained hit@5: ${sc.change.hit5Gained.join(', ')}`);
319
+ }
320
+ for (const u of cmp.unmatchedStages) {
321
+ lines.push(` [${u.stage}] only in the ${u.side === 'baseline' ? 'baseline' : 'current run'} — not compared`);
322
+ }
323
+ const p = cmp.pool.overall;
324
+ lines.push(` pool recall: ${poolValue(p.recall, pct, 4)}`);
325
+ lines.push(` short pools: ${poolValue(p.short, String, 0)}`);
326
+ for (const category of CATEGORIES) {
327
+ const d = cmp.pool.byCategory[category];
328
+ if (d)
329
+ lines.push(` ${category.padEnd(10)} — recall ${poolValue(d.recall, pct, 4)} short ${poolValue(d.short, String, 0)}`);
330
+ }
331
+ return lines;
332
+ }
333
+ function renderProgress(p) {
334
+ const head = `[${String(p.index).padStart(String(p.total).length)}/${p.total}] ${p.id}`;
335
+ const secs = `(${(p.ms / 1000).toFixed(1)}s)`;
336
+ if (p.invalid)
337
+ return `${head} invalid — ${p.invalid.reason} ${secs}`;
338
+ const ranks = Object.keys(p.result.stages)
339
+ .map(stage => `${stage} ${p.result.stages[stage]?.rank ?? '—'}`)
340
+ .join(' → ');
341
+ return `${head} ${ranks}${p.result.rerankFailed ? ' (rerank failed)' : ''} ${secs}`;
342
+ }
98
343
  function timestampSlug(date) {
99
344
  const pad = (n) => String(n).padStart(2, '0');
100
345
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}_${pad(date.getHours())}-${pad(date.getMinutes())}`;
101
346
  }
102
- /** Writes the JSON + Markdown result files for one eval run; returns their paths. */
103
347
  function writeResultFiles(resultsDir, suiteLabel, result, now, comparison) {
104
348
  fs.mkdirSync(resultsDir, { recursive: true });
105
- const base = `eval_${suiteLabel}_${timestampSlug(now)}`;
349
+ const stem = `eval_${suiteLabel}_${timestampSlug(now)}`;
350
+ const taken = (base) => fs.existsSync(path.join(resultsDir, `${base}.json`)) || fs.existsSync(path.join(resultsDir, `${base}.md`));
351
+ let base = stem;
352
+ for (let n = 2; taken(base); n++)
353
+ base = `${stem}-${n}`;
106
354
  const jsonPath = path.join(resultsDir, `${base}.json`);
107
355
  const mdPath = path.join(resultsDir, `${base}.md`);
108
356
  fs.writeFileSync(jsonPath, JSON.stringify(result, null, 2) + '\n', 'utf-8');
109
357
  fs.writeFileSync(mdPath, renderMarkdown(result, comparison), 'utf-8');
110
358
  return { jsonPath, mdPath };
111
359
  }
360
+ const EMPTY_POOL = { count: 0, recall: 0, short: 0 };
361
+ function normaliseResultFile(raw) {
362
+ const run = raw?.run;
363
+ if (!run || run.stages !== undefined || run.metrics?.overall === undefined)
364
+ return withThresholdConditions(withCorpusMetadata(raw));
365
+ const stage = raw.metadata?.rerankerModel != null ? 'reranked' : 'vector';
366
+ const cases = run.cases.map(c => ({
367
+ id: c.id, project: c.project, category: c.category, query: c.query,
368
+ stages: { [stage]: { rank: c.rank, matched: c.matched } },
369
+ poolRank: null,
370
+ poolSize: 0,
371
+ rerankFailed: false,
372
+ corpus: null,
373
+ }));
374
+ return {
375
+ metadata: { ...raw.metadata, conditions: null, corpora: [], liveCases: cases.length },
376
+ run: {
377
+ cases,
378
+ invalid: run.invalid ?? [],
379
+ warnings: run.warnings ?? [],
380
+ stages: [stage],
381
+ metrics: { [stage]: run.metrics },
382
+ pool: { overall: EMPTY_POOL, byCategory: {} },
383
+ stageChanges: [],
384
+ rerankFailures: 0,
385
+ byCorpus: {},
386
+ },
387
+ legacy: true,
388
+ };
389
+ }
390
+ function withCorpusMetadata(file) {
391
+ if (!file?.metadata || file.metadata.corpora !== undefined)
392
+ return file;
393
+ return {
394
+ ...file,
395
+ metadata: { ...file.metadata, corpora: [], liveCases: file.run.cases.length },
396
+ run: { ...file.run, cases: file.run.cases.map(c => ({ ...c, corpus: c.corpus ?? null })), byCorpus: file.run.byCorpus ?? {} },
397
+ };
398
+ }
399
+ function withThresholdConditions(file) {
400
+ const conditions = file?.metadata?.conditions;
401
+ if (conditions?.similarityThreshold === undefined)
402
+ return file;
403
+ const { similarityThreshold, ...rest } = conditions;
404
+ return {
405
+ ...file,
406
+ metadata: { ...file.metadata, conditions: { ...rest, thresholds: [{ value: similarityThreshold, source: 'not recorded' }] } },
407
+ };
408
+ }
112
409
  function readResultFile(filePath) {
113
- return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
410
+ return normaliseResultFile(JSON.parse(fs.readFileSync(filePath, 'utf-8')));
114
411
  }
@@ -0,0 +1,5 @@
1
+ import type { IDatabase } from './interfaces/IDatabase';
2
+ import type { VectorIndexRecord } from './interfaces/IRetrievalEvaluator';
3
+ export declare function describeVectorIndex(db: Pick<IDatabase, 'query' | 'pgvectorVersion'>): Promise<VectorIndexRecord>;
4
+ export declare function formatIndexes(record: VectorIndexRecord): string;
5
+ export declare function formatVectorIndex(record: VectorIndexRecord | null): string;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeVectorIndex = describeVectorIndex;
4
+ exports.formatIndexes = formatIndexes;
5
+ exports.formatVectorIndex = formatVectorIndex;
6
+ const BUILD_DEFAULTS = {
7
+ hnsw: { m: '16', ef_construction: '64' },
8
+ ivfflat: { lists: '100' },
9
+ };
10
+ const INDEX_SQL = `
11
+ SELECT c.relname AS name, am.amname AS method, oc.opcname AS opclass, c.reloptions AS options
12
+ FROM pg_index i
13
+ JOIN pg_class c ON c.oid = i.indexrelid
14
+ JOIN pg_am am ON am.oid = c.relam
15
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = i.indkey[0]
16
+ JOIN pg_opclass oc ON oc.oid = i.indclass[0]
17
+ WHERE i.indrelid = to_regclass('code_chunk') AND a.attname = 'embedding' AND i.indisvalid
18
+ ORDER BY c.relname`;
19
+ const SCAN_SETTINGS_SQL = `
20
+ SELECT current_setting('hnsw.max_scan_tuples', true) AS max_scan_tuples,
21
+ current_setting('hnsw.scan_mem_multiplier', true) AS scan_mem_multiplier
22
+ FROM (SELECT '[1]'::vector) AS loaded`;
23
+ function numericSetting(value) {
24
+ if (value == null || value === '')
25
+ return null;
26
+ const n = Number(value);
27
+ return Number.isFinite(n) ? n : null;
28
+ }
29
+ function parseOptions(options) {
30
+ const params = {};
31
+ for (const option of options ?? []) {
32
+ const eq = option.indexOf('=');
33
+ if (eq > 0)
34
+ params[option.slice(0, eq)] = option.slice(eq + 1);
35
+ }
36
+ return params;
37
+ }
38
+ async function describeVectorIndex(db) {
39
+ const rows = await db.query(INDEX_SQL);
40
+ const [settings] = await db.query(SCAN_SETTINGS_SQL);
41
+ return {
42
+ pgvector: await db.pgvectorVersion(),
43
+ indexes: rows.map(r => ({
44
+ name: r.name,
45
+ method: r.method,
46
+ opclass: r.opclass,
47
+ params: { ...BUILD_DEFAULTS[r.method], ...parseOptions(r.options) },
48
+ })),
49
+ maxScanTuples: numericSetting(settings?.max_scan_tuples),
50
+ scanMemMultiplier: numericSetting(settings?.scan_mem_multiplier),
51
+ };
52
+ }
53
+ function formatIndexes(record) {
54
+ return record.indexes.map(i => `${i.name} ${i.method} (${[i.opclass, ...Object.entries(i.params).map(([k, v]) => `${k}=${v}`)].join(', ')})`).join('; ') || 'none';
55
+ }
56
+ function formatVectorIndex(record) {
57
+ if (!record)
58
+ return 'not recorded';
59
+ return `${formatIndexes(record)} | pgvector ${record.pgvector ?? 'not installed'} | hnsw.max_scan_tuples ${record.maxScanTuples ?? 'not defined'} | hnsw.scan_mem_multiplier ${record.scanMemMultiplier ?? 'not defined'}`;
60
+ }
@@ -19,10 +19,5 @@ export type IndexingExclusion = {
19
19
  } | {
20
20
  kind: 'ragignore';
21
21
  };
22
- /**
23
- * Why `collectFiles` would skip the file at `relPath` (relative to the project
24
- * root, `/`-separated), or null when it would collect it. Checks mirror
25
- * `collectFiles`: file name, `IGNORE_DIRS` segments, `.ragignore` on every prefix.
26
- */
27
22
  export declare function indexingExclusion(relPath: string, ig: ReturnType<typeof ignore>): IndexingExclusion | null;
28
23
  export declare function collectFiles(dir: string, ig: ReturnType<typeof ignore>, rootDir: string): string[];
@@ -80,11 +80,6 @@ function loadIgnore(projectDir) {
80
80
  function isIndexableFileName(name) {
81
81
  return SOURCE_EXTENSIONS.test(name) && !IGNORE_EXTENSIONS.test(name);
82
82
  }
83
- /**
84
- * Why `collectFiles` would skip the file at `relPath` (relative to the project
85
- * root, `/`-separated), or null when it would collect it. Checks mirror
86
- * `collectFiles`: file name, `IGNORE_DIRS` segments, `.ragignore` on every prefix.
87
- */
88
83
  function indexingExclusion(relPath, ig) {
89
84
  const segments = relPath.split('/');
90
85
  if (!isIndexableFileName(segments[segments.length - 1]))
@@ -12,33 +12,15 @@ export interface GraphData {
12
12
  nodes: ProjectNode[];
13
13
  edges: Edge[];
14
14
  }
15
- /** A single named graph tab (Workspace, or one per nested Nx repo). */
16
15
  export interface GraphView {
17
16
  name: string;
18
17
  nodes: ProjectNode[];
19
18
  edges: Edge[];
20
19
  }
21
- /**
22
- * Build the project graph data (nodes + relation edges) from a project list.
23
- * Pure apart from reading each project's `.ragmeta.json`. Shared by the console
24
- * graph, the graph views, and the web dashboard's `/api/graph`.
25
- */
26
20
  export declare function buildGraphData(projects: {
27
21
  name: string;
28
22
  path: string;
29
23
  }[]): GraphData;
30
- /**
31
- * Split a project list into graph tabs. A project whose name contains a `/`
32
- * belongs to a nested Nx repo (`<repo>/<sub-project>`); everything else is a
33
- * flat top-level project.
34
- *
35
- * - **Workspace** tab: flat projects + one node per Nx repo (collapsed to a
36
- * single `nxRepo` node); edges from `.ragmeta.json` relations.
37
- * - **One tab per Nx repo**: its sub-projects as nodes, with edges between them
38
- * taken from real import dependencies (`projectEdges`, from `code_dependency`).
39
- *
40
- * Returns the Workspace view first, then repo views sorted by name.
41
- */
42
24
  export declare function buildGraphViews(projects: {
43
25
  name: string;
44
26
  path: string;
@@ -46,13 +28,6 @@ export declare function buildGraphViews(projects: {
46
28
  from: string;
47
29
  to: string;
48
30
  }[], workspaceRoot: string): GraphView[];
49
- /**
50
- * Render the full interactive graph page (vis-network + inner tabs, edge-type
51
- * filters, legend, project toggle list). Used for the standalone
52
- * `project-graph.html` and served by the web dashboard at `GET /graph` (embedded
53
- * via iframe) so both share one implementation. Tab 0 is the Workspace view;
54
- * double-clicking an Nx-repo node opens that repo's tab.
55
- */
56
31
  export declare function buildHtml(views: GraphView[]): string;
57
32
  export declare function generateProjectGraph(projects: {
58
33
  name: string;
package/dist/src/graph.js CHANGED
@@ -41,11 +41,6 @@ const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const file_utils_1 = require("./file-utils");
43
43
  const EMPTY_META = { description: null, stack: null, notes: null, relations: null };
44
- /**
45
- * Build the project graph data (nodes + relation edges) from a project list.
46
- * Pure apart from reading each project's `.ragmeta.json`. Shared by the console
47
- * graph, the graph views, and the web dashboard's `/api/graph`.
48
- */
49
44
  function buildGraphData(projects) {
50
45
  const nodes = projects.map(p => {
51
46
  const meta = (0, file_utils_1.readRagMeta)(p.path);
@@ -53,18 +48,6 @@ function buildGraphData(projects) {
53
48
  });
54
49
  return { nodes, edges: buildEdges(nodes) };
55
50
  }
56
- /**
57
- * Split a project list into graph tabs. A project whose name contains a `/`
58
- * belongs to a nested Nx repo (`<repo>/<sub-project>`); everything else is a
59
- * flat top-level project.
60
- *
61
- * - **Workspace** tab: flat projects + one node per Nx repo (collapsed to a
62
- * single `nxRepo` node); edges from `.ragmeta.json` relations.
63
- * - **One tab per Nx repo**: its sub-projects as nodes, with edges between them
64
- * taken from real import dependencies (`projectEdges`, from `code_dependency`).
65
- *
66
- * Returns the Workspace view first, then repo views sorted by name.
67
- */
68
51
  function buildGraphViews(projects, projectEdges, workspaceRoot) {
69
52
  const flat = [];
70
53
  const groups = new Map();
@@ -84,7 +67,6 @@ function buildGraphViews(projects, projectEdges, workspaceRoot) {
84
67
  }
85
68
  }
86
69
  const repoNames = [...groups.keys()].sort();
87
- // Workspace: flat projects + one collapsed node per repo (reads repo-root ragmeta).
88
70
  const workspaceProjects = [
89
71
  ...flat,
90
72
  ...repoNames.map(repo => ({ name: repo, path: path.join(workspaceRoot, repo) })),
@@ -95,7 +77,6 @@ function buildGraphViews(projects, projectEdges, workspaceRoot) {
95
77
  if (repoSet.has(n.name))
96
78
  n.nxRepo = true;
97
79
  const workspace = { name: 'Workspace', nodes: ws.nodes, edges: ws.edges };
98
- // Per-repo: sub-project nodes + intra-repo import edges.
99
80
  const repoViews = repoNames.map(repo => {
100
81
  const subs = groups.get(repo);
101
82
  const { nodes } = buildGraphData(subs);
@@ -151,7 +132,6 @@ function printConsole(views, workspacePath) {
151
132
  console.log(`\n Full graph: file://${path.join(workspacePath, 'project-graph.html')}`);
152
133
  }
153
134
  }
154
- /** Prepare a view for vis-network: colored nodes (+ synthetic nodes for unknown edge targets) and indexed edges. */
155
135
  function viewToVis(view) {
156
136
  const nodes = view.nodes.map(n => ({
157
137
  id: n.name,
@@ -173,13 +153,6 @@ function viewToVis(view) {
173
153
  const labels = [...new Set(view.edges.map(e => e.label))].sort();
174
154
  return { name: view.name, nodes, edges, labels };
175
155
  }
176
- /**
177
- * Render the full interactive graph page (vis-network + inner tabs, edge-type
178
- * filters, legend, project toggle list). Used for the standalone
179
- * `project-graph.html` and served by the web dashboard at `GET /graph` (embedded
180
- * via iframe) so both share one implementation. Tab 0 is the Workspace view;
181
- * double-clicking an Nx-repo node opens that repo's tab.
182
- */
183
156
  function buildHtml(views) {
184
157
  const visViews = views.map(viewToVis);
185
158
  return `<!DOCTYPE html>
@@ -365,7 +338,6 @@ function buildHtml(views) {
365
338
  </body>
366
339
  </html>`;
367
340
  }
368
- // Single entry point — builds the tabbed views, prints a console summary, writes the HTML.
369
341
  function generateProjectGraph(projects, projectEdges, workspacePath) {
370
342
  const views = buildGraphViews(projects, projectEdges, workspacePath);
371
343
  printConsole(views, workspacePath);
package/dist/src/help.js CHANGED
@@ -16,7 +16,15 @@ function printHelp(command) {
16
16
  console.log(' --project [name] Scope this run to one discovered project or monorepo unit');
17
17
  console.log(' by exact name, bypassing selection and pruning; omit the');
18
18
  console.log(' name for an interactive single-choice prompt');
19
+ console.log(' --require-cwd Needs --project <name>; the run is refused unless the current');
20
+ console.log(' directory is that project\'s directory');
19
21
  console.log(' -h, --help Show this help message');
22
+ console.log('');
23
+ console.log('Exit codes:');
24
+ console.log(' 0 No file failed and nothing else failed; includes an interrupted run,');
25
+ console.log(' which may have left files unindexed');
26
+ console.log(' 2 The run finished with failed files and the rest of the index is usable');
27
+ console.log(' 1 Any other failure');
20
28
  break;
21
29
  case 'rag-mcp':
22
30
  case 'mcp':
@@ -75,7 +83,7 @@ function printHelp(command) {
75
83
  console.log(' -h, --help Show this help message');
76
84
  break;
77
85
  default:
78
- console.log('Usage: rag-index [--reindex] [--select] [--project [name]]');
86
+ console.log('Usage: rag-index [--reindex] [--select] [--project [name]] [--require-cwd]');
79
87
  console.log(' rag-mcp');
80
88
  console.log(' rag-setup');
81
89
  console.log(' rag-config <show|get|set|reset|edit>');
@@ -0,0 +1,3 @@
1
+ export declare const ITERATIVE_SCAN_MODE = "relaxed_order";
2
+ export declare function hnswSettings(dbLimit: number, iterative: boolean): Record<string, string | number>;
3
+ export declare function supportsIterativeScan(version: string): boolean;