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.
Files changed (62) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +15 -8
  3. package/dist/app.js +45 -7
  4. package/dist/src/commands/EvalCommand.d.ts +10 -0
  5. package/dist/src/commands/EvalCommand.js +231 -0
  6. package/dist/src/commands/IndexCommand.js +79 -53
  7. package/dist/src/db/index.d.ts +1 -1
  8. package/dist/src/db/index.js +1 -2
  9. package/dist/src/db/schema.d.ts +5 -29
  10. package/dist/src/db/schema.js +2 -8
  11. package/dist/src/dependency-traversal.d.ts +16 -0
  12. package/dist/src/dependency-traversal.js +45 -0
  13. package/dist/src/enriched-prompt.d.ts +3 -2
  14. package/dist/src/enriched-prompt.js +3 -2
  15. package/dist/src/eval-results.d.ts +12 -0
  16. package/dist/src/eval-results.js +114 -0
  17. package/dist/src/file-utils.d.ts +15 -0
  18. package/dist/src/file-utils.js +24 -1
  19. package/dist/src/help.js +4 -1
  20. package/dist/src/interfaces/IAstParser.d.ts +13 -1
  21. package/dist/src/interfaces/IDatabase.d.ts +0 -14
  22. package/dist/src/interfaces/IRetrievalEvaluator.d.ts +94 -0
  23. package/dist/src/interfaces/IRetrievalEvaluator.js +2 -0
  24. package/dist/src/interfaces/ISearcher.d.ts +11 -16
  25. package/dist/src/interfaces/index.d.ts +1 -0
  26. package/dist/src/project-scope.d.ts +19 -0
  27. package/dist/src/project-scope.js +49 -0
  28. package/dist/src/project-selector.d.ts +6 -0
  29. package/dist/src/project-selector.js +18 -0
  30. package/dist/src/retrieval-eval.d.ts +16 -0
  31. package/dist/src/retrieval-eval.js +214 -0
  32. package/dist/src/services/AstParserService.d.ts +66 -1
  33. package/dist/src/services/AstParserService.js +432 -28
  34. package/dist/src/services/DatabaseService.d.ts +0 -12
  35. package/dist/src/services/DatabaseService.js +2 -52
  36. package/dist/src/services/IndexerService.d.ts +8 -0
  37. package/dist/src/services/IndexerService.js +24 -11
  38. package/dist/src/services/McpServer.d.ts +7 -0
  39. package/dist/src/services/McpServer.js +42 -38
  40. package/dist/src/services/OpenAICompatibleEmbedder.js +2 -1
  41. package/dist/src/services/SearcherService.d.ts +189 -25
  42. package/dist/src/services/SearcherService.js +505 -203
  43. package/dist/src/symbol-rows.d.ts +14 -9
  44. package/dist/src/symbol-rows.js +17 -11
  45. package/dist/src/tools/FindSymbolTool.js +14 -5
  46. package/dist/src/tools/GetCallersTool.d.ts +3 -1
  47. package/dist/src/tools/GetCallersTool.js +16 -6
  48. package/dist/src/tools/GetDependenciesTool.d.ts +3 -1
  49. package/dist/src/tools/GetDependenciesTool.js +15 -8
  50. package/dist/src/tools/GetDependentsTool.d.ts +3 -1
  51. package/dist/src/tools/GetDependentsTool.js +31 -16
  52. package/dist/src/tools/ListFilesTool.d.ts +3 -1
  53. package/dist/src/tools/ListFilesTool.js +14 -12
  54. package/dist/src/tools/ListProjectsTool.js +5 -1
  55. package/dist/src/tools/ReadFileRangeTool.d.ts +3 -1
  56. package/dist/src/tools/ReadFileRangeTool.js +12 -11
  57. package/dist/src/tools/ReadFileTool.d.ts +3 -1
  58. package/dist/src/tools/ReadFileTool.js +12 -10
  59. package/dist/src/tools/SearchCodebaseTool.js +3 -1
  60. package/dist/src/tools/format-helpers.d.ts +16 -1
  61. package/dist/src/tools/format-helpers.js +64 -0
  62. 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
- constructor(db: IDatabase, embedder: IEmbedder, discovery: IProjectDiscovery, config: Config, logger?: ILogger);
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, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
135
- getDependents(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
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
- * 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.
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
- outdatedNotice(project: string | null): Promise<string | null>;
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(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>;
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 (distinguishable
175
- * from a clean "no matches", which is `ok: true, output: null`).
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
  }