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.
- package/CHANGELOG.md +65 -1
- package/README.md +60 -33
- package/dist/app.js +46 -22
- package/dist/src/chunking/AstChunker.d.ts +12 -0
- package/dist/src/chunking/AstChunker.js +30 -0
- package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
- package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
- package/dist/src/chunking/FragmentMerger.d.ts +11 -0
- package/dist/src/chunking/FragmentMerger.js +61 -0
- package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
- package/dist/src/chunking/ImportOnlyFilter.js +14 -0
- package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
- package/dist/src/chunking/LangchainTextSplitter.js +133 -0
- package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
- package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
- package/dist/src/chunking/RunPacker.d.ts +12 -0
- package/dist/src/chunking/RunPacker.js +84 -0
- package/dist/src/chunking/create-chunker.d.ts +3 -0
- package/dist/src/chunking/create-chunker.js +15 -0
- package/dist/src/chunking/interfaces.d.ts +27 -0
- package/dist/src/chunking/interfaces.js +2 -0
- package/dist/src/commands/ConfigCommand.js +1 -9
- package/dist/src/commands/EvalCommand.d.ts +11 -0
- package/dist/src/commands/EvalCommand.js +264 -0
- package/dist/src/commands/IndexCommand.js +70 -66
- package/dist/src/commands/McpCommand.js +2 -8
- package/dist/src/commands/WebCommand.js +2 -5
- package/dist/src/config-error.d.ts +0 -4
- package/dist/src/config-error.js +0 -4
- package/dist/src/config-store.d.ts +1 -54
- package/dist/src/config-store.js +8 -69
- package/dist/src/config.d.ts +8 -17
- package/dist/src/config.js +35 -34
- package/dist/src/constants.d.ts +2 -28
- package/dist/src/constants.js +6 -28
- package/dist/src/container.d.ts +6 -0
- package/dist/src/container.js +12 -3
- package/dist/src/db/index.d.ts +1 -1
- package/dist/src/db/index.js +1 -2
- package/dist/src/db/schema.d.ts +22 -29
- package/dist/src/db/schema.js +3 -14
- package/dist/src/declarations/DeclarationTree.d.ts +19 -0
- package/dist/src/declarations/DeclarationTree.js +66 -0
- package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
- package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
- package/dist/src/declarations/SourceLines.d.ts +37 -0
- package/dist/src/declarations/SourceLines.js +415 -0
- package/dist/src/dependency-traversal.d.ts +10 -0
- package/dist/src/dependency-traversal.js +39 -0
- package/dist/src/enriched-prompt.d.ts +0 -5
- package/dist/src/enriched-prompt.js +0 -5
- package/dist/src/error-record.d.ts +12 -0
- package/dist/src/error-record.js +248 -0
- package/dist/src/eval-corpus.d.ts +16 -0
- package/dist/src/eval-corpus.js +169 -0
- package/dist/src/eval-results.d.ts +15 -0
- package/dist/src/eval-results.js +397 -0
- package/dist/src/file-utils.d.ts +10 -0
- package/dist/src/file-utils.js +19 -1
- package/dist/src/graph.d.ts +0 -25
- package/dist/src/graph.js +0 -28
- package/dist/src/help.js +4 -1
- package/dist/src/hnsw-settings.d.ts +3 -0
- package/dist/src/hnsw-settings.js +24 -0
- package/dist/src/interfaces/IAstParser.d.ts +5 -30
- package/dist/src/interfaces/IChunker.d.ts +3 -12
- package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
- package/dist/src/interfaces/ICorpusInspector.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +2 -36
- package/dist/src/interfaces/IEmbedder.d.ts +0 -9
- package/dist/src/interfaces/ILogger.d.ts +1 -11
- package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
- package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
- package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +216 -0
- package/dist/src/interfaces/IRetrievalEvaluator.js +2 -0
- package/dist/src/interfaces/ISearcher.d.ts +26 -20
- package/dist/src/interfaces/index.d.ts +1 -0
- package/dist/src/interrupt.js +0 -2
- package/dist/src/owner-path.d.ts +9 -10
- package/dist/src/owner-path.js +11 -23
- package/dist/src/project-scope.d.ts +7 -0
- package/dist/src/project-scope.js +37 -0
- package/dist/src/project-selector.d.ts +1 -0
- package/dist/src/project-selector.js +13 -6
- package/dist/src/retrieval-eval.d.ts +24 -0
- package/dist/src/retrieval-eval.js +500 -0
- package/dist/src/search/result-shaping.d.ts +4 -0
- package/dist/src/search/result-shaping.js +19 -0
- package/dist/src/services/AstParserService.d.ts +28 -23
- package/dist/src/services/AstParserService.js +391 -98
- package/dist/src/services/DatabaseService.d.ts +2 -19
- package/dist/src/services/DatabaseService.js +44 -81
- package/dist/src/services/EmbedderFactory.d.ts +0 -4
- package/dist/src/services/EmbedderFactory.js +0 -5
- package/dist/src/services/IndexerService.d.ts +2 -10
- package/dist/src/services/IndexerService.js +29 -45
- package/dist/src/services/InfinityEmbedder.d.ts +0 -21
- package/dist/src/services/InfinityEmbedder.js +0 -15
- package/dist/src/services/LoggerService.d.ts +7 -5
- package/dist/src/services/LoggerService.js +37 -41
- package/dist/src/services/McpServer.d.ts +25 -10
- package/dist/src/services/McpServer.js +101 -64
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
- package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
- package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
- package/dist/src/services/NxMonorepoResolver.js +5 -56
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
- package/dist/src/services/OpenAICompatibleEmbedder.js +1 -14
- package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
- package/dist/src/services/ProjectDiscoveryService.js +0 -4
- package/dist/src/services/SearcherService.d.ts +127 -70
- package/dist/src/services/SearcherService.js +526 -305
- package/dist/src/services/VoyageEmbedder.d.ts +0 -6
- package/dist/src/services/VoyageEmbedder.js +0 -6
- package/dist/src/services/WebServerService.d.ts +1 -53
- package/dist/src/services/WebServerService.js +11 -57
- package/dist/src/services/http-retry.d.ts +0 -7
- package/dist/src/services/http-retry.js +1 -10
- package/dist/src/services/infinity-provisioning.d.ts +0 -74
- package/dist/src/services/infinity-provisioning.js +5 -88
- package/dist/src/services/legacy-ollama-index.d.ts +0 -16
- package/dist/src/services/legacy-ollama-index.js +0 -23
- package/dist/src/services/reranker.d.ts +0 -9
- package/dist/src/services/reranker.js +46 -12
- package/dist/src/setup.d.ts +11 -23
- package/dist/src/setup.js +98 -71
- package/dist/src/symbol-name.d.ts +0 -12
- package/dist/src/symbol-name.js +0 -13
- package/dist/src/symbol-rows.d.ts +2 -11
- package/dist/src/symbol-rows.js +5 -13
- package/dist/src/tool-call-log.d.ts +2 -0
- package/dist/src/tool-call-log.js +24 -0
- package/dist/src/tools/FindSymbolTool.js +14 -5
- package/dist/src/tools/GetCallersTool.d.ts +3 -1
- package/dist/src/tools/GetCallersTool.js +16 -6
- package/dist/src/tools/GetDependenciesTool.d.ts +3 -1
- package/dist/src/tools/GetDependenciesTool.js +15 -8
- package/dist/src/tools/GetDependentsTool.d.ts +3 -1
- package/dist/src/tools/GetDependentsTool.js +31 -16
- package/dist/src/tools/ListFilesTool.d.ts +3 -1
- package/dist/src/tools/ListFilesTool.js +14 -12
- package/dist/src/tools/ListProjectsTool.js +5 -1
- package/dist/src/tools/ReadFileRangeTool.d.ts +3 -1
- package/dist/src/tools/ReadFileRangeTool.js +12 -11
- package/dist/src/tools/ReadFileTool.d.ts +3 -1
- package/dist/src/tools/ReadFileTool.js +12 -10
- package/dist/src/tools/SearchCodebaseTool.js +7 -2
- package/dist/src/tools/format-helpers.d.ts +10 -1
- package/dist/src/tools/format-helpers.js +145 -8
- package/dist/src/web/dashboard.d.ts +0 -8
- package/dist/src/web/dashboard.js +7 -10
- package/package.json +10 -9
- package/dist/src/services/AstChunker.d.ts +0 -42
- package/dist/src/services/AstChunker.js +0 -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,
|
|
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);
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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,
|
|
135
|
-
getDependents(filePath: string,
|
|
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(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
}
|