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.
- package/dist/cli/commands/base-command-handler.js +1 -1
- package/dist/cli/commands/base-command-handler.js.map +1 -1
- package/dist/cli/commands/handlers/setup-command-handler.d.ts +2 -0
- package/dist/cli/commands/handlers/setup-command-handler.d.ts.map +1 -1
- package/dist/cli/commands/handlers/setup-command-handler.js +217 -156
- package/dist/cli/commands/handlers/setup-command-handler.js.map +1 -1
- package/dist/cli/commands/reconcile.js.map +1 -1
- package/dist/cli/commands/services/context-aware-clarification-service.js.map +1 -1
- package/dist/cli/commands/services/task-decomposition-service.js.map +1 -1
- package/dist/cli/commands/services/user-interaction-service.js +1 -1
- package/dist/cli/commands/services/user-interaction-service.js.map +1 -1
- package/dist/cli/context-optimizer.js +1 -1
- package/dist/cli/context-optimizer.js.map +1 -1
- package/dist/cli/features/code-graph/graph-builder.js.map +1 -1
- package/dist/cli/features/compilation/verifier.js +1 -1
- package/dist/cli/features/compilation/verifier.js.map +1 -1
- package/dist/cli/features/database/database-documentation-generator.js.map +1 -1
- package/dist/cli/features/documentation/map-analyzer.js.map +1 -1
- package/dist/cli/features/duplication/detector.js.map +1 -1
- package/dist/cli/features/duplication/services/duplication-detection-service.js.map +1 -1
- package/dist/cli/features/semantic-graph/semantic-graph-tool.js +1 -1
- package/dist/cli/features/semantic-graph/semantic-graph-tool.js.map +1 -1
- package/dist/cli/git/git-commit-utils.js +2 -2
- package/dist/cli/git/git-commit-utils.js.map +1 -1
- package/dist/cli/git/git-integration.js.map +1 -1
- package/dist/cli/knowledge/analyzers/semantic-analyzer.js.map +1 -1
- package/dist/cli/knowledge/analyzers/services/file-discovery-service.js.map +1 -1
- package/dist/cli/knowledge/graph/knowledge-graph.js.map +1 -1
- package/dist/cli/knowledge/graph/managers/graph-mutation-manager.js.map +1 -1
- package/dist/cli/knowledge/graph/managers/graph-query-manager.js.map +1 -1
- package/dist/cli/knowledge/graph/managers/graph-state-manager.js.map +1 -1
- package/dist/cli/knowledge/graph/managers/graph-traversal-manager.js.map +1 -1
- package/dist/cli/knowledge/graph/services/architectural-insight-detector.js.map +1 -1
- package/dist/cli/knowledge/graph/services/graph-analyzer.js.map +1 -1
- package/dist/cli/knowledge/graph/services/graph-utility-service.js +1 -1
- package/dist/cli/knowledge/graph/services/graph-utility-service.js.map +1 -1
- package/dist/cli/knowledge/repository/knowledge-repository.js.map +1 -1
- package/dist/cli/managers/interrupt-manager.js +2 -2
- package/dist/cli/managers/interrupt-manager.js.map +1 -1
- package/dist/cli/quality/services/testing-service.js +1 -1
- package/dist/cli/quality/services/testing-service.js.map +1 -1
- package/dist/cli/quality-checker.js.map +1 -1
- package/dist/cli/services/analysis/coding-patterns-analyzer.js.map +1 -1
- package/dist/cli/services/analysis/coding-standards-generator.js.map +1 -1
- package/dist/cli/services/analysis/deduplication/code-consolidation-handler.js.map +1 -1
- package/dist/cli/services/analysis/deduplication/duplicate-code-detector.js.map +1 -1
- package/dist/cli/services/data/code-relationship-parser.js.map +1 -1
- package/dist/cli/services/data/embedding/services/file-processor.js.map +1 -1
- package/dist/cli/services/data/semantic-graph/builders/relationship-builder.js.map +1 -1
- package/dist/cli/services/data/semantic-graph/language-detector.js.map +1 -1
- package/dist/cli/services/data/semantic-graph/parsers/python-parser.js +1 -1
- package/dist/cli/services/data/semantic-graph/parsers/python-parser.js.map +1 -1
- package/dist/cli/services/data/semantic-graph/parsers/tree-sitter-python-parser.js +1 -1
- package/dist/cli/services/data/semantic-graph/parsers/tree-sitter-python-parser.js.map +1 -1
- package/dist/cli/services/data/semantic-graph/semantic-graph.js.map +1 -1
- package/dist/cli/services/integration/codeseeker-instruction-service.js.map +1 -1
- package/dist/cli/services/monitoring/file-watcher-service.js.map +1 -1
- package/dist/cli/services/project-identity-service.js.map +1 -1
- package/dist/cli/workflow/workflow-orchestrator.js.map +1 -1
- package/dist/config/database-config.js +1 -1
- package/dist/config/database-config.js.map +1 -1
- package/dist/database/adapters/postgresql.js +1 -1
- package/dist/database/adapters/postgresql.js.map +1 -1
- package/dist/database/database.js.map +1 -1
- package/dist/integrations/claude/claude-cli-integration.js.map +1 -1
- package/dist/integrations/claude/conversation-manager.js +1 -1
- package/dist/integrations/claude/conversation-manager.js.map +1 -1
- package/dist/mcp/indexing-service.d.ts +23 -0
- package/dist/mcp/indexing-service.d.ts.map +1 -1
- package/dist/mcp/indexing-service.js +227 -19
- package/dist/mcp/indexing-service.js.map +1 -1
- package/dist/mcp/mcp-server.d.ts +57 -10
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +505 -187
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/query-cache-service.d.ts +83 -0
- package/dist/mcp/query-cache-service.d.ts.map +1 -0
- package/dist/mcp/query-cache-service.js +227 -0
- package/dist/mcp/query-cache-service.js.map +1 -0
- package/dist/orchestrator/sequential-workflow-orchestrator.js.map +1 -1
- package/dist/orchestrator/tool-database-api.js.map +1 -1
- package/dist/orchestrator/workflow-visualizer.js.map +1 -1
- package/dist/shared/analysis-repository-consolidated.js +2 -2
- package/dist/shared/analysis-repository-consolidated.js.map +1 -1
- package/dist/shared/analysis-repository.js +1 -1
- package/dist/shared/analysis-repository.js.map +1 -1
- package/dist/shared/change-assessment-system.js.map +1 -1
- package/dist/shared/codebase-analyzer.js.map +1 -1
- package/dist/shared/documentation-rag-service.js.map +1 -1
- package/dist/shared/intelligent-cycle-features.js.map +1 -1
- package/dist/shared/managers/cache-manager.js +1 -1
- package/dist/shared/managers/cache-manager.js.map +1 -1
- package/dist/shared/managers/git-branch-manager.js.map +1 -1
- package/dist/shared/memory-system/memory-system.js.map +1 -1
- package/dist/shared/offline-first-cache.js.map +1 -1
- package/dist/shared/tool-interface.js +1 -1
- package/dist/shared/tool-interface.js.map +1 -1
- package/dist/shared/validation-cycle/validation-cycle.js.map +1 -1
- package/dist/storage/embedded/graphology-graph-store.d.ts +1 -0
- package/dist/storage/embedded/graphology-graph-store.d.ts.map +1 -1
- package/dist/storage/embedded/graphology-graph-store.js +38 -0
- package/dist/storage/embedded/graphology-graph-store.js.map +1 -1
- package/dist/storage/embedded/minisearch-text-store.js.map +1 -1
- package/dist/storage/interfaces.d.ts +2 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/server/neo4j-graph-store.d.ts +1 -0
- package/dist/storage/server/neo4j-graph-store.d.ts.map +1 -1
- package/dist/storage/server/neo4j-graph-store.js +23 -1
- package/dist/storage/server/neo4j-graph-store.js.map +1 -1
- package/dist/storage/storage-provider.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -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.
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
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
|
-
|
|
124
|
-
this.server.registerTool('
|
|
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" → ✅
|
|
131
|
-
'Returns absolute file paths ready for the Read tool. If not indexed, call
|
|
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
|
|
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
|
|
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
|
|
419
|
+
`ACTION REQUIRED: Call index({path: "${projectPath}"}) then retry this search.`,
|
|
216
420
|
}],
|
|
217
421
|
isError: true,
|
|
218
422
|
};
|
|
219
423
|
}
|
|
220
424
|
}
|
|
221
|
-
//
|
|
222
|
-
|
|
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:
|
|
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
|
-
|
|
326
|
-
this.server.registerTool('
|
|
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
|
|
329
|
-
'Use this instead of
|
|
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" →
|
|
332
|
-
'"I need to add validation like login" →
|
|
333
|
-
'Use
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
525
|
-
this.server.registerTool('
|
|
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 →
|
|
530
|
-
'Reading a service →
|
|
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:
|
|
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
|
-
|
|
640
|
-
this.server.registerTool('
|
|
641
|
-
description: '**UNDERSTAND CODE CONNECTIONS** - Use after
|
|
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)
|
|
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
|
|
651
|
-
query: zod_1.z.string().optional().describe('Fallback: semantic search to find seed files (prefer using filepaths from
|
|
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
|
|
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:
|
|
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
|
-
|
|
898
|
-
this.server.registerTool('
|
|
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
|
|
902
|
-
'Example:
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
956
|
-
this.server.registerTool('
|
|
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
|
|
960
|
-
'Example:
|
|
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
|
-
|
|
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
|
-
|
|
998
|
-
id:
|
|
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
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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(
|
|
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:
|
|
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
|
|
1337
|
+
* Tool 6: Notify file changes for incremental updates
|
|
1064
1338
|
*/
|
|
1065
|
-
|
|
1066
|
-
this.server.registerTool('
|
|
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
|
|
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
|
|
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 -
|
|
1396
|
+
// Full reindex mode - NOW RUNS IN BACKGROUND
|
|
1123
1397
|
if (full_reindex) {
|
|
1124
|
-
//
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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(
|
|
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:
|
|
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
|
-
//
|
|
1271
|
-
this.server.registerTool('
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
1358
|
-
this.server.registerTool('
|
|
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:
|
|
1363
|
-
'Example:
|
|
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
|
|
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:
|
|
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
|
|
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: '
|
|
1463
|
-
install_specific: '
|
|
1464
|
-
list_available: '
|
|
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:
|
|
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
|
-
|
|
1493
|
-
this.server.registerTool('
|
|
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:
|
|
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
|
|
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: '
|
|
1562
|
-
include: '
|
|
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
|
|
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:
|
|
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
|
*/
|