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
@@ -0,0 +1,31 @@
1
+ import type { IDatabase, ILogger } from '../interfaces';
2
+ import type { IToolPaths, PathFailure, ResolvedToolPath } from '../interfaces/IToolPaths';
3
+ import { type WorkspaceFs } from './fs-access';
4
+ import type { ProjectRoots } from './ProjectRoots';
5
+ export declare class ToolPaths implements IToolPaths {
6
+ private db;
7
+ private roots;
8
+ private logger;
9
+ private fs;
10
+ private realRootCache?;
11
+ constructor(db: IDatabase, roots: ProjectRoots, logger: ILogger, fs: WorkspaceFs);
12
+ private realWorkspaceRoot;
13
+ resolveToolPath(input: string | null | undefined): Promise<ResolvedToolPath>;
14
+ private realLocation;
15
+ checkRealLocation(relPath: string, absPath: string, kind: 'file' | 'dir'): Promise<{
16
+ ok: true;
17
+ } | Extract<PathFailure, {
18
+ reason: 'outside_workspace' | 'unreadable';
19
+ }>>;
20
+ grepSearchDir(project: string): Promise<string | null>;
21
+ private pathExists;
22
+ isIndexedPath(relPath: string): Promise<boolean>;
23
+ isContentPath(relPath: string): Promise<boolean>;
24
+ private hasContentUnder;
25
+ findPathCandidates(relPath: string, kind: 'file' | 'dir'): Promise<string[]>;
26
+ candidatesOrNone(relPath: string, kind: 'file' | 'dir'): Promise<string[]>;
27
+ private nearNames;
28
+ private substituteProjectName;
29
+ checkGraphPath(relPath: string): Promise<PathFailure | null>;
30
+ private explainNotIndexed;
31
+ }
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ToolPaths = void 0;
37
+ const path = __importStar(require("path"));
38
+ const file_utils_1 = require("../file-utils");
39
+ const symbol_name_1 = require("../symbol-name");
40
+ const index_paths_1 = require("../search/index-paths");
41
+ const fs_access_1 = require("./fs-access");
42
+ const path_helpers_1 = require("./path-helpers");
43
+ const PATH_MAX_BYTES = 4096;
44
+ const PATH_CANDIDATE_LIMIT = 3;
45
+ const NEAR_NAME_MAX_DISTANCE = 2;
46
+ const INDEX_PATH_COLUMNS = [
47
+ { table: 'code_chunk', column: '"filePath"', content: true },
48
+ { table: 'code_symbol', column: '"filePath"', content: true },
49
+ { table: 'code_dependency', column: '"fromFile"', content: true },
50
+ { table: 'code_call', column: '"fromFile"', content: true },
51
+ { table: 'code_dependency', column: '"toFile"', content: false },
52
+ { table: 'code_call', column: '"calleeFile"', content: false },
53
+ ];
54
+ class ToolPaths {
55
+ db;
56
+ roots;
57
+ logger;
58
+ fs;
59
+ realRootCache;
60
+ constructor(db, roots, logger, fs) {
61
+ this.db = db;
62
+ this.roots = roots;
63
+ this.logger = logger;
64
+ this.fs = fs;
65
+ }
66
+ async realWorkspaceRoot() {
67
+ if (this.realRootCache !== undefined)
68
+ return this.realRootCache;
69
+ try {
70
+ this.realRootCache = await this.fs.realpath(this.roots.workspaceRoot());
71
+ return this.realRootCache;
72
+ }
73
+ catch (err) {
74
+ this.logger.warn('ToolPaths: workspaceRoot real path unavailable', { reason: (0, fs_access_1.errorCode)(err) });
75
+ return null;
76
+ }
77
+ }
78
+ async resolveToolPath(input) {
79
+ if (input == null || input === '')
80
+ return { ok: false, reason: 'invalid', missing: true };
81
+ if (input.includes('\\'))
82
+ return { ok: false, reason: 'invalid', missing: false };
83
+ if (Buffer.byteLength(input) >= PATH_MAX_BYTES)
84
+ return { ok: false, reason: 'unreadable', code: 'ENAMETOOLONG' };
85
+ if (path.posix.isAbsolute(input)) {
86
+ const abs = (0, path_helpers_1.stripTrailingSlash)(path.posix.normalize(input));
87
+ const realRoot = await this.realWorkspaceRoot();
88
+ for (const root of realRoot ? [this.roots.workspaceRoot(), realRoot] : [this.roots.workspaceRoot()]) {
89
+ if (!(0, path_helpers_1.isWithin)(abs, root))
90
+ continue;
91
+ const relative = abs === root ? '.' : abs.slice(root === '/' ? 1 : root.length + 1);
92
+ return { ok: false, reason: 'absolute', relative };
93
+ }
94
+ return { ok: false, reason: 'outside_workspace' };
95
+ }
96
+ const relPath = (0, path_helpers_1.stripTrailingSlash)(path.posix.normalize(input));
97
+ if (relPath === '..' || relPath.startsWith('../'))
98
+ return { ok: false, reason: 'outside_workspace' };
99
+ return { ok: true, relPath, absPath: this.roots.toAbs(relPath) };
100
+ }
101
+ async realLocation(absPath) {
102
+ if (Buffer.byteLength(absPath) >= PATH_MAX_BYTES)
103
+ return { ok: false, code: 'ENAMETOOLONG' };
104
+ const root = (await this.realWorkspaceRoot()) ?? this.roots.workspaceRoot();
105
+ let existing = absPath;
106
+ const rest = [];
107
+ for (;;) {
108
+ try {
109
+ const real = path.join(await this.fs.realpath(existing), ...rest);
110
+ return { ok: true, inside: (0, path_helpers_1.isWithin)(real, root) };
111
+ }
112
+ catch (err) {
113
+ if ((0, fs_access_1.errorCode)(err) !== 'ENOENT')
114
+ return { ok: false, code: (0, fs_access_1.errorCode)(err) };
115
+ const parent = path.dirname(existing);
116
+ if (parent === existing)
117
+ return { ok: true, inside: false };
118
+ rest.unshift(path.basename(existing));
119
+ existing = parent;
120
+ }
121
+ }
122
+ }
123
+ async checkRealLocation(relPath, absPath, kind) {
124
+ const loc = await this.realLocation(absPath);
125
+ if (!loc.ok)
126
+ return { ok: false, reason: 'unreadable', code: loc.code };
127
+ if (loc.inside)
128
+ return { ok: true };
129
+ const exempt = kind === 'file' ? await this.isContentPath(relPath) : await this.hasContentUnder(relPath);
130
+ return exempt ? { ok: true } : { ok: false, reason: 'outside_workspace' };
131
+ }
132
+ async grepSearchDir(project) {
133
+ let discovered;
134
+ try {
135
+ discovered = this.roots.findProjectRootByName(project);
136
+ }
137
+ catch (err) {
138
+ this.logger.error('ToolPaths: project discovery failed', { project, err });
139
+ return null;
140
+ }
141
+ let absDir;
142
+ if (discovered !== null) {
143
+ absDir = this.roots.toAbs(discovered);
144
+ }
145
+ else {
146
+ if (Buffer.byteLength(project) >= PATH_MAX_BYTES)
147
+ return null;
148
+ absDir = path.join(this.roots.workspaceRoot(), project);
149
+ const loc = await this.realLocation(absDir);
150
+ if (!loc.ok || !loc.inside)
151
+ return null;
152
+ }
153
+ try {
154
+ return (await this.fs.stat(absDir)).isDirectory() ? absDir : null;
155
+ }
156
+ catch (err) {
157
+ const code = (0, fs_access_1.errorCode)(err);
158
+ return code === 'ENOENT' || code === 'ENOTDIR' ? null : absDir;
159
+ }
160
+ }
161
+ async pathExists(columns, predicate, param) {
162
+ const branches = columns.map(c => `SELECT 1 FROM ${c.table} WHERE ${predicate.replace(/\$col/g, c.column)}`);
163
+ const [{ exists }] = await this.db.query(`SELECT EXISTS (${branches.join(' UNION ALL ')}) AS exists`, [param]);
164
+ return exists;
165
+ }
166
+ isIndexedPath(relPath) {
167
+ return this.pathExists(INDEX_PATH_COLUMNS, '$col = $1', relPath);
168
+ }
169
+ isContentPath(relPath) {
170
+ return this.pathExists(INDEX_PATH_COLUMNS.filter(c => c.content), '$col = $1', relPath);
171
+ }
172
+ hasContentUnder(relPath) {
173
+ const prefix = relPath === '.' ? '' : (0, symbol_name_1.escapeLike)(relPath) + '/';
174
+ return this.pathExists(INDEX_PATH_COLUMNS.filter(c => c.content), `$col LIKE $1 ESCAPE '\\'`, prefix + '%');
175
+ }
176
+ async findPathCandidates(relPath, kind) {
177
+ const union = INDEX_PATH_COLUMNS.map(c => `SELECT ${c.column} AS p FROM ${c.table}`).join(' UNION ALL ');
178
+ const notOutside = `NOT ${(0, index_paths_1.OUTSIDE_WORKSPACE_VALUE)('p')}`;
179
+ if (kind === 'file') {
180
+ const substituted = await this.substituteProjectName(relPath);
181
+ const rows = await this.db.query(`SELECT p FROM (${union}) s
182
+ WHERE p LIKE $1 ESCAPE '\\' AND ${notOutside}
183
+ GROUP BY p ORDER BY p COLLATE "C" LIMIT ${PATH_CANDIDATE_LIMIT + 1}`, ['%/' + (0, symbol_name_1.escapeLike)(relPath)]);
184
+ const first = substituted ? [substituted] : [];
185
+ return [...first, ...rows.map(r => r.p).filter(p => p !== substituted)].slice(0, PATH_CANDIDATE_LIMIT);
186
+ }
187
+ const projectRoot = this.roots.findProjectRootByName(relPath);
188
+ const first = projectRoot && projectRoot !== relPath ? [projectRoot] : [];
189
+ const rows = await this.db.query(`SELECT d AS p FROM (
190
+ SELECT substring(p FROM 1 FOR position($2::text IN p) + length($3::text)) AS d
191
+ FROM (${union}) s
192
+ WHERE p LIKE $1 ESCAPE '\\' AND ${notOutside}
193
+ ) dirs
194
+ GROUP BY d ORDER BY d COLLATE "C" LIMIT ${PATH_CANDIDATE_LIMIT + 1}`, ['%/' + (0, symbol_name_1.escapeLike)(relPath) + '/%', '/' + relPath + '/', relPath]);
195
+ const rest = rows.map(r => r.p).filter(p => !first.includes(p));
196
+ return [...first, ...rest].slice(0, PATH_CANDIDATE_LIMIT);
197
+ }
198
+ async candidatesOrNone(relPath, kind) {
199
+ let candidates;
200
+ try {
201
+ candidates = await this.findPathCandidates(relPath, kind);
202
+ }
203
+ catch (err) {
204
+ this.logger.error('ToolPaths: path candidate lookup failed', { relPath, err });
205
+ return [];
206
+ }
207
+ if (candidates.length >= PATH_CANDIDATE_LIMIT)
208
+ return candidates;
209
+ const near = (await this.nearNames(relPath, kind)).filter(p => !candidates.includes(p));
210
+ return [...candidates, ...near].slice(0, PATH_CANDIDATE_LIMIT);
211
+ }
212
+ async nearNames(relPath, kind) {
213
+ if (relPath === '.')
214
+ return [];
215
+ const parent = path.posix.dirname(relPath);
216
+ const segment = path.posix.basename(relPath);
217
+ const absParent = this.roots.toAbs(parent);
218
+ let entries;
219
+ try {
220
+ const loc = await this.realLocation(absParent);
221
+ if (!loc.ok)
222
+ throw Object.assign(new Error(`real location unavailable: ${loc.code}`), { code: loc.code });
223
+ if (!loc.inside)
224
+ return [];
225
+ entries = await this.fs.readdir(absParent);
226
+ }
227
+ catch (err) {
228
+ const code = (0, fs_access_1.errorCode)(err);
229
+ if (code === 'ENOENT' || code === 'ENOTDIR')
230
+ return [];
231
+ this.logger.error('ToolPaths: near-name lookup failed', { relPath, err });
232
+ return [];
233
+ }
234
+ const segmentLength = [...segment].length;
235
+ const near = [];
236
+ for (const entry of entries) {
237
+ if (kind === 'file' ? !entry.isFile() : !entry.isDirectory())
238
+ continue;
239
+ if (file_utils_1.IGNORE_DIRS.has(entry.name))
240
+ continue;
241
+ const distance = (0, path_helpers_1.editDistance)(entry.name, segment, NEAR_NAME_MAX_DISTANCE);
242
+ if (distance > NEAR_NAME_MAX_DISTANCE || distance >= segmentLength / 2)
243
+ continue;
244
+ near.push({ name: entry.name, distance });
245
+ }
246
+ near.sort((a, b) => a.distance - b.distance || Buffer.compare(Buffer.from(a.name, 'utf8'), Buffer.from(b.name, 'utf8')));
247
+ return near.slice(0, PATH_CANDIDATE_LIMIT).map(e => (parent === '.' ? e.name : `${parent}/${e.name}`));
248
+ }
249
+ async substituteProjectName(relPath) {
250
+ let named = [];
251
+ for (let attempt = 0; attempt < 2 && named.length === 0; attempt++) {
252
+ if (attempt === 1)
253
+ this.roots.refresh();
254
+ named = [...this.roots.entries()].filter(([name]) => relPath.startsWith(name + '/'));
255
+ }
256
+ const longest = named.sort((a, b) => b[0].length - a[0].length)[0];
257
+ if (!longest)
258
+ return null;
259
+ const [name, absRoot] = longest;
260
+ const root = this.roots.toRel(absRoot);
261
+ if (root === name)
262
+ return null;
263
+ const candidate = (root === '.' ? '' : root + '/') + relPath.slice(name.length + 1);
264
+ if (await this.isIndexedPath(candidate))
265
+ return candidate;
266
+ try {
267
+ return (await this.fs.stat(this.roots.toAbs(candidate))).isFile() ? candidate : null;
268
+ }
269
+ catch {
270
+ return null;
271
+ }
272
+ }
273
+ async checkGraphPath(relPath) {
274
+ if (await this.isIndexedPath(relPath))
275
+ return null;
276
+ const absPath = this.roots.toAbs(relPath);
277
+ const loc = await this.realLocation(absPath);
278
+ if (!loc.ok)
279
+ return { ok: false, reason: 'unreadable', code: loc.code };
280
+ if (!loc.inside)
281
+ return { ok: false, reason: 'not_found', candidates: await this.candidatesOrNone(relPath, 'file') };
282
+ let stat;
283
+ try {
284
+ stat = await this.fs.stat(absPath);
285
+ }
286
+ catch (err) {
287
+ if ((0, fs_access_1.errorCode)(err) !== 'ENOENT')
288
+ return { ok: false, reason: 'unreadable', code: (0, fs_access_1.errorCode)(err) };
289
+ return { ok: false, reason: 'not_found', candidates: await this.candidatesOrNone(relPath, 'file') };
290
+ }
291
+ if (!stat.isFile())
292
+ return { ok: false, reason: 'not_a_file' };
293
+ return { ok: false, reason: 'not_indexed', explanation: await this.explainNotIndexed(relPath) };
294
+ }
295
+ async explainNotIndexed(relPath) {
296
+ const owner = this.roots.findProjectRoot(r => r !== relPath && (0, path_helpers_1.isWithin)(relPath, r));
297
+ if (!owner)
298
+ return { kind: 'no_project' };
299
+ const inProject = owner.rel === '.' ? relPath : relPath.slice(owner.rel.length + 1);
300
+ const exclusion = (0, file_utils_1.indexingExclusion)(inProject, (0, file_utils_1.loadIgnore)(this.roots.toAbs(owner.rel)));
301
+ if (exclusion)
302
+ return exclusion;
303
+ const [{ exists }] = await this.db.query(`SELECT EXISTS (
304
+ SELECT 1 FROM code_chunk WHERE "serviceName" = $1
305
+ UNION ALL SELECT 1 FROM code_symbol WHERE project = $1
306
+ UNION ALL SELECT 1 FROM code_dependency WHERE project = $1
307
+ UNION ALL SELECT 1 FROM code_call WHERE project = $1
308
+ ) AS exists`, [owner.name]);
309
+ return exists ? { kind: 'unknown' } : { kind: 'project_not_indexed', project: owner.name };
310
+ }
311
+ }
312
+ exports.ToolPaths = ToolPaths;
@@ -0,0 +1,19 @@
1
+ import type { ILogger } from '../interfaces';
2
+ import type { FileRangeResult, IWorkspaceFiles, ListFilesResult } from '../interfaces/IWorkspaceFiles';
3
+ import { type WorkspaceFs } from './fs-access';
4
+ import type { ProjectRoots } from './ProjectRoots';
5
+ import type { ToolPaths } from './ToolPaths';
6
+ export declare class WorkspaceFiles implements IWorkspaceFiles {
7
+ private paths;
8
+ private roots;
9
+ private logger;
10
+ private fs;
11
+ constructor(paths: ToolPaths, roots: ProjectRoots, logger: ILogger, fs: WorkspaceFs);
12
+ listFiles(relPath: string, depth: number, pattern: string | null): Promise<ListFilesResult>;
13
+ private openableFile;
14
+ private readLines;
15
+ private readFailure;
16
+ readFile(relPath: string, maxLines: number): Promise<FileRangeResult>;
17
+ readFileRange(relPath: string, startLine: number, endLine: number): Promise<FileRangeResult>;
18
+ private walkDir;
19
+ }
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WorkspaceFiles = void 0;
37
+ const path = __importStar(require("path"));
38
+ const readline = __importStar(require("readline"));
39
+ const stream_1 = require("stream");
40
+ const file_utils_1 = require("../file-utils");
41
+ const fs_access_1 = require("./fs-access");
42
+ const MAX_LINE_BYTES = 64 * 1024 * 1024;
43
+ const LINE_TOO_LONG = 'ELINETOOLONG';
44
+ const BINARY_FILE = 'EBINARY';
45
+ const BINARY_SNIFF_BYTES = 8 * 1024;
46
+ const LF = 0x0a;
47
+ const CR = 0x0d;
48
+ function openLineBytes(open, chunk) {
49
+ const breaks = [chunk.indexOf(LF), chunk.indexOf(CR)].filter(i => i >= 0);
50
+ const head = breaks.length === 0 ? chunk.length : Math.min(...breaks);
51
+ const total = open + head;
52
+ if (total > MAX_LINE_BYTES)
53
+ return null;
54
+ if (breaks.length === 0)
55
+ return total;
56
+ return chunk.length - 1 - Math.max(chunk.lastIndexOf(LF), chunk.lastIndexOf(CR));
57
+ }
58
+ function lineLimitGuard() {
59
+ let open = 0;
60
+ let sniffed = 0;
61
+ let held = [];
62
+ const lineLimit = (chunk) => {
63
+ const next = openLineBytes(open, chunk);
64
+ if (next === null)
65
+ return Object.assign(new Error('line too long'), { code: LINE_TOO_LONG });
66
+ open = next;
67
+ return null;
68
+ };
69
+ return new stream_1.Transform({
70
+ encoding: 'utf-8',
71
+ transform(chunk, _encoding, done) {
72
+ if (sniffed < BINARY_SNIFF_BYTES) {
73
+ const head = chunk.subarray(0, BINARY_SNIFF_BYTES - sniffed);
74
+ if (head.includes(0)) {
75
+ done(Object.assign(new Error('binary file'), { code: BINARY_FILE }));
76
+ return;
77
+ }
78
+ sniffed += head.length;
79
+ held.push(chunk);
80
+ if (sniffed < BINARY_SNIFF_BYTES) {
81
+ done();
82
+ return;
83
+ }
84
+ chunk = Buffer.concat(held);
85
+ held = [];
86
+ }
87
+ const err = lineLimit(chunk);
88
+ if (err)
89
+ done(err);
90
+ else
91
+ done(null, chunk);
92
+ },
93
+ flush(done) {
94
+ if (held.length === 0) {
95
+ done();
96
+ return;
97
+ }
98
+ const chunk = Buffer.concat(held);
99
+ held = [];
100
+ const err = lineLimit(chunk);
101
+ if (err)
102
+ done(err);
103
+ else
104
+ done(null, chunk);
105
+ },
106
+ });
107
+ }
108
+ class WorkspaceFiles {
109
+ paths;
110
+ roots;
111
+ logger;
112
+ fs;
113
+ constructor(paths, roots, logger, fs) {
114
+ this.paths = paths;
115
+ this.roots = roots;
116
+ this.logger = logger;
117
+ this.fs = fs;
118
+ }
119
+ async listFiles(relPath, depth, pattern) {
120
+ const t0 = Date.now();
121
+ this.logger.debug('WorkspaceFiles: listFiles', { relPath, depth, pattern });
122
+ const absPath = this.roots.toAbs(relPath);
123
+ const loc = await this.paths.checkRealLocation(relPath, absPath, 'dir');
124
+ if (!loc.ok)
125
+ return loc;
126
+ let stat;
127
+ try {
128
+ stat = await this.fs.stat(absPath);
129
+ }
130
+ catch (err) {
131
+ if ((0, fs_access_1.errorCode)(err) !== 'ENOENT')
132
+ return { ok: false, reason: 'unreadable', code: (0, fs_access_1.errorCode)(err) };
133
+ return { ok: false, reason: 'not_found', candidates: await this.paths.candidatesOrNone(relPath, 'dir') };
134
+ }
135
+ if (!stat.isDirectory())
136
+ return { ok: false, reason: 'not_a_directory' };
137
+ const clampedDepth = Math.min(5, Math.max(1, depth));
138
+ const lines = [];
139
+ try {
140
+ await this.walkDir(absPath, 0, clampedDepth - 1, pattern, lines);
141
+ }
142
+ catch (err) {
143
+ return { ok: false, reason: 'unreadable', code: (0, fs_access_1.errorCode)(err) };
144
+ }
145
+ this.logger.debug('WorkspaceFiles: listFiles result', { entries: lines.length, ms: Date.now() - t0 });
146
+ if (lines.length === 0) {
147
+ return { ok: true, tree: pattern ? `No files matching "${pattern}" found within depth ${clampedDepth}.` : '(empty directory)' };
148
+ }
149
+ return { ok: true, tree: lines.join('\n') };
150
+ }
151
+ async openableFile(relPath, absPath) {
152
+ const loc = await this.paths.checkRealLocation(relPath, absPath, 'file');
153
+ if (!loc.ok)
154
+ return loc;
155
+ let stat;
156
+ try {
157
+ stat = await this.fs.stat(absPath);
158
+ }
159
+ catch (err) {
160
+ if ((0, fs_access_1.errorCode)(err) !== 'ENOENT')
161
+ return { ok: false, reason: 'unreadable', code: (0, fs_access_1.errorCode)(err) };
162
+ if (await this.paths.isIndexedPath(relPath))
163
+ return { ok: false, reason: 'deleted' };
164
+ return { ok: false, reason: 'not_found', candidates: await this.paths.candidatesOrNone(relPath, 'file') };
165
+ }
166
+ return stat.isFile() ? null : { ok: false, reason: 'not_a_file' };
167
+ }
168
+ async readLines(absPath, onLine) {
169
+ const stream = this.fs.createReadStream(absPath);
170
+ const guard = lineLimitGuard();
171
+ guard.on('error', () => { });
172
+ stream.on('error', err => guard.destroy(err));
173
+ stream.pipe(guard);
174
+ const rl = readline.createInterface({ input: guard, crlfDelay: Infinity });
175
+ let lineNo = 0;
176
+ try {
177
+ for await (const line of rl) {
178
+ lineNo++;
179
+ if (!onLine(line, lineNo))
180
+ break;
181
+ }
182
+ return { ok: true, lineCount: lineNo };
183
+ }
184
+ catch (err) {
185
+ if ((0, fs_access_1.errorCode)(err) === LINE_TOO_LONG && !onLine('', lineNo + 1))
186
+ return { ok: true, lineCount: lineNo };
187
+ return { ok: false, code: (0, fs_access_1.errorCode)(err) };
188
+ }
189
+ finally {
190
+ rl.close();
191
+ guard.destroy();
192
+ stream.destroy?.();
193
+ }
194
+ }
195
+ readFailure(code) {
196
+ return code === BINARY_FILE ? { ok: false, reason: 'binary' } : { ok: false, reason: 'unreadable', code };
197
+ }
198
+ async readFile(relPath, maxLines) {
199
+ const t0 = Date.now();
200
+ this.logger.debug('WorkspaceFiles: readFile', { relPath, maxLines });
201
+ const absPath = this.roots.toAbs(relPath);
202
+ const failure = await this.openableFile(relPath, absPath);
203
+ if (failure)
204
+ return failure;
205
+ const lines = [];
206
+ let hasMore = false;
207
+ const read = await this.readLines(absPath, line => {
208
+ if (lines.length < maxLines) {
209
+ lines.push(line);
210
+ return true;
211
+ }
212
+ hasMore = true;
213
+ return false;
214
+ });
215
+ if (!read.ok)
216
+ return this.readFailure(read.code);
217
+ this.logger.debug('WorkspaceFiles: readFile result', { linesRead: lines.length, hasMore, ms: Date.now() - t0 });
218
+ return { ok: true, filePath: relPath, startLine: 1, endLine: lines.length, lines, hasMore };
219
+ }
220
+ async readFileRange(relPath, startLine, endLine) {
221
+ const t0 = Date.now();
222
+ this.logger.debug('WorkspaceFiles: readFileRange', { relPath, startLine, endLine });
223
+ if (endLine < startLine)
224
+ return { ok: false, reason: 'bad_range', startLine, endLine };
225
+ const absPath = this.roots.toAbs(relPath);
226
+ const failure = await this.openableFile(relPath, absPath);
227
+ if (failure)
228
+ return failure;
229
+ const lines = [];
230
+ let lastRead = 0;
231
+ let hasMore = false;
232
+ const read = await this.readLines(absPath, (line, lineNo) => {
233
+ if (lineNo > endLine) {
234
+ hasMore = true;
235
+ return false;
236
+ }
237
+ if (lineNo >= startLine)
238
+ lines.push(line);
239
+ lastRead = lineNo;
240
+ return true;
241
+ });
242
+ if (!read.ok)
243
+ return this.readFailure(read.code);
244
+ if (!hasMore && startLine > Math.max(read.lineCount, 1)) {
245
+ return { ok: false, reason: 'past_end', lineCount: read.lineCount, startLine };
246
+ }
247
+ this.logger.debug('WorkspaceFiles: readFileRange result', { linesRead: lines.length, hasMore, ms: Date.now() - t0 });
248
+ return { ok: true, filePath: relPath, startLine, endLine: Math.min(endLine, lastRead), lines, hasMore };
249
+ }
250
+ async walkDir(absDir, currentDepth, maxDepth, pattern, lines) {
251
+ const entries = await this.fs.readdir(absDir);
252
+ entries.sort((a, b) => {
253
+ const ad = a.isDirectory() ? 0 : 1;
254
+ const bd = b.isDirectory() ? 0 : 1;
255
+ if (ad !== bd)
256
+ return ad - bd;
257
+ return a.name.localeCompare(b.name);
258
+ });
259
+ for (const entry of entries) {
260
+ if (file_utils_1.IGNORE_DIRS.has(entry.name))
261
+ continue;
262
+ const indent = ' '.repeat(currentDepth);
263
+ if (entry.isDirectory()) {
264
+ const at = lines.push(`${indent}${entry.name}/`) - 1;
265
+ if (currentDepth < maxDepth) {
266
+ try {
267
+ await this.walkDir(path.join(absDir, entry.name), currentDepth + 1, maxDepth, pattern, lines);
268
+ }
269
+ catch {
270
+ lines[at] += ' (unreadable)';
271
+ continue;
272
+ }
273
+ }
274
+ if (pattern && lines.length === at + 1)
275
+ lines.pop();
276
+ }
277
+ else {
278
+ if (pattern && !entry.name.includes(pattern))
279
+ continue;
280
+ lines.push(`${indent}${entry.name}`);
281
+ }
282
+ }
283
+ }
284
+ }
285
+ exports.WorkspaceFiles = WorkspaceFiles;
@@ -0,0 +1,9 @@
1
+ import * as fs from 'fs';
2
+ export interface WorkspaceFs {
3
+ realpath(p: string): Promise<string>;
4
+ stat(p: string): Promise<fs.Stats>;
5
+ readdir(p: string): Promise<fs.Dirent[]>;
6
+ createReadStream(p: string): NodeJS.ReadableStream;
7
+ }
8
+ export declare const NODE_FS: WorkspaceFs;
9
+ export declare function errorCode(err: unknown): string;