codesearch-mcp 0.2.1-alpha → 0.2.3-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 +40 -0
- package/README.md +15 -8
- package/dist/app.js +45 -7
- package/dist/src/commands/EvalCommand.d.ts +10 -0
- package/dist/src/commands/EvalCommand.js +231 -0
- package/dist/src/commands/IndexCommand.js +79 -53
- package/dist/src/db/index.d.ts +1 -1
- package/dist/src/db/index.js +1 -2
- package/dist/src/db/schema.d.ts +5 -29
- package/dist/src/db/schema.js +2 -8
- package/dist/src/dependency-traversal.d.ts +16 -0
- package/dist/src/dependency-traversal.js +45 -0
- package/dist/src/enriched-prompt.d.ts +3 -2
- package/dist/src/enriched-prompt.js +3 -2
- package/dist/src/eval-results.d.ts +12 -0
- package/dist/src/eval-results.js +114 -0
- package/dist/src/file-utils.d.ts +15 -0
- package/dist/src/file-utils.js +24 -1
- package/dist/src/help.js +4 -1
- package/dist/src/interfaces/IAstParser.d.ts +13 -1
- package/dist/src/interfaces/IDatabase.d.ts +0 -14
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +94 -0
- package/dist/src/interfaces/IRetrievalEvaluator.js +2 -0
- package/dist/src/interfaces/ISearcher.d.ts +11 -16
- package/dist/src/interfaces/index.d.ts +1 -0
- package/dist/src/project-scope.d.ts +19 -0
- package/dist/src/project-scope.js +49 -0
- package/dist/src/project-selector.d.ts +6 -0
- package/dist/src/project-selector.js +18 -0
- package/dist/src/retrieval-eval.d.ts +16 -0
- package/dist/src/retrieval-eval.js +214 -0
- package/dist/src/services/AstParserService.d.ts +66 -1
- package/dist/src/services/AstParserService.js +432 -28
- package/dist/src/services/DatabaseService.d.ts +0 -12
- package/dist/src/services/DatabaseService.js +2 -52
- package/dist/src/services/IndexerService.d.ts +8 -0
- package/dist/src/services/IndexerService.js +24 -11
- package/dist/src/services/McpServer.d.ts +7 -0
- package/dist/src/services/McpServer.js +42 -38
- package/dist/src/services/OpenAICompatibleEmbedder.js +2 -1
- package/dist/src/services/SearcherService.d.ts +189 -25
- package/dist/src/services/SearcherService.js +505 -203
- package/dist/src/symbol-rows.d.ts +14 -9
- package/dist/src/symbol-rows.js +17 -11
- 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 +3 -1
- package/dist/src/tools/format-helpers.d.ts +16 -1
- package/dist/src/tools/format-helpers.js +64 -0
- package/package.json +9 -8
|
@@ -1,7 +1,8 @@
|
|
|
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';
|
|
7
8
|
export interface ChunkRow {
|
|
@@ -23,6 +24,58 @@ export interface GraphContext {
|
|
|
23
24
|
imports: string[];
|
|
24
25
|
usedBy: string[];
|
|
25
26
|
}
|
|
27
|
+
/** A tool path argument refused on its form alone, before any lookup. */
|
|
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
|
+
/** A well-formed path that leads to no readable data. */
|
|
54
|
+
export type PathFailure = {
|
|
55
|
+
ok: false;
|
|
56
|
+
reason: 'outside_workspace';
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
reason: 'unreadable';
|
|
60
|
+
code: string;
|
|
61
|
+
} | {
|
|
62
|
+
ok: false;
|
|
63
|
+
reason: 'not_found';
|
|
64
|
+
candidates: string[];
|
|
65
|
+
} | {
|
|
66
|
+
ok: false;
|
|
67
|
+
reason: 'deleted';
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
reason: 'not_indexed';
|
|
71
|
+
explanation: NotIndexedExplanation;
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
reason: 'not_a_file';
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
reason: 'not_a_directory';
|
|
78
|
+
};
|
|
26
79
|
export type FileRangeResult = {
|
|
27
80
|
ok: true;
|
|
28
81
|
filePath: string;
|
|
@@ -30,10 +83,36 @@ export type FileRangeResult = {
|
|
|
30
83
|
endLine: number;
|
|
31
84
|
lines: string[];
|
|
32
85
|
hasMore: boolean;
|
|
86
|
+
} | PathFailure | {
|
|
87
|
+
ok: false;
|
|
88
|
+
reason: 'past_end';
|
|
89
|
+
lineCount: number;
|
|
90
|
+
startLine: number;
|
|
91
|
+
} | {
|
|
92
|
+
ok: false;
|
|
93
|
+
reason: 'bad_range';
|
|
94
|
+
startLine: number;
|
|
95
|
+
endLine: number;
|
|
96
|
+
};
|
|
97
|
+
export type ListFilesResult = {
|
|
98
|
+
ok: true;
|
|
99
|
+
tree: string;
|
|
100
|
+
} | PathFailure;
|
|
101
|
+
export type GrepSymbolResult = {
|
|
102
|
+
ok: true;
|
|
103
|
+
matches: GrepMatch[];
|
|
104
|
+
truncated: boolean;
|
|
105
|
+
partial: boolean;
|
|
33
106
|
} | {
|
|
34
107
|
ok: false;
|
|
35
|
-
reason: 'not_indexed' | 'not_on_disk' | 'invalid_path';
|
|
36
108
|
};
|
|
109
|
+
/** Filesystem access of the path tools — injectable so error codes are testable without permissions. */
|
|
110
|
+
export interface SearcherFs {
|
|
111
|
+
realpath(p: string): Promise<string>;
|
|
112
|
+
stat(p: string): Promise<fs.Stats>;
|
|
113
|
+
readdir(p: string): Promise<fs.Dirent[]>;
|
|
114
|
+
createReadStream(p: string): NodeJS.ReadableStream;
|
|
115
|
+
}
|
|
37
116
|
export interface DependencyRow {
|
|
38
117
|
fromFile: string;
|
|
39
118
|
fromSymbol: string | null;
|
|
@@ -50,12 +129,16 @@ export interface ProjectRow extends ProjectMeta {
|
|
|
50
129
|
type?: 'app' | 'lib';
|
|
51
130
|
/** Real filesystem root of the project (resolved via discovery). */
|
|
52
131
|
path?: string;
|
|
132
|
+
/** `path` in workspace-relative form (`.` for the workspace root). */
|
|
133
|
+
relPath?: string;
|
|
134
|
+
/** Error code of `stat` on `path`, null when it exists. */
|
|
135
|
+
rootError?: string | null;
|
|
53
136
|
}
|
|
54
137
|
export interface GrepMatch {
|
|
55
138
|
filePath: string;
|
|
56
139
|
line: number;
|
|
57
140
|
}
|
|
58
|
-
export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'get_accessor' | 'set_accessor' | 'constructor' | 'function_property' | 'method_signature' | 'all';
|
|
141
|
+
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
142
|
export interface CallerRow {
|
|
60
143
|
fromFile: string;
|
|
61
144
|
fromSymbol: string | null;
|
|
@@ -73,6 +156,8 @@ export interface SymbolHit {
|
|
|
73
156
|
qualified: string;
|
|
74
157
|
isStatic: boolean;
|
|
75
158
|
source: 'index' | 'grep';
|
|
159
|
+
/** Qualified TypeScript name of the decorated declaration (GraphQL rows only); null otherwise. */
|
|
160
|
+
target: string | null;
|
|
76
161
|
}
|
|
77
162
|
export declare class SearcherService {
|
|
78
163
|
private db;
|
|
@@ -80,7 +165,18 @@ export declare class SearcherService {
|
|
|
80
165
|
private discovery;
|
|
81
166
|
private config;
|
|
82
167
|
private logger;
|
|
83
|
-
|
|
168
|
+
private readonly fs;
|
|
169
|
+
constructor(db: IDatabase, embedder: IEmbedder, discovery: IProjectDiscovery, config: Config, logger?: ILogger, fsDeps?: Partial<SearcherFs>);
|
|
170
|
+
/**
|
|
171
|
+
* Whether `code_symbol.target` exists — `true` once a check finds it (never
|
|
172
|
+
* re-checked again in this process), `false` until then (re-checked on
|
|
173
|
+
* every lookup, so a `rag-index` run that adds the column takes effect
|
|
174
|
+
* without restarting `rag-mcp`). See "find_symbol works before the target
|
|
175
|
+
* column exists" (`graphql-symbols`).
|
|
176
|
+
*/
|
|
177
|
+
private targetColumnPresent;
|
|
178
|
+
/** Real path of `workspaceRoot`; cached only once resolved, so a transient failure is retried. */
|
|
179
|
+
private realRootCache?;
|
|
84
180
|
/** Lazily-built, cached map of project name → real filesystem root. */
|
|
85
181
|
private rootMap?;
|
|
86
182
|
/** Cached, lazily-built map of leaf project name → real filesystem root (via discovery). */
|
|
@@ -92,6 +188,65 @@ export declare class SearcherService {
|
|
|
92
188
|
* project since removed from disk, or a flat project not surfaced by discovery).
|
|
93
189
|
*/
|
|
94
190
|
private resolveProjectRoot;
|
|
191
|
+
private toRel;
|
|
192
|
+
private toAbs;
|
|
193
|
+
private workspaceRoot;
|
|
194
|
+
private realWorkspaceRoot;
|
|
195
|
+
/**
|
|
196
|
+
* Lexical validation of a tool path argument: refusals for an empty value,
|
|
197
|
+
* `\`, an absolute path and a `..` escape; otherwise the canonical
|
|
198
|
+
* workspace-relative form. The only filesystem access is the cached real path
|
|
199
|
+
* of `workspaceRoot`.
|
|
200
|
+
*/
|
|
201
|
+
resolveToolPath(input: string | null | undefined): Promise<ResolvedToolPath>;
|
|
202
|
+
/**
|
|
203
|
+
* Whether the real location of `absPath` — of the target, else of its nearest
|
|
204
|
+
* existing ancestor plus the remaining segments — lies under the real
|
|
205
|
+
* workspace root.
|
|
206
|
+
*/
|
|
207
|
+
private realLocation;
|
|
208
|
+
/**
|
|
209
|
+
* Workspace boundary of the disk tools. A real location outside the workspace
|
|
210
|
+
* is allowed only for content the indexer already read (a symlinked share):
|
|
211
|
+
* the file itself for `file`, any file below it for `dir`.
|
|
212
|
+
*/
|
|
213
|
+
checkRealLocation(relPath: string, absPath: string, kind: 'file' | 'dir'): Promise<{
|
|
214
|
+
ok: true;
|
|
215
|
+
} | Extract<PathFailure, {
|
|
216
|
+
reason: 'outside_workspace' | 'unreadable';
|
|
217
|
+
}>>;
|
|
218
|
+
private pathExists;
|
|
219
|
+
/** Whether any table of the index holds `relPath` (a canonical path, never `../…`). */
|
|
220
|
+
isIndexedPath(relPath: string): Promise<boolean>;
|
|
221
|
+
/** Whether the indexer read the file at `relPath`. */
|
|
222
|
+
isContentPath(relPath: string): Promise<boolean>;
|
|
223
|
+
private hasContentUnder;
|
|
224
|
+
/**
|
|
225
|
+
* Up to three index paths ending in `/<relPath>` (`file`) or directories
|
|
226
|
+
* ending in `/<relPath>` that hold one (`dir`), in byte order. For `dir`, the
|
|
227
|
+
* root of a project named `relPath` comes first.
|
|
228
|
+
*/
|
|
229
|
+
findPathCandidates(relPath: string, kind: 'file' | 'dir'): Promise<string[]>;
|
|
230
|
+
/** `findPathCandidates`, with a failed lookup logged and answered by no candidates. */
|
|
231
|
+
private candidatesOrNone;
|
|
232
|
+
/**
|
|
233
|
+
* `relPath` with a leading project name replaced by that project's root
|
|
234
|
+
* (`repo/app/src/x.ts` → `repo/apps/app/src/x.ts`), or null when no name
|
|
235
|
+
* prefixes it, the root equals the name, or the result names nothing.
|
|
236
|
+
*/
|
|
237
|
+
private substituteProjectName;
|
|
238
|
+
private findProjectRootByName;
|
|
239
|
+
/** Longest workspace-relative project root accepted by `match`; discovery is re-run once before giving up. */
|
|
240
|
+
private findProjectRoot;
|
|
241
|
+
/** Root directory (workspace-relative) of the project the text-search fallback for `relPath` runs in. */
|
|
242
|
+
fallbackRoot(relPath: string): string | null;
|
|
243
|
+
/**
|
|
244
|
+
* Why a graph tool has no data for `relPath`, or null when the path is in the
|
|
245
|
+
* index path set. Touches the disk only for an unknown path, and answers a
|
|
246
|
+
* real location outside the workspace as `not_found` without looking at it.
|
|
247
|
+
*/
|
|
248
|
+
checkGraphPath(relPath: string): Promise<PathFailure | null>;
|
|
249
|
+
private explainNotIndexed;
|
|
95
250
|
/**
|
|
96
251
|
* Project-level dependency edges aggregated from `code_dependency`: an edge
|
|
97
252
|
* `A → B` means some file in project A imports a file owned by project B.
|
|
@@ -119,7 +274,7 @@ export declare class SearcherService {
|
|
|
119
274
|
assertIndexMatches(): Promise<void>;
|
|
120
275
|
private compareIndexed;
|
|
121
276
|
listProjects(): Promise<ProjectRow[]>;
|
|
122
|
-
searchChunks(query: string, limit: number, project: string | null, threshold?: number): Promise<SearchResult[]>;
|
|
277
|
+
searchChunks(query: string, limit: number, project: string | null, threshold?: number, rerankOverride?: boolean): Promise<SearchResult[]>;
|
|
123
278
|
/**
|
|
124
279
|
* Emit one structured search-tuning line (via `logger.metric`, at `info`
|
|
125
280
|
* regardless of `logging.level`). `candidates` are in cosine order, so a
|
|
@@ -131,34 +286,42 @@ export declare class SearcherService {
|
|
|
131
286
|
private emitSearchMetrics;
|
|
132
287
|
fetchNeighbors(results: SearchResult[]): Promise<NeighborResult[]>;
|
|
133
288
|
fetchGraphContext(results: SearchResult[]): Promise<Map<string, GraphContext>>;
|
|
134
|
-
getDependencies(filePath: string,
|
|
135
|
-
getDependents(filePath: string,
|
|
289
|
+
getDependencies(filePath: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
|
|
290
|
+
getDependents(filePath: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
|
|
136
291
|
getCallers(symbol: string, project: string, calleeFile?: string | null): Promise<CallerRow[]>;
|
|
137
292
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* scope or read markers only drops the notice (warn logged); the lookup
|
|
142
|
-
* itself is unaffected.
|
|
293
|
+
* Whole-word text search for `symbol` under `rootDir` (workspace-relative).
|
|
294
|
+
* grep exiting ≥ 2 after printing matches is a partial result; without
|
|
295
|
+
* matches, or when grep cannot be spawned, the search failed.
|
|
143
296
|
*/
|
|
144
|
-
|
|
145
|
-
grepSymbol(symbol: string, project: string, excludeFile: string): Promise<{
|
|
146
|
-
matches: GrepMatch[];
|
|
147
|
-
truncated: boolean;
|
|
148
|
-
}>;
|
|
297
|
+
grepSymbol(symbol: string, rootDir: string, excludeFile: string): Promise<GrepSymbolResult>;
|
|
149
298
|
findSymbol(name: string, project: string | null, kind: SymbolKind): Promise<{
|
|
150
299
|
hits: SymbolHit[];
|
|
151
300
|
truncated: boolean;
|
|
152
301
|
grepFailed: boolean;
|
|
153
302
|
}>;
|
|
303
|
+
/**
|
|
304
|
+
* Whether `code_symbol.target` exists. A present result is cached for the
|
|
305
|
+
* process; an absent one is re-checked on every call. Propagates on a
|
|
306
|
+
* failing check, same as any other failed index query.
|
|
307
|
+
*/
|
|
308
|
+
private hasTargetColumn;
|
|
154
309
|
private findSymbolFromIndex;
|
|
155
310
|
private grepFindSymbol;
|
|
156
|
-
listFiles(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
311
|
+
listFiles(relPath: string, depth: number, pattern: string | null): Promise<ListFilesResult>;
|
|
312
|
+
/**
|
|
313
|
+
* The read tools' common front: workspace boundary, then existence and type.
|
|
314
|
+
* A missing file still in the index is `deleted`; otherwise `not_found`.
|
|
315
|
+
*/
|
|
316
|
+
private openableFile;
|
|
317
|
+
/**
|
|
318
|
+
* Streams the file's lines (as `readline` yields them) to `onLine` until it
|
|
319
|
+
* returns false. An open or read error discards everything read so far.
|
|
320
|
+
*/
|
|
321
|
+
private readLines;
|
|
322
|
+
readFile(relPath: string, maxLines: number): Promise<FileRangeResult>;
|
|
323
|
+
readFileRange(relPath: string, startLine: number, endLine: number): Promise<FileRangeResult>;
|
|
324
|
+
/** Throws when `absDir` itself cannot be read; an unreadable subdirectory is marked in place. */
|
|
162
325
|
private walkDir;
|
|
163
326
|
/**
|
|
164
327
|
* ERE pattern for a grep-fallback kind, or null when that kind is never
|
|
@@ -171,8 +334,9 @@ export declare class SearcherService {
|
|
|
171
334
|
private inferKind;
|
|
172
335
|
/**
|
|
173
336
|
* Async wrapper around grep to avoid blocking the event loop. `ok: false`
|
|
174
|
-
* means grep could not be spawned or exited with status ≥ 2
|
|
175
|
-
* from a clean "no matches",
|
|
337
|
+
* means grep could not be spawned (`output: null`) or exited with status ≥ 2,
|
|
338
|
+
* keeping whatever it printed — distinguishable from a clean "no matches",
|
|
339
|
+
* which is `ok: true, output: null`.
|
|
176
340
|
*/
|
|
177
341
|
private spawnGrep;
|
|
178
342
|
}
|