codesearch-mcp 0.2.2-alpha → 0.3.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 (158) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +60 -33
  3. package/dist/app.js +46 -22
  4. package/dist/src/chunking/AstChunker.d.ts +12 -0
  5. package/dist/src/chunking/AstChunker.js +30 -0
  6. package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
  7. package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
  8. package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
  9. package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
  10. package/dist/src/chunking/FragmentMerger.d.ts +11 -0
  11. package/dist/src/chunking/FragmentMerger.js +61 -0
  12. package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
  13. package/dist/src/chunking/ImportOnlyFilter.js +14 -0
  14. package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
  15. package/dist/src/chunking/LangchainTextSplitter.js +133 -0
  16. package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
  17. package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
  18. package/dist/src/chunking/RunPacker.d.ts +12 -0
  19. package/dist/src/chunking/RunPacker.js +84 -0
  20. package/dist/src/chunking/create-chunker.d.ts +3 -0
  21. package/dist/src/chunking/create-chunker.js +15 -0
  22. package/dist/src/chunking/interfaces.d.ts +27 -0
  23. package/dist/src/chunking/interfaces.js +2 -0
  24. package/dist/src/commands/ConfigCommand.js +1 -9
  25. package/dist/src/commands/EvalCommand.d.ts +11 -0
  26. package/dist/src/commands/EvalCommand.js +264 -0
  27. package/dist/src/commands/IndexCommand.js +70 -66
  28. package/dist/src/commands/McpCommand.js +2 -8
  29. package/dist/src/commands/WebCommand.js +2 -5
  30. package/dist/src/config-error.d.ts +0 -4
  31. package/dist/src/config-error.js +0 -4
  32. package/dist/src/config-store.d.ts +1 -54
  33. package/dist/src/config-store.js +8 -69
  34. package/dist/src/config.d.ts +8 -17
  35. package/dist/src/config.js +35 -34
  36. package/dist/src/constants.d.ts +2 -28
  37. package/dist/src/constants.js +6 -28
  38. package/dist/src/container.d.ts +6 -0
  39. package/dist/src/container.js +12 -3
  40. package/dist/src/db/index.d.ts +1 -1
  41. package/dist/src/db/index.js +1 -2
  42. package/dist/src/db/schema.d.ts +22 -29
  43. package/dist/src/db/schema.js +3 -14
  44. package/dist/src/declarations/DeclarationTree.d.ts +19 -0
  45. package/dist/src/declarations/DeclarationTree.js +66 -0
  46. package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
  47. package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
  48. package/dist/src/declarations/SourceLines.d.ts +37 -0
  49. package/dist/src/declarations/SourceLines.js +415 -0
  50. package/dist/src/dependency-traversal.d.ts +10 -0
  51. package/dist/src/dependency-traversal.js +39 -0
  52. package/dist/src/enriched-prompt.d.ts +0 -5
  53. package/dist/src/enriched-prompt.js +0 -5
  54. package/dist/src/error-record.d.ts +12 -0
  55. package/dist/src/error-record.js +248 -0
  56. package/dist/src/eval-corpus.d.ts +16 -0
  57. package/dist/src/eval-corpus.js +169 -0
  58. package/dist/src/eval-results.d.ts +15 -0
  59. package/dist/src/eval-results.js +397 -0
  60. package/dist/src/file-utils.d.ts +10 -0
  61. package/dist/src/file-utils.js +19 -1
  62. package/dist/src/graph.d.ts +0 -25
  63. package/dist/src/graph.js +0 -28
  64. package/dist/src/help.js +4 -1
  65. package/dist/src/hnsw-settings.d.ts +3 -0
  66. package/dist/src/hnsw-settings.js +24 -0
  67. package/dist/src/interfaces/IAstParser.d.ts +5 -30
  68. package/dist/src/interfaces/IChunker.d.ts +3 -12
  69. package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
  70. package/dist/src/interfaces/ICorpusInspector.js +2 -0
  71. package/dist/src/interfaces/IDatabase.d.ts +2 -36
  72. package/dist/src/interfaces/IEmbedder.d.ts +0 -9
  73. package/dist/src/interfaces/ILogger.d.ts +1 -11
  74. package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
  75. package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
  76. package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
  77. package/dist/src/interfaces/IRetrievalEvaluator.d.ts +216 -0
  78. package/dist/src/interfaces/IRetrievalEvaluator.js +2 -0
  79. package/dist/src/interfaces/ISearcher.d.ts +26 -20
  80. package/dist/src/interfaces/index.d.ts +1 -0
  81. package/dist/src/interrupt.js +0 -2
  82. package/dist/src/owner-path.d.ts +9 -10
  83. package/dist/src/owner-path.js +11 -23
  84. package/dist/src/project-scope.d.ts +7 -0
  85. package/dist/src/project-scope.js +37 -0
  86. package/dist/src/project-selector.d.ts +1 -0
  87. package/dist/src/project-selector.js +13 -6
  88. package/dist/src/retrieval-eval.d.ts +24 -0
  89. package/dist/src/retrieval-eval.js +500 -0
  90. package/dist/src/search/result-shaping.d.ts +4 -0
  91. package/dist/src/search/result-shaping.js +19 -0
  92. package/dist/src/services/AstParserService.d.ts +28 -23
  93. package/dist/src/services/AstParserService.js +391 -98
  94. package/dist/src/services/DatabaseService.d.ts +2 -19
  95. package/dist/src/services/DatabaseService.js +44 -81
  96. package/dist/src/services/EmbedderFactory.d.ts +0 -4
  97. package/dist/src/services/EmbedderFactory.js +0 -5
  98. package/dist/src/services/IndexerService.d.ts +2 -10
  99. package/dist/src/services/IndexerService.js +29 -45
  100. package/dist/src/services/InfinityEmbedder.d.ts +0 -21
  101. package/dist/src/services/InfinityEmbedder.js +0 -15
  102. package/dist/src/services/LoggerService.d.ts +7 -5
  103. package/dist/src/services/LoggerService.js +37 -41
  104. package/dist/src/services/McpServer.d.ts +25 -10
  105. package/dist/src/services/McpServer.js +101 -64
  106. package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
  107. package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
  108. package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
  109. package/dist/src/services/NxMonorepoResolver.js +5 -56
  110. package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
  111. package/dist/src/services/OpenAICompatibleEmbedder.js +1 -14
  112. package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
  113. package/dist/src/services/ProjectDiscoveryService.js +0 -4
  114. package/dist/src/services/SearcherService.d.ts +127 -70
  115. package/dist/src/services/SearcherService.js +526 -305
  116. package/dist/src/services/VoyageEmbedder.d.ts +0 -6
  117. package/dist/src/services/VoyageEmbedder.js +0 -6
  118. package/dist/src/services/WebServerService.d.ts +1 -53
  119. package/dist/src/services/WebServerService.js +11 -57
  120. package/dist/src/services/http-retry.d.ts +0 -7
  121. package/dist/src/services/http-retry.js +1 -10
  122. package/dist/src/services/infinity-provisioning.d.ts +0 -74
  123. package/dist/src/services/infinity-provisioning.js +5 -88
  124. package/dist/src/services/legacy-ollama-index.d.ts +0 -16
  125. package/dist/src/services/legacy-ollama-index.js +0 -23
  126. package/dist/src/services/reranker.d.ts +0 -9
  127. package/dist/src/services/reranker.js +46 -12
  128. package/dist/src/setup.d.ts +11 -23
  129. package/dist/src/setup.js +98 -71
  130. package/dist/src/symbol-name.d.ts +0 -12
  131. package/dist/src/symbol-name.js +0 -13
  132. package/dist/src/symbol-rows.d.ts +2 -11
  133. package/dist/src/symbol-rows.js +5 -13
  134. package/dist/src/tool-call-log.d.ts +2 -0
  135. package/dist/src/tool-call-log.js +24 -0
  136. package/dist/src/tools/FindSymbolTool.js +14 -5
  137. package/dist/src/tools/GetCallersTool.d.ts +3 -1
  138. package/dist/src/tools/GetCallersTool.js +16 -6
  139. package/dist/src/tools/GetDependenciesTool.d.ts +3 -1
  140. package/dist/src/tools/GetDependenciesTool.js +15 -8
  141. package/dist/src/tools/GetDependentsTool.d.ts +3 -1
  142. package/dist/src/tools/GetDependentsTool.js +31 -16
  143. package/dist/src/tools/ListFilesTool.d.ts +3 -1
  144. package/dist/src/tools/ListFilesTool.js +14 -12
  145. package/dist/src/tools/ListProjectsTool.js +5 -1
  146. package/dist/src/tools/ReadFileRangeTool.d.ts +3 -1
  147. package/dist/src/tools/ReadFileRangeTool.js +12 -11
  148. package/dist/src/tools/ReadFileTool.d.ts +3 -1
  149. package/dist/src/tools/ReadFileTool.js +12 -10
  150. package/dist/src/tools/SearchCodebaseTool.js +7 -2
  151. package/dist/src/tools/format-helpers.d.ts +10 -1
  152. package/dist/src/tools/format-helpers.js +145 -8
  153. package/dist/src/web/dashboard.d.ts +0 -8
  154. package/dist/src/web/dashboard.js +7 -10
  155. package/package.json +10 -9
  156. package/dist/src/services/AstChunker.d.ts +0 -42
  157. package/dist/src/services/AstChunker.js +0 -158
  158. package/docs/web-dashboard.md +0 -206
