lmgrep 0.1.18 → 0.2.1

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 (414) hide show
  1. package/README.md +253 -62
  2. package/completions/_lmgrep +49 -40
  3. package/dist/application/Lmgrep.d.ts +107 -0
  4. package/dist/application/Lmgrep.js +99 -0
  5. package/dist/application/Lmgrep.js.map +1 -0
  6. package/dist/application/LmgrepFactory.d.ts +40 -0
  7. package/dist/application/LmgrepFactory.js +155 -0
  8. package/dist/application/LmgrepFactory.js.map +1 -0
  9. package/dist/application/indexing/BranchBootstrapper.d.ts +21 -0
  10. package/dist/application/indexing/BranchBootstrapper.js +52 -0
  11. package/dist/application/indexing/BranchBootstrapper.js.map +1 -0
  12. package/dist/application/indexing/BranchManifestSweeper.d.ts +17 -0
  13. package/dist/application/indexing/BranchManifestSweeper.js +35 -0
  14. package/dist/application/indexing/BranchManifestSweeper.js.map +1 -0
  15. package/dist/application/indexing/Duration.d.ts +9 -0
  16. package/dist/application/indexing/Duration.js +25 -0
  17. package/dist/application/indexing/Duration.js.map +1 -0
  18. package/dist/application/indexing/EmbeddingAbortError.d.ts +11 -0
  19. package/dist/application/indexing/EmbeddingAbortError.js +20 -0
  20. package/dist/application/indexing/EmbeddingAbortError.js.map +1 -0
  21. package/dist/application/indexing/EmbeddingPipeline.d.ts +59 -0
  22. package/dist/application/indexing/EmbeddingPipeline.js +140 -0
  23. package/dist/application/indexing/EmbeddingPipeline.js.map +1 -0
  24. package/dist/application/indexing/IndexBuilder.d.ts +112 -0
  25. package/dist/application/indexing/IndexBuilder.js +370 -0
  26. package/dist/application/indexing/IndexBuilder.js.map +1 -0
  27. package/dist/application/indexing/IndexingProgress.d.ts +34 -0
  28. package/dist/application/indexing/IndexingProgress.js +2 -0
  29. package/dist/application/indexing/IndexingProgress.js.map +1 -0
  30. package/dist/application/operations/Deadline.d.ts +13 -0
  31. package/dist/application/operations/Deadline.js +29 -0
  32. package/dist/application/operations/Deadline.js.map +1 -0
  33. package/dist/application/operations/HealthMonitor.d.ts +58 -0
  34. package/dist/application/operations/HealthMonitor.js +128 -0
  35. package/dist/application/operations/HealthMonitor.js.map +1 -0
  36. package/dist/application/operations/IndexAlternatives.d.ts +35 -0
  37. package/dist/application/operations/IndexAlternatives.js +61 -0
  38. package/dist/application/operations/IndexAlternatives.js.map +1 -0
  39. package/dist/application/operations/IndexInventory.d.ts +69 -0
  40. package/dist/application/operations/IndexInventory.js +99 -0
  41. package/dist/application/operations/IndexInventory.js.map +1 -0
  42. package/dist/application/operations/StatusService.d.ts +90 -0
  43. package/dist/application/operations/StatusService.js +171 -0
  44. package/dist/application/operations/StatusService.js.map +1 -0
  45. package/dist/application/operations/WatchService.d.ts +67 -0
  46. package/dist/application/operations/WatchService.js +145 -0
  47. package/dist/application/operations/WatchService.js.map +1 -0
  48. package/dist/application/operations/WorkingTreeDiff.d.ts +19 -0
  49. package/dist/application/operations/WorkingTreeDiff.js +36 -0
  50. package/dist/application/operations/WorkingTreeDiff.js.map +1 -0
  51. package/dist/application/research/ResearchAgent.d.ts +68 -0
  52. package/dist/application/research/ResearchAgent.js +201 -0
  53. package/dist/application/research/ResearchAgent.js.map +1 -0
  54. package/dist/application/research/ResearchPrompts.d.ts +16 -0
  55. package/dist/application/research/ResearchPrompts.js +34 -0
  56. package/dist/application/research/ResearchPrompts.js.map +1 -0
  57. package/dist/application/search/SearchCriteria.d.ts +37 -0
  58. package/dist/application/search/SearchCriteria.js +48 -0
  59. package/dist/application/search/SearchCriteria.js.map +1 -0
  60. package/dist/application/search/SearchService.d.ts +36 -0
  61. package/dist/application/search/SearchService.js +108 -0
  62. package/dist/application/search/SearchService.js.map +1 -0
  63. package/dist/application/search/SearchTargetResolver.d.ts +56 -0
  64. package/dist/application/search/SearchTargetResolver.js +104 -0
  65. package/dist/application/search/SearchTargetResolver.js.map +1 -0
  66. package/dist/cli.d.ts +1 -1
  67. package/dist/cli.js +4 -953
  68. package/dist/cli.js.map +1 -1
  69. package/dist/domain/config/ConfigSource.d.ts +16 -0
  70. package/dist/domain/config/ConfigSource.js +2 -0
  71. package/dist/domain/config/ConfigSource.js.map +1 -0
  72. package/dist/domain/config/LmgrepConfig.d.ts +53 -0
  73. package/dist/domain/config/LmgrepConfig.js +2 -0
  74. package/dist/domain/config/LmgrepConfig.js.map +1 -0
  75. package/dist/domain/corpus/Chunk.d.ts +49 -0
  76. package/dist/domain/corpus/Chunk.js +60 -0
  77. package/dist/domain/corpus/Chunk.js.map +1 -0
  78. package/dist/domain/corpus/CodeLocation.d.ts +18 -0
  79. package/dist/domain/corpus/CodeLocation.js +34 -0
  80. package/dist/domain/corpus/CodeLocation.js.map +1 -0
  81. package/dist/domain/corpus/ContentHash.d.ts +15 -0
  82. package/dist/domain/corpus/ContentHash.js +30 -0
  83. package/dist/domain/corpus/ContentHash.js.map +1 -0
  84. package/dist/domain/corpus/FileVersion.d.ts +31 -0
  85. package/dist/domain/corpus/FileVersion.js +47 -0
  86. package/dist/domain/corpus/FileVersion.js.map +1 -0
  87. package/dist/domain/corpus/SourceFile.d.ts +26 -0
  88. package/dist/domain/corpus/SourceFile.js +47 -0
  89. package/dist/domain/corpus/SourceFile.js.map +1 -0
  90. package/dist/domain/corpus/Vector.d.ts +37 -0
  91. package/dist/domain/corpus/Vector.js +96 -0
  92. package/dist/domain/corpus/Vector.js.map +1 -0
  93. package/dist/domain/ports/ChatModelPort.d.ts +57 -0
  94. package/dist/domain/ports/ChatModelPort.js +2 -0
  95. package/dist/domain/ports/ChatModelPort.js.map +1 -0
  96. package/dist/domain/ports/ChunkRepositoryPort.d.ts +42 -0
  97. package/dist/domain/ports/ChunkRepositoryPort.js +2 -0
  98. package/dist/domain/ports/ChunkRepositoryPort.js.map +1 -0
  99. package/dist/domain/ports/ChunkerPort.d.ts +5 -0
  100. package/dist/domain/ports/ChunkerPort.js +2 -0
  101. package/dist/domain/ports/ChunkerPort.js.map +1 -0
  102. package/dist/domain/ports/DatabaseSessionPort.d.ts +10 -0
  103. package/dist/domain/ports/DatabaseSessionPort.js +2 -0
  104. package/dist/domain/ports/DatabaseSessionPort.js.map +1 -0
  105. package/dist/domain/ports/EmbedderPort.d.ts +14 -0
  106. package/dist/domain/ports/EmbedderPort.js +2 -0
  107. package/dist/domain/ports/EmbedderPort.js.map +1 -0
  108. package/dist/domain/ports/FileManifestRepositoryPort.d.ts +32 -0
  109. package/dist/domain/ports/FileManifestRepositoryPort.js +2 -0
  110. package/dist/domain/ports/FileManifestRepositoryPort.js.map +1 -0
  111. package/dist/domain/ports/GitPort.d.ts +24 -0
  112. package/dist/domain/ports/GitPort.js +2 -0
  113. package/dist/domain/ports/GitPort.js.map +1 -0
  114. package/dist/domain/ports/IndexMaintenancePort.d.ts +58 -0
  115. package/dist/domain/ports/IndexMaintenancePort.js +2 -0
  116. package/dist/domain/ports/IndexMaintenancePort.js.map +1 -0
  117. package/dist/domain/ports/IndexMetadataPort.d.ts +23 -0
  118. package/dist/domain/ports/IndexMetadataPort.js +2 -0
  119. package/dist/domain/ports/IndexMetadataPort.js.map +1 -0
  120. package/dist/domain/ports/LockPort.d.ts +17 -0
  121. package/dist/domain/ports/LockPort.js +2 -0
  122. package/dist/domain/ports/LockPort.js.map +1 -0
  123. package/dist/domain/ports/LoggerPort.d.ts +5 -0
  124. package/dist/domain/ports/LoggerPort.js +2 -0
  125. package/dist/domain/ports/LoggerPort.js.map +1 -0
  126. package/dist/domain/ports/ProjectIndexesPort.d.ts +20 -0
  127. package/dist/domain/ports/ProjectIndexesPort.js +2 -0
  128. package/dist/domain/ports/ProjectIndexesPort.js.map +1 -0
  129. package/dist/domain/ports/ProjectRegistryPort.d.ts +42 -0
  130. package/dist/domain/ports/ProjectRegistryPort.js +2 -0
  131. package/dist/domain/ports/ProjectRegistryPort.js.map +1 -0
  132. package/dist/domain/ports/StateDirectoryPort.d.ts +29 -0
  133. package/dist/domain/ports/StateDirectoryPort.js +2 -0
  134. package/dist/domain/ports/StateDirectoryPort.js.map +1 -0
  135. package/dist/domain/ports/WorkspacePort.d.ts +38 -0
  136. package/dist/domain/ports/WorkspacePort.js +2 -0
  137. package/dist/domain/ports/WorkspacePort.js.map +1 -0
  138. package/dist/domain/project/Branch.d.ts +20 -0
  139. package/dist/domain/project/Branch.js +33 -0
  140. package/dist/domain/project/Branch.js.map +1 -0
  141. package/dist/domain/project/DatabaseLocation.d.ts +32 -0
  142. package/dist/domain/project/DatabaseLocation.js +35 -0
  143. package/dist/domain/project/DatabaseLocation.js.map +1 -0
  144. package/dist/domain/project/IndexMetadata.d.ts +17 -0
  145. package/dist/domain/project/IndexMetadata.js +2 -0
  146. package/dist/domain/project/IndexMetadata.js.map +1 -0
  147. package/dist/domain/project/ModelIdentity.d.ts +58 -0
  148. package/dist/domain/project/ModelIdentity.js +94 -0
  149. package/dist/domain/project/ModelIdentity.js.map +1 -0
  150. package/dist/domain/project/Project.d.ts +17 -0
  151. package/dist/domain/project/Project.js +21 -0
  152. package/dist/domain/project/Project.js.map +1 -0
  153. package/dist/domain/project/ProjectId.d.ts +28 -0
  154. package/dist/domain/project/ProjectId.js +58 -0
  155. package/dist/domain/project/ProjectId.js.map +1 -0
  156. package/dist/domain/project/ProjectLocator.d.ts +112 -0
  157. package/dist/domain/project/ProjectLocator.js +178 -0
  158. package/dist/domain/project/ProjectLocator.js.map +1 -0
  159. package/dist/domain/research/Citation.d.ts +27 -0
  160. package/dist/domain/research/Citation.js +43 -0
  161. package/dist/domain/research/Citation.js.map +1 -0
  162. package/dist/domain/research/EvidenceLedger.d.ts +47 -0
  163. package/dist/domain/research/EvidenceLedger.js +111 -0
  164. package/dist/domain/research/EvidenceLedger.js.map +1 -0
  165. package/dist/domain/research/ResearchTrace.d.ts +19 -0
  166. package/dist/domain/research/ResearchTrace.js +22 -0
  167. package/dist/domain/research/ResearchTrace.js.map +1 -0
  168. package/dist/domain/retrieval/Hit.d.ts +19 -0
  169. package/dist/domain/retrieval/Hit.js +28 -0
  170. package/dist/domain/retrieval/Hit.js.map +1 -0
  171. package/dist/domain/retrieval/HitList.d.ts +35 -0
  172. package/dist/domain/retrieval/HitList.js +81 -0
  173. package/dist/domain/retrieval/HitList.js.map +1 -0
  174. package/dist/domain/retrieval/MissingIndexError.d.ts +11 -0
  175. package/dist/domain/retrieval/MissingIndexError.js +15 -0
  176. package/dist/domain/retrieval/MissingIndexError.js.map +1 -0
  177. package/dist/index.d.ts +43 -45
  178. package/dist/index.js +30 -567
  179. package/dist/index.js.map +1 -1
  180. package/dist/infrastructure/ai/AiSdkChatModel.d.ts +31 -0
  181. package/dist/infrastructure/ai/AiSdkChatModel.js +137 -0
  182. package/dist/infrastructure/ai/AiSdkChatModel.js.map +1 -0
  183. package/dist/infrastructure/ai/AiSdkEmbedder.d.ts +28 -0
  184. package/dist/infrastructure/ai/AiSdkEmbedder.js +79 -0
  185. package/dist/infrastructure/ai/AiSdkEmbedder.js.map +1 -0
  186. package/dist/infrastructure/ai/DockerModelRunnerProbe.d.ts +16 -0
  187. package/dist/infrastructure/ai/DockerModelRunnerProbe.js +52 -0
  188. package/dist/infrastructure/ai/DockerModelRunnerProbe.js.map +1 -0
  189. package/dist/infrastructure/ai/EmbeddingPrefixes.d.ts +21 -0
  190. package/dist/infrastructure/ai/EmbeddingPrefixes.js +40 -0
  191. package/dist/infrastructure/ai/EmbeddingPrefixes.js.map +1 -0
  192. package/dist/infrastructure/ai/LmStudioProbe.d.ts +18 -0
  193. package/dist/infrastructure/ai/LmStudioProbe.js +49 -0
  194. package/dist/infrastructure/ai/LmStudioProbe.js.map +1 -0
  195. package/dist/infrastructure/ai/LocalModelReloader.d.ts +21 -0
  196. package/dist/infrastructure/ai/LocalModelReloader.js +61 -0
  197. package/dist/infrastructure/ai/LocalModelReloader.js.map +1 -0
  198. package/dist/infrastructure/ai/LocalRuntimeDetector.d.ts +36 -0
  199. package/dist/infrastructure/ai/LocalRuntimeDetector.js +63 -0
  200. package/dist/infrastructure/ai/LocalRuntimeDetector.js.map +1 -0
  201. package/dist/infrastructure/ai/ModelRuntime.d.ts +32 -0
  202. package/dist/infrastructure/ai/ModelRuntime.js +2 -0
  203. package/dist/infrastructure/ai/ModelRuntime.js.map +1 -0
  204. package/dist/infrastructure/ai/OllamaProbe.d.ts +22 -0
  205. package/dist/infrastructure/ai/OllamaProbe.js +71 -0
  206. package/dist/infrastructure/ai/OllamaProbe.js.map +1 -0
  207. package/dist/infrastructure/ai/ProviderFailure.d.ts +16 -0
  208. package/dist/infrastructure/ai/ProviderFailure.js +35 -0
  209. package/dist/infrastructure/ai/ProviderFailure.js.map +1 -0
  210. package/dist/infrastructure/ai/ProviderModuleLoader.d.ts +14 -0
  211. package/dist/infrastructure/ai/ProviderModuleLoader.js +56 -0
  212. package/dist/infrastructure/ai/ProviderModuleLoader.js.map +1 -0
  213. package/dist/infrastructure/ai/ProviderRegistry.d.ts +38 -0
  214. package/dist/infrastructure/ai/ProviderRegistry.js +63 -0
  215. package/dist/infrastructure/ai/ProviderRegistry.js.map +1 -0
  216. package/dist/infrastructure/fs/ConfigLoader.d.ts +59 -0
  217. package/dist/infrastructure/fs/ConfigLoader.js +194 -0
  218. package/dist/infrastructure/fs/ConfigLoader.js.map +1 -0
  219. package/dist/infrastructure/fs/DatabaseLocks.d.ts +52 -0
  220. package/dist/infrastructure/fs/DatabaseLocks.js +92 -0
  221. package/dist/infrastructure/fs/DatabaseLocks.js.map +1 -0
  222. package/dist/infrastructure/fs/DiskUsage.d.ts +6 -0
  223. package/dist/infrastructure/fs/DiskUsage.js +41 -0
  224. package/dist/infrastructure/fs/DiskUsage.js.map +1 -0
  225. package/dist/infrastructure/fs/IndexableFileRules.d.ts +33 -0
  226. package/dist/infrastructure/fs/IndexableFileRules.js +177 -0
  227. package/dist/infrastructure/fs/IndexableFileRules.js.map +1 -0
  228. package/dist/infrastructure/fs/Loggers.d.ts +14 -0
  229. package/dist/infrastructure/fs/Loggers.js +18 -0
  230. package/dist/infrastructure/fs/Loggers.js.map +1 -0
  231. package/dist/infrastructure/fs/PidFileLock.d.ts +40 -0
  232. package/dist/infrastructure/fs/PidFileLock.js +97 -0
  233. package/dist/infrastructure/fs/PidFileLock.js.map +1 -0
  234. package/dist/infrastructure/fs/ProjectIndexes.d.ts +8 -0
  235. package/dist/infrastructure/fs/ProjectIndexes.js +35 -0
  236. package/dist/infrastructure/fs/ProjectIndexes.js.map +1 -0
  237. package/dist/infrastructure/fs/ProjectMetadataStore.d.ts +42 -0
  238. package/dist/infrastructure/fs/ProjectMetadataStore.js +86 -0
  239. package/dist/infrastructure/fs/ProjectMetadataStore.js.map +1 -0
  240. package/dist/infrastructure/fs/ProjectRegistry.d.ts +25 -0
  241. package/dist/infrastructure/fs/ProjectRegistry.js +83 -0
  242. package/dist/infrastructure/fs/ProjectRegistry.js.map +1 -0
  243. package/dist/infrastructure/fs/StateDirectory.d.ts +28 -0
  244. package/dist/infrastructure/fs/StateDirectory.js +71 -0
  245. package/dist/infrastructure/fs/StateDirectory.js.map +1 -0
  246. package/dist/infrastructure/fs/Workspace.d.ts +25 -0
  247. package/dist/infrastructure/fs/Workspace.js +90 -0
  248. package/dist/infrastructure/fs/Workspace.js.map +1 -0
  249. package/dist/infrastructure/git/GitClient.d.ts +34 -0
  250. package/dist/infrastructure/git/GitClient.js +74 -0
  251. package/dist/infrastructure/git/GitClient.js.map +1 -0
  252. package/dist/infrastructure/lancedb/ChunkRepository.d.ts +38 -0
  253. package/dist/infrastructure/lancedb/ChunkRepository.js +166 -0
  254. package/dist/infrastructure/lancedb/ChunkRepository.js.map +1 -0
  255. package/dist/infrastructure/lancedb/DatabaseImporter.d.ts +22 -0
  256. package/dist/infrastructure/lancedb/DatabaseImporter.js +74 -0
  257. package/dist/infrastructure/lancedb/DatabaseImporter.js.map +1 -0
  258. package/dist/infrastructure/lancedb/FileManifestRepository.d.ts +34 -0
  259. package/dist/infrastructure/lancedb/FileManifestRepository.js +137 -0
  260. package/dist/infrastructure/lancedb/FileManifestRepository.js.map +1 -0
  261. package/dist/infrastructure/lancedb/IndexMaintenance.d.ts +53 -0
  262. package/dist/infrastructure/lancedb/IndexMaintenance.js +227 -0
  263. package/dist/infrastructure/lancedb/IndexMaintenance.js.map +1 -0
  264. package/dist/infrastructure/lancedb/LanceTables.d.ts +67 -0
  265. package/dist/infrastructure/lancedb/LanceTables.js +159 -0
  266. package/dist/infrastructure/lancedb/LanceTables.js.map +1 -0
  267. package/dist/{lib/native-tuning.js → infrastructure/lancedb/NativeTuning.js} +1 -1
  268. package/dist/infrastructure/lancedb/NativeTuning.js.map +1 -0
  269. package/dist/infrastructure/lancedb/RowReplication.d.ts +27 -0
  270. package/dist/infrastructure/lancedb/RowReplication.js +82 -0
  271. package/dist/infrastructure/lancedb/RowReplication.js.map +1 -0
  272. package/dist/infrastructure/lancedb/VectorIndexPolicy.d.ts +60 -0
  273. package/dist/infrastructure/lancedb/VectorIndexPolicy.js +72 -0
  274. package/dist/infrastructure/lancedb/VectorIndexPolicy.js.map +1 -0
  275. package/dist/infrastructure/p2p/IndexShare.d.ts +45 -0
  276. package/dist/infrastructure/p2p/IndexShare.js +160 -0
  277. package/dist/infrastructure/p2p/IndexShare.js.map +1 -0
  278. package/dist/infrastructure/p2p/PeerSwarm.d.ts +16 -0
  279. package/dist/infrastructure/p2p/PeerSwarm.js +41 -0
  280. package/dist/infrastructure/p2p/PeerSwarm.js.map +1 -0
  281. package/dist/infrastructure/p2p/SecureChannel.d.ts +60 -0
  282. package/dist/infrastructure/p2p/SecureChannel.js +71 -0
  283. package/dist/infrastructure/p2p/SecureChannel.js.map +1 -0
  284. package/dist/infrastructure/p2p/ShareCode.d.ts +25 -0
  285. package/dist/infrastructure/p2p/ShareCode.js +304 -0
  286. package/dist/infrastructure/p2p/ShareCode.js.map +1 -0
  287. package/dist/infrastructure/process/ProcessRegistry.d.ts +50 -0
  288. package/dist/infrastructure/process/ProcessRegistry.js +109 -0
  289. package/dist/infrastructure/process/ProcessRegistry.js.map +1 -0
  290. package/dist/infrastructure/treesitter/ChunkContextBuilder.d.ts +45 -0
  291. package/dist/infrastructure/treesitter/ChunkContextBuilder.js +159 -0
  292. package/dist/infrastructure/treesitter/ChunkContextBuilder.js.map +1 -0
  293. package/dist/infrastructure/treesitter/EmbeddedTreeSitterAssets.d.ts +8 -0
  294. package/dist/infrastructure/treesitter/EmbeddedTreeSitterAssets.js +12 -0
  295. package/dist/infrastructure/treesitter/EmbeddedTreeSitterAssets.js.map +1 -0
  296. package/dist/infrastructure/treesitter/LanguageCatalog.d.ts +30 -0
  297. package/dist/{lib/chunker/languages.js → infrastructure/treesitter/LanguageCatalog.js} +38 -29
  298. package/dist/infrastructure/treesitter/LanguageCatalog.js.map +1 -0
  299. package/dist/infrastructure/treesitter/SlidingWindowChunker.d.ts +15 -0
  300. package/dist/infrastructure/treesitter/SlidingWindowChunker.js +42 -0
  301. package/dist/infrastructure/treesitter/SlidingWindowChunker.js.map +1 -0
  302. package/dist/infrastructure/treesitter/TreeSitterChunker.d.ts +42 -0
  303. package/dist/infrastructure/treesitter/TreeSitterChunker.js +165 -0
  304. package/dist/infrastructure/treesitter/TreeSitterChunker.js.map +1 -0
  305. package/dist/mcp.d.ts +1 -1
  306. package/dist/mcp.js +14 -72
  307. package/dist/mcp.js.map +1 -1
  308. package/dist/presentation/cli/Cli.d.ts +31 -0
  309. package/dist/presentation/cli/Cli.js +120 -0
  310. package/dist/presentation/cli/Cli.js.map +1 -0
  311. package/dist/presentation/cli/CliOptions.d.ts +18 -0
  312. package/dist/presentation/cli/CliOptions.js +25 -0
  313. package/dist/presentation/cli/CliOptions.js.map +1 -0
  314. package/dist/presentation/cli/CommandContext.d.ts +50 -0
  315. package/dist/presentation/cli/CommandContext.js +88 -0
  316. package/dist/presentation/cli/CommandContext.js.map +1 -0
  317. package/dist/presentation/cli/ConfigTemplate.d.ts +31 -0
  318. package/dist/presentation/cli/ConfigTemplate.js +83 -0
  319. package/dist/presentation/cli/ConfigTemplate.js.map +1 -0
  320. package/dist/presentation/cli/Renderer.d.ts +90 -0
  321. package/dist/presentation/cli/Renderer.js +290 -0
  322. package/dist/presentation/cli/Renderer.js.map +1 -0
  323. package/dist/presentation/cli/commands/AskCommand.d.ts +14 -0
  324. package/dist/presentation/cli/commands/AskCommand.js +48 -0
  325. package/dist/presentation/cli/commands/AskCommand.js.map +1 -0
  326. package/dist/presentation/cli/commands/CompletionsCommand.d.ts +18 -0
  327. package/dist/presentation/cli/commands/CompletionsCommand.js +96 -0
  328. package/dist/presentation/cli/commands/CompletionsCommand.js.map +1 -0
  329. package/dist/presentation/cli/commands/ConfigCommands.d.ts +56 -0
  330. package/dist/presentation/cli/commands/ConfigCommands.js +220 -0
  331. package/dist/presentation/cli/commands/ConfigCommands.js.map +1 -0
  332. package/dist/presentation/cli/commands/IndexCommand.d.ts +18 -0
  333. package/dist/presentation/cli/commands/IndexCommand.js +54 -0
  334. package/dist/presentation/cli/commands/IndexCommand.js.map +1 -0
  335. package/dist/presentation/cli/commands/ProjectsCommand.d.ts +48 -0
  336. package/dist/presentation/cli/commands/ProjectsCommand.js +240 -0
  337. package/dist/presentation/cli/commands/ProjectsCommand.js.map +1 -0
  338. package/dist/presentation/cli/commands/SearchCommand.d.ts +19 -0
  339. package/dist/presentation/cli/commands/SearchCommand.js +55 -0
  340. package/dist/presentation/cli/commands/SearchCommand.js.map +1 -0
  341. package/dist/presentation/cli/commands/ServerCommands.d.ts +13 -0
  342. package/dist/presentation/cli/commands/ServerCommands.js +40 -0
  343. package/dist/presentation/cli/commands/ServerCommands.js.map +1 -0
  344. package/dist/presentation/cli/commands/ShareCommands.d.ts +22 -0
  345. package/dist/presentation/cli/commands/ShareCommands.js +134 -0
  346. package/dist/presentation/cli/commands/ShareCommands.js.map +1 -0
  347. package/dist/presentation/cli/commands/StatusCommand.d.ts +15 -0
  348. package/dist/presentation/cli/commands/StatusCommand.js +53 -0
  349. package/dist/presentation/cli/commands/StatusCommand.js.map +1 -0
  350. package/dist/presentation/mcp/HitFormatter.d.ts +17 -0
  351. package/dist/presentation/mcp/HitFormatter.js +42 -0
  352. package/dist/presentation/mcp/HitFormatter.js.map +1 -0
  353. package/dist/presentation/mcp/IndexWatchController.d.ts +17 -0
  354. package/dist/presentation/mcp/IndexWatchController.js +31 -0
  355. package/dist/presentation/mcp/IndexWatchController.js.map +1 -0
  356. package/dist/presentation/mcp/LmgrepCore.d.ts +66 -0
  357. package/dist/presentation/mcp/LmgrepCore.js +178 -0
  358. package/dist/presentation/mcp/LmgrepCore.js.map +1 -0
  359. package/dist/presentation/mcp/McpServer.d.ts +18 -0
  360. package/dist/presentation/mcp/McpServer.js +63 -0
  361. package/dist/presentation/mcp/McpServer.js.map +1 -0
  362. package/dist/presentation/mcp/ToolDescriptions.d.ts +40 -0
  363. package/dist/presentation/mcp/ToolDescriptions.js +85 -0
  364. package/dist/presentation/mcp/ToolDescriptions.js.map +1 -0
  365. package/package.json +93 -89
  366. package/dist/lib/build.d.ts +0 -21
  367. package/dist/lib/build.js +0 -374
  368. package/dist/lib/build.js.map +0 -1
  369. package/dist/lib/chunker/context.d.ts +0 -21
  370. package/dist/lib/chunker/context.js +0 -131
  371. package/dist/lib/chunker/context.js.map +0 -1
  372. package/dist/lib/chunker/index.d.ts +0 -7
  373. package/dist/lib/chunker/index.js +0 -209
  374. package/dist/lib/chunker/index.js.map +0 -1
  375. package/dist/lib/chunker/languages.d.ts +0 -15
  376. package/dist/lib/chunker/languages.js.map +0 -1
  377. package/dist/lib/config.d.ts +0 -12
  378. package/dist/lib/config.js +0 -83
  379. package/dist/lib/config.js.map +0 -1
  380. package/dist/lib/embedder.d.ts +0 -65
  381. package/dist/lib/embedder.js +0 -236
  382. package/dist/lib/embedder.js.map +0 -1
  383. package/dist/lib/facet-session.d.ts +0 -44
  384. package/dist/lib/facet-session.js +0 -125
  385. package/dist/lib/facet-session.js.map +0 -1
  386. package/dist/lib/native-tuning.js.map +0 -1
  387. package/dist/lib/p2p.d.ts +0 -32
  388. package/dist/lib/p2p.js +0 -281
  389. package/dist/lib/p2p.js.map +0 -1
  390. package/dist/lib/providers.d.ts +0 -1
  391. package/dist/lib/providers.js +0 -41
  392. package/dist/lib/providers.js.map +0 -1
  393. package/dist/lib/repair.d.ts +0 -10
  394. package/dist/lib/repair.js +0 -57
  395. package/dist/lib/repair.js.map +0 -1
  396. package/dist/lib/scanner.d.ts +0 -34
  397. package/dist/lib/scanner.js +0 -242
  398. package/dist/lib/scanner.js.map +0 -1
  399. package/dist/lib/search-tool.d.ts +0 -55
  400. package/dist/lib/search-tool.js +0 -319
  401. package/dist/lib/search-tool.js.map +0 -1
  402. package/dist/lib/serve.d.ts +0 -11
  403. package/dist/lib/serve.js +0 -144
  404. package/dist/lib/serve.js.map +0 -1
  405. package/dist/lib/store.d.ts +0 -252
  406. package/dist/lib/store.js +0 -1216
  407. package/dist/lib/store.js.map +0 -1
  408. package/dist/lib/types.d.ts +0 -194
  409. package/dist/lib/types.js +0 -13
  410. package/dist/lib/types.js.map +0 -1
  411. package/dist/lib/vocab.d.ts +0 -20
  412. package/dist/lib/vocab.js +0 -275
  413. package/dist/lib/vocab.js.map +0 -1
  414. /package/dist/{lib/native-tuning.d.ts → infrastructure/lancedb/NativeTuning.d.ts} +0 -0
package/dist/lib/store.js DELETED
@@ -1,1216 +0,0 @@
1
- import { connect } from "@lancedb/lancedb";
2
- import { execSync } from "node:child_process";
3
- import { createHash } from "node:crypto";
4
- import { mkdirSync, statSync, writeFileSync, readFileSync, readdirSync, existsSync, unlinkSync, } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { join, resolve } from "node:path";
7
- const CHUNKS_TABLE = "chunks";
8
- const FILES_TABLE = "files";
9
- const VOCAB_TABLE = "vocab";
10
- const DELETE_BATCH_SIZE = 50;
11
- function buildInFilter(column, values) {
12
- const escaped = values.map((v) => `'${v.replace(/'/g, "''")}'`);
13
- return `${column} IN (${escaped.join(", ")})`;
14
- }
15
- async function batchDelete(table, column, values) {
16
- for (let i = 0; i < values.length; i += DELETE_BATCH_SIZE) {
17
- const batch = values.slice(i, i + DELETE_BATCH_SIZE);
18
- await table.delete(buildInFilter(column, batch));
19
- }
20
- }
21
- /**
22
- * Remove redundant search rows. Two passes, assuming `rows` is already in
23
- * best-first order (ANN returns ascending distance = descending score):
24
- *
25
- * 1. Exact duplicates by chunk id — collapses identical rows produced by
26
- * concurrent unlocked indexing (same filePath:row:contentHash).
27
- * 2. Overlapping line ranges within the same file — collapses the
28
- * fallback chunker's sliding-window overlap (and any parent/child or
29
- * near-duplicate spans), keeping the highest-scoring chunk of each
30
- * overlapping cluster. Tree-sitter chunks are node-bounded and don't
31
- * overlap, so this only ever drops genuine near-duplicates.
32
- */
33
- function dedupeRows(rows) {
34
- const seenIds = new Set();
35
- const keptRanges = new Map();
36
- const out = [];
37
- for (const r of rows) {
38
- if (seenIds.has(r.id))
39
- continue;
40
- seenIds.add(r.id);
41
- const ranges = keptRanges.get(r.filePath);
42
- if (ranges) {
43
- const overlaps = ranges.some(([s, e]) => r.startLine <= e && s <= r.endLine);
44
- if (overlaps)
45
- continue;
46
- ranges.push([r.startLine, r.endLine]);
47
- }
48
- else {
49
- keptRanges.set(r.filePath, [[r.startLine, r.endLine]]);
50
- }
51
- out.push(r);
52
- }
53
- return out;
54
- }
55
- /**
56
- * Legacy indexes (pre branch-scoping) have a `files` table without a `branch`
57
- * column. Detect and backfill it with the current branch so queries that filter
58
- * by branch keep working.
59
- */
60
- async function migrateBranchColumn(table, currentBranch) {
61
- const schema = await table.schema();
62
- if (schema.fields.some((f) => f.name === "branch"))
63
- return;
64
- const escaped = currentBranch.replace(/'/g, "''");
65
- await table.addColumns([
66
- { name: "branch", valueSql: `CAST('${escaped}' AS STRING)` },
67
- ]);
68
- }
69
- /**
70
- * Legacy chunk tables (pre version-scoping) have no `fileHash` column. Backfill
71
- * it with "" — the empty string is treated as a wildcard at search time so
72
- * legacy chunks keep appearing until the file is re-indexed and gets a real
73
- * file-version hash.
74
- */
75
- async function migrateFileHashColumn(table) {
76
- const schema = await table.schema();
77
- if (schema.fields.some((f) => f.name === "fileHash"))
78
- return;
79
- await table.addColumns([
80
- { name: "fileHash", valueSql: `CAST('' AS STRING)` },
81
- ]);
82
- }
83
- function git(cwd, ...args) {
84
- try {
85
- return execSync(`git ${args.join(" ")}`, {
86
- cwd,
87
- stdio: ["ignore", "pipe", "ignore"],
88
- timeout: 5000,
89
- })
90
- .toString()
91
- .trim();
92
- }
93
- catch {
94
- return undefined;
95
- }
96
- }
97
- /**
98
- * Resolve the project identity for a directory.
99
- *
100
- * For git repos: uses the remote origin URL as identity so that multiple
101
- * worktrees of the same repo share one index.
102
- *
103
- * For non-git directories: falls back to hashing the absolute path.
104
- *
105
- * Returns { id, root } where id is the string to hash for the DB path,
106
- * and root is the project root directory (git toplevel or cwd).
107
- */
108
- export function resolveProject(cwd) {
109
- const absolute = resolve(cwd);
110
- const gitRoot = git(absolute, "rev-parse", "--show-toplevel");
111
- if (gitRoot) {
112
- const branch = git(gitRoot, "rev-parse", "--abbrev-ref", "HEAD") ?? "HEAD";
113
- const remoteUrl = git(gitRoot, "remote", "get-url", "origin");
114
- if (remoteUrl) {
115
- return { id: remoteUrl, root: gitRoot, branch };
116
- }
117
- // Git repo with no remote — use the git root path
118
- return { id: gitRoot, root: gitRoot, branch };
119
- }
120
- return { id: absolute, root: absolute, branch: "_default" };
121
- }
122
- function slugifyId(id) {
123
- // Strip git URL scheme and trailing .git so worktrees of the same repo
124
- // produce the same human-readable prefix regardless of the worktree path.
125
- // git@host:user/repo.git → user/repo
126
- // https://host/user/repo.git → host/user/repo (we'll take the last two)
127
- // /abs/path/to/project → path/to/project (we'll take the last two)
128
- let s = id.replace(/\.git$/, "");
129
- const scpMatch = s.match(/^[^@]+@[^:]+:(.+)$/);
130
- if (scpMatch) {
131
- s = scpMatch[1];
132
- }
133
- else {
134
- s = s.replace(/^[a-z]+:\/\/[^/]+\//, "");
135
- }
136
- const parts = s.split("/").filter(Boolean);
137
- return parts.slice(-2).join("-").replace(/[^a-zA-Z0-9_-]/g, "_");
138
- }
139
- export function buildSlug(id) {
140
- const hash = createHash("sha256").update(id).digest("hex").slice(0, 8);
141
- return `${slugifyId(id)}-${hash}`;
142
- }
143
- export function getDbPath(cwd) {
144
- const { id } = resolveProject(cwd);
145
- return join(homedir(), ".local", "state", "lmgrep", buildSlug(id));
146
- }
147
- /**
148
- * Compute the DB path using the pre-git-aware scheme (absolute path hash).
149
- * Used by `lmgrep import` to find legacy indexes.
150
- */
151
- export function getLegacyDbPath(cwd) {
152
- const absolute = resolve(cwd);
153
- const hash = createHash("sha256").update(absolute).digest("hex").slice(0, 6);
154
- const parts = absolute.split("/").filter(Boolean);
155
- const slug = parts.slice(-2).join("-").replace(/[^a-zA-Z0-9_-]/g, "_");
156
- return join(homedir(), ".local", "state", "lmgrep", `${slug}-${hash}`);
157
- }
158
- /**
159
- * Find the project root and compute the prefix (subdirectory offset).
160
- * For git repos, the root is the git toplevel. For non-git dirs, walks up
161
- * looking for an existing index.
162
- */
163
- export function findIndexedAncestor(cwd) {
164
- const absolute = resolve(cwd);
165
- const { root } = resolveProject(cwd);
166
- // For git repos, the root is always the git toplevel
167
- const dbPath = getDbPath(root);
168
- try {
169
- if (statSync(dbPath).isDirectory()) {
170
- const prefix = root === absolute ? "" : absolute.slice(root.length + 1);
171
- return { root, prefix };
172
- }
173
- }
174
- catch {
175
- // no index yet
176
- }
177
- // For non-git dirs, walk up looking for an ancestor with an index
178
- if (root === absolute) {
179
- let current = resolve(absolute, "..");
180
- while (true) {
181
- const ancestorDb = getDbPath(current);
182
- try {
183
- if (statSync(ancestorDb).isDirectory()) {
184
- const prefix = absolute.slice(current.length + 1);
185
- return { root: current, prefix };
186
- }
187
- }
188
- catch {
189
- // keep climbing
190
- }
191
- const parent = resolve(current, "..");
192
- if (parent === current)
193
- break;
194
- current = parent;
195
- }
196
- }
197
- return undefined;
198
- }
199
- // --- Project metadata ---
200
- const METADATA_FILE = "lmgrep.json";
201
- /**
202
- * Extract the base model family name from a full model string.
203
- * Strips provider prefix (e.g. "openai:", "ollama:") and quantization/tag
204
- * suffixes (e.g. ":Q4_K_M", ":latest", ":fp16").
205
- *
206
- * Examples:
207
- * "openai:nomic-embed-text" → "nomic-embed-text"
208
- * "ollama:nomic-embed-text:Q4_K_M" → "nomic-embed-text"
209
- * "lmstudio:bge-large-en:fp16" → "bge-large-en"
210
- * "openai:text-embedding-3-small" → "text-embedding-3-small"
211
- */
212
- export function extractModelFamily(model) {
213
- // Strip provider prefix (first colon-separated segment)
214
- const colonIdx = model.indexOf(":");
215
- if (colonIdx === -1)
216
- return model;
217
- const rest = model.slice(colonIdx + 1);
218
- // Strip quant/tag suffix: known patterns like Q4_K_M, Q8_0, fp16, latest, etc.
219
- // These appear as the last colon-separated segment
220
- const lastColon = rest.lastIndexOf(":");
221
- if (lastColon === -1)
222
- return rest;
223
- const suffix = rest.slice(lastColon + 1);
224
- // Match common quantization and tag patterns
225
- if (/^(Q\d|q\d|fp\d|f\d|latest|gguf|ggml)/i.test(suffix)) {
226
- return rest.slice(0, lastColon);
227
- }
228
- // Not a recognized suffix — keep the whole thing (could be part of model name)
229
- return rest;
230
- }
231
- export function writeProjectMetadata(cwd, extra) {
232
- const dbPath = getDbPath(cwd);
233
- const { id, root, branch } = resolveProject(cwd);
234
- const gitRoot = git(resolve(cwd), "rev-parse", "--show-toplevel");
235
- const remote = gitRoot
236
- ? git(gitRoot, "remote", "get-url", "origin") ?? undefined
237
- : undefined;
238
- mkdirSync(dbPath, { recursive: true });
239
- // Preserve existing model/dimensions if not provided (don't overwrite baseline)
240
- const existing = readProjectMetadata(dbPath);
241
- const metadata = {
242
- root,
243
- remote,
244
- branch,
245
- indexedAt: new Date().toISOString(),
246
- model: existing?.model ?? extra?.model,
247
- dimensions: existing?.dimensions ?? extra?.dimensions,
248
- };
249
- writeFileSync(join(dbPath, METADATA_FILE), JSON.stringify(metadata, null, 2));
250
- }
251
- export function readProjectMetadata(dbPath) {
252
- const metaPath = join(dbPath, METADATA_FILE);
253
- try {
254
- return JSON.parse(readFileSync(metaPath, "utf-8"));
255
- }
256
- catch {
257
- return undefined;
258
- }
259
- }
260
- /**
261
- * Scan all lmgrep indexes and return their metadata.
262
- */
263
- export function discoverIndexedProjects() {
264
- const baseDir = join(homedir(), ".local", "state", "lmgrep");
265
- if (!existsSync(baseDir))
266
- return [];
267
- const results = [];
268
- for (const entry of readdirSync(baseDir, { withFileTypes: true })) {
269
- if (!entry.isDirectory())
270
- continue;
271
- const dbPath = join(baseDir, entry.name);
272
- const metadata = readProjectMetadata(dbPath);
273
- if (metadata) {
274
- results.push({ dbPath, metadata });
275
- }
276
- }
277
- return results;
278
- }
279
- // --- DB-level write lock ---
280
- function isProcessAlive(pid) {
281
- try {
282
- process.kill(pid, 0);
283
- return true;
284
- }
285
- catch {
286
- return false;
287
- }
288
- }
289
- /**
290
- * Acquire an exclusive write lock for a project's DB.
291
- * Returns true if the lock was acquired, false if another process holds it.
292
- */
293
- export function acquireDbLock(cwd) {
294
- const lockPath = `${getDbPath(cwd)}.lock`;
295
- if (existsSync(lockPath)) {
296
- try {
297
- const pid = Number.parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
298
- if (isProcessAlive(pid))
299
- return false;
300
- }
301
- catch {
302
- // stale lock, take over
303
- }
304
- }
305
- const dbPath = getDbPath(cwd);
306
- mkdirSync(dbPath, { recursive: true });
307
- writeFileSync(lockPath, `${process.pid}\n`);
308
- return true;
309
- }
310
- /**
311
- * Release the write lock for a project's DB.
312
- */
313
- export function releaseDbLock(cwd) {
314
- try {
315
- unlinkSync(`${getDbPath(cwd)}.lock`);
316
- }
317
- catch { }
318
- }
319
- // --- Per-build write mutex ---
320
- //
321
- // The `.lock` maintainer lock (above) is held for a watcher/serve process's
322
- // whole lifetime and doubles as a liveness registry for `lmgrep status`. It
323
- // cannot also serve as a write mutex, because then a one-shot `lmgrep index`
324
- // could never run while a watcher is up. This separate `.writelock` is a
325
- // short-lived mutex acquired around each build() so that the watcher and an
326
- // ad-hoc `lmgrep index` serialize their writes instead of racing into
327
- // duplicate rows. Named `.writelock` (not `.write.lock`) so it does not match
328
- // the `.lock` suffix scan in discoverRunningProcesses.
329
- function writeLockPath(cwd) {
330
- return `${getDbPath(cwd)}.writelock`;
331
- }
332
- function tryAcquireWriteLock(cwd) {
333
- const lockPath = writeLockPath(cwd);
334
- if (existsSync(lockPath)) {
335
- try {
336
- const pid = Number.parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
337
- if (isProcessAlive(pid))
338
- return false;
339
- }
340
- catch {
341
- // stale/corrupt lock, take over
342
- }
343
- }
344
- mkdirSync(getDbPath(cwd), { recursive: true });
345
- writeFileSync(lockPath, `${process.pid}\n`);
346
- return true;
347
- }
348
- function releaseWriteLock(cwd) {
349
- const lockPath = writeLockPath(cwd);
350
- try {
351
- // Only remove the lock if we still own it.
352
- const pid = Number.parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
353
- if (pid === process.pid)
354
- unlinkSync(lockPath);
355
- }
356
- catch { }
357
- }
358
- /**
359
- * Run `fn` while holding the project's write mutex, so concurrent indexers
360
- * (a watcher plus an ad-hoc `lmgrep index`) can't write at the same time and
361
- * produce duplicate chunk rows. Waits up to `waitMs` for a busy lock, taking
362
- * over a lock held by a dead process. Throws if the lock can't be acquired in
363
- * time.
364
- */
365
- export async function withWriteLock(cwd, fn, opts = {}) {
366
- const waitMs = opts.waitMs ?? 120_000;
367
- const pollMs = opts.pollMs ?? 200;
368
- let waited = 0;
369
- while (!tryAcquireWriteLock(cwd)) {
370
- if (waited >= waitMs) {
371
- throw new Error("Could not acquire the index write lock — another indexer is busy. " +
372
- "Try again once it finishes.");
373
- }
374
- await new Promise((r) => setTimeout(r, pollMs));
375
- waited += pollMs;
376
- }
377
- try {
378
- return await fn();
379
- }
380
- finally {
381
- releaseWriteLock(cwd);
382
- }
383
- }
384
- /**
385
- * Check if a write lock is held by a live process.
386
- */
387
- export function isDbLocked(cwd) {
388
- const lockPath = `${getDbPath(cwd)}.lock`;
389
- if (!existsSync(lockPath))
390
- return false;
391
- try {
392
- const pid = Number.parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
393
- return isProcessAlive(pid);
394
- }
395
- catch {
396
- return false;
397
- }
398
- }
399
- function getProcessInfo(pid) {
400
- try {
401
- const name = readFileSync(`/proc/${pid}/comm`, "utf-8").trim();
402
- const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf-8")
403
- .replace(/\0/g, " ")
404
- .trim();
405
- return { name, cmdline };
406
- }
407
- catch {
408
- return undefined;
409
- }
410
- }
411
- function classifyProcess(info) {
412
- if (info.name === "lmgrep-mcp" || info.cmdline.includes("mcp"))
413
- return "mcp";
414
- if (info.cmdline.includes("serve"))
415
- return "serve";
416
- return "cli";
417
- }
418
- /**
419
- * Scan all lock files to find running lmgrep processes,
420
- * which indexes they hold, and whether they are watching for changes.
421
- */
422
- export function discoverRunningProcesses() {
423
- const baseDir = join(homedir(), ".local", "state", "lmgrep");
424
- if (!existsSync(baseDir))
425
- return [];
426
- const results = [];
427
- const seen = new Set();
428
- for (const entry of readdirSync(baseDir)) {
429
- if (!entry.endsWith(".lock"))
430
- continue;
431
- const lockPath = join(baseDir, entry);
432
- let pid;
433
- try {
434
- pid = Number.parseInt(readFileSync(lockPath, "utf-8").trim(), 10);
435
- }
436
- catch {
437
- continue;
438
- }
439
- if (!isProcessAlive(pid) || seen.has(pid))
440
- continue;
441
- seen.add(pid);
442
- const info = getProcessInfo(pid);
443
- if (!info)
444
- continue;
445
- const kind = classifyProcess(info);
446
- // Resolve which project this lock belongs to
447
- const dbDir = entry.slice(0, -".lock".length);
448
- const dbPath = join(baseDir, dbDir);
449
- const metadata = readProjectMetadata(dbPath);
450
- results.push({
451
- pid,
452
- processName: info.name,
453
- cmdline: info.cmdline,
454
- kind,
455
- projectRoot: metadata?.root,
456
- // MCP and serve processes watch; plain CLI invocations don't
457
- watching: kind === "mcp" || kind === "serve",
458
- });
459
- }
460
- return results;
461
- }
462
- export class Store {
463
- dbPath;
464
- branch;
465
- db;
466
- chunksTable;
467
- filesTable;
468
- vocabTable;
469
- constructor(dbPath, branch = "_default") {
470
- this.dbPath = dbPath;
471
- this.branch = branch;
472
- }
473
- static forProject(cwd) {
474
- const { branch } = resolveProject(cwd);
475
- return new Store(getDbPath(cwd), branch);
476
- }
477
- // --- Connection ---
478
- async connection() {
479
- if (this.db)
480
- return this.db;
481
- mkdirSync(this.dbPath, { recursive: true });
482
- this.db = await connect(this.dbPath);
483
- return this.db;
484
- }
485
- async openChunks() {
486
- if (this.chunksTable)
487
- return this.chunksTable;
488
- const conn = await this.connection();
489
- const tables = await conn.tableNames();
490
- if (tables.includes(CHUNKS_TABLE)) {
491
- const t = await conn.openTable(CHUNKS_TABLE);
492
- await migrateFileHashColumn(t);
493
- this.chunksTable = t;
494
- return this.chunksTable;
495
- }
496
- return undefined;
497
- }
498
- async openFiles() {
499
- if (this.filesTable)
500
- return this.filesTable;
501
- const conn = await this.connection();
502
- const tables = await conn.tableNames();
503
- if (tables.includes(FILES_TABLE)) {
504
- const t = await conn.openTable(FILES_TABLE);
505
- await migrateBranchColumn(t, this.branch);
506
- this.filesTable = t;
507
- return this.filesTable;
508
- }
509
- return undefined;
510
- }
511
- // --- Vocab ---
512
- async openVocab() {
513
- if (this.vocabTable)
514
- return this.vocabTable;
515
- const conn = await this.connection();
516
- const tables = await conn.tableNames();
517
- if (tables.includes(VOCAB_TABLE)) {
518
- this.vocabTable = await conn.openTable(VOCAB_TABLE);
519
- return this.vocabTable;
520
- }
521
- return undefined;
522
- }
523
- async hasVocab() {
524
- return (await this.openVocab()) !== undefined;
525
- }
526
- /**
527
- * Return the set of vocab terms already embedded in the vocab table.
528
- * Used at index time to skip re-embedding.
529
- */
530
- async getVocabTerms() {
531
- const t = await this.openVocab();
532
- if (!t)
533
- return new Set();
534
- const rows = await t.query().select(["term"]).toArray();
535
- return new Set(rows.map((r) => r.term));
536
- }
537
- async addVocab(entries) {
538
- if (entries.length === 0)
539
- return;
540
- // Dedup within the batch
541
- const seen = new Set();
542
- const batchUnique = [];
543
- for (const e of entries) {
544
- if (seen.has(e.term))
545
- continue;
546
- seen.add(e.term);
547
- batchUnique.push(e);
548
- }
549
- // Skip terms already in the table
550
- const known = await this.getVocabTerms();
551
- const records = batchUnique
552
- .filter((e) => !known.has(e.term))
553
- .map((e) => ({ term: e.term, vector: e.vector }));
554
- if (records.length === 0)
555
- return;
556
- const conn = await this.connection();
557
- const tables = await conn.tableNames();
558
- if (tables.includes(VOCAB_TABLE)) {
559
- const t = await conn.openTable(VOCAB_TABLE);
560
- this.vocabTable = t;
561
- await t.add(records);
562
- }
563
- else {
564
- this.vocabTable = await conn.createTable(VOCAB_TABLE, records);
565
- }
566
- }
567
- /**
568
- * ANN search against the vocab table. Returns top-N terms closest to the
569
- * given vector by cosine distance.
570
- */
571
- async searchVocab(vector, limit, excludeTerms) {
572
- const t = await this.openVocab();
573
- if (!t)
574
- return [];
575
- const fetch = excludeTerms ? limit + excludeTerms.size : limit;
576
- const rows = await t.search(vector).limit(fetch).toArray();
577
- const out = [];
578
- for (const r of rows) {
579
- const term = r.term;
580
- if (excludeTerms?.has(term))
581
- continue;
582
- out.push({
583
- term,
584
- score: r._distance != null ? 1 - r._distance : 0,
585
- });
586
- if (out.length >= limit)
587
- break;
588
- }
589
- return out;
590
- }
591
- async vocabCount() {
592
- const t = await this.openVocab();
593
- if (!t)
594
- return 0;
595
- return t.countRows();
596
- }
597
- async dropVocab() {
598
- const conn = await this.connection();
599
- const tables = await conn.tableNames();
600
- if (tables.includes(VOCAB_TABLE)) {
601
- await conn.dropTable(VOCAB_TABLE);
602
- }
603
- this.vocabTable = undefined;
604
- }
605
- // --- Chunks ---
606
- async addChunks(chunks) {
607
- if (chunks.length === 0)
608
- return;
609
- const conn = await this.connection();
610
- const records = chunks.map((c) => ({
611
- id: c.id,
612
- filePath: c.filePath,
613
- startLine: c.startLine,
614
- endLine: c.endLine,
615
- type: c.type,
616
- name: c.name,
617
- content: c.content,
618
- context: c.context,
619
- hash: c.hash,
620
- fileHash: c.fileHash ?? "",
621
- vector: c.vector,
622
- }));
623
- const tables = await conn.tableNames();
624
- if (tables.includes(CHUNKS_TABLE)) {
625
- const t = await conn.openTable(CHUNKS_TABLE);
626
- this.chunksTable = t;
627
- await t.add(records);
628
- }
629
- else {
630
- this.chunksTable = await conn.createTable(CHUNKS_TABLE, records);
631
- }
632
- }
633
- /**
634
- * Delete chunks for files that are no longer referenced by ANY branch.
635
- * If another branch still has a file hash entry for a given path,
636
- * the chunks are kept (they're shared via content-addressing).
637
- */
638
- async deleteChunksByFiles(filePaths) {
639
- const t = await this.openChunks();
640
- if (!t || filePaths.length === 0)
641
- return;
642
- const filesTable = await this.openFiles();
643
- if (!filesTable) {
644
- // No files table means no other branches — safe to delete all
645
- await batchDelete(t, "filePath", filePaths);
646
- return;
647
- }
648
- // Find which files are still referenced by other branches
649
- const escaped = this.branch.replace(/'/g, "''");
650
- const stillReferenced = new Set();
651
- for (let i = 0; i < filePaths.length; i += DELETE_BATCH_SIZE) {
652
- const batch = filePaths.slice(i, i + DELETE_BATCH_SIZE);
653
- const pathFilter = buildInFilter("filePath", batch);
654
- const refs = await filesTable
655
- .query()
656
- .where(`branch != '${escaped}' AND ${pathFilter}`)
657
- .select(["filePath"])
658
- .toArray();
659
- for (const r of refs) {
660
- stillReferenced.add(r.filePath);
661
- }
662
- }
663
- // Only delete chunks for files not referenced by other branches
664
- const toDelete = filePaths.filter((fp) => !stillReferenced.has(fp));
665
- if (toDelete.length > 0) {
666
- await batchDelete(t, "filePath", toDelete);
667
- }
668
- }
669
- branchVersionsCache;
670
- /**
671
- * Map of filePath -> file-version hash for the current branch (cached).
672
- * Used to scope search to the exact file versions this branch references,
673
- * so stale chunks from another version of the same path are excluded.
674
- */
675
- async getBranchFileVersions() {
676
- if (this.branchVersionsCache)
677
- return this.branchVersionsCache;
678
- const t = await this.openFiles();
679
- if (!t)
680
- return undefined;
681
- const escaped = this.branch.replace(/'/g, "''");
682
- const rows = await t
683
- .query()
684
- .where(`branch = '${escaped}'`)
685
- .select(["filePath", "fileHash"])
686
- .toArray();
687
- const map = new Map();
688
- for (const r of rows) {
689
- map.set(r.filePath, r.fileHash);
690
- }
691
- this.branchVersionsCache = map;
692
- return this.branchVersionsCache;
693
- }
694
- /** Invalidate the branch files cache (call after index/import). */
695
- invalidateBranchFilesCache() {
696
- this.branchVersionsCache = undefined;
697
- }
698
- async search(queryVector, limit = 25, filePrefix, typeFilter,
699
- /** Pass false to skip branch scoping (e.g. for cross-project search). */
700
- scopeToBranch = true) {
701
- const t = await this.openChunks();
702
- if (!t) {
703
- throw new Error("No index found. Run `lmgrep index` first.");
704
- }
705
- const branchVersions = scopeToBranch
706
- ? await this.getBranchFileVersions()
707
- : undefined;
708
- // Over-fetch: branch/version filtering and dedup both discard rows, so
709
- // pull extra to still return `limit` distinct results.
710
- const fetchLimit = branchVersions ? limit * 3 : limit * 2;
711
- let query = t.search(queryVector).limit(fetchLimit);
712
- const conditions = [];
713
- if (filePrefix) {
714
- conditions.push(`filePath LIKE '${filePrefix.replace(/'/g, "''")}%'`);
715
- }
716
- if (typeFilter && typeFilter.length > 0) {
717
- const escaped = typeFilter.map((t) => `'${t.replace(/'/g, "''")}'`);
718
- conditions.push(`type IN (${escaped.join(", ")})`);
719
- }
720
- if (conditions.length > 0) {
721
- query = query.where(conditions.join(" AND "));
722
- }
723
- const results = await query.toArray();
724
- let mapped = results.map((r) => ({
725
- id: r.id,
726
- filePath: r.filePath,
727
- startLine: r.startLine,
728
- endLine: r.endLine,
729
- type: r.type,
730
- name: r.name,
731
- content: r.content,
732
- context: r.context,
733
- score: r._distance != null ? 1 - r._distance : 0,
734
- fileHash: r.fileHash ?? "",
735
- }));
736
- if (branchVersions) {
737
- mapped = mapped.filter((r) => {
738
- const want = branchVersions.get(r.filePath);
739
- // "" fileHash = legacy chunk, treated as wildcard.
740
- return want !== undefined && (r.fileHash === "" || r.fileHash === want);
741
- });
742
- }
743
- return dedupeRows(mapped)
744
- .slice(0, limit)
745
- .map(({ id, fileHash, ...rest }) => rest);
746
- }
747
- /**
748
- * Same as search() but also returns each chunk's vector — needed for
749
- * client-side clustering (facet command).
750
- */
751
- async searchWithVectors(queryVector, limit = 25, filePrefix, scopeToBranch = true) {
752
- const t = await this.openChunks();
753
- if (!t) {
754
- throw new Error("No index found. Run `lmgrep index` first.");
755
- }
756
- const branchVersions = scopeToBranch
757
- ? await this.getBranchFileVersions()
758
- : undefined;
759
- const fetchLimit = branchVersions ? limit * 3 : limit * 2;
760
- let query = t.search(queryVector).limit(fetchLimit);
761
- if (filePrefix) {
762
- query = query.where(`filePath LIKE '${filePrefix.replace(/'/g, "''")}%'`);
763
- }
764
- const results = await query.toArray();
765
- let mapped = results.map((r) => ({
766
- id: r.id,
767
- filePath: r.filePath,
768
- startLine: r.startLine,
769
- endLine: r.endLine,
770
- type: r.type,
771
- name: r.name,
772
- content: r.content,
773
- context: r.context,
774
- score: r._distance != null ? 1 - r._distance : 0,
775
- fileHash: r.fileHash ?? "",
776
- vector: Array.from(r.vector),
777
- }));
778
- if (branchVersions) {
779
- mapped = mapped.filter((r) => {
780
- const want = branchVersions.get(r.filePath);
781
- return want !== undefined && (r.fileHash === "" || r.fileHash === want);
782
- });
783
- }
784
- return dedupeRows(mapped)
785
- .slice(0, limit)
786
- .map(({ fileHash, ...rest }) => rest);
787
- }
788
- /**
789
- * Fetch chunks (with vectors) by id. Used to rehydrate faceting sessions.
790
- * Missing ids are silently dropped.
791
- */
792
- async getChunksByIds(ids) {
793
- if (ids.length === 0)
794
- return [];
795
- const t = await this.openChunks();
796
- if (!t)
797
- return [];
798
- const escaped = ids.map((i) => `'${i.replace(/'/g, "''")}'`).join(",");
799
- const rows = await t.query().where(`id IN (${escaped})`).toArray();
800
- return rows.map((r) => ({
801
- id: r.id,
802
- filePath: r.filePath,
803
- startLine: r.startLine,
804
- endLine: r.endLine,
805
- type: r.type,
806
- name: r.name,
807
- content: r.content,
808
- context: r.context,
809
- score: 0,
810
- vector: Array.from(r.vector),
811
- }));
812
- }
813
- async getIndexedFiles() {
814
- const t = await this.openChunks();
815
- if (!t)
816
- return new Map();
817
- const rows = await t.query().select(["filePath", "hash"]).toArray();
818
- const map = new Map();
819
- for (const row of rows) {
820
- const fp = row.filePath;
821
- const hash = row.hash;
822
- const existing = map.get(fp) ?? [];
823
- existing.push(hash);
824
- map.set(fp, existing);
825
- }
826
- return map;
827
- }
828
- async getIndexedHashes() {
829
- const t = await this.openChunks();
830
- if (!t)
831
- return new Set();
832
- const rows = await t.query().select(["hash"]).toArray();
833
- return new Set(rows.map((r) => r.hash));
834
- }
835
- /**
836
- * Given a set of chunk hashes, return those that already exist in the
837
- * chunks table. Runs as batched IN() queries in the DB.
838
- */
839
- async filterExistingChunkHashes(hashes) {
840
- const t = await this.openChunks();
841
- if (!t || hashes.length === 0)
842
- return new Set();
843
- const existing = new Set();
844
- const unique = [...new Set(hashes)];
845
- for (let i = 0; i < unique.length; i += DELETE_BATCH_SIZE) {
846
- const batch = unique.slice(i, i + DELETE_BATCH_SIZE);
847
- const filter = buildInFilter("hash", batch);
848
- const rows = await t
849
- .query()
850
- .where(filter)
851
- .select(["hash"])
852
- .toArray();
853
- for (const r of rows) {
854
- existing.add(r.hash);
855
- }
856
- }
857
- return existing;
858
- }
859
- async chunkCount() {
860
- const t = await this.openChunks();
861
- if (!t)
862
- return 0;
863
- return await t.countRows();
864
- }
865
- /**
866
- * Stream chunk texts (name + content only) in batches. Used by vocab
867
- * backfill to avoid loading all chunks into memory at once.
868
- */
869
- async *streamChunkTexts(batchSize = 1000) {
870
- const t = await this.openChunks();
871
- if (!t)
872
- return;
873
- const stream = await t
874
- .query()
875
- .select(["name", "content"])
876
- .toArray();
877
- for (let i = 0; i < stream.length; i += batchSize) {
878
- yield stream.slice(i, i + batchSize).map((r) => ({
879
- name: r.name ?? "",
880
- content: r.content ?? "",
881
- }));
882
- }
883
- }
884
- // --- File hashes (change detection) ---
885
- async getFileHashes() {
886
- const t = await this.openFiles();
887
- if (!t)
888
- return new Map();
889
- const escaped = this.branch.replace(/'/g, "''");
890
- const rows = await t
891
- .query()
892
- .where(`branch = '${escaped}'`)
893
- .select(["filePath", "fileHash"])
894
- .toArray();
895
- const map = new Map();
896
- for (const row of rows) {
897
- map.set(row.filePath, row.fileHash);
898
- }
899
- return map;
900
- }
901
- /**
902
- * Given a set of file hashes, return those that already exist in the
903
- * files table on ANY branch. The query runs in the DB, not in JS.
904
- */
905
- async filterKnownFileHashes(hashes) {
906
- const t = await this.openFiles();
907
- if (!t || hashes.length === 0)
908
- return new Set();
909
- const known = new Set();
910
- for (let i = 0; i < hashes.length; i += DELETE_BATCH_SIZE) {
911
- const batch = hashes.slice(i, i + DELETE_BATCH_SIZE);
912
- const filter = buildInFilter("fileHash", batch);
913
- const rows = await t
914
- .query()
915
- .where(filter)
916
- .select(["fileHash"])
917
- .toArray();
918
- for (const r of rows) {
919
- known.add(r.fileHash);
920
- }
921
- }
922
- return known;
923
- }
924
- async upsertFileHashes(entries) {
925
- if (entries.length === 0)
926
- return;
927
- const records = entries.map((e) => ({
928
- filePath: e.filePath,
929
- fileHash: e.fileHash,
930
- branch: e.branch ?? this.branch,
931
- }));
932
- const conn = await this.connection();
933
- const tables = await conn.tableNames();
934
- if (tables.includes(FILES_TABLE)) {
935
- const t = await conn.openTable(FILES_TABLE);
936
- this.filesTable = t;
937
- // Delete existing entries for the same branch + filePath pairs.
938
- // Group by branch so we don't accidentally clobber other branches'
939
- // rows for the same path.
940
- const byBranch = new Map();
941
- for (const r of records) {
942
- const list = byBranch.get(r.branch) ?? [];
943
- list.push(r.filePath);
944
- byBranch.set(r.branch, list);
945
- }
946
- for (const [branch, paths] of byBranch) {
947
- const escaped = branch.replace(/'/g, "''");
948
- for (let i = 0; i < paths.length; i += DELETE_BATCH_SIZE) {
949
- const batch = paths.slice(i, i + DELETE_BATCH_SIZE);
950
- const pathFilter = buildInFilter("filePath", batch);
951
- await t.delete(`branch = '${escaped}' AND ${pathFilter}`);
952
- }
953
- }
954
- await t.add(records);
955
- }
956
- else {
957
- this.filesTable = await conn.createTable(FILES_TABLE, records);
958
- }
959
- }
960
- async deleteFileHashes(filePaths) {
961
- const t = await this.openFiles();
962
- if (!t || filePaths.length === 0)
963
- return;
964
- const escaped = this.branch.replace(/'/g, "''");
965
- for (let i = 0; i < filePaths.length; i += DELETE_BATCH_SIZE) {
966
- const batch = filePaths.slice(i, i + DELETE_BATCH_SIZE);
967
- const pathFilter = buildInFilter("filePath", batch);
968
- await t.delete(`branch = '${escaped}' AND ${pathFilter}`);
969
- }
970
- }
971
- // --- Admin ---
972
- async *streamAllChunks(batchSize) {
973
- const t = await this.openChunks();
974
- if (!t)
975
- return;
976
- const total = await t.countRows();
977
- for (let offset = 0; offset < total; offset += batchSize) {
978
- const rows = await t.query().limit(batchSize).offset(offset).toArray();
979
- if (rows.length === 0)
980
- break;
981
- yield rows.map((r) => ({
982
- id: r.id,
983
- filePath: r.filePath,
984
- startLine: r.startLine,
985
- endLine: r.endLine,
986
- type: r.type,
987
- name: r.name,
988
- content: r.content,
989
- context: r.context,
990
- hash: r.hash,
991
- fileHash: r.fileHash ?? "",
992
- vector: Array.from(r.vector),
993
- }));
994
- }
995
- }
996
- /**
997
- * Remove redundant rows from the chunks table:
998
- * - exact duplicate ids (identical rows from concurrent unlocked indexing)
999
- * - orphaned versions: chunks whose fileHash is no longer referenced by any
1000
- * branch's manifest (left behind when a file was edited on one branch
1001
- * while another branch still pointed at the old path). Legacy chunks with
1002
- * an empty fileHash are kept.
1003
- * Rewrites the table from the surviving rows. Loads chunks into memory in
1004
- * batches, so this is a maintenance operation, not a hot path.
1005
- */
1006
- async dedupeChunks() {
1007
- const t = await this.openChunks();
1008
- if (!t)
1009
- return { before: 0, after: 0, duplicateIds: 0, staleVersions: 0 };
1010
- const before = await t.countRows();
1011
- // Which (filePath, fileHash) pairs any branch still references.
1012
- const refs = new Map();
1013
- for (const e of await this.getAllFileEntries()) {
1014
- const set = refs.get(e.filePath) ?? new Set();
1015
- set.add(e.fileHash);
1016
- refs.set(e.filePath, set);
1017
- }
1018
- const kept = [];
1019
- const seenIds = new Set();
1020
- let duplicateIds = 0;
1021
- let staleVersions = 0;
1022
- for await (const batch of this.streamAllChunks(2000)) {
1023
- for (const r of batch) {
1024
- const id = r.id;
1025
- if (seenIds.has(id)) {
1026
- duplicateIds++;
1027
- continue;
1028
- }
1029
- seenIds.add(id);
1030
- const filePath = r.filePath;
1031
- const fileHash = r.fileHash ?? "";
1032
- if (fileHash !== "" && !refs.get(filePath)?.has(fileHash)) {
1033
- staleVersions++;
1034
- continue;
1035
- }
1036
- kept.push({
1037
- id,
1038
- filePath,
1039
- startLine: r.startLine,
1040
- endLine: r.endLine,
1041
- type: r.type,
1042
- name: r.name,
1043
- content: r.content,
1044
- context: r.context,
1045
- hash: r.hash,
1046
- fileHash,
1047
- vector: r.vector,
1048
- });
1049
- }
1050
- }
1051
- if (duplicateIds + staleVersions === 0) {
1052
- return { before, after: before, duplicateIds, staleVersions };
1053
- }
1054
- // Rewrite the table from the survivors.
1055
- const conn = await this.connection();
1056
- if ((await conn.tableNames()).includes(CHUNKS_TABLE)) {
1057
- await conn.dropTable(CHUNKS_TABLE);
1058
- }
1059
- this.chunksTable = undefined;
1060
- if (kept.length > 0)
1061
- await this.addChunks(kept);
1062
- return { before, after: kept.length, duplicateIds, staleVersions };
1063
- }
1064
- async getAllFileEntries() {
1065
- const t = await this.openFiles();
1066
- if (!t)
1067
- return [];
1068
- const rows = await t.query().toArray();
1069
- return rows.map((r) => ({
1070
- filePath: r.filePath,
1071
- fileHash: r.fileHash,
1072
- branch: r.branch ?? this.branch,
1073
- }));
1074
- }
1075
- async reset() {
1076
- const conn = await this.connection();
1077
- const tables = await conn.tableNames();
1078
- if (tables.includes(CHUNKS_TABLE))
1079
- await conn.dropTable(CHUNKS_TABLE);
1080
- if (tables.includes(FILES_TABLE))
1081
- await conn.dropTable(FILES_TABLE);
1082
- if (tables.includes(VOCAB_TABLE))
1083
- await conn.dropTable(VOCAB_TABLE);
1084
- this.chunksTable = undefined;
1085
- this.filesTable = undefined;
1086
- this.vocabTable = undefined;
1087
- }
1088
- async compact() {
1089
- const t = await this.openChunks();
1090
- if (t)
1091
- await t.optimize();
1092
- const f = await this.openFiles();
1093
- if (f)
1094
- await f.optimize();
1095
- const v = await this.openVocab();
1096
- if (v)
1097
- await v.optimize();
1098
- }
1099
- /**
1100
- * Import all chunks and file hashes from another Store's database.
1101
- * Returns { chunks, files } counts of imported records.
1102
- */
1103
- async importFrom(sourcePath) {
1104
- const sourceConn = await connect(sourcePath);
1105
- const sourceTables = await sourceConn.tableNames();
1106
- let chunks = 0;
1107
- let files = 0;
1108
- if (sourceTables.includes(CHUNKS_TABLE)) {
1109
- const sourceChunks = await sourceConn.openTable(CHUNKS_TABLE);
1110
- const rawChunkRows = await sourceChunks.query().toArray();
1111
- if (rawChunkRows.length > 0) {
1112
- // Convert Arrow typed arrays to plain JS objects so LanceDB can
1113
- // infer the schema when creating a new table.
1114
- const rows = rawChunkRows.map((r) => ({
1115
- id: r.id,
1116
- filePath: r.filePath,
1117
- startLine: r.startLine,
1118
- endLine: r.endLine,
1119
- type: r.type,
1120
- name: r.name,
1121
- content: r.content,
1122
- context: r.context,
1123
- hash: r.hash,
1124
- fileHash: r.fileHash ?? "",
1125
- vector: Array.from(r.vector),
1126
- }));
1127
- const conn = await this.connection();
1128
- const destTables = await conn.tableNames();
1129
- if (destTables.includes(CHUNKS_TABLE)) {
1130
- const t = await conn.openTable(CHUNKS_TABLE);
1131
- this.chunksTable = t;
1132
- await t.add(rows);
1133
- }
1134
- else {
1135
- this.chunksTable = await conn.createTable(CHUNKS_TABLE, rows);
1136
- }
1137
- chunks = rows.length;
1138
- }
1139
- }
1140
- if (sourceTables.includes(FILES_TABLE)) {
1141
- const sourceFiles = await sourceConn.openTable(FILES_TABLE);
1142
- const rawRows = await sourceFiles.query().toArray();
1143
- if (rawRows.length > 0) {
1144
- // Ensure branch column exists (legacy DBs won't have it)
1145
- const rows = rawRows.map((r) => ({
1146
- filePath: r.filePath,
1147
- fileHash: r.fileHash,
1148
- branch: r.branch ?? this.branch,
1149
- }));
1150
- const conn = await this.connection();
1151
- const destTables = await conn.tableNames();
1152
- if (destTables.includes(FILES_TABLE)) {
1153
- const t = await conn.openTable(FILES_TABLE);
1154
- this.filesTable = t;
1155
- await t.add(rows);
1156
- }
1157
- else {
1158
- this.filesTable = await conn.createTable(FILES_TABLE, rows);
1159
- }
1160
- files = rows.length;
1161
- }
1162
- }
1163
- return { chunks, files };
1164
- }
1165
- /**
1166
- * Return all distinct branch names present in the files table.
1167
- */
1168
- async getStoredBranches() {
1169
- const t = await this.openFiles();
1170
- if (!t)
1171
- return [];
1172
- const rows = await t.query().select(["branch"]).toArray();
1173
- return [...new Set(rows.map((r) => r.branch))];
1174
- }
1175
- /**
1176
- * Delete all file manifest rows for a given branch.
1177
- */
1178
- async deleteBranchManifest(branch) {
1179
- const t = await this.openFiles();
1180
- if (!t)
1181
- return;
1182
- const escaped = branch.replace(/'/g, "''");
1183
- await t.delete(`branch = '${escaped}'`);
1184
- }
1185
- /**
1186
- * Copy another branch's file manifest to this store's branch.
1187
- * Used to bootstrap a new branch from a merge base.
1188
- */
1189
- async copyBranchManifest(sourceBranch) {
1190
- const t = await this.openFiles();
1191
- if (!t)
1192
- return 0;
1193
- const escaped = sourceBranch.replace(/'/g, "''");
1194
- const rows = await t
1195
- .query()
1196
- .where(`branch = '${escaped}'`)
1197
- .select(["filePath", "fileHash"])
1198
- .toArray();
1199
- if (rows.length === 0)
1200
- return 0;
1201
- const records = rows.map((r) => ({
1202
- filePath: r.filePath,
1203
- fileHash: r.fileHash,
1204
- branch: this.branch,
1205
- }));
1206
- await t.add(records);
1207
- return records.length;
1208
- }
1209
- async close() {
1210
- this.chunksTable = undefined;
1211
- this.filesTable = undefined;
1212
- this.vocabTable = undefined;
1213
- this.db = undefined;
1214
- }
1215
- }
1216
- //# sourceMappingURL=store.js.map