codesearch-mcp 0.0.11-alpha → 0.0.13-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/dist/app.js CHANGED
@@ -47,7 +47,7 @@ async function main() {
47
47
  const isIndex = argv1 === 'rag-index' || process.argv[2] === 'index';
48
48
  const isMcp = argv1 === 'rag-mcp' || process.argv[2] === 'mcp';
49
49
  const isSetup = argv1 === 'rag-setup' || process.argv[2] === 'setup';
50
- const isBackfill = argv1 === 'rag-index' && process.argv[2] === 'backfill';
50
+ const isBackfill = (argv1 === 'rag-index' || process.argv[2] === 'backfill') && process.argv[2] === 'backfill';
51
51
  const mode = isBackfill ? 'backfill' : isIndex ? 'index' : isMcp ? 'mcp' : isSetup ? 'setup' : (process.argv[2] ?? '');
52
52
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
53
53
  (0, help_1.printHelp)(argv1 !== 'app.js' ? argv1 : mode);
@@ -10,7 +10,7 @@ export interface SymbolEntry {
10
10
  project: string;
11
11
  filePath: string;
12
12
  name: string;
13
- kind: 'function' | 'class' | 'interface' | 'type' | 'variable';
13
+ kind: 'function' | 'class' | 'interface' | 'type' | 'variable' | 'method';
14
14
  signature: string | null;
15
15
  line: number | null;
16
16
  col: number | null;
@@ -116,7 +116,8 @@ class AstParserService {
116
116
  const resolved = decl.getModuleSpecifierSourceFile();
117
117
  if (!resolved) {
118
118
  const specifier = decl.getModuleSpecifierValue();
119
- if (specifier.startsWith('.')) {
119
+ const isCodeImport = !/\.(json|css|scss|less|svg|png|jpg|gif|ico|woff2?|ttf|eot)(\?.*)?$/.test(specifier);
120
+ if (specifier.startsWith('.') && isCodeImport) {
120
121
  this.logger.debug('AstParser: unresolved import', { specifier, file: filePath });
121
122
  }
122
123
  continue;
@@ -80,7 +80,7 @@ class IndexerService {
80
80
  }
81
81
  try {
82
82
  const { symbols: rawSymbols } = await this.ast.analyzeFile(absPath, serviceName, projectRoot);
83
- const symbols = rawSymbols.map(s => ({ ...s, filePath }));
83
+ const symbols = [...new Map(rawSymbols.map(s => ({ ...s, filePath })).map(s => [`${s.name}:${s.kind}`, s])).values()];
84
84
  await this.db.transaction(async (manager) => {
85
85
  await manager.delete(db_1.CodeSymbol, { project: serviceName, filePath });
86
86
  if (symbols.length > 0) {
@@ -223,7 +223,7 @@ class IndexerService {
223
223
  toFile: path.relative(this.config.workspaceRoot, e.toFile),
224
224
  }));
225
225
  // normalize symbols: replace absolute filePath with workspace-relative
226
- const symbols = rawSymbols.map(s => ({ ...s, filePath: relFilePath }));
226
+ const symbols = [...new Map(rawSymbols.map(s => ({ ...s, filePath: relFilePath })).map(s => [`${s.name}:${s.kind}`, s])).values()];
227
227
  const depsMs = Date.now() - t0;
228
228
  const code = fs.readFileSync(filePath, 'utf-8');
229
229
  const chunks = await this.getSplitter(filePath).createDocuments([code]);
@@ -58,7 +58,21 @@ class LoggerService {
58
58
  if (filePath)
59
59
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
60
60
  const transport = filePath
61
- ? pino_1.default.transport({ targets: [{ target: 'pino/file', options: { destination: filePath }, level }] })
61
+ ? pino_1.default.transport({
62
+ targets: [{
63
+ target: 'pino-roll',
64
+ options: {
65
+ file: filePath,
66
+ frequency: 'daily',
67
+ dateFormat: 'yyyy-MM-dd',
68
+ mkdir: true,
69
+ symlink: true,
70
+ limit: { count: 30 },
71
+ size: '50m',
72
+ },
73
+ level,
74
+ }],
75
+ })
62
76
  : undefined;
63
77
  const logger = transport
64
78
  ? (0, pino_1.default)({ level }, transport)
@@ -58,7 +58,7 @@ You have access to a local semantic code search server. Use these tools to navig
58
58
  - **read_file_range** — read a specific line range from any indexed file. Use this to expand context around a search result.
59
59
  - **get_dependencies** — show what files a given file imports (direct or transitive). Use to understand what a file depends on.
60
60
  - **get_dependents** — show which files import a given file (reverse graph). Use before refactoring to find all callers.
61
- - **find_symbol** — find where a symbol (function, class, type, interface, variable) is defined by exact name. Faster than semantic search when you know the name.
61
+ - **find_symbol** — find where a symbol (function, class, type, interface, variable, method) is defined by exact name. Faster than semantic search when you know the name.
62
62
  - **list_files** — browse the directory tree of a project path. Use to understand project structure before reading files.
63
63
  - **read_file** — read a complete file by absolute path with line numbers. Truncates at max_lines with a hint to continue.
64
64
 
@@ -68,9 +68,10 @@ You have access to a local semantic code search server. Use these tools to navig
68
68
  2. Use \`search_codebase\` to find relevant code by meaning.
69
69
  3. If a result looks **truncated or incomplete** (e.g. you see only the middle of a function), call \`read_file_range\` with the returned \`file_path\` and \`start_line\` set to the **Line** number shown in the result. Example: if the result shows **Line:** 150, pass \`start_line: 150\`.
70
70
  4. For exploratory searches where you expect results to span chunk boundaries, pass \`include_neighbors: true\` to automatically include the chunk before and after each match.
71
- 5. Use \`find_symbol\` when you know the exact name of a function or class and want its definition location immediately — faster than semantic search.
71
+ 5. Use \`find_symbol\` when you know the exact name of a function, class, or method and want its definition location immediately — faster than semantic search. Use \`kind: "method"\` to find public class methods like \`canActivate\`, \`execute\`, \`handle\`.
72
72
  6. Use \`list_files\` to explore project structure (src/, components/, etc.) before diving into files.
73
73
  7. Use \`read_file\` to read a complete file when you have its absolute path from \`search_codebase\` results.
74
+ 8. If \`search_codebase\` returns results that seem off-topic, pass a lower \`threshold\` (e.g. \`0.35\`) to filter out weak semantic matches.
74
75
 
75
76
  ## When to use read_file_range vs include_neighbors
76
77
 
@@ -83,7 +84,7 @@ You have access to a local semantic code search server. Use these tools to navig
83
84
 
84
85
  ## Relevance scores
85
86
 
86
- Lower score = better match (cosine distance). Results above the configured threshold are filtered out automatically.
87
+ Lower score = better match (cosine distance). Results above the configured threshold are filtered out automatically. Pass \`threshold\` to \`search_codebase\` to override the default per-query (e.g. \`0.35\` for strict matching, \`0.55\` for broader results).
87
88
 
88
89
  ## Dependency graph
89
90
 
@@ -112,6 +113,7 @@ class McpServer {
112
113
  limit: { type: 'number', description: `Max results (default ${this.config.search.defaultLimit}, max ${this.config.search.maxLimit})` },
113
114
  project: { type: 'string', description: 'Filter by project name (optional). Use list_projects to see available names.' },
114
115
  include_neighbors: { type: 'boolean', description: 'Also include the chunk before and after each result from the same file (Small-to-Big retrieval). Useful when results look truncated.' },
116
+ threshold: { type: 'number', description: `Max cosine distance (0–1). Lower = stricter. Default: ${this.config.search.similarityThreshold}. Use a lower value (e.g. 0.35) to filter out weak matches.` },
115
117
  },
116
118
  required: ['query'],
117
119
  },
@@ -164,13 +166,13 @@ class McpServer {
164
166
  },
165
167
  {
166
168
  name: 'find_symbol',
167
- description: 'Find TypeScript/JavaScript symbol definitions (functions, classes, types, interfaces, variables) by exact name. Use when you know the symbol name and want its definition location fast.',
169
+ description: 'Find TypeScript/JavaScript symbol definitions (functions, classes, types, interfaces, variables, methods) by exact name. Use when you know the symbol name and want its definition location fast.',
168
170
  inputSchema: {
169
171
  type: 'object',
170
172
  properties: {
171
173
  name: { type: 'string', description: 'Exact symbol name (case-sensitive, whole-word match)' },
172
174
  project: { type: 'string', description: 'Limit search to this project (optional). Use list_projects to find names.' },
173
- kind: { type: 'string', enum: ['function', 'class', 'type', 'interface', 'variable', 'all'], description: 'Symbol kind filter. Default: all.' },
175
+ kind: { type: 'string', enum: ['function', 'class', 'type', 'interface', 'variable', 'method', 'all'], description: 'Symbol kind filter. Default: all.' },
174
176
  },
175
177
  required: ['name'],
176
178
  },
@@ -232,8 +234,13 @@ class McpServer {
232
234
  const limit = Math.min(Math.max(1, Number(req.params.arguments?.limit) || this.config.search.defaultLimit), this.config.search.maxLimit);
233
235
  const project = req.params.arguments?.project ? String(req.params.arguments.project) : null;
234
236
  const includeNeighbors = Boolean(req.params.arguments?.include_neighbors);
237
+ const rawThreshold = req.params.arguments?.threshold;
238
+ const parsedThreshold = Number(rawThreshold);
239
+ const threshold = rawThreshold != null && !isNaN(parsedThreshold)
240
+ ? Math.min(1, Math.max(0, parsedThreshold))
241
+ : undefined;
235
242
  const { query, translated } = await this.searcher.prepareQuery(raw);
236
- const results = await this.searcher.searchChunks(query, limit, project);
243
+ const results = await this.searcher.searchChunks(query, limit, project, threshold);
237
244
  if (results.length === 0) {
238
245
  return { content: [{ type: 'text', text: 'No relevant results found.' }] };
239
246
  }
@@ -318,7 +325,7 @@ class McpServer {
318
325
  const name = String(req.params.arguments?.name ?? '');
319
326
  const project = req.params.arguments?.project ? String(req.params.arguments.project) : null;
320
327
  const rawKind = req.params.arguments?.kind;
321
- const validKinds = ['function', 'class', 'type', 'interface', 'variable', 'all'];
328
+ const validKinds = ['function', 'class', 'type', 'interface', 'variable', 'method', 'all'];
322
329
  const kind = validKinds.includes(rawKind) ? rawKind : 'all';
323
330
  const { hits, truncated } = await this.searcher.findSymbol(name, project, kind);
324
331
  if (hits.length === 0) {
@@ -45,7 +45,7 @@ export interface GrepMatch {
45
45
  filePath: string;
46
46
  line: number;
47
47
  }
48
- export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'all';
48
+ export type SymbolKind = 'function' | 'class' | 'type' | 'interface' | 'variable' | 'method' | 'all';
49
49
  export interface SymbolHit {
50
50
  filePath: string;
51
51
  line: number;
@@ -64,7 +64,7 @@ export declare class SearcherService {
64
64
  translated: boolean;
65
65
  }>;
66
66
  listProjects(): Promise<ProjectRow[]>;
67
- searchChunks(query: string, limit: number, project: string | null): Promise<SearchResult[]>;
67
+ searchChunks(query: string, limit: number, project: string | null, threshold?: number): Promise<SearchResult[]>;
68
68
  fetchNeighbors(results: SearchResult[]): Promise<NeighborResult[]>;
69
69
  getDependencies(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
70
70
  getDependents(filePath: string, project: string, symbol?: string | null, transitive?: boolean): Promise<DependencyRow[]>;
@@ -59,8 +59,11 @@ class SearcherService {
59
59
  }
60
60
  async prepareQuery(raw) {
61
61
  if (this.config.search.autoTranslate && this.isNonEnglish(raw) && this.embedder.translate) {
62
- return { query: await this.embedder.translate(raw, this.config.search.translateModel), translated: true };
62
+ const query = await this.embedder.translate(raw, this.config.search.translateModel);
63
+ this.logger.debug('SearcherService: prepareQuery', { translated: true, raw: raw.slice(0, 120), query: query.slice(0, 120) });
64
+ return { query, translated: true };
63
65
  }
66
+ this.logger.debug('SearcherService: prepareQuery', { translated: false, query: raw.slice(0, 120) });
64
67
  return { query: raw, translated: false };
65
68
  }
66
69
  async listProjects() {
@@ -73,25 +76,36 @@ class SearcherService {
73
76
  return { ...r, ...(typeof meta === 'object' ? meta : EMPTY_META) };
74
77
  });
75
78
  }
76
- async searchChunks(query, limit, project) {
79
+ async searchChunks(query, limit, project, threshold) {
80
+ const effectiveThreshold = threshold ?? this.config.search.similarityThreshold;
81
+ this.logger.debug('SearcherService: searchChunks', {
82
+ query: query.slice(0, 120), limit, project, threshold: effectiveThreshold,
83
+ });
77
84
  const embedding = await this.embedder.embed(query);
78
- const params = [`[${embedding}]`, this.config.search.similarityThreshold, limit];
85
+ const params = [`[${embedding}]`, effectiveThreshold, limit];
79
86
  let projectFilter = '';
80
87
  if (project) {
81
88
  params.push(project);
82
89
  projectFilter = `AND "serviceName" = $${params.length}`;
83
90
  }
84
- return this.db.query(`SELECT "serviceName", "filePath", "startLine", content,
91
+ const results = await this.db.query(`SELECT "serviceName", "filePath", "startLine", content,
85
92
  ROUND((embedding <=> $1)::numeric, 4)::float8 AS score
86
93
  FROM code_chunk
87
94
  WHERE embedding <=> $1 < $2
88
95
  ${projectFilter}
89
96
  ORDER BY embedding <=> $1
90
97
  LIMIT $3`, params);
98
+ this.logger.debug('SearcherService: searchChunks results', {
99
+ count: results.length,
100
+ scores: results.map(r => r.score),
101
+ files: results.map(r => r.filePath),
102
+ });
103
+ return results;
91
104
  }
92
105
  async fetchNeighbors(results) {
93
106
  if (results.length === 0)
94
107
  return [];
108
+ this.logger.debug('SearcherService: fetchNeighbors', { count: results.length });
95
109
  const values = results.map((_, i) => `(${i}, $${i * 2 + 1}, $${i * 2 + 2}::int)`).join(',');
96
110
  const params = results.flatMap(r => [r.filePath, r.startLine]);
97
111
  const rows = await this.db.query(`SELECT r.idx,
@@ -109,13 +123,19 @@ class SearcherService {
109
123
  ORDER BY "startLine" ASC LIMIT 1
110
124
  ) next ON true
111
125
  ORDER BY r.idx`, params);
112
- return rows.map((row, i) => ({
126
+ const neighbors = rows.map((row, i) => ({
113
127
  result: results[i],
114
128
  prevChunk: row.prev_fp ? { serviceName: row.prev_sn, filePath: row.prev_fp, startLine: row.prev_sl, content: row.prev_c } : null,
115
129
  nextChunk: row.next_fp ? { serviceName: row.next_sn, filePath: row.next_fp, startLine: row.next_sl, content: row.next_c } : null,
116
130
  }));
131
+ this.logger.debug('SearcherService: fetchNeighbors results', {
132
+ withPrev: neighbors.filter(n => n.prevChunk).length,
133
+ withNext: neighbors.filter(n => n.nextChunk).length,
134
+ });
135
+ return neighbors;
117
136
  }
118
137
  async getDependencies(filePath, project, symbol = null, transitive = false) {
138
+ this.logger.debug('SearcherService: getDependencies', { filePath, project, symbol, transitive });
119
139
  const params = [filePath, project];
120
140
  let symbolFilter = '';
121
141
  if (symbol) {
@@ -127,8 +147,10 @@ class SearcherService {
127
147
  WHERE "fromFile" = $1 AND project = $2
128
148
  ${symbolFilter}
129
149
  ORDER BY "toFile"`, params);
130
- if (!transitive)
150
+ if (!transitive) {
151
+ this.logger.debug('SearcherService: getDependencies result', { direct: direct.length });
131
152
  return direct;
153
+ }
132
154
  const indirect = await this.db.query(`WITH RECURSIVE deps AS (
133
155
  SELECT "toFile", 1 AS depth, "toFile" AS via
134
156
  FROM code_dependency
@@ -151,9 +173,11 @@ class SearcherService {
151
173
  WHERE depth > 1
152
174
  GROUP BY "toFile"
153
175
  ORDER BY MIN(depth), "toFile"`, [filePath, project]);
176
+ this.logger.debug('SearcherService: getDependencies result', { direct: direct.length, indirect: indirect.length });
154
177
  return [...direct, ...indirect];
155
178
  }
156
179
  async getDependents(filePath, project, symbol = null, transitive = false) {
180
+ this.logger.debug('SearcherService: getDependents', { filePath, project, symbol, transitive });
157
181
  const params = [filePath, project];
158
182
  let symbolFilter = '';
159
183
  if (symbol) {
@@ -165,8 +189,10 @@ class SearcherService {
165
189
  WHERE "toFile" = $1 AND project = $2
166
190
  ${symbolFilter}
167
191
  ORDER BY "fromFile"`, params);
168
- if (!transitive)
192
+ if (!transitive) {
193
+ this.logger.debug('SearcherService: getDependents result', { direct: direct.length });
169
194
  return direct;
195
+ }
170
196
  const indirect = await this.db.query(`WITH RECURSIVE rdeps AS (
171
197
  SELECT "fromFile", 1 AS depth, "fromFile" AS via
172
198
  FROM code_dependency
@@ -189,9 +215,11 @@ class SearcherService {
189
215
  WHERE depth > 1
190
216
  GROUP BY "fromFile"
191
217
  ORDER BY MIN(depth), "fromFile"`, [filePath, project]);
218
+ this.logger.debug('SearcherService: getDependents result', { direct: direct.length, indirect: indirect.length });
192
219
  return [...direct, ...indirect];
193
220
  }
194
221
  grepSymbol(symbol, project, excludeFile) {
222
+ this.logger.debug('SearcherService: grepSymbol', { symbol, project, excludeFile });
195
223
  const projectDir = path.join(this.config.workspaceRoot, project);
196
224
  const result = (0, child_process_1.spawnSync)('grep', [
197
225
  '-rn', '-F', '-w',
@@ -216,16 +244,21 @@ class SearcherService {
216
244
  return [];
217
245
  return [{ filePath: path.relative(this.config.workspaceRoot, absFile), line: parseInt(lineNum, 10) }];
218
246
  });
219
- return { matches: all.slice(0, GREP_MAX_RESULTS), truncated: all.length > GREP_MAX_RESULTS };
247
+ const truncated = all.length > GREP_MAX_RESULTS;
248
+ this.logger.debug('SearcherService: grepSymbol result', { matches: Math.min(all.length, GREP_MAX_RESULTS), truncated });
249
+ return { matches: all.slice(0, GREP_MAX_RESULTS), truncated };
220
250
  }
221
251
  // Sync wrapper: validates name (throws synchronously), then returns Promise.
222
- // Falls back to grep when the symbol index (code_symbol table) is unavailable.
252
+ // Methods are never indexed — always resolved via grep. Other kinds use index with grep fallback.
223
253
  findSymbol(name, project, kind) {
224
254
  if (!/^[\w$]+$/.test(name))
225
255
  throw new Error('Invalid symbol name');
256
+ if (kind === 'method')
257
+ return Promise.resolve(this.grepFindSymbol(name, project, kind));
226
258
  return this.findSymbolFromIndex(name, project, kind);
227
259
  }
228
260
  async findSymbolFromIndex(name, project, kind) {
261
+ this.logger.debug('SearcherService: findSymbol', { name, project, kind, source: 'index' });
229
262
  try {
230
263
  const params = [name];
231
264
  let projectFilter = '';
@@ -252,6 +285,7 @@ class SearcherService {
252
285
  kind: row.kind,
253
286
  signature: row.signature ?? row.name,
254
287
  }));
288
+ this.logger.debug('SearcherService: findSymbol result', { source: 'index', hits: hits.length, truncated });
255
289
  return { hits, truncated };
256
290
  }
257
291
  catch {
@@ -260,6 +294,7 @@ class SearcherService {
260
294
  }
261
295
  }
262
296
  grepFindSymbol(name, project, kind) {
297
+ this.logger.debug('SearcherService: findSymbol', { name, project, kind, source: 'grep' });
263
298
  const searchDir = project
264
299
  ? path.join(this.config.workspaceRoot, project)
265
300
  : this.config.workspaceRoot;
@@ -300,9 +335,11 @@ class SearcherService {
300
335
  signature: matchedLine.trim(),
301
336
  });
302
337
  }
338
+ this.logger.debug('SearcherService: findSymbol result', { source: 'grep', hits: hits.length, truncated: hits.length >= FIND_SYMBOL_LIMIT });
303
339
  return { hits, truncated: hits.length >= FIND_SYMBOL_LIMIT };
304
340
  }
305
341
  listFiles(relativePath, project, depth, pattern) {
342
+ this.logger.debug('SearcherService: listFiles', { relativePath, project, depth, pattern });
306
343
  const projectRoot = path.join(this.config.workspaceRoot, project);
307
344
  const resolvedTarget = path.resolve(projectRoot, relativePath);
308
345
  const resolvedProject = path.resolve(projectRoot);
@@ -318,12 +355,14 @@ class SearcherService {
318
355
  const clampedDepth = Math.min(5, Math.max(1, depth));
319
356
  const lines = [];
320
357
  this.walkDir(resolvedTarget, 0, clampedDepth - 1, pattern, lines);
358
+ this.logger.debug('SearcherService: listFiles result', { entries: lines.length });
321
359
  if (lines.length === 0) {
322
360
  return { tree: pattern ? `No files matching "${pattern}" found.` : '(empty directory)' };
323
361
  }
324
362
  return { tree: lines.join('\n') };
325
363
  }
326
364
  async readFileAbsolute(absPath, maxLines) {
365
+ this.logger.debug('SearcherService: readFileAbsolute', { absPath, maxLines });
327
366
  const resolvedRoot = path.resolve(this.config.workspaceRoot);
328
367
  const resolved = path.resolve(absPath);
329
368
  if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + path.sep)) {
@@ -356,9 +395,11 @@ class SearcherService {
356
395
  throw err;
357
396
  }
358
397
  const relPath = path.relative(resolvedRoot, resolved);
398
+ this.logger.debug('SearcherService: readFileAbsolute result', { linesRead: lines.length, hasMore });
359
399
  return { ok: true, filePath: relPath, startLine: 1, endLine: lines.length, lines, hasMore };
360
400
  }
361
401
  async readFileRange(filePath, startLine, endLine) {
402
+ this.logger.debug('SearcherService: readFileRange', { filePath, startLine, endLine });
362
403
  const resolvedRoot = path.resolve(this.config.workspaceRoot);
363
404
  const absPath = path.resolve(resolvedRoot, filePath);
364
405
  if (absPath !== resolvedRoot && !absPath.startsWith(resolvedRoot + path.sep)) {
@@ -388,6 +429,7 @@ class SearcherService {
388
429
  break;
389
430
  }
390
431
  }
432
+ this.logger.debug('SearcherService: readFileRange result', { linesRead: lines.length, hasMore });
391
433
  return { ok: true, filePath, startLine, endLine: Math.min(endLine, lastRead), lines, hasMore };
392
434
  }
393
435
  isNonEnglish(text) {
@@ -432,6 +474,7 @@ class SearcherService {
432
474
  type: `(export[[:space:]]+)?type[[:space:]]+${name}[[:space:]=<]`,
433
475
  interface: `(export[[:space:]]+)?interface[[:space:]]+${name}[[:space:]{<]`,
434
476
  variable: `(export[[:space:]]+)?(const|let|var)[[:space:]]+${name}[[:space:]=:]`,
477
+ method: `(public[[:space:]]+|async[[:space:]]+|static[[:space:]]+|override[[:space:]]+)+(async[[:space:]]+)?${name}[[:space:]]*[(<]`,
435
478
  };
436
479
  if (kind === 'all')
437
480
  return Object.values(patterns).join('|');
@@ -447,6 +490,8 @@ class SearcherService {
447
490
  return 'interface';
448
491
  if (/\btype\s+$/.test(before))
449
492
  return 'type';
493
+ if (/\b(public|async|static|override)\s+$/.test(before))
494
+ return 'method';
450
495
  return 'variable';
451
496
  }
452
497
  }
@@ -1,3 +1,5 @@
1
+ name: codesearch-mcp
2
+
1
3
  services:
2
4
  db:
3
5
  image: pgvector/pgvector:pg17
@@ -8,7 +10,7 @@ services:
8
10
  ports:
9
11
  - "5433:5432"
10
12
  volumes:
11
- - ${HOME}/.local/share/codesearch-mcp/pgdata:/var/lib/postgresql/data
13
+ - pgdata:/var/lib/postgresql/data
12
14
 
13
15
  ollama:
14
16
  image: ollama/ollama
@@ -18,4 +20,5 @@ services:
18
20
  - ollama_data:/root/.ollama
19
21
 
20
22
  volumes:
23
+ pgdata:
21
24
  ollama_data:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codesearch-mcp",
3
- "version": "0.0.11-alpha",
3
+ "version": "0.0.13-alpha",
4
4
  "description": "Local semantic codebase search via MCP — indexes projects into pgvector, exposes search to Claude",
5
5
  "license": "MIT",
6
6
  "main": "dist/app.js",
@@ -21,9 +21,11 @@
21
21
  "start:mcp": "ts-node app.ts mcp",
22
22
  "index:workspace": "ts-node app.ts index",
23
23
  "index:reindex": "ts-node app.ts index --reindex",
24
+ "index:backfill": "ts-node app.ts backfill",
24
25
  "typecheck": "tsc --noEmit",
25
26
  "test": "vitest run",
26
- "test:watch": "vitest"
27
+ "test:watch": "vitest",
28
+ "verify": "tsc --noEmit && vitest run"
27
29
  },
28
30
  "dependencies": {
29
31
  "@inquirer/prompts": "^8.4.1",
@@ -35,6 +37,7 @@
35
37
  "ollama": "^0.5.0",
36
38
  "pg": "^8.13.0",
37
39
  "pino": "^10.3.1",
40
+ "pino-roll": "^4.0.0",
38
41
  "reflect-metadata": "^0.2.2",
39
42
  "ts-morph": "^28.0.0",
40
43
  "typeorm": "^0.3.20",