@@ -37,27 +37,6 @@ exports.NxMonorepoResolver = void 0;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const file_utils_1 = require("../file-utils");
40
- /**
41
- * Nx monorepo resolver.
42
- *
43
- * Detection: presence of `nx.json` at the workspace root.
44
- *
45
- * Project discovery strategies (results deduplicated by absolute path):
46
- * 1. Read `workspace.json` or `angular.json` → projects map
47
- * 2. Scan standard Nx directories (`apps/`, `libs/`, `packages/`) for
48
- * `project.json`, then `package.json` (only when strategy 1 found nothing)
49
- * 3. `tsconfig.base.json` `paths` — root-level libs referenced only by path
50
- * alias (e.g. `contracts/`, `interfaces/`) that the directory scan misses
51
- * 4. Root-level fallback: treat the workspace root itself as a project
52
- * (standalone Nx setup) when nothing else matched
53
- *
54
- * tsconfig resolution:
55
- * - Returns `tsconfig.base.json` at workspace root (Nx convention)
56
- * - Falls back to `tsconfig.json` at workspace root
57
- *
58
- * Cross-project import resolution is handled by the AST parser via the
59
- * workspace-wide tsconfig `paths` (tsconfig.base.json), so no scope map is built here.
60
- */
61
40
  class NxMonorepoResolver {
62
41
  logger;
63
42
  constructor(logger) {
@@ -67,15 +46,12 @@ class NxMonorepoResolver {
67
46
  return fs.existsSync(path.join(rootDir, 'nx.json'));
68
47
  }
69
48
  discoverProjects(rootDir) {
70
- // Deduplicate across strategies by absolute project path.
71
49
  const byPath = new Map();
72
50
  const add = (p) => { if (p && !byPath.has(p.path))
73
51
  byPath.set(p.path, p); };
74
- // Strategy 1: workspace.json / angular.json (authoritative when present)
75
52
  const workspaceProjects = this.readWorkspaceJson(rootDir);
76
53
  for (const p of workspaceProjects)
77
54
  add(p);
78
- // Strategy 2 & 3: scan standard directories (only when no workspace.json map)
79
55
  if (byPath.size === 0) {
80
56
  for (const dir of ['apps', 'libs', 'packages']) {
81
57
  const fullDir = path.join(rootDir, dir);
@@ -86,13 +62,8 @@ class NxMonorepoResolver {
86
62
  add(p);
87
63
  }
88
64
  }
89
- // Strategy 4: tsconfig.base.json `paths` — captures libs that live at the
90
- // repo root (e.g. `contracts/`, `interfaces/`) referenced by path aliases,
91
- // which the apps/libs/packages scan misses. Only real Nx projects (with a
92
- // project.json/package.json) are added; dedup handles overlap with the scan.
93
65
  for (const p of this.discoverFromTsconfigPaths(rootDir))
94
66
  add(p);
95
- // Root-level fallback (standalone Nx setup)
96
67
  if (byPath.size === 0) {
97
68
  const rootProject = this.tryReadProject(rootDir, rootDir);
98
69
  if (rootProject)
@@ -102,13 +73,6 @@ class NxMonorepoResolver {
102
73
  this.logger.debug('NxMonorepoResolver: discovered projects', { count: projects.length });
103
74
  return projects;
104
75
  }
105
- /**
106
- * Discover projects from `tsconfig.base.json` `compilerOptions.paths`. Each
107
- * alias (e.g. `@scope/interfaces` → `interfaces/src/index.ts`) resolves to a
108
- * lib root (the path before `/src/`), which for real Nx libs contains a
109
- * project.json. Aliases pointing at non-projects (no project.json/package.json,
110
- * e.g. `@env` → `environments/environment.ts`) are skipped.
111
- */
112
76
  discoverFromTsconfigPaths(rootDir) {
113
77
  const tsconfigPath = this.getRootTsConfigPath(rootDir);
114
78
  if (!tsconfigPath)
@@ -118,7 +82,7 @@ class NxMonorepoResolver {
118
82
  json = this.parseJsonc(fs.readFileSync(tsconfigPath, 'utf-8'));
119
83
  }
120
84
  catch (err) {
121
- this.logger.warn('NxMonorepoResolver: failed to parse tsconfig paths', { file: tsconfigPath, reason: err.message });
85
+ this.logger.warn('NxMonorepoResolver: failed to parse tsconfig paths', { file: tsconfigPath, err });
122
86
  return [];
123
87
  }
124
88
  const co = json?.compilerOptions ?? {};
@@ -134,13 +98,12 @@ class NxMonorepoResolver {
134
98
  const srcIdx = target.indexOf('/src/');
135
99
  const relRoot = srcIdx >= 0 ? target.slice(0, srcIdx) : path.dirname(target);
136
100
  const libRoot = path.resolve(rootDir, baseUrl, relRoot);
137
- const project = this.tryReadProject(libRoot, rootDir); // null unless a real project marker exists
101
+ const project = this.tryReadProject(libRoot, rootDir);
138
102
  if (project)
139
103
  projects.push(project);
140
104
  }
141
105
  return projects;
142
106
  }
143
- /** Parse JSON that may contain comments / trailing commas (tsconfig style). */
144
107
  parseJsonc(text) {
145
108
  try {
146
109
  return JSON.parse(text);
@@ -154,17 +117,14 @@ class NxMonorepoResolver {
154
117
  }
155
118
  }
156
119
  getRootTsConfigPath(rootDir) {
157
- // Nx convention: tsconfig.base.json at workspace root
158
120
  const basePath = path.join(rootDir, 'tsconfig.base.json');
159
121
  if (fs.existsSync(basePath))
160
122
  return basePath;
161
- // Fallback: tsconfig.json at workspace root
162
123
  const rootPath = path.join(rootDir, 'tsconfig.json');
163
124
  if (fs.existsSync(rootPath))
164
125
  return rootPath;
165
126
  return null;
166
127
  }
167
- // ── Private helpers ─────────────────────────────────────────────────────────
168
128
  readWorkspaceJson(rootDir) {
169
129
  const projects = [];
170
130
  for (const filename of ['workspace.json', 'angular.json']) {
@@ -175,9 +135,6 @@ class NxMonorepoResolver {
175
135
  const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
176
136
  const projectsMap = content.projects ?? {};
177
137
  for (const [name, value] of Object.entries(projectsMap)) {
178
- // value can be a string (path) or an object with a `root` property.
179
- // Guard per-entry: a malformed entry (object without `root`) must not
180
- // abort discovery of the remaining projects in this file.
181
138
  const rawRoot = typeof value === 'string'
182
139
  ? value
183
140
  : value?.root;
@@ -196,7 +153,7 @@ class NxMonorepoResolver {
196
153
  catch (err) {
197
154
  this.logger.warn('NxMonorepoResolver: failed to parse workspace file', {
198
155
  file: filePath,
199
- reason: err.message,
156
+ err,
200
157
  });
201
158
  }
202
159
  }
@@ -222,14 +179,12 @@ class NxMonorepoResolver {
222
179
  projects.push(project);
223
180
  continue;
224
181
  }
225
- // Check for nested grouping directories (e.g. libs/shared/utils)
226
182
  const nested = this.scanDirectory(projectPath, defaultType, rootDir);
227
183
  projects.push(...nested);
228
184
  }
229
185
  return projects;
230
186
  }
231
187
  tryReadProject(projectPath, rootDir, defaultType = 'lib') {
232
- // Check for project.json (Nx project marker)
233
188
  const projectJsonPath = path.join(projectPath, 'project.json');
234
189
  const packageJsonPath = path.join(projectPath, 'package.json');
235
190
  const hasProjectJson = fs.existsSync(projectJsonPath);
@@ -239,7 +194,6 @@ class NxMonorepoResolver {
239
194
  let name = path.relative(rootDir, projectPath).replace(/\\/g, '/');
240
195
  let type = defaultType;
241
196
  let packageName = null;
242
- // Read project.json for name and type
243
197
  if (hasProjectJson) {
244
198
  try {
245
199
  const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
@@ -253,14 +207,12 @@ class NxMonorepoResolver {
253
207
  catch (err) {
254
208
  this.logger.warn('NxMonorepoResolver: failed to parse project.json', {
255
209
  file: projectJsonPath,
256
- reason: err.message,
210
+ err,
257
211
  });
258
212
  }
259
213
  }
260
- // Read package.json for npm scope
261
214
  if (hasPackageJson) {
262
215
  packageName = this.readPackageName(projectPath);
263
- // If no project.json name, use package name without scope
264
216
  if (!hasProjectJson && packageName) {
265
217
  const shortName = packageName.replace(/^@[^/]+\//, '');
266
218
  name = shortName;
@@ -281,7 +233,6 @@ class NxMonorepoResolver {
281
233
  }
282
234
  }
283
235
  inferProjectType(projectPath, name) {
284
- // Check project.json
285
236
  const projectJsonPath = path.join(projectPath, 'project.json');
286
237
  if (fs.existsSync(projectJsonPath)) {
287
238
  try {
@@ -291,15 +242,13 @@ class NxMonorepoResolver {
291
242
  if (projectJson.projectType === 'library')
292
243
  return 'lib';
293
244
  }
294
- catch { /* fall through */ }
245
+ catch { }
295
246
  }
296
- // Infer from path
297
247
  const rel = projectPath.replace(/\\/g, '/');
298
248
  if (rel.includes('/apps/') || rel.includes('/app/'))
299
249
  return 'app';
300
250
  if (rel.includes('/libs/') || rel.includes('/lib/') || rel.includes('/packages/'))
301
251
  return 'lib';
302
- // Infer from name
303
252
  if (name.includes('lib') || name.includes('shared') || name.includes('util'))
304
253
  return 'lib';
305
254
  return 'app';
@@ -5,12 +5,6 @@ export interface EmbedderDeps {
5
5
  fetchImpl?: typeof fetch;
6
6
  baseDelayMs?: number;
7
7
  }
8
- /**
9
- * Embedder for any server exposing an OpenAI-style `/embeddings` endpoint:
10
- * OpenAI cloud, Infinity, TEI, vLLM, LM Studio, LocalAI. One request carries all
11
- * inputs (`{ input: string[], model, dimensions? }`) and returns
12
- * `{ data: [{ embedding, index }] }`, re-ordered to input order.
13
- */
14
8
  export declare class OpenAICompatibleEmbedder implements IEmbedder {
15
9
  protected config: Config;
16
10
  private logger;
@@ -6,12 +6,6 @@ const config_1 = require("../config");
6
6
  const reranker_1 = require("./reranker");
7
7
  const http_retry_1 = require("./http-retry");
8
8
  const constants_1 = require("../constants");
9
- /**
10
- * Embedder for any server exposing an OpenAI-style `/embeddings` endpoint:
11
- * OpenAI cloud, Infinity, TEI, vLLM, LM Studio, LocalAI. One request carries all
12
- * inputs (`{ input: string[], model, dimensions? }`) and returns
13
- * `{ data: [{ embedding, index }] }`, re-ordered to input order.
14
- */
15
9
  class OpenAICompatibleEmbedder {
16
10
  config;
17
11
  logger;
@@ -24,7 +18,6 @@ class OpenAICompatibleEmbedder {
24
18
  this.config = config;
25
19
  this.logger = logger;
26
20
  const base = config.embedding.baseURL.replace(/\/+$/, '');
27
- // Allow a baseURL that already includes the path (…/v1) or not.
28
21
  this.url = /\/embeddings$/.test(base) ? base : `${base}/embeddings`;
29
22
  this.doFetch = deps.fetchImpl ?? fetch;
30
23
  this.baseDelayMs = deps.baseDelayMs;
@@ -40,8 +33,6 @@ class OpenAICompatibleEmbedder {
40
33
  return [];
41
34
  return (0, http_retry_1.withRetry)(async () => {
42
35
  const headers = { 'Content-Type': 'application/json' };
43
- // Resolve the key lazily — an unset `env:` ref throws here (request time),
44
- // not at construction, so `rag-web`/`rag-mcp` still start.
45
36
  const apiKey = (0, config_1.resolveEnvRef)(this.config.embedding.apiKey);
46
37
  if (apiKey)
47
38
  headers['Authorization'] = `Bearer ${apiKey}`;
@@ -51,7 +42,7 @@ class OpenAICompatibleEmbedder {
51
42
  const res = await this.doFetch(this.url, {
52
43
  method: 'POST',
53
44
  headers,
54
- signal: AbortSignal.timeout(120_000),
45
+ signal: AbortSignal.timeout(240_000),
55
46
  body: JSON.stringify(body),
56
47
  });
57
48
  if (!res.ok) {
@@ -59,13 +50,9 @@ class OpenAICompatibleEmbedder {
59
50
  throw new http_retry_1.HttpError(res.status, `embeddings request failed: ${res.status} ${errBody}`);
60
51
  }
61
52
  const data = await res.json();
62
- // Guard against a partial/mismatched response — a missing vector would
63
- // otherwise be inserted as "[undefined]" and fail the whole file, or (with
64
- // reordering + no index) silently misalign vectors to the wrong chunks.
65
53
  if (!Array.isArray(data.data) || data.data.length !== texts.length) {
66
54
  throw new Error(`embeddings response returned ${data.data?.length ?? 0} vectors for ${texts.length} inputs`);
67
55
  }
68
- // Sort by `index` so vectors line up with input order regardless of server ordering.
69
56
  return [...data.data]
70
57
  .sort((a, b) => (a.index ?? 0) - (b.index ?? 0))
71
58
  .map(d => d.embedding);
@@ -1,8 +1,4 @@
1
1
  import type { IProjectDiscovery, DiscoveredProject } from '../interfaces/IProjectDiscovery';
2
- /**
3
- * Default project discovery: finds immediate subdirectories
4
- * of the workspace root that contain a package.json.
5
- */
6
2
  export declare class ProjectDiscoveryService implements IProjectDiscovery {
7
3
  discover(rootDir: string): DiscoveredProject[];
8
4
  }
@@ -37,10 +37,6 @@ exports.ProjectDiscoveryService = void 0;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const file_utils_1 = require("../file-utils");
40
- /**
41
- * Default project discovery: finds immediate subdirectories
42
- * of the workspace root that contain a package.json.
43
- */
44
40
  class ProjectDiscoveryService {
45
41
  discover(rootDir) {
46
42
  return fs.readdirSync(rootDir, { withFileTypes: true })
@@ -1,9 +1,11 @@
1
+ import * as fs from 'fs';
1
2
  import type { Config } from '../config';
2
3
  import type { IEmbedder } from '../interfaces';
3
4
  import type { IDatabase } from '../interfaces';
4
- import { type ProjectMeta } from '../file-utils';
5
+ import { type IndexingExclusion, type ProjectMeta } from '../file-utils';
5
6
  import type { ILogger } from '../interfaces';
6
7
  import type { IProjectDiscovery } from '../interfaces';
8
+ import type { StagedSearchResult } from '../interfaces/ISearcher';
7
9
  export interface ChunkRow {
8
10
  serviceName: string;
9
11
  filePath: string;
@@ -23,6 +25,56 @@ export interface GraphContext {
23
25
  imports: string[];
24
26
  usedBy: string[];
25
27
  }
28
+ export type PathRefusal = {
29
+ ok: false;
30
+ reason: 'invalid';
31
+ missing: boolean;
32
+ } | {
33
+ ok: false;
34
+ reason: 'absolute';
35
+ relative: string;
36
+ } | {
37
+ ok: false;
38
+ reason: 'outside_workspace';
39
+ };
40
+ export type ResolvedToolPath = {
41
+ ok: true;
42
+ relPath: string;
43
+ absPath: string;
44
+ } | PathRefusal;
45
+ export type NotIndexedExplanation = IndexingExclusion | {
46
+ kind: 'no_project';
47
+ } | {
48
+ kind: 'project_not_indexed';
49
+ project: string;
50
+ } | {
51
+ kind: 'unknown';
52
+ };
53
+ export type PathFailure = {
54
+ ok: false;
55
+ reason: 'outside_workspace';
56
+ } | {
57
+ ok: false;
58
+ reason: 'unreadable';
59
+ code: string;
60
+ } | {
61
+ ok: false;
62
+ reason: 'not_found';
63
+ candidates: string[];
64
+ } | {
65
+ ok: false;
66
+ reason: 'deleted';
67
+ } | {
68
+ ok: false;
69
+ reason: 'not_indexed';
70
+ explanation: NotIndexedExplanation;
71
+ } | {
72
+ ok: false;
73
+ reason: 'not_a_file';
74
+ } | {
75
+ ok: false;
76
+ reason: 'not_a_directory';
77
+ };
26
78
  export type FileRangeResult = {
27
79
  ok: true;
28
80
  filePath: string;
@@ -30,10 +82,35 @@ export type FileRangeResult = {
30
82
  endLine: number;
31
83
  lines: string[];
32
84
  hasMore: boolean;
85
+ } | PathFailure | {
86
+ ok: false;
87
+ reason: 'past_end';
88
+ lineCount: number;
89
+ startLine: number;
90
+ } | {
91
+ ok: false;
92
+ reason: 'bad_range';
93
+ startLine: number;
94
+ endLine: number;
95
+ };
96
+ export type ListFilesResult = {
97
+ ok: true;
98
+ tree: string;
99
+ } | PathFailure;
100
+ export type GrepSymbolResult = {
101
+ ok: true;
102
+ matches: GrepMatch[];
103
+ truncated: boolean;
104
+ partial: boolean;
33
105
  } | {
34
106
  ok: false;
35
- reason: 'not_indexed' | 'not_on_disk' | 'invalid_path';
36
107
  };
108
+ export interface SearcherFs {
109
+ realpath(p: string): Promise<string>;
110
+ stat(p: string): Promise<fs.Stats>;
111
+ readdir(p: string): Promise<fs.Dirent[]>;
112
+ createReadStream(p: string): NodeJS.ReadableStream;
113
+ }
37
114
  export interface DependencyRow {
38
115
  fromFile: string;
39
116
  fromSymbol: string | null;
@@ -42,20 +119,22 @@ export interface DependencyRow {
42
119
  depType: string;
43
120
  depth?: number;
44
121
  via?: string;
122
+ id?: string;
45
123
  }
46
124
  export interface ProjectRow extends ProjectMeta {
47
125
  serviceName: string;
48
126
  chunks: number;
49
127
  packageName?: string | null;
50
128
  type?: 'app' | 'lib';
51
- /** Real filesystem root of the project (resolved via discovery). */
52
129
  path?: string;
130
+ relPath?: string;
131
+ rootError?: string | null;
53
132
  }
54
133
  export interface GrepMatch {
55
134
  filePath: string;
56
135
  line: number;
57
136
  }
58
- export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'get_accessor' | 'set_accessor' | 'constructor' | 'function_property' | 'method_signature' | 'all';
137
+ export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'get_accessor' | 'set_accessor' | 'constructor' | 'function_property' | 'method_signature' | 'graphql_query' | 'graphql_mutation' | 'graphql_subscription' | 'graphql_field' | 'graphql_type' | 'all';
59
138
  export interface CallerRow {
60
139
  fromFile: string;
61
140
  fromSymbol: string | null;
@@ -69,36 +148,52 @@ export interface SymbolHit {
69
148
  column: number;
70
149
  kind: Exclude<SymbolKind, 'all'>;
71
150
  signature: string;
72
- /** `container.name`, or `name` when the container is null. */
73
151
  qualified: string;
74
152
  isStatic: boolean;
75
153
  source: 'index' | 'grep';
154
+ target: string | null;
76
155
  }
156
+ export declare function rerankDocument(row: ChunkRow): string;
77
157
  export declare class SearcherService {
78
158
  private db;
79
159
  private embedder;
80
160
  private discovery;
81
161
  private config;
82
162
  private logger;
83
- constructor(db: IDatabase, embedder: IEmbedder, discovery: IProjectDiscovery, config: Config, logger?: ILogger);
84
- /** Lazily-built, cached map of project name → real filesystem root. */
163
+ private readonly fs;
164
+ constructor(db: IDatabase, embedder: IEmbedder, discovery: IProjectDiscovery, config: Config, logger?: ILogger, fsDeps?: Partial<SearcherFs>);
165
+ private targetColumnPresent;
166
+ private realRootCache?;
85
167
  private rootMap?;
86
- /** Cached, lazily-built map of leaf project name → real filesystem root (via discovery). */
168
+ private pgvectorVersionRead?;
169
+ private degradedScanWarned;
170
+ private iterativeScanEnabled;
171
+ private warnDegradedScan;
87
172
  private projectRootMap;
88
- /**
89
- * Resolve a project name to its real filesystem root. Uses discovery (leaf
90
- * projects) so nested-monorepo names like `repo/app` map to their true path;
91
- * falls back to `join(workspaceRoot, name)` when the name is unknown (e.g. a
92
- * project since removed from disk, or a flat project not surfaced by discovery).
93
- */
94
173
  private resolveProjectRoot;
95
- /**
96
- * Project-level dependency edges aggregated from `code_dependency`: an edge
97
- * `A → B` means some file in project A imports a file owned by project B.
98
- * Used by the graph to draw links between a monorepo's sub-projects. Only
99
- * edges whose target file belongs to an indexed project are returned (targets
100
- * outside any indexed project — e.g. unindexed libs — are skipped).
101
- */
174
+ private toRel;
175
+ private toAbs;
176
+ private workspaceRoot;
177
+ private realWorkspaceRoot;
178
+ resolveToolPath(input: string | null | undefined): Promise<ResolvedToolPath>;
179
+ private realLocation;
180
+ checkRealLocation(relPath: string, absPath: string, kind: 'file' | 'dir'): Promise<{
181
+ ok: true;
182
+ } | Extract<PathFailure, {
183
+ reason: 'outside_workspace' | 'unreadable';
184
+ }>>;
185
+ private pathExists;
186
+ isIndexedPath(relPath: string): Promise<boolean>;
187
+ isContentPath(relPath: string): Promise<boolean>;
188
+ private hasContentUnder;
189
+ findPathCandidates(relPath: string, kind: 'file' | 'dir'): Promise<string[]>;
190
+ private candidatesOrNone;
191
+ private substituteProjectName;
192
+ private findProjectRootByName;
193
+ private findProjectRoot;
194
+ fallbackRoot(relPath: string): string | null;
195
+ checkGraphPath(relPath: string): Promise<PathFailure | null>;
196
+ private explainNotIndexed;
102
197
  getProjectEdges(): Promise<{
103
198
  from: string;
104
199
  to: string;
@@ -107,72 +202,34 @@ export declare class SearcherService {
107
202
  query: string;
108
203
  translated: boolean;
109
204
  }>;
110
- /**
111
- * Refuse when the configured embedding model disagrees with the one that built
112
- * the index — embedding a query into a foreign vector space returns noise. A
113
- * missing `index_meta` row with an empty (or altogether absent) `code_chunk`
114
- * does not block — but a *populated* `code_chunk` with no `index_meta` row is a
115
- * pre-`index_meta` install, treated as the legacy `ollama`/`bge-m3` index (same
116
- * interpretation as the `rag-index` legacy-Ollama guard), read-only — this check
117
- * never writes `index_meta`.
118
- */
119
205
  assertIndexMatches(): Promise<void>;
120
206
  private compareIndexed;
121
207
  listProjects(): Promise<ProjectRow[]>;
122
- searchChunks(query: string, limit: number, project: string | null, threshold?: number): Promise<SearchResult[]>;
123
- /**
124
- * Emit one structured search-tuning line (via `logger.metric`, at `info`
125
- * regardless of `logging.level`). `candidates` are in cosine order, so a
126
- * candidate's array index is its cosine rank — `maxCosineRankSurvived` is the
127
- * deepest cosine position that survived reranking into the returned set. Read
128
- * it alongside `poolSaturated` to tune `candidateFloor`/`overfetch`/threshold
129
- * per hardware. Never logs chunk content, embeddings, or credentials.
130
- */
208
+ searchChunks(query: string, limit: number, project: string | null, threshold?: number, rerankOverride?: boolean, perFile?: number): Promise<SearchResult[]>;
209
+ searchChunksWithStages(query: string, limit: number, project: string | null, threshold?: number, rerankOverride?: boolean, perFile?: number): Promise<StagedSearchResult>;
131
210
  private emitSearchMetrics;
132
211
  fetchNeighbors(results: SearchResult[]): Promise<NeighborResult[]>;
133
212
  fetchGraphContext(results: SearchResult[]): Promise<Map<string, GraphContext>>;
134
- getDependencies(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
135
- getDependents(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
213
+ getDependencies(filePath: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
214
+ getDependents(filePath: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
136
215
  getCallers(symbol: string, project: string, calleeFile?: string | null): Promise<CallerRow[]>;
137
- /**
138
- * One-line warning when any project in `scope` (the given `project`, or
139
- * every project with `code_chunk`/`code_symbol` rows) is outdated — shown
140
- * first in `find_symbol`/`get_callers` output. Any failure to determine the
141
- * scope or read markers only drops the notice (warn logged); the lookup
142
- * itself is unaffected.
143
- */
144
- outdatedNotice(project: string | null): Promise<string | null>;
145
- grepSymbol(symbol: string, project: string, excludeFile: string): Promise<{
146
- matches: GrepMatch[];
147
- truncated: boolean;
148
- }>;
216
+ grepSymbol(symbol: string, rootDir: string, excludeFile: string): Promise<GrepSymbolResult>;
149
217
  findSymbol(name: string, project: string | null, kind: SymbolKind): Promise<{
150
218
  hits: SymbolHit[];
151
219
  truncated: boolean;
152
220
  grepFailed: boolean;
153
221
  }>;
222
+ private hasTargetColumn;
154
223
  private findSymbolFromIndex;
155
224
  private grepFindSymbol;
156
- listFiles(relativePath: string, project: string, depth: number, pattern: string | null): {
157
- tree: string;
158
- error?: string;
159
- };
160
- readFileAbsolute(absPath: string, maxLines: number): Promise<FileRangeResult>;
161
- readFileRange(filePath: string, startLine: number, endLine: number): Promise<FileRangeResult>;
225
+ listFiles(relPath: string, depth: number, pattern: string | null): Promise<ListFilesResult>;
226
+ private openableFile;
227
+ private readLines;
228
+ readFile(relPath: string, maxLines: number): Promise<FileRangeResult>;
229
+ readFileRange(relPath: string, startLine: number, endLine: number): Promise<FileRangeResult>;
162
230
  private walkDir;
163
- /**
164
- * ERE pattern for a grep-fallback kind, or null when that kind is never
165
- * grepped (`function_property`/`method_signature` are index-only; a bare
166
- * `constructor` search only makes sense when the name itself is `constructor`).
167
- */
168
231
  private buildFindSymbolPattern;
169
- /** 1-based index of the first whole-word occurrence of `name` in `line`, or -1. */
170
232
  private static wholeWordIndex;
171
233
  private inferKind;
172
- /**
173
- * Async wrapper around grep to avoid blocking the event loop. `ok: false`
174
- * means grep could not be spawned or exited with status ≥ 2 (distinguishable
175
- * from a clean "no matches", which is `ok: true, output: null`).
176
- */
177
234
  private spawnGrep;
178
235
  }