codeseeker 1.4.4 → 1.7.0

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 (113) hide show
  1. package/dist/cli/commands/base-command-handler.js +1 -1
  2. package/dist/cli/commands/base-command-handler.js.map +1 -1
  3. package/dist/cli/commands/handlers/setup-command-handler.d.ts +2 -0
  4. package/dist/cli/commands/handlers/setup-command-handler.d.ts.map +1 -1
  5. package/dist/cli/commands/handlers/setup-command-handler.js +217 -156
  6. package/dist/cli/commands/handlers/setup-command-handler.js.map +1 -1
  7. package/dist/cli/commands/reconcile.js.map +1 -1
  8. package/dist/cli/commands/services/context-aware-clarification-service.js.map +1 -1
  9. package/dist/cli/commands/services/task-decomposition-service.js.map +1 -1
  10. package/dist/cli/commands/services/user-interaction-service.js +1 -1
  11. package/dist/cli/commands/services/user-interaction-service.js.map +1 -1
  12. package/dist/cli/context-optimizer.js +1 -1
  13. package/dist/cli/context-optimizer.js.map +1 -1
  14. package/dist/cli/features/code-graph/graph-builder.js.map +1 -1
  15. package/dist/cli/features/compilation/verifier.js +1 -1
  16. package/dist/cli/features/compilation/verifier.js.map +1 -1
  17. package/dist/cli/features/database/database-documentation-generator.js.map +1 -1
  18. package/dist/cli/features/documentation/map-analyzer.js.map +1 -1
  19. package/dist/cli/features/duplication/detector.js.map +1 -1
  20. package/dist/cli/features/duplication/services/duplication-detection-service.js.map +1 -1
  21. package/dist/cli/features/semantic-graph/semantic-graph-tool.js +1 -1
  22. package/dist/cli/features/semantic-graph/semantic-graph-tool.js.map +1 -1
  23. package/dist/cli/git/git-commit-utils.js +2 -2
  24. package/dist/cli/git/git-commit-utils.js.map +1 -1
  25. package/dist/cli/git/git-integration.js.map +1 -1
  26. package/dist/cli/knowledge/analyzers/semantic-analyzer.js.map +1 -1
  27. package/dist/cli/knowledge/analyzers/services/file-discovery-service.js.map +1 -1
  28. package/dist/cli/knowledge/graph/knowledge-graph.js.map +1 -1
  29. package/dist/cli/knowledge/graph/managers/graph-mutation-manager.js.map +1 -1
  30. package/dist/cli/knowledge/graph/managers/graph-query-manager.js.map +1 -1
  31. package/dist/cli/knowledge/graph/managers/graph-state-manager.js.map +1 -1
  32. package/dist/cli/knowledge/graph/managers/graph-traversal-manager.js.map +1 -1
  33. package/dist/cli/knowledge/graph/services/architectural-insight-detector.js.map +1 -1
  34. package/dist/cli/knowledge/graph/services/graph-analyzer.js.map +1 -1
  35. package/dist/cli/knowledge/graph/services/graph-utility-service.js +1 -1
  36. package/dist/cli/knowledge/graph/services/graph-utility-service.js.map +1 -1
  37. package/dist/cli/knowledge/repository/knowledge-repository.js.map +1 -1
  38. package/dist/cli/managers/interrupt-manager.js +2 -2
  39. package/dist/cli/managers/interrupt-manager.js.map +1 -1
  40. package/dist/cli/quality/services/testing-service.js +1 -1
  41. package/dist/cli/quality/services/testing-service.js.map +1 -1
  42. package/dist/cli/quality-checker.js.map +1 -1
  43. package/dist/cli/services/analysis/coding-patterns-analyzer.js.map +1 -1
  44. package/dist/cli/services/analysis/coding-standards-generator.js.map +1 -1
  45. package/dist/cli/services/analysis/deduplication/code-consolidation-handler.js.map +1 -1
  46. package/dist/cli/services/analysis/deduplication/duplicate-code-detector.js.map +1 -1
  47. package/dist/cli/services/data/code-relationship-parser.js.map +1 -1
  48. package/dist/cli/services/data/embedding/services/file-processor.js.map +1 -1
  49. package/dist/cli/services/data/semantic-graph/builders/relationship-builder.js.map +1 -1
  50. package/dist/cli/services/data/semantic-graph/language-detector.js.map +1 -1
  51. package/dist/cli/services/data/semantic-graph/parsers/python-parser.js +1 -1
  52. package/dist/cli/services/data/semantic-graph/parsers/python-parser.js.map +1 -1
  53. package/dist/cli/services/data/semantic-graph/parsers/tree-sitter-python-parser.js +1 -1
  54. package/dist/cli/services/data/semantic-graph/parsers/tree-sitter-python-parser.js.map +1 -1
  55. package/dist/cli/services/data/semantic-graph/semantic-graph.js.map +1 -1
  56. package/dist/cli/services/integration/codeseeker-instruction-service.js.map +1 -1
  57. package/dist/cli/services/monitoring/file-watcher-service.js.map +1 -1
  58. package/dist/cli/services/project-identity-service.js.map +1 -1
  59. package/dist/cli/workflow/workflow-orchestrator.js.map +1 -1
  60. package/dist/config/database-config.js +1 -1
  61. package/dist/config/database-config.js.map +1 -1
  62. package/dist/database/adapters/postgresql.js +1 -1
  63. package/dist/database/adapters/postgresql.js.map +1 -1
  64. package/dist/database/database.js.map +1 -1
  65. package/dist/integrations/claude/claude-cli-integration.js.map +1 -1
  66. package/dist/integrations/claude/conversation-manager.js +1 -1
  67. package/dist/integrations/claude/conversation-manager.js.map +1 -1
  68. package/dist/mcp/indexing-service.d.ts +23 -0
  69. package/dist/mcp/indexing-service.d.ts.map +1 -1
  70. package/dist/mcp/indexing-service.js +227 -19
  71. package/dist/mcp/indexing-service.js.map +1 -1
  72. package/dist/mcp/mcp-server.d.ts +57 -10
  73. package/dist/mcp/mcp-server.d.ts.map +1 -1
  74. package/dist/mcp/mcp-server.js +505 -187
  75. package/dist/mcp/mcp-server.js.map +1 -1
  76. package/dist/mcp/query-cache-service.d.ts +83 -0
  77. package/dist/mcp/query-cache-service.d.ts.map +1 -0
  78. package/dist/mcp/query-cache-service.js +227 -0
  79. package/dist/mcp/query-cache-service.js.map +1 -0
  80. package/dist/orchestrator/sequential-workflow-orchestrator.js.map +1 -1
  81. package/dist/orchestrator/tool-database-api.js.map +1 -1
  82. package/dist/orchestrator/workflow-visualizer.js.map +1 -1
  83. package/dist/shared/analysis-repository-consolidated.js +2 -2
  84. package/dist/shared/analysis-repository-consolidated.js.map +1 -1
  85. package/dist/shared/analysis-repository.js +1 -1
  86. package/dist/shared/analysis-repository.js.map +1 -1
  87. package/dist/shared/change-assessment-system.js.map +1 -1
  88. package/dist/shared/codebase-analyzer.js.map +1 -1
  89. package/dist/shared/documentation-rag-service.js.map +1 -1
  90. package/dist/shared/intelligent-cycle-features.js.map +1 -1
  91. package/dist/shared/managers/cache-manager.js +1 -1
  92. package/dist/shared/managers/cache-manager.js.map +1 -1
  93. package/dist/shared/managers/git-branch-manager.js.map +1 -1
  94. package/dist/shared/memory-system/memory-system.js.map +1 -1
  95. package/dist/shared/offline-first-cache.js.map +1 -1
  96. package/dist/shared/tool-interface.js +1 -1
  97. package/dist/shared/tool-interface.js.map +1 -1
  98. package/dist/shared/validation-cycle/validation-cycle.js.map +1 -1
  99. package/dist/storage/embedded/graphology-graph-store.d.ts +1 -0
  100. package/dist/storage/embedded/graphology-graph-store.d.ts.map +1 -1
  101. package/dist/storage/embedded/graphology-graph-store.js +38 -0
  102. package/dist/storage/embedded/graphology-graph-store.js.map +1 -1
  103. package/dist/storage/embedded/minisearch-text-store.js.map +1 -1
  104. package/dist/storage/interfaces.d.ts +2 -0
  105. package/dist/storage/interfaces.d.ts.map +1 -1
  106. package/dist/storage/server/neo4j-graph-store.d.ts +1 -0
  107. package/dist/storage/server/neo4j-graph-store.d.ts.map +1 -1
  108. package/dist/storage/server/neo4j-graph-store.js +23 -1
  109. package/dist/storage/server/neo4j-graph-store.js.map +1 -1
  110. package/dist/storage/storage-provider.js.map +1 -1
  111. package/dist/utils/config.js +1 -1
  112. package/dist/utils/config.js.map +1 -1
  113. package/package.json +1 -1
@@ -66,6 +66,7 @@ const semantic_search_orchestrator_1 = require("../cli/commands/services/semanti
66
66
  const indexing_service_1 = require("./indexing-service");
67
67
  const coding_standards_generator_1 = require("../cli/services/analysis/coding-standards-generator");
68
68
  const language_support_service_1 = require("../cli/services/project/language-support-service");
69
+ const query_cache_service_1 = require("./query-cache-service");
69
70
  // Version from package.json
70
71
  const VERSION = '2.0.0';
71
72
  /**
@@ -76,6 +77,25 @@ class CodeSeekerMcpServer {
76
77
  searchOrchestrator;
77
78
  indexingService;
78
79
  languageSupportService;
80
+ queryCache;
81
+ // Background indexing state
82
+ indexingJobs = new Map();
83
+ // Mutex for concurrent indexing protection
84
+ indexingMutex = new Set();
85
+ // Cancellation tokens for running indexing jobs
86
+ cancellationTokens = new Map();
87
+ // Job cleanup interval (clean completed/failed jobs after 1 hour)
88
+ JOB_TTL_MS = 60 * 60 * 1000; // 1 hour
89
+ cleanupTimer = null;
90
+ // Dangerous paths that should never be indexed (security)
91
+ DANGEROUS_PATHS = [
92
+ // System directories
93
+ '/etc', '/var', '/usr', '/bin', '/sbin', '/lib', '/boot', '/root', '/proc', '/sys', '/dev',
94
+ // Windows system directories
95
+ 'C:\\Windows', 'C:\\Program Files', 'C:\\Program Files (x86)', 'C:\\ProgramData',
96
+ // User sensitive directories
97
+ '.ssh', '.gnupg', '.aws', '.azure', '.config',
98
+ ];
79
99
  constructor() {
80
100
  this.server = new mcp_js_1.McpServer({
81
101
  name: 'codeseeker',
@@ -84,7 +104,191 @@ class CodeSeekerMcpServer {
84
104
  this.searchOrchestrator = new semantic_search_orchestrator_1.SemanticSearchOrchestrator();
85
105
  this.indexingService = new indexing_service_1.IndexingService();
86
106
  this.languageSupportService = new language_support_service_1.LanguageSupportService();
107
+ this.queryCache = (0, query_cache_service_1.getQueryCacheService)();
87
108
  this.registerTools();
109
+ // Start cleanup timer for old indexing jobs
110
+ this.startJobCleanupTimer();
111
+ }
112
+ /**
113
+ * Start periodic cleanup of completed/failed indexing jobs
114
+ */
115
+ startJobCleanupTimer() {
116
+ // Clean up every 10 minutes
117
+ this.cleanupTimer = setInterval(() => {
118
+ this.cleanupOldJobs();
119
+ }, 10 * 60 * 1000);
120
+ // Ensure timer doesn't prevent process exit
121
+ if (this.cleanupTimer.unref) {
122
+ this.cleanupTimer.unref();
123
+ }
124
+ }
125
+ /**
126
+ * Clean up completed/failed jobs older than TTL
127
+ */
128
+ cleanupOldJobs() {
129
+ const now = Date.now();
130
+ const jobsToDelete = [];
131
+ for (const [projectId, job] of this.indexingJobs) {
132
+ // Only clean up non-running jobs
133
+ if (job.status !== 'running' && job.completedAt) {
134
+ const age = now - job.completedAt.getTime();
135
+ if (age > this.JOB_TTL_MS) {
136
+ jobsToDelete.push(projectId);
137
+ }
138
+ }
139
+ }
140
+ for (const projectId of jobsToDelete) {
141
+ this.indexingJobs.delete(projectId);
142
+ }
143
+ }
144
+ /**
145
+ * Validate that a path is safe to index (security)
146
+ * Returns error message if unsafe, null if safe
147
+ */
148
+ validateProjectPath(projectPath) {
149
+ const normalizedPath = path.normalize(projectPath);
150
+ // Check for path traversal attempts
151
+ if (normalizedPath.includes('..')) {
152
+ return 'Path traversal detected: paths with ".." are not allowed';
153
+ }
154
+ // Check for dangerous system directories
155
+ const lowerPath = normalizedPath.toLowerCase();
156
+ for (const dangerous of this.DANGEROUS_PATHS) {
157
+ const lowerDangerous = dangerous.toLowerCase();
158
+ if (lowerPath === lowerDangerous || lowerPath.startsWith(lowerDangerous + path.sep)) {
159
+ return `Security: cannot index system directory "${dangerous}"`;
160
+ }
161
+ }
162
+ // Check path components for sensitive directories
163
+ const pathParts = normalizedPath.split(path.sep);
164
+ for (const part of pathParts) {
165
+ const lowerPart = part.toLowerCase();
166
+ if (lowerPart === '.ssh' || lowerPart === '.gnupg' || lowerPart === '.aws') {
167
+ return `Security: cannot index sensitive directory "${part}"`;
168
+ }
169
+ }
170
+ return null; // Path is safe
171
+ }
172
+ /**
173
+ * Start background indexing for a project
174
+ * Returns immediately, indexing happens asynchronously
175
+ */
176
+ startBackgroundIndexing(projectId, projectName, projectPath, clearExisting = true) {
177
+ // Create cancellation token
178
+ const cancellationToken = { cancelled: false };
179
+ this.cancellationTokens.set(projectId, cancellationToken);
180
+ // Create job entry
181
+ const job = {
182
+ projectId,
183
+ projectName,
184
+ projectPath,
185
+ status: 'running',
186
+ startedAt: new Date(),
187
+ progress: {
188
+ phase: 'starting',
189
+ filesProcessed: 0,
190
+ filesTotal: 0,
191
+ chunksCreated: 0,
192
+ },
193
+ };
194
+ this.indexingJobs.set(projectId, job);
195
+ // Release mutex once job is registered (actual indexing is tracked by job status)
196
+ this.indexingMutex.delete(projectId);
197
+ // Start indexing in background (don't await)
198
+ this.runBackgroundIndexing(job, clearExisting, cancellationToken).catch((error) => {
199
+ job.status = 'failed';
200
+ job.error = error instanceof Error ? error.message : String(error);
201
+ job.completedAt = new Date();
202
+ this.cancellationTokens.delete(projectId);
203
+ });
204
+ }
205
+ /**
206
+ * Cancel a running indexing job
207
+ */
208
+ cancelIndexing(projectId) {
209
+ const token = this.cancellationTokens.get(projectId);
210
+ if (token) {
211
+ token.cancelled = true;
212
+ return true;
213
+ }
214
+ return false;
215
+ }
216
+ /**
217
+ * Run the actual indexing (called asynchronously)
218
+ */
219
+ async runBackgroundIndexing(job, clearExisting, cancellationToken) {
220
+ try {
221
+ const storageManager = await (0, storage_1.getStorageManager)();
222
+ const vectorStore = storageManager.getVectorStore();
223
+ const graphStore = storageManager.getGraphStore();
224
+ // Clear existing data if requested
225
+ if (clearExisting) {
226
+ await vectorStore.deleteByProject(job.projectId);
227
+ await graphStore.deleteByProject(job.projectId);
228
+ }
229
+ // Check for cancellation before starting
230
+ if (cancellationToken.cancelled) {
231
+ job.status = 'failed';
232
+ job.error = 'Indexing cancelled by user';
233
+ job.completedAt = new Date();
234
+ this.cancellationTokens.delete(job.projectId);
235
+ return;
236
+ }
237
+ // Run indexing with progress tracking
238
+ const result = await this.indexingService.indexProject(job.projectPath, job.projectId, (progress) => {
239
+ // Check for cancellation during indexing
240
+ if (cancellationToken.cancelled) {
241
+ throw new Error('Indexing cancelled by user');
242
+ }
243
+ job.progress = {
244
+ phase: progress.phase,
245
+ filesProcessed: progress.filesProcessed,
246
+ filesTotal: progress.filesTotal,
247
+ chunksCreated: progress.chunksCreated,
248
+ };
249
+ });
250
+ // Update job with results
251
+ job.status = 'completed';
252
+ job.completedAt = new Date();
253
+ job.result = {
254
+ filesIndexed: result.filesIndexed,
255
+ chunksCreated: result.chunksCreated,
256
+ nodesCreated: result.nodesCreated,
257
+ edgesCreated: result.edgesCreated,
258
+ durationMs: result.durationMs,
259
+ };
260
+ // Generate coding standards after indexing (if not cancelled)
261
+ if (!cancellationToken.cancelled) {
262
+ try {
263
+ const generator = new coding_standards_generator_1.CodingStandardsGenerator(vectorStore);
264
+ await generator.generateStandards(job.projectId, job.projectPath);
265
+ }
266
+ catch {
267
+ // Non-fatal - standards generation is optional
268
+ }
269
+ }
270
+ // Invalidate query cache for this project (full reindex)
271
+ try {
272
+ await this.queryCache.invalidateProject(job.projectId);
273
+ }
274
+ catch {
275
+ // Non-fatal - cache invalidation is optional
276
+ }
277
+ // Clean up cancellation token
278
+ this.cancellationTokens.delete(job.projectId);
279
+ }
280
+ catch (error) {
281
+ job.status = 'failed';
282
+ job.error = error instanceof Error ? error.message : String(error);
283
+ job.completedAt = new Date();
284
+ this.cancellationTokens.delete(job.projectId);
285
+ }
286
+ }
287
+ /**
288
+ * Get indexing status for a project
289
+ */
290
+ getIndexingStatus(projectId) {
291
+ return this.indexingJobs.get(projectId);
88
292
  }
89
293
  /**
90
294
  * Find CodeSeeker project by walking up directory tree from startPath
@@ -107,28 +311,28 @@ class CodeSeekerMcpServer {
107
311
  * Register all MCP tools
108
312
  */
109
313
  registerTools() {
110
- this.registerSearchCodeTool();
111
- this.registerFindAndReadTool();
112
- this.registerGetFileContextTool();
113
- this.registerGetCodeRelationshipsTool();
114
- this.registerListProjectsTool();
115
- this.registerIndexProjectTool();
116
- this.registerNotifyFileChangesTool();
117
- this.registerInstallLanguageSupportTool();
118
- this.registerManageIndexTool();
314
+ this.registerSearchTool();
315
+ this.registerSearchAndReadTool();
316
+ this.registerReadWithContextTool();
317
+ this.registerShowDependenciesTool();
318
+ this.registerProjectsTool();
319
+ this.registerIndexTool();
320
+ this.registerSyncTool();
321
+ this.registerInstallParsersTool();
322
+ this.registerExcludeTool();
119
323
  }
120
324
  /**
121
325
  * Tool 1: Semantic search across indexed projects
122
326
  */
123
- registerSearchCodeTool() {
124
- this.server.registerTool('search_code', {
327
+ registerSearchTool() {
328
+ this.server.registerTool('search', {
125
329
  description: '**DEFAULT TOOL FOR CODE DISCOVERY** - Use this BEFORE grep/glob for any code search. ' +
126
330
  'This semantic search finds code by meaning, not just text patterns. ' +
127
331
  'ALWAYS use for: "Where is X handled?", "Find the auth logic", "How does Y work?", "What calls Z?" ' +
128
332
  'Only fall back to grep when: you need exact literal strings, regex patterns, or already know the exact file. ' +
129
333
  'Why better than grep: finds "user authentication" even if code says "login", "session", "credentials". ' +
130
- 'Examples: ❌ grep -r "damage.*ship" → ✅ search_code("how ships take damage"). ' +
131
- 'Returns absolute file paths ready for the Read tool. If not indexed, call index_project first.',
334
+ 'Examples: ❌ grep -r "damage.*ship" → ✅ search("how ships take damage"). ' +
335
+ 'Returns absolute file paths ready for the Read tool. If not indexed, call index first.',
132
336
  inputSchema: {
133
337
  query: zod_1.z.string().describe('Natural language query or code snippet (e.g., "validation logic", "error handling")'),
134
338
  project: zod_1.z.string().optional().describe('Project path (optional - auto-detects from indexed projects if omitted)'),
@@ -180,7 +384,7 @@ class CodeSeekerMcpServer {
180
384
  return {
181
385
  content: [{
182
386
  type: 'text',
183
- text: `No indexed projects found. Use index_project to index a project first.`,
387
+ text: `No indexed projects found. Use index to index a project first.`,
184
388
  }],
185
389
  isError: true,
186
390
  };
@@ -200,7 +404,7 @@ class CodeSeekerMcpServer {
200
404
  content: [{
201
405
  type: 'text',
202
406
  text: `⚠️ Project "${path.basename(projectPath)}" found but not indexed.\n\n` +
203
- `ACTION REQUIRED: Call index_project({path: "${projectPath}"}) then retry this search.`,
407
+ `ACTION REQUIRED: Call index({path: "${projectPath}"}) then retry this search.`,
204
408
  }],
205
409
  isError: true,
206
410
  };
@@ -212,14 +416,35 @@ class CodeSeekerMcpServer {
212
416
  content: [{
213
417
  type: 'text',
214
418
  text: `⚠️ Project "${path.basename(projectPath)}" needs indexing.\n\n` +
215
- `ACTION REQUIRED: Call index_project({path: "${projectPath}"}) then retry this search.`,
419
+ `ACTION REQUIRED: Call index({path: "${projectPath}"}) then retry this search.`,
216
420
  }],
217
421
  isError: true,
218
422
  };
219
423
  }
220
424
  }
221
- // Perform search
222
- const results = await this.searchOrchestrator.performSemanticSearch(query, projectPath);
425
+ // Check cache first (only for 'full' mode - exists mode is fast enough)
426
+ let results;
427
+ let fromCache = false;
428
+ const cacheProjectId = projectRecord?.id || this.generateProjectId(projectPath);
429
+ if (mode === 'full') {
430
+ const cached = await this.queryCache.get(query, cacheProjectId, search_type);
431
+ if (cached) {
432
+ results = cached.results;
433
+ fromCache = true;
434
+ }
435
+ else {
436
+ // Perform actual search
437
+ results = await this.searchOrchestrator.performSemanticSearch(query, projectPath);
438
+ // Cache results for future queries
439
+ if (results.length > 0) {
440
+ await this.queryCache.set(query, cacheProjectId, results, search_type);
441
+ }
442
+ }
443
+ }
444
+ else {
445
+ // exists mode - always search fresh (it's fast)
446
+ results = await this.searchOrchestrator.performSemanticSearch(query, projectPath);
447
+ }
223
448
  const limitedResults = results.slice(0, mode === 'exists' ? 5 : limit);
224
449
  if (limitedResults.length === 0) {
225
450
  // For exists mode, return structured response
@@ -294,6 +519,10 @@ class CodeSeekerMcpServer {
294
519
  search_type,
295
520
  results: formattedResults,
296
521
  };
522
+ // Add cache indicator
523
+ if (fromCache) {
524
+ response.cached = true;
525
+ }
297
526
  // Add truncation warning when results were limited
298
527
  if (wasLimited) {
299
528
  response.truncated = true;
@@ -308,11 +537,10 @@ class CodeSeekerMcpServer {
308
537
  };
309
538
  }
310
539
  catch (error) {
311
- const message = error instanceof Error ? error.message : String(error);
312
540
  return {
313
541
  content: [{
314
542
  type: 'text',
315
- text: `Search failed: ${message}`,
543
+ text: this.formatErrorMessage('Search', error instanceof Error ? error : String(error), { projectPath: project }),
316
544
  }],
317
545
  isError: true,
318
546
  };
@@ -322,15 +550,15 @@ class CodeSeekerMcpServer {
322
550
  /**
323
551
  * Tool 2: Find and read - combined search + read in one call
324
552
  */
325
- registerFindAndReadTool() {
326
- this.server.registerTool('find_and_read', {
553
+ registerSearchAndReadTool() {
554
+ this.server.registerTool('search_and_read', {
327
555
  description: '**SEARCH + READ IN ONE STEP** - Use when you need to see actual code, not just file paths. ' +
328
- 'Combines search_code + Read into a single call. Saves a round-trip when you know you\'ll need to read results. ' +
329
- 'Use this instead of search_code when: implementing something similar, understanding HOW code works, ' +
556
+ 'Combines search + Read into a single call. Saves a round-trip when you know you\'ll need to read results. ' +
557
+ 'Use this instead of search when: implementing something similar, understanding HOW code works, ' +
330
558
  'user asks "show me the X code", or you need full context to make changes. ' +
331
- 'Examples: "Show me how damage is calculated" → find_and_read("damage calculation"). ' +
332
- '"I need to add validation like login" → find_and_read("login form validation"). ' +
333
- 'Use search_code instead when: you only need file paths, checking if something exists (mode="exists"), ' +
559
+ 'Examples: "Show me how damage is calculated" → search_and_read("damage calculation"). ' +
560
+ '"I need to add validation like login" → search_and_read("login form validation"). ' +
561
+ 'Use search instead when: you only need file paths, checking if something exists (mode="exists"), ' +
334
562
  'or want to see many results before picking one. Returns full file content with line numbers.',
335
563
  inputSchema: {
336
564
  query: zod_1.z.string().describe('Natural language query or code snippet (e.g., "validation logic", "error handling")'),
@@ -382,7 +610,7 @@ class CodeSeekerMcpServer {
382
610
  return {
383
611
  content: [{
384
612
  type: 'text',
385
- text: `No indexed projects found. Use index_project to index a project first.`,
613
+ text: `No indexed projects found. Use index to index a project first.`,
386
614
  }],
387
615
  isError: true,
388
616
  };
@@ -401,7 +629,7 @@ class CodeSeekerMcpServer {
401
629
  content: [{
402
630
  type: 'text',
403
631
  text: `⚠️ Project "${path.basename(projectPath)}" found but not indexed.\n\n` +
404
- `ACTION REQUIRED: Call index_project({path: "${projectPath}"}) then retry.`,
632
+ `ACTION REQUIRED: Call index({path: "${projectPath}"}) then retry.`,
405
633
  }],
406
634
  isError: true,
407
635
  };
@@ -412,7 +640,7 @@ class CodeSeekerMcpServer {
412
640
  content: [{
413
641
  type: 'text',
414
642
  text: `⚠️ Project "${path.basename(projectPath)}" needs indexing.\n\n` +
415
- `ACTION REQUIRED: Call index_project({path: "${projectPath}"}) then retry.`,
643
+ `ACTION REQUIRED: Call index({path: "${projectPath}"}) then retry.`,
416
644
  }],
417
645
  isError: true,
418
646
  };
@@ -507,11 +735,10 @@ class CodeSeekerMcpServer {
507
735
  };
508
736
  }
509
737
  catch (error) {
510
- const message = error instanceof Error ? error.message : String(error);
511
738
  return {
512
739
  content: [{
513
740
  type: 'text',
514
- text: `Find and read failed: ${message}`,
741
+ text: this.formatErrorMessage('Find and read', error instanceof Error ? error : String(error), { projectPath: project }),
515
742
  }],
516
743
  isError: true,
517
744
  };
@@ -521,13 +748,13 @@ class CodeSeekerMcpServer {
521
748
  /**
522
749
  * Tool 3: Get file with semantic context
523
750
  */
524
- registerGetFileContextTool() {
525
- this.server.registerTool('get_file_context', {
751
+ registerReadWithContextTool() {
752
+ this.server.registerTool('read_with_context', {
526
753
  description: '**READ FILE WITH RELATED CODE** - Enhanced Read that includes semantically similar code. ' +
527
754
  'Use instead of basic Read when: reading a file for the first time, the file references other modules, ' +
528
755
  'or you want to discover patterns used elsewhere in the codebase. ' +
529
- 'Examples: Understanding a component → get_file_context("src/Button.tsx") returns Button + similar patterns. ' +
530
- 'Reading a service → get_file_context("src/api.ts") returns api.ts + related implementations. ' +
756
+ 'Examples: Understanding a component → read_with_context("src/Button.tsx") returns Button + similar patterns. ' +
757
+ 'Reading a service → read_with_context("src/api.ts") returns api.ts + related implementations. ' +
531
758
  'Use basic Read instead when: you just need file contents, already understand the codebase, or making quick edits. ' +
532
759
  'Set include_related=false to get just the file without related chunks.',
533
760
  inputSchema: {
@@ -621,11 +848,10 @@ class CodeSeekerMcpServer {
621
848
  };
622
849
  }
623
850
  catch (error) {
624
- const message = error instanceof Error ? error.message : String(error);
625
851
  return {
626
852
  content: [{
627
853
  type: 'text',
628
- text: `Failed to get file context: ${message}`,
854
+ text: this.formatErrorMessage('Get file context', error instanceof Error ? error : String(error), { projectPath: project }),
629
855
  }],
630
856
  isError: true,
631
857
  };
@@ -636,19 +862,19 @@ class CodeSeekerMcpServer {
636
862
  * Tool 3: Get code relationships from the knowledge graph
637
863
  * Uses "Seed + Expand" strategy like the CLI's GraphAnalysisService
638
864
  */
639
- registerGetCodeRelationshipsTool() {
640
- this.server.registerTool('get_code_relationships', {
641
- description: '**UNDERSTAND CODE CONNECTIONS** - Use after search_code to explore how files relate. ' +
865
+ registerShowDependenciesTool() {
866
+ this.server.registerTool('show_dependencies', {
867
+ description: '**UNDERSTAND CODE CONNECTIONS** - Use after search to explore how files relate. ' +
642
868
  'Maps imports, class hierarchies, function calls, dependencies. Essential for understanding impact of changes. ' +
643
869
  'Use when: planning refactors ("what breaks if I change this?"), understanding architecture ("what depends on this?"), ' +
644
870
  'tracing data flow ("where does this come from?"), before changing shared code. ' +
645
- 'WORKFLOW: 1) search_code to find files, 2) pass those paths here via filepaths parameter. ' +
871
+ 'WORKFLOW: 1) search to find files, 2) pass those paths here via filepaths parameter. ' +
646
872
  'Filter with relationship_types: ["imports"], ["calls"], ["extends"] to reduce noise. ' +
647
873
  'Use direction="in" to find what USES this file, direction="out" for what this file USES.',
648
874
  inputSchema: {
649
875
  filepath: zod_1.z.string().optional().describe('Single file path to explore (prefer filepaths for multiple)'),
650
- filepaths: zod_1.z.array(zod_1.z.string()).optional().describe('PREFERRED: Array of file paths from search_code results'),
651
- query: zod_1.z.string().optional().describe('Fallback: semantic search to find seed files (prefer using filepaths from search_code)'),
876
+ filepaths: zod_1.z.array(zod_1.z.string()).optional().describe('PREFERRED: Array of file paths from search results'),
877
+ query: zod_1.z.string().optional().describe('Fallback: semantic search to find seed files (prefer using filepaths from search)'),
652
878
  depth: zod_1.z.number().optional().default(1).describe('How many relationship hops to traverse (1-3, default: 1). Use 1 for focused results, 2+ can return many nodes.'),
653
879
  relationship_types: zod_1.z.array(zod_1.z.enum([
654
880
  'imports', 'exports', 'calls', 'extends', 'implements', 'contains', 'uses', 'depends_on'
@@ -693,7 +919,7 @@ class CodeSeekerMcpServer {
693
919
  return {
694
920
  content: [{
695
921
  type: 'text',
696
- text: 'Project not indexed. Use index_project first.',
922
+ text: 'Project not indexed. Use index first.',
697
923
  }],
698
924
  isError: true,
699
925
  };
@@ -880,11 +1106,10 @@ class CodeSeekerMcpServer {
880
1106
  };
881
1107
  }
882
1108
  catch (error) {
883
- const message = error instanceof Error ? error.message : String(error);
884
1109
  return {
885
1110
  content: [{
886
1111
  type: 'text',
887
- text: `Failed to get code relationships: ${message}`,
1112
+ text: this.formatErrorMessage('Get code relationships', error instanceof Error ? error : String(error), { projectPath: project }),
888
1113
  }],
889
1114
  isError: true,
890
1115
  };
@@ -894,12 +1119,12 @@ class CodeSeekerMcpServer {
894
1119
  /**
895
1120
  * Tool 4: List indexed projects
896
1121
  */
897
- registerListProjectsTool() {
898
- this.server.registerTool('list_projects', {
1122
+ registerProjectsTool() {
1123
+ this.server.registerTool('projects', {
899
1124
  description: 'List all indexed projects with their metadata. ' +
900
1125
  'Returns project names, paths, indexed file counts, and last index timestamps. ' +
901
- 'Use to discover available projects before running search_code or get_code_relationships. ' +
902
- 'Example: list_projects() shows all projects ready for semantic search.',
1126
+ 'Use to discover available projects before running search or show_dependencies. ' +
1127
+ 'Example: projects() shows all projects ready for semantic search.',
903
1128
  }, async () => {
904
1129
  try {
905
1130
  const storageManager = await (0, storage_1.getStorageManager)();
@@ -910,21 +1135,28 @@ class CodeSeekerMcpServer {
910
1135
  return {
911
1136
  content: [{
912
1137
  type: 'text',
913
- text: 'No projects indexed. Use index_project to add a project.',
1138
+ text: 'No projects indexed. Use index to add a project.',
914
1139
  }],
915
1140
  };
916
1141
  }
917
- // Get file and chunk counts for each project
1142
+ // Get file and chunk counts for each project, plus indexing status
918
1143
  const projectsWithCounts = await Promise.all(projects.map(async (p) => {
919
1144
  const fileCount = await vectorStore.countFiles(p.id);
920
1145
  const chunkCount = await vectorStore.count(p.id);
921
- return {
1146
+ // Check for background indexing status
1147
+ const indexingStatus = this._getIndexingStatusForProject(p.id);
1148
+ const projectInfo = {
922
1149
  name: p.name,
923
1150
  path: p.path,
924
1151
  files: fileCount,
925
1152
  chunks: chunkCount,
926
1153
  last_indexed: p.updatedAt.toISOString(),
927
1154
  };
1155
+ // Add indexing status if job exists
1156
+ if (indexingStatus) {
1157
+ Object.assign(projectInfo, indexingStatus);
1158
+ }
1159
+ return projectInfo;
928
1160
  }));
929
1161
  return {
930
1162
  content: [{
@@ -938,11 +1170,10 @@ class CodeSeekerMcpServer {
938
1170
  };
939
1171
  }
940
1172
  catch (error) {
941
- const message = error instanceof Error ? error.message : String(error);
942
1173
  return {
943
1174
  content: [{
944
1175
  type: 'text',
945
- text: `Failed to list projects: ${message}`,
1176
+ text: this.formatErrorMessage('List projects', error instanceof Error ? error : String(error)),
946
1177
  }],
947
1178
  isError: true,
948
1179
  };
@@ -951,13 +1182,15 @@ class CodeSeekerMcpServer {
951
1182
  }
952
1183
  /**
953
1184
  * Tool 5: Index a project (with proper embeddings and knowledge graph)
1185
+ * NOW RUNS IN BACKGROUND to prevent MCP timeouts
954
1186
  */
955
- registerIndexProjectTool() {
956
- this.server.registerTool('index_project', {
1187
+ registerIndexTool() {
1188
+ this.server.registerTool('index', {
957
1189
  description: 'Index a project directory for semantic search and knowledge graph. ' +
958
1190
  'Scans code, documentation, configs, and other text files. Generates vector embeddings and extracts code relationships. ' +
959
- 'Run once per project, then use notify_file_changes for incremental updates. ' +
960
- 'Example: index_project({path: "/home/user/my-app"}) indexes all files in my-app.',
1191
+ 'Run once per project, then use sync for incremental updates. ' +
1192
+ 'Example: index({path: "/home/user/my-app"}) indexes all files in my-app.' +
1193
+ '\n\nNOTE: Indexing runs in BACKGROUND to prevent timeouts. Use projects() to check indexing status.',
961
1194
  inputSchema: {
962
1195
  path: zod_1.z.string().describe('Absolute path to the project directory'),
963
1196
  name: zod_1.z.string().optional().describe('Project name (defaults to directory name)'),
@@ -969,6 +1202,17 @@ class CodeSeekerMcpServer {
969
1202
  const absolutePath = path.isAbsolute(projectPath)
970
1203
  ? projectPath
971
1204
  : path.resolve(projectPath);
1205
+ // Security: validate path is safe to index
1206
+ const pathError = this.validateProjectPath(absolutePath);
1207
+ if (pathError) {
1208
+ return {
1209
+ content: [{
1210
+ type: 'text',
1211
+ text: pathError,
1212
+ }],
1213
+ isError: true,
1214
+ };
1215
+ }
972
1216
  if (!fs.existsSync(absolutePath)) {
973
1217
  return {
974
1218
  content: [{
@@ -988,84 +1232,114 @@ class CodeSeekerMcpServer {
988
1232
  };
989
1233
  }
990
1234
  const projectName = name || path.basename(absolutePath);
991
- // Get storage and create project
1235
+ const projectId = this.generateProjectId(absolutePath);
1236
+ // Mutex: prevent concurrent indexing of same project (race condition protection)
1237
+ if (this.indexingMutex.has(projectId)) {
1238
+ return {
1239
+ content: [{
1240
+ type: 'text',
1241
+ text: JSON.stringify({
1242
+ status: 'already_indexing',
1243
+ project_name: projectName,
1244
+ project_path: absolutePath,
1245
+ message: 'Indexing request already being processed. Please wait.',
1246
+ }, null, 2),
1247
+ }],
1248
+ };
1249
+ }
1250
+ // Check if already indexing (from job status)
1251
+ const existingJob = this.getIndexingStatus(projectId);
1252
+ if (existingJob?.status === 'running') {
1253
+ return {
1254
+ content: [{
1255
+ type: 'text',
1256
+ text: JSON.stringify({
1257
+ status: 'already_indexing',
1258
+ project_name: projectName,
1259
+ project_path: absolutePath,
1260
+ progress: existingJob.progress,
1261
+ message: 'Indexing already in progress. Use projects() to check status.',
1262
+ }, null, 2),
1263
+ }],
1264
+ };
1265
+ }
1266
+ // Acquire mutex before starting
1267
+ this.indexingMutex.add(projectId);
1268
+ // Get storage and create project entry
992
1269
  const storageManager = await (0, storage_1.getStorageManager)();
993
1270
  const projectStore = storageManager.getProjectStore();
994
- const vectorStore = storageManager.getVectorStore();
995
- const graphStore = storageManager.getGraphStore();
996
1271
  // Create or update project
997
- const project = await projectStore.upsert({
998
- id: this.generateProjectId(absolutePath),
1272
+ await projectStore.upsert({
1273
+ id: projectId,
999
1274
  name: projectName,
1000
1275
  path: absolutePath,
1001
- metadata: { indexedAt: new Date().toISOString() },
1276
+ metadata: { indexedAt: new Date().toISOString(), indexing: true },
1002
1277
  });
1003
- // Clear existing index data for clean reindex
1004
- await vectorStore.deleteByProject(project.id);
1005
- await graphStore.deleteByProject(project.id);
1006
1278
  // Delete coding standards file (will be regenerated)
1007
1279
  const codingStandardsPath = path.join(absolutePath, '.codeseeker', 'coding-standards.json');
1008
1280
  if (fs.existsSync(codingStandardsPath)) {
1009
- fs.unlinkSync(codingStandardsPath);
1010
- }
1011
- // Use IndexingService for proper indexing with embeddings and graph
1012
- // Track progress for detailed reporting
1013
- let lastProgress;
1014
- const result = await this.indexingService.indexProject(absolutePath, project.id, (progress) => {
1015
- lastProgress = progress;
1016
- });
1017
- // Build response with progress details
1018
- const response = {
1019
- success: result.success,
1020
- project_name: projectName,
1021
- project_path: absolutePath,
1022
- files_indexed: result.filesIndexed,
1023
- chunks_created: result.chunksCreated,
1024
- graph_nodes: result.nodesCreated,
1025
- graph_edges: result.edgesCreated,
1026
- duration_ms: result.durationMs,
1027
- };
1028
- // Add scanning summary if available
1029
- if (lastProgress?.scanningStatus) {
1030
- response.scanning_summary = {
1031
- folders_scanned: lastProgress.scanningStatus.foldersScanned,
1032
- files_found: lastProgress.scanningStatus.filesFound,
1033
- };
1034
- }
1035
- // Add warnings (file limits, recommendations)
1036
- if (result.warnings && result.warnings.length > 0) {
1037
- response.warnings = result.warnings;
1038
- }
1039
- // Add errors if any
1040
- if (result.errors.length > 0) {
1041
- response.errors = result.errors.slice(0, 5);
1281
+ try {
1282
+ fs.unlinkSync(codingStandardsPath);
1283
+ }
1284
+ catch { /* ignore */ }
1042
1285
  }
1286
+ // Start background indexing (returns immediately)
1287
+ this.startBackgroundIndexing(projectId, projectName, absolutePath, true);
1288
+ // Return immediately with "started" status
1043
1289
  return {
1044
1290
  content: [{
1045
1291
  type: 'text',
1046
- text: JSON.stringify(response, null, 2),
1292
+ text: JSON.stringify({
1293
+ status: 'indexing_started',
1294
+ project_name: projectName,
1295
+ project_path: absolutePath,
1296
+ message: 'Indexing started in background. Search will work with partial results. Use projects() to check progress.',
1297
+ }, null, 2),
1047
1298
  }],
1048
1299
  };
1300
+ // OLD SYNCHRONOUS CODE REMOVED - was causing MCP timeouts
1301
+ // Now handled by startBackgroundIndexing()
1049
1302
  }
1050
1303
  catch (error) {
1051
1304
  const message = error instanceof Error ? error.message : String(error);
1052
1305
  return {
1053
1306
  content: [{
1054
1307
  type: 'text',
1055
- text: `Failed to index project: ${message}`,
1308
+ text: JSON.stringify({ error: message }, null, 2),
1056
1309
  }],
1057
1310
  isError: true,
1058
1311
  };
1059
1312
  }
1060
1313
  });
1061
1314
  }
1315
+ // Remove the old synchronous indexing response builder (now in background)
1316
+ _unusedOldIndexingResponse() {
1317
+ // This is a placeholder to mark where old code was removed
1318
+ // The synchronous indexing logic is now in runBackgroundIndexing()
1319
+ }
1320
+ /**
1321
+ * Tool 5b: Check indexing status (part of projects output now)
1322
+ */
1323
+ _getIndexingStatusForProject(projectId) {
1324
+ const job = this.indexingJobs.get(projectId);
1325
+ if (!job)
1326
+ return null;
1327
+ return {
1328
+ indexing_status: job.status,
1329
+ indexing_progress: job.progress,
1330
+ indexing_result: job.result,
1331
+ indexing_error: job.error,
1332
+ indexing_started: job.startedAt.toISOString(),
1333
+ indexing_completed: job.completedAt?.toISOString(),
1334
+ };
1335
+ }
1062
1336
  /**
1063
- * Tool 5: Notify file changes for incremental updates
1337
+ * Tool 6: Notify file changes for incremental updates
1064
1338
  */
1065
- registerNotifyFileChangesTool() {
1066
- this.server.registerTool('notify_file_changes', {
1339
+ registerSyncTool() {
1340
+ this.server.registerTool('sync', {
1067
1341
  description: '**KEEP INDEX IN SYNC** - Call this after creating, editing, or deleting files. ' +
1068
- 'IMPORTANT: If search_code returns stale results or grep finds content not in search results, ' +
1342
+ 'IMPORTANT: If search returns stale results or grep finds content not in search results, ' +
1069
1343
  'call this tool immediately to sync. Fast incremental updates (~100-500ms per file). ' +
1070
1344
  'Use after: Edit/Write tool, file deletions, or when search results seem outdated. ' +
1071
1345
  'For large changes (git pull, branch switch, many files), use full_reindex: true instead.',
@@ -1113,68 +1387,49 @@ class CodeSeekerMcpServer {
1113
1387
  content: [{
1114
1388
  type: 'text',
1115
1389
  text: project
1116
- ? `Project not found: ${project}. Use list_projects to see available projects.`
1390
+ ? `Project not found: ${project}. Use projects to see available projects.`
1117
1391
  : `Could not auto-detect project. Specify project name or use absolute paths in changes. Available: ${projects.map(p => p.name).join(', ')}`,
1118
1392
  }],
1119
1393
  isError: true,
1120
1394
  };
1121
1395
  }
1122
- // Full reindex mode - use IndexingService
1396
+ // Full reindex mode - NOW RUNS IN BACKGROUND
1123
1397
  if (full_reindex) {
1124
- // Clear existing index for this project
1125
- await vectorStore.deleteByProject(found.id);
1126
- await graphStore.deleteByProject(found.id);
1398
+ // Check if already indexing
1399
+ const existingJob = this.getIndexingStatus(found.id);
1400
+ if (existingJob?.status === 'running') {
1401
+ return {
1402
+ content: [{
1403
+ type: 'text',
1404
+ text: JSON.stringify({
1405
+ status: 'already_indexing',
1406
+ project: found.name,
1407
+ progress: existingJob.progress,
1408
+ message: 'Full reindex already in progress. Use projects() to check status.',
1409
+ }, null, 2),
1410
+ }],
1411
+ };
1412
+ }
1127
1413
  // Delete coding standards file (will be regenerated)
1128
1414
  const codingStandardsPath = path.join(found.path, '.codeseeker', 'coding-standards.json');
1129
1415
  if (fs.existsSync(codingStandardsPath)) {
1130
- fs.unlinkSync(codingStandardsPath);
1131
- }
1132
- // Re-index all files using IndexingService (with proper embeddings and graph)
1133
- let lastProgress;
1134
- const result = await this.indexingService.indexProject(found.path, found.id, (progress) => {
1135
- lastProgress = progress;
1136
- });
1137
- // Update project metadata
1138
- await projectStore.upsert({
1139
- id: found.id,
1140
- name: found.name,
1141
- path: found.path,
1142
- metadata: {
1143
- ...found.metadata,
1144
- lastFullReindex: new Date().toISOString(),
1145
- },
1146
- });
1147
- // Build response with all details
1148
- const response = {
1149
- success: result.success,
1150
- mode: 'full_reindex',
1151
- project: found.name,
1152
- files_indexed: result.filesIndexed,
1153
- chunks_created: result.chunksCreated,
1154
- graph_nodes: result.nodesCreated,
1155
- graph_edges: result.edgesCreated,
1156
- duration_ms: result.durationMs,
1157
- message: `Complete reindex finished. ${result.filesIndexed} files indexed, ${result.chunksCreated} chunks created.`,
1158
- };
1159
- // Add scanning summary
1160
- if (lastProgress?.scanningStatus) {
1161
- response.scanning_summary = {
1162
- folders_scanned: lastProgress.scanningStatus.foldersScanned,
1163
- files_found: lastProgress.scanningStatus.filesFound,
1164
- };
1165
- }
1166
- // Add warnings
1167
- if (result.warnings && result.warnings.length > 0) {
1168
- response.warnings = result.warnings;
1169
- }
1170
- // Add errors
1171
- if (result.errors.length > 0) {
1172
- response.errors = result.errors.slice(0, 5);
1416
+ try {
1417
+ fs.unlinkSync(codingStandardsPath);
1418
+ }
1419
+ catch { /* ignore */ }
1173
1420
  }
1421
+ // Start background indexing (returns immediately)
1422
+ this.startBackgroundIndexing(found.id, found.name, found.path, true);
1423
+ // Return immediately with "started" status
1174
1424
  return {
1175
1425
  content: [{
1176
1426
  type: 'text',
1177
- text: JSON.stringify(response, null, 2),
1427
+ text: JSON.stringify({
1428
+ status: 'reindex_started',
1429
+ mode: 'full_reindex',
1430
+ project: found.name,
1431
+ message: 'Full reindex started in background. Search will work with partial results. Use projects() to check progress.',
1432
+ }, null, 2),
1178
1433
  }],
1179
1434
  };
1180
1435
  }
@@ -1236,6 +1491,15 @@ class CodeSeekerMcpServer {
1236
1491
  // Don't fail the whole operation if standards update fails
1237
1492
  console.error('Failed to update coding standards:', error);
1238
1493
  }
1494
+ // Invalidate query cache for this project (files changed)
1495
+ let cacheInvalidated = 0;
1496
+ try {
1497
+ cacheInvalidated = await this.queryCache.invalidateProject(found.id);
1498
+ }
1499
+ catch (error) {
1500
+ // Don't fail if cache invalidation fails
1501
+ console.error('Failed to invalidate query cache:', error);
1502
+ }
1239
1503
  const duration = Date.now() - startTime;
1240
1504
  return {
1241
1505
  content: [{
@@ -1249,6 +1513,7 @@ class CodeSeekerMcpServer {
1249
1513
  files_skipped: filesSkipped > 0 ? filesSkipped : undefined,
1250
1514
  chunks_created: chunksCreated,
1251
1515
  chunks_deleted: chunksDeleted,
1516
+ cache_invalidated: cacheInvalidated > 0 ? cacheInvalidated : undefined,
1252
1517
  duration_ms: duration,
1253
1518
  note: filesSkipped > 0 ? `${filesSkipped} file(s) unchanged (skipped via mtime/hash check)` : undefined,
1254
1519
  errors: errors.length > 0 ? errors.slice(0, 5) : undefined,
@@ -1257,22 +1522,21 @@ class CodeSeekerMcpServer {
1257
1522
  };
1258
1523
  }
1259
1524
  catch (error) {
1260
- const message = error instanceof Error ? error.message : String(error);
1261
1525
  return {
1262
1526
  content: [{
1263
1527
  type: 'text',
1264
- text: `Failed to process file changes: ${message}`,
1528
+ text: this.formatErrorMessage('Process file changes', error instanceof Error ? error : String(error), { projectPath: project }),
1265
1529
  }],
1266
1530
  isError: true,
1267
1531
  };
1268
1532
  }
1269
1533
  });
1270
- // get_coding_standards - Get auto-detected coding standards
1271
- this.server.registerTool('get_coding_standards', {
1534
+ // standards - Get auto-detected coding standards
1535
+ this.server.registerTool('standards', {
1272
1536
  description: 'Get auto-detected coding patterns and standards for a project. ' +
1273
1537
  'Returns validation patterns, error handling patterns, logging patterns, and testing patterns ' +
1274
1538
  'discovered from the codebase. Use this to write code that follows project conventions. ' +
1275
- 'Example: get_coding_standards({project: "my-app", category: "validation"})',
1539
+ 'Example: standards({project: "my-app", category: "validation"})',
1276
1540
  inputSchema: {
1277
1541
  project: zod_1.z.string().describe('Project name or path'),
1278
1542
  category: zod_1.z.enum(['validation', 'error-handling', 'logging', 'testing', 'all']).optional().default('all')
@@ -1292,7 +1556,7 @@ class CodeSeekerMcpServer {
1292
1556
  return {
1293
1557
  content: [{
1294
1558
  type: 'text',
1295
- text: `Project not found: ${project}. Use list_projects to see available projects.`,
1559
+ text: `Project not found: ${project}. Use projects to see available projects.`,
1296
1560
  }],
1297
1561
  isError: true,
1298
1562
  };
@@ -1315,7 +1579,7 @@ class CodeSeekerMcpServer {
1315
1579
  return {
1316
1580
  content: [{
1317
1581
  type: 'text',
1318
- text: 'No coding standards detected yet. The project may need to be indexed first using index_project.',
1582
+ text: 'No coding standards detected yet. The project may need to be indexed first using index.',
1319
1583
  }],
1320
1584
  isError: true,
1321
1585
  };
@@ -1340,11 +1604,10 @@ class CodeSeekerMcpServer {
1340
1604
  };
1341
1605
  }
1342
1606
  catch (error) {
1343
- const message = error instanceof Error ? error.message : String(error);
1344
1607
  return {
1345
1608
  content: [{
1346
1609
  type: 'text',
1347
- text: `Failed to get coding standards: ${message}`,
1610
+ text: this.formatErrorMessage('Get coding standards', error instanceof Error ? error : String(error), { projectPath: project }),
1348
1611
  }],
1349
1612
  isError: true,
1350
1613
  };
@@ -1354,13 +1617,13 @@ class CodeSeekerMcpServer {
1354
1617
  /**
1355
1618
  * Tool 7: Install language support (Tree-sitter parsers)
1356
1619
  */
1357
- registerInstallLanguageSupportTool() {
1358
- this.server.registerTool('install_language_support', {
1620
+ registerInstallParsersTool() {
1621
+ this.server.registerTool('install_parsers', {
1359
1622
  description: 'Analyze project languages and install Tree-sitter parsers for better code understanding. ' +
1360
1623
  'Detects which programming languages are used in a project and installs enhanced parsers. ' +
1361
1624
  'Enhanced parsers provide better AST extraction for imports, classes, functions, and relationships. ' +
1362
- 'Example: install_language_support({project: "/path/to/project"}) to auto-detect and install. ' +
1363
- 'Example: install_language_support({languages: ["python", "java"]}) to install specific parsers.',
1625
+ 'Example: install_parsers({project: "/path/to/project"}) to auto-detect and install. ' +
1626
+ 'Example: install_parsers({languages: ["python", "java"]}) to install specific parsers.',
1364
1627
  inputSchema: {
1365
1628
  project: zod_1.z.string().optional().describe('Project path to analyze for languages (auto-detects needed parsers)'),
1366
1629
  languages: zod_1.z.array(zod_1.z.string()).optional().describe('Specific languages to install parsers for (e.g., ["python", "java", "csharp"])'),
@@ -1391,7 +1654,7 @@ class CodeSeekerMcpServer {
1391
1654
  description: p.description,
1392
1655
  })),
1393
1656
  install_command: available.length > 0
1394
- ? `Use install_language_support({languages: [${available.slice(0, 3).map(p => `"${p.language.toLowerCase()}"`).join(', ')}]})`
1657
+ ? `Use install_parsers({languages: [${available.slice(0, 3).map(p => `"${p.language.toLowerCase()}"`).join(', ')}]})`
1395
1658
  : 'All parsers are already installed!',
1396
1659
  }, null, 2),
1397
1660
  }],
@@ -1409,7 +1672,7 @@ class CodeSeekerMcpServer {
1409
1672
  failed: result.failed.length > 0 ? result.failed : undefined,
1410
1673
  message: result.message,
1411
1674
  next_step: result.success
1412
- ? 'Reindex your project to use the new parsers: notify_file_changes({project: "...", full_reindex: true})'
1675
+ ? 'Reindex your project to use the new parsers: sync({project: "...", full_reindex: true})'
1413
1676
  : 'Check the errors above and try again.',
1414
1677
  }, null, 2),
1415
1678
  }],
@@ -1447,7 +1710,7 @@ class CodeSeekerMcpServer {
1447
1710
  })),
1448
1711
  recommendations: analysis.recommendations,
1449
1712
  install_command: missingLanguages.length > 0
1450
- ? `Use install_language_support({languages: [${missingLanguages.map(l => `"${l}"`).join(', ')}]}) to install enhanced parsers`
1713
+ ? `Use install_parsers({languages: [${missingLanguages.map(l => `"${l}"`).join(', ')}]}) to install enhanced parsers`
1451
1714
  : 'All detected languages have parsers installed!',
1452
1715
  }, null, 2),
1453
1716
  }],
@@ -1459,9 +1722,9 @@ class CodeSeekerMcpServer {
1459
1722
  type: 'text',
1460
1723
  text: JSON.stringify({
1461
1724
  usage: {
1462
- analyze_project: 'install_language_support({project: "/path/to/project"}) - Detect languages and suggest parsers',
1463
- install_specific: 'install_language_support({languages: ["python", "java"]}) - Install parsers for specific languages',
1464
- list_available: 'install_language_support({list_available: true}) - Show all available parsers',
1725
+ analyze_project: 'install_parsers({project: "/path/to/project"}) - Detect languages and suggest parsers',
1726
+ install_specific: 'install_parsers({languages: ["python", "java"]}) - Install parsers for specific languages',
1727
+ list_available: 'install_parsers({list_available: true}) - Show all available parsers',
1465
1728
  },
1466
1729
  supported_languages: [
1467
1730
  'TypeScript (bundled)', 'JavaScript (bundled)',
@@ -1473,11 +1736,10 @@ class CodeSeekerMcpServer {
1473
1736
  };
1474
1737
  }
1475
1738
  catch (error) {
1476
- const message = error instanceof Error ? error.message : String(error);
1477
1739
  return {
1478
1740
  content: [{
1479
1741
  type: 'text',
1480
- text: `Failed to manage language support: ${message}`,
1742
+ text: this.formatErrorMessage('Manage language support', error instanceof Error ? error : String(error), { projectPath: project }),
1481
1743
  }],
1482
1744
  isError: true,
1483
1745
  };
@@ -1489,12 +1751,12 @@ class CodeSeekerMcpServer {
1489
1751
  * Allows Claude to exclude files that shouldn't be indexed (like Unity's Library folder)
1490
1752
  * and include files that were wrongly excluded
1491
1753
  */
1492
- registerManageIndexTool() {
1493
- this.server.registerTool('manage_index', {
1754
+ registerExcludeTool() {
1755
+ this.server.registerTool('exclude', {
1494
1756
  description: 'Dynamically manage which files are included or excluded from the index. ' +
1495
1757
  'Use this to exclude files that shouldn\'t be searched (e.g., Library/, build outputs, generated files) ' +
1496
1758
  'or include files that were incorrectly excluded. Exclusions persist in .codeseeker/exclusions.json. ' +
1497
- 'Example: manage_index({action: "exclude", project: "my-app", paths: ["Library/**", "Temp/**"]}) ' +
1759
+ 'Example: exclude({action: "exclude", project: "my-app", paths: ["Library/**", "Temp/**"]}) ' +
1498
1760
  'to exclude Unity folders. Changes take effect immediately - excluded files are removed from the index.',
1499
1761
  inputSchema: {
1500
1762
  action: zod_1.z.enum(['exclude', 'include', 'list']).describe('Action: "exclude" adds paths to exclusion list and removes from index, ' +
@@ -1520,7 +1782,7 @@ class CodeSeekerMcpServer {
1520
1782
  return {
1521
1783
  content: [{
1522
1784
  type: 'text',
1523
- text: `Project not found: ${project}. Use list_projects to see available projects.`,
1785
+ text: `Project not found: ${project}. Use projects to see available projects.`,
1524
1786
  }],
1525
1787
  isError: true,
1526
1788
  };
@@ -1558,8 +1820,8 @@ class CodeSeekerMcpServer {
1558
1820
  patterns: exclusions.patterns,
1559
1821
  last_modified: exclusions.lastModified,
1560
1822
  usage: {
1561
- exclude: 'manage_index({action: "exclude", project: "...", paths: ["pattern/**"]})',
1562
- include: 'manage_index({action: "include", project: "...", paths: ["pattern/**"]})',
1823
+ exclude: 'exclude({action: "exclude", project: "...", paths: ["pattern/**"]})',
1824
+ include: 'exclude({action: "include", project: "...", paths: ["pattern/**"]})',
1563
1825
  }
1564
1826
  }, null, 2),
1565
1827
  }],
@@ -1665,7 +1927,7 @@ class CodeSeekerMcpServer {
1665
1927
  `Files matching these patterns will be indexed on next reindex.`
1666
1928
  : 'No patterns were removed (none matched).',
1667
1929
  next_step: removedPatterns.length > 0
1668
- ? 'Run notify_file_changes({project: "...", full_reindex: true}) to index the previously excluded files.'
1930
+ ? 'Run sync({project: "...", full_reindex: true}) to index the previously excluded files.'
1669
1931
  : undefined
1670
1932
  }, null, 2),
1671
1933
  }],
@@ -1681,11 +1943,10 @@ class CodeSeekerMcpServer {
1681
1943
  };
1682
1944
  }
1683
1945
  catch (error) {
1684
- const message = error instanceof Error ? error.message : String(error);
1685
1946
  return {
1686
1947
  content: [{
1687
1948
  type: 'text',
1688
- text: `Failed to manage index: ${message}`,
1949
+ text: this.formatErrorMessage('Manage index exclusions', error instanceof Error ? error : String(error), { projectPath: project }),
1689
1950
  }],
1690
1951
  isError: true,
1691
1952
  };
@@ -1741,6 +2002,63 @@ class CodeSeekerMcpServer {
1741
2002
  generateProjectId(projectPath) {
1742
2003
  return crypto.createHash('md5').update(projectPath).digest('hex');
1743
2004
  }
2005
+ /**
2006
+ * Generate actionable error message based on error type
2007
+ */
2008
+ formatErrorMessage(operation, error, context) {
2009
+ const message = error instanceof Error ? error.message : String(error);
2010
+ const lowerMessage = message.toLowerCase();
2011
+ // Common error patterns with actionable guidance
2012
+ if (lowerMessage.includes('enoent') || lowerMessage.includes('not found') || lowerMessage.includes('no such file')) {
2013
+ return `${operation} failed: File or directory not found.\n\n` +
2014
+ `TROUBLESHOOTING:\n` +
2015
+ `• Verify the path exists: ${context?.projectPath || 'the specified path'}\n` +
2016
+ `• Check for typos in the path\n` +
2017
+ `• Ensure you have read permissions`;
2018
+ }
2019
+ if (lowerMessage.includes('eacces') || lowerMessage.includes('permission denied')) {
2020
+ return `${operation} failed: Permission denied.\n\n` +
2021
+ `TROUBLESHOOTING:\n` +
2022
+ `• Check file/folder permissions\n` +
2023
+ `• Run with appropriate access rights\n` +
2024
+ `• Avoid system-protected directories`;
2025
+ }
2026
+ if (lowerMessage.includes('timeout') || lowerMessage.includes('timed out')) {
2027
+ return `${operation} failed: Operation timed out.\n\n` +
2028
+ `TROUBLESHOOTING:\n` +
2029
+ `• Try again - the operation may complete on retry\n` +
2030
+ `• For large projects, indexing runs in background - check status with projects()\n` +
2031
+ `• Check network connectivity if using server storage mode`;
2032
+ }
2033
+ if (lowerMessage.includes('connection') || lowerMessage.includes('econnrefused') || lowerMessage.includes('network')) {
2034
+ return `${operation} failed: Connection error.\n\n` +
2035
+ `TROUBLESHOOTING:\n` +
2036
+ `• Check if storage services are running (if using server mode)\n` +
2037
+ `• Verify network connectivity\n` +
2038
+ `• Consider switching to embedded mode for local development`;
2039
+ }
2040
+ if (lowerMessage.includes('not indexed') || lowerMessage.includes('no project')) {
2041
+ const pathHint = context?.projectPath ? `index({path: "${context.projectPath}"})` : 'index({path: "/path/to/project"})';
2042
+ return `${operation} failed: Project not indexed.\n\n` +
2043
+ `ACTION REQUIRED:\n` +
2044
+ `• First run: ${pathHint}\n` +
2045
+ `• Then retry your search\n` +
2046
+ `• Use projects() to see indexed projects`;
2047
+ }
2048
+ if (lowerMessage.includes('out of memory') || lowerMessage.includes('heap')) {
2049
+ return `${operation} failed: Out of memory.\n\n` +
2050
+ `TROUBLESHOOTING:\n` +
2051
+ `• Try indexing fewer files at once\n` +
2052
+ `• Use exclude() to skip large directories (e.g., node_modules, dist)\n` +
2053
+ `• Increase Node.js memory: NODE_OPTIONS=--max-old-space-size=4096`;
2054
+ }
2055
+ // Default: include original message with generic guidance
2056
+ return `${operation} failed: ${message}\n\n` +
2057
+ `TROUBLESHOOTING:\n` +
2058
+ `• Check the error message above for details\n` +
2059
+ `• Use projects() to verify project status\n` +
2060
+ `• Try sync({project: "...", full_reindex: true}) if index seems corrupted`;
2061
+ }
1744
2062
  /**
1745
2063
  * Start the MCP server
1746
2064
  */