code-graph-context 2.5.4 → 2.6.1
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/core/embeddings/natural-language-to-cypher.service.js +5 -5
- package/dist/core/parsers/parser-factory.js +3 -3
- package/dist/mcp/constants.js +70 -557
- package/dist/mcp/handlers/graph-generator.handler.js +10 -10
- package/dist/mcp/handlers/traversal.handler.js +2 -2
- package/dist/mcp/mcp.server.js +1 -1
- package/dist/mcp/services/job-manager.js +2 -2
- package/dist/mcp/services/watch-manager.js +4 -4
- package/dist/mcp/tools/detect-dead-code.tool.js +0 -16
- package/dist/mcp/tools/detect-duplicate-code.tool.js +0 -27
- package/dist/mcp/tools/impact-analysis.tool.js +0 -13
- package/dist/mcp/tools/index.js +4 -15
- package/dist/mcp/tools/list-projects.tool.js +0 -2
- package/dist/mcp/tools/list-watchers.tool.js +0 -2
- package/dist/mcp/tools/natural-language-to-cypher.tool.js +0 -6
- package/dist/mcp/tools/parse-typescript-project.tool.js +5 -5
- package/dist/mcp/tools/search-codebase.tool.js +0 -19
- package/dist/mcp/tools/swarm-claim-task.tool.js +220 -93
- package/dist/mcp/tools/swarm-cleanup.tool.js +0 -8
- package/dist/mcp/tools/swarm-complete-task.tool.js +79 -88
- package/dist/mcp/tools/swarm-get-tasks.tool.js +0 -9
- package/dist/mcp/tools/swarm-orchestrate.tool.js +55 -92
- package/dist/mcp/tools/swarm-post-task.tool.js +32 -11
- package/dist/mcp/tools/traverse-from-node.tool.js +0 -15
- package/package.json +1 -1
|
@@ -23,12 +23,12 @@ export class GraphGeneratorHandler {
|
|
|
23
23
|
this.projectId = projectId;
|
|
24
24
|
}
|
|
25
25
|
async generateGraph(graphJsonPath, batchSize = DEFAULTS.batchSize, clearExisting = true) {
|
|
26
|
-
console.
|
|
26
|
+
console.error(`Generating graph from JSON file: ${graphJsonPath}`);
|
|
27
27
|
await debugLog('Starting graph generation', { graphJsonPath, batchSize, clearExisting, projectId: this.projectId });
|
|
28
28
|
try {
|
|
29
29
|
const graphData = await this.loadGraphData(graphJsonPath);
|
|
30
30
|
const { nodes, edges, metadata } = graphData;
|
|
31
|
-
console.
|
|
31
|
+
console.error(`Generating graph with ${nodes.length} nodes and ${edges.length} edges`);
|
|
32
32
|
await debugLog('Graph data loaded', { nodeCount: nodes.length, edgeCount: edges.length });
|
|
33
33
|
if (clearExisting) {
|
|
34
34
|
await this.clearExistingData();
|
|
@@ -57,18 +57,18 @@ export class GraphGeneratorHandler {
|
|
|
57
57
|
}
|
|
58
58
|
async clearExistingData() {
|
|
59
59
|
if (this.projectId) {
|
|
60
|
-
console.
|
|
60
|
+
console.error(`Clearing existing graph data for project: ${this.projectId}...`);
|
|
61
61
|
await this.neo4jService.run(QUERIES.CLEAR_PROJECT, { projectId: this.projectId });
|
|
62
62
|
await debugLog('Existing project graph data cleared', { projectId: this.projectId });
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
|
-
console.
|
|
65
|
+
console.error('Clearing ALL existing graph data (no projectId set)...');
|
|
66
66
|
await this.neo4jService.run(QUERIES.CLEAR_DATABASE);
|
|
67
67
|
await debugLog('Existing graph data cleared');
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
async createProjectIndexes() {
|
|
71
|
-
console.
|
|
71
|
+
console.error('Creating project indexes...');
|
|
72
72
|
await this.neo4jService.run(QUERIES.CREATE_PROJECT_INDEX_EMBEDDED);
|
|
73
73
|
await this.neo4jService.run(QUERIES.CREATE_PROJECT_INDEX_SOURCEFILE);
|
|
74
74
|
await this.neo4jService.run(QUERIES.CREATE_PROJECT_ID_INDEX_EMBEDDED);
|
|
@@ -77,12 +77,12 @@ export class GraphGeneratorHandler {
|
|
|
77
77
|
await debugLog('Project indexes created');
|
|
78
78
|
}
|
|
79
79
|
async importNodes(nodes, batchSize) {
|
|
80
|
-
console.
|
|
80
|
+
console.error(`Importing ${nodes.length} nodes with embeddings...`);
|
|
81
81
|
for (let i = 0; i < nodes.length; i += batchSize) {
|
|
82
82
|
const batch = await this.processNodeBatch(nodes.slice(i, i + batchSize));
|
|
83
83
|
const result = await this.neo4jService.run(QUERIES.CREATE_NODE, { nodes: batch });
|
|
84
84
|
const batchEnd = Math.min(i + batchSize, nodes.length);
|
|
85
|
-
console.
|
|
85
|
+
console.error(`Created ${result[0].created} nodes in batch ${i + 1}-${batchEnd}`);
|
|
86
86
|
await debugLog('Node batch imported', {
|
|
87
87
|
batchStart: i + 1,
|
|
88
88
|
batchEnd,
|
|
@@ -158,7 +158,7 @@ export class GraphGeneratorHandler {
|
|
|
158
158
|
return nodeResults;
|
|
159
159
|
}
|
|
160
160
|
async importEdges(edges, batchSize) {
|
|
161
|
-
console.
|
|
161
|
+
console.error(`Importing ${edges.length} edges using APOC...`);
|
|
162
162
|
for (let i = 0; i < edges.length; i += batchSize) {
|
|
163
163
|
const batch = edges.slice(i, i + batchSize).map((edge) => ({
|
|
164
164
|
...edge,
|
|
@@ -169,7 +169,7 @@ export class GraphGeneratorHandler {
|
|
|
169
169
|
projectId: this.projectId,
|
|
170
170
|
});
|
|
171
171
|
const batchEnd = Math.min(i + batchSize, edges.length);
|
|
172
|
-
console.
|
|
172
|
+
console.error(`Created ${result[0].created} edges in batch ${i + 1}-${batchEnd}`);
|
|
173
173
|
await debugLog('Edge batch imported', {
|
|
174
174
|
batchStart: i + 1,
|
|
175
175
|
batchEnd,
|
|
@@ -178,7 +178,7 @@ export class GraphGeneratorHandler {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
async createVectorIndexes() {
|
|
181
|
-
console.
|
|
181
|
+
console.error('Creating vector indexes...');
|
|
182
182
|
await this.neo4jService.run(QUERIES.CREATE_EMBEDDED_VECTOR_INDEX);
|
|
183
183
|
await debugLog('Vector indexes created');
|
|
184
184
|
}
|
|
@@ -153,7 +153,7 @@ export class TraversalHandler {
|
|
|
153
153
|
const nodeMap = new Map(); // Dedupe nodes
|
|
154
154
|
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
155
155
|
if (currentSourceIds.length === 0) {
|
|
156
|
-
console.
|
|
156
|
+
console.error(`No source nodes to explore at depth ${depth}`);
|
|
157
157
|
break;
|
|
158
158
|
}
|
|
159
159
|
const traversalResults = await this.neo4jService.run(QUERIES.EXPLORE_DEPTH_LEVEL(direction, maxNodesPerDepth), {
|
|
@@ -165,7 +165,7 @@ export class TraversalHandler {
|
|
|
165
165
|
projectId,
|
|
166
166
|
});
|
|
167
167
|
if (traversalResults.length === 0) {
|
|
168
|
-
console.
|
|
168
|
+
console.error(`No connections found at depth ${depth}`);
|
|
169
169
|
break;
|
|
170
170
|
}
|
|
171
171
|
// Collect node IDs for next depth exploration
|
package/dist/mcp/mcp.server.js
CHANGED
|
@@ -13,7 +13,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
13
13
|
const __dirname = dirname(__filename);
|
|
14
14
|
// Go up two levels from dist/mcp/mcp.server.js to the root
|
|
15
15
|
const rootDir = join(__dirname, '..', '..');
|
|
16
|
-
dotenv.config({ path: join(rootDir, '.env') });
|
|
16
|
+
dotenv.config({ path: join(rootDir, '.env'), quiet: true });
|
|
17
17
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
18
18
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
19
|
import { MCP_SERVER_CONFIG, MESSAGES } from './constants.js';
|
|
@@ -33,7 +33,7 @@ class JobManager {
|
|
|
33
33
|
this.cleanupInterval = setInterval(() => {
|
|
34
34
|
const cleaned = this.cleanupOldJobs();
|
|
35
35
|
if (cleaned > 0) {
|
|
36
|
-
console.
|
|
36
|
+
console.error(`[JobManager] Cleaned up ${cleaned} old jobs`);
|
|
37
37
|
}
|
|
38
38
|
}, JOBS.cleanupIntervalMs);
|
|
39
39
|
// Don't prevent Node.js from exiting if this is the only timer
|
|
@@ -63,7 +63,7 @@ class JobManager {
|
|
|
63
63
|
`Please wait for jobs to complete or cancel existing jobs.`);
|
|
64
64
|
}
|
|
65
65
|
if (cleaned > 0) {
|
|
66
|
-
console.
|
|
66
|
+
console.error(`[JobManager] Auto-cleaned ${cleaned} old jobs to make room for new job`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
const id = generateJobId();
|
|
@@ -249,7 +249,7 @@ class WatchManager {
|
|
|
249
249
|
},
|
|
250
250
|
timestamp: new Date().toISOString(),
|
|
251
251
|
});
|
|
252
|
-
console.
|
|
252
|
+
console.error(`[WatchManager] Incremental parse completed for ${state.projectId}: ` +
|
|
253
253
|
`${result.nodesUpdated} nodes, ${result.edgesUpdated} edges in ${elapsedMs}ms`);
|
|
254
254
|
}
|
|
255
255
|
catch (error) {
|
|
@@ -309,7 +309,7 @@ class WatchManager {
|
|
|
309
309
|
edgesUpdated: result.edgesUpdated
|
|
310
310
|
});
|
|
311
311
|
if (result.nodesUpdated > 0 || result.edgesUpdated > 0) {
|
|
312
|
-
console.
|
|
312
|
+
console.error(`[WatchManager] Synced missed changes for ${state.projectId}: ` +
|
|
313
313
|
`${result.nodesUpdated} nodes, ${result.edgesUpdated} edges`);
|
|
314
314
|
}
|
|
315
315
|
})
|
|
@@ -370,7 +370,7 @@ class WatchManager {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
this.watchers.delete(projectId);
|
|
373
|
-
console.
|
|
373
|
+
console.error(`[WatchManager] Stopped watching project: ${projectId}`);
|
|
374
374
|
return true;
|
|
375
375
|
}
|
|
376
376
|
/**
|
|
@@ -394,7 +394,7 @@ class WatchManager {
|
|
|
394
394
|
async stopAllWatchers() {
|
|
395
395
|
const projectIds = Array.from(this.watchers.keys());
|
|
396
396
|
await Promise.all(projectIds.map((id) => this.stopWatching(id)));
|
|
397
|
-
console.
|
|
397
|
+
console.error(`[WatchManager] Stopped all ${projectIds.length} watchers`);
|
|
398
398
|
}
|
|
399
399
|
/**
|
|
400
400
|
* Convert internal state to public info
|
|
@@ -158,12 +158,6 @@ export const createDetectDeadCodeTool = (server) => {
|
|
|
158
158
|
if (!projectResult.success)
|
|
159
159
|
return projectResult.error;
|
|
160
160
|
const resolvedProjectId = projectResult.projectId;
|
|
161
|
-
await debugLog('Dead code detection started', {
|
|
162
|
-
projectId: resolvedProjectId,
|
|
163
|
-
excludePatterns,
|
|
164
|
-
excludeSemanticTypes,
|
|
165
|
-
minConfidence,
|
|
166
|
-
});
|
|
167
161
|
// Query project's actual semantic types (data-driven, per-project detection)
|
|
168
162
|
const semanticTypesResult = (await neo4jService.run(QUERIES.GET_PROJECT_SEMANTIC_TYPES, {
|
|
169
163
|
projectId: resolvedProjectId,
|
|
@@ -389,16 +383,6 @@ export const createDetectDeadCodeTool = (server) => {
|
|
|
389
383
|
affectedFiles,
|
|
390
384
|
};
|
|
391
385
|
}
|
|
392
|
-
await debugLog('Dead code detection complete', {
|
|
393
|
-
projectId: resolvedProjectId,
|
|
394
|
-
totalCount: deadCodeItems.length,
|
|
395
|
-
filteredCount: filteredItems.length,
|
|
396
|
-
filterCategory,
|
|
397
|
-
riskLevel,
|
|
398
|
-
summaryOnly,
|
|
399
|
-
offset,
|
|
400
|
-
limit,
|
|
401
|
-
});
|
|
402
386
|
return createSuccessResponse(JSON.stringify(result, null, 2));
|
|
403
387
|
}
|
|
404
388
|
catch (error) {
|
|
@@ -151,12 +151,6 @@ export const createDetectDuplicateCodeTool = (server) => {
|
|
|
151
151
|
if (!projectResult.success)
|
|
152
152
|
return projectResult.error;
|
|
153
153
|
const resolvedProjectId = projectResult.projectId;
|
|
154
|
-
await debugLog('Duplicate code detection started', {
|
|
155
|
-
projectId: resolvedProjectId,
|
|
156
|
-
type,
|
|
157
|
-
minSimilarity,
|
|
158
|
-
scope,
|
|
159
|
-
});
|
|
160
154
|
const coreTypes = getScopeFilter(scope);
|
|
161
155
|
const duplicateGroups = [];
|
|
162
156
|
let groupCounter = 1;
|
|
@@ -320,16 +314,6 @@ export const createDetectDuplicateCodeTool = (server) => {
|
|
|
320
314
|
usedInSemanticGroup.add(nodeId1);
|
|
321
315
|
usedInSemanticGroup.add(nodeId2);
|
|
322
316
|
}
|
|
323
|
-
// Log semantic query diagnostics
|
|
324
|
-
await debugLog('Semantic query diagnostics', {
|
|
325
|
-
rawResults: semanticQueryResults,
|
|
326
|
-
filteredAsSameFile,
|
|
327
|
-
filteredAsSeenPair,
|
|
328
|
-
filteredAsStructural,
|
|
329
|
-
filteredAsUsedInGroup,
|
|
330
|
-
structuralPairsCount: structuralPairs.size,
|
|
331
|
-
finalSemanticGroups: duplicateGroups.filter((g) => g.type === 'semantic').length,
|
|
332
|
-
});
|
|
333
317
|
}
|
|
334
318
|
// Sort by similarity (descending)
|
|
335
319
|
duplicateGroups.sort((a, b) => b.similarity - a.similarity);
|
|
@@ -367,8 +351,6 @@ export const createDetectDuplicateCodeTool = (server) => {
|
|
|
367
351
|
message: `${embeddingCount} nodes have embeddings but no semantic duplicates found above ${minSimilarity} similarity threshold.`,
|
|
368
352
|
};
|
|
369
353
|
}
|
|
370
|
-
// Log diagnostic so user sees it in debug output
|
|
371
|
-
await debugLog('Semantic duplicate diagnostic', semanticDiagnostic);
|
|
372
354
|
}
|
|
373
355
|
// Build summary with warning if no embeddings
|
|
374
356
|
let summary = totalGroups === 0
|
|
@@ -427,15 +409,6 @@ export const createDetectDuplicateCodeTool = (server) => {
|
|
|
427
409
|
if (semanticDiagnostic) {
|
|
428
410
|
result.semanticDiagnostic = semanticDiagnostic;
|
|
429
411
|
}
|
|
430
|
-
await debugLog('Duplicate code detection complete', {
|
|
431
|
-
projectId: resolvedProjectId,
|
|
432
|
-
totalGroups,
|
|
433
|
-
structuralGroups: allStructuralGroups.length,
|
|
434
|
-
semanticGroups: allSemanticGroups.length,
|
|
435
|
-
summaryOnly,
|
|
436
|
-
offset,
|
|
437
|
-
maxResults,
|
|
438
|
-
});
|
|
439
412
|
return createSuccessResponse(JSON.stringify(result, null, 2));
|
|
440
413
|
}
|
|
441
414
|
catch (error) {
|
|
@@ -81,13 +81,6 @@ export const createImpactAnalysisTool = (server) => {
|
|
|
81
81
|
if (!nodeId && !filePath) {
|
|
82
82
|
return createErrorResponse('Either nodeId or filePath must be provided');
|
|
83
83
|
}
|
|
84
|
-
await debugLog('Impact analysis started', {
|
|
85
|
-
projectId: resolvedProjectId,
|
|
86
|
-
nodeId,
|
|
87
|
-
filePath,
|
|
88
|
-
maxDepth,
|
|
89
|
-
frameworkConfig,
|
|
90
|
-
});
|
|
91
84
|
// Merge default weights with framework-specific weights
|
|
92
85
|
const weights = { ...DEFAULT_RELATIONSHIP_WEIGHTS, ...frameworkConfig?.relationshipWeights };
|
|
93
86
|
const highRiskTypes = new Set(frameworkConfig?.highRiskTypes ?? []);
|
|
@@ -213,12 +206,6 @@ export const createImpactAnalysisTool = (server) => {
|
|
|
213
206
|
affectedFiles,
|
|
214
207
|
criticalPaths,
|
|
215
208
|
};
|
|
216
|
-
await debugLog('Impact analysis complete', {
|
|
217
|
-
nodeId: nodeId ?? filePath,
|
|
218
|
-
riskLevel,
|
|
219
|
-
directCount: directDependents.length,
|
|
220
|
-
transitiveCount: transitiveDependents.length,
|
|
221
|
-
});
|
|
222
209
|
return createSuccessResponse(JSON.stringify(result, null, 2));
|
|
223
210
|
}
|
|
224
211
|
catch (error) {
|
package/dist/mcp/tools/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* MCP Tool Factory
|
|
3
3
|
* Centralized tool creation and registration
|
|
4
4
|
*/
|
|
5
|
-
import { debugLog } from '../utils.js';
|
|
6
5
|
import { createCheckParseStatusTool } from './check-parse-status.tool.js';
|
|
7
6
|
import { createDetectDeadCodeTool } from './detect-dead-code.tool.js';
|
|
8
7
|
import { createDetectDuplicateCodeTool } from './detect-duplicate-code.tool.js';
|
|
@@ -30,25 +29,15 @@ let globalToolCallCount = 0;
|
|
|
30
29
|
/**
|
|
31
30
|
* Log tool call start (exported for use by individual tools)
|
|
32
31
|
*/
|
|
33
|
-
export const logToolCallStart = async (
|
|
32
|
+
export const logToolCallStart = async (_toolName, _params) => {
|
|
34
33
|
globalToolCallCount++;
|
|
35
|
-
|
|
36
|
-
await debugLog(`Tool call START: ${toolName}`, {
|
|
37
|
-
callId,
|
|
38
|
-
totalCalls: globalToolCallCount,
|
|
39
|
-
params: params ? JSON.stringify(params).substring(0, 500) : 'none',
|
|
40
|
-
});
|
|
41
|
-
return callId;
|
|
34
|
+
return globalToolCallCount;
|
|
42
35
|
};
|
|
43
36
|
/**
|
|
44
37
|
* Log tool call end (exported for use by individual tools)
|
|
45
38
|
*/
|
|
46
|
-
export const logToolCallEnd = async (
|
|
47
|
-
|
|
48
|
-
callId,
|
|
49
|
-
success,
|
|
50
|
-
duration: duration ? `${duration}ms` : 'unknown',
|
|
51
|
-
});
|
|
39
|
+
export const logToolCallEnd = async (_toolName, _callId, _success, _duration) => {
|
|
40
|
+
// No-op - verbose logging disabled
|
|
52
41
|
};
|
|
53
42
|
/**
|
|
54
43
|
* Register all MCP tools with the server
|
|
@@ -14,7 +14,6 @@ export const createListProjectsTool = (server) => {
|
|
|
14
14
|
}, async () => {
|
|
15
15
|
const neo4jService = new Neo4jService();
|
|
16
16
|
try {
|
|
17
|
-
await debugLog('Listing projects');
|
|
18
17
|
const results = await neo4jService.run(LIST_PROJECTS_QUERY, {});
|
|
19
18
|
if (results.length === 0) {
|
|
20
19
|
return createSuccessResponse('No projects found. Use parse_typescript_project to add a project first.');
|
|
@@ -28,7 +27,6 @@ export const createListProjectsTool = (server) => {
|
|
|
28
27
|
edgeCount: r.edgeCount,
|
|
29
28
|
updatedAt: r.updatedAt?.toString() ?? 'Unknown',
|
|
30
29
|
}));
|
|
31
|
-
await debugLog('Projects listed', { count: projects.length });
|
|
32
30
|
// Format output for readability
|
|
33
31
|
const header = `Found ${projects.length} project(s):\n\n`;
|
|
34
32
|
const formatStats = (p) => {
|
|
@@ -12,7 +12,6 @@ export const createListWatchersTool = (server) => {
|
|
|
12
12
|
inputSchema: {},
|
|
13
13
|
}, async () => {
|
|
14
14
|
try {
|
|
15
|
-
await debugLog('Listing watchers');
|
|
16
15
|
const watchers = watchManager.listWatchers();
|
|
17
16
|
if (watchers.length === 0) {
|
|
18
17
|
return createSuccessResponse('No active file watchers.\n\n' +
|
|
@@ -20,7 +19,6 @@ export const createListWatchersTool = (server) => {
|
|
|
20
19
|
'- Use start_watch_project with a projectId\n' +
|
|
21
20
|
'- Or use parse_typescript_project with watch: true (requires async: false)');
|
|
22
21
|
}
|
|
23
|
-
await debugLog('Watchers listed', { count: watchers.length });
|
|
24
22
|
const header = `Found ${watchers.length} active watcher(s):\n\n`;
|
|
25
23
|
const watcherList = watchers
|
|
26
24
|
.map((w) => {
|
|
@@ -47,7 +47,6 @@ export const createNaturalLanguageToCypherTool = (server) => {
|
|
|
47
47
|
await debugLog('Natural language service not available', { projectId: resolvedProjectId, query });
|
|
48
48
|
return createSuccessResponse(MESSAGES.errors.serviceNotInitialized);
|
|
49
49
|
}
|
|
50
|
-
await debugLog('Natural language to Cypher conversion started', { projectId: resolvedProjectId, query });
|
|
51
50
|
const cypherResult = await naturalLanguageToCypherService.promptToQuery(query, resolvedProjectId);
|
|
52
51
|
// Validate Cypher syntax using EXPLAIN (no execution, just parse)
|
|
53
52
|
const parameters = { ...cypherResult.parameters, projectId: resolvedProjectId };
|
|
@@ -67,11 +66,6 @@ export const createNaturalLanguageToCypherTool = (server) => {
|
|
|
67
66
|
}
|
|
68
67
|
// Execute the validated query
|
|
69
68
|
const results = await neo4jService.run(cypherResult.cypher, parameters);
|
|
70
|
-
await debugLog('Cypher query executed', {
|
|
71
|
-
projectId: resolvedProjectId,
|
|
72
|
-
cypher: cypherResult.cypher,
|
|
73
|
-
resultsCount: results.length,
|
|
74
|
-
});
|
|
75
69
|
const formattedResponse = formatQueryResults(results, query, cypherResult);
|
|
76
70
|
return createSuccessResponse(JSON.stringify(formattedResponse, null, 2));
|
|
77
71
|
}
|
|
@@ -225,10 +225,10 @@ export const createParseTypescriptProjectTool = (server) => {
|
|
|
225
225
|
const discoveredFiles = await parser.discoverSourceFiles();
|
|
226
226
|
const totalFiles = discoveredFiles.length;
|
|
227
227
|
const shouldUseStreaming = useStreaming === 'always' || (useStreaming === 'auto' && totalFiles > PARSING.streamingThreshold && chunkSize > 0);
|
|
228
|
-
console.
|
|
228
|
+
console.error(`📊 Project has ${totalFiles} files. Streaming: ${shouldUseStreaming ? 'enabled' : 'disabled'}`);
|
|
229
229
|
if (shouldUseStreaming && clearExisting !== false) {
|
|
230
230
|
// Use streaming import for large projects
|
|
231
|
-
console.
|
|
231
|
+
console.error(`🚀 Using streaming import with chunk size ${chunkSize}`);
|
|
232
232
|
await debugLog('Using streaming import', { totalFiles, chunkSize });
|
|
233
233
|
// Create Project node BEFORE starting import (status: parsing)
|
|
234
234
|
const projectName = await getProjectName(projectPath);
|
|
@@ -298,7 +298,7 @@ export const createParseTypescriptProjectTool = (server) => {
|
|
|
298
298
|
projectType,
|
|
299
299
|
});
|
|
300
300
|
const { nodes, edges, savedCrossFileEdges, resolvedProjectId: finalProjectId } = graphData;
|
|
301
|
-
console.
|
|
301
|
+
console.error(`Parsed ${nodes.length} nodes / ${edges.length} edges for project ${finalProjectId}`);
|
|
302
302
|
await debugLog('Parsing completed', {
|
|
303
303
|
nodeCount: nodes.length,
|
|
304
304
|
edgeCount: edges.length,
|
|
@@ -306,7 +306,7 @@ export const createParseTypescriptProjectTool = (server) => {
|
|
|
306
306
|
});
|
|
307
307
|
const outputPath = join(projectPath, FILE_PATHS.graphOutput);
|
|
308
308
|
writeFileSync(outputPath, JSON.stringify(graphData, null, LOG_CONFIG.jsonIndentation));
|
|
309
|
-
console.
|
|
309
|
+
console.error(`Graph data written to ${outputPath}`);
|
|
310
310
|
try {
|
|
311
311
|
// Set projectId for project-scoped operations (clear, indexes)
|
|
312
312
|
graphGeneratorHandler.setProjectId(finalProjectId);
|
|
@@ -321,7 +321,7 @@ export const createParseTypescriptProjectTool = (server) => {
|
|
|
321
321
|
const recreatedCount = recreateResult[0]?.recreatedCount ?? 0;
|
|
322
322
|
await debugLog('Cross-file edges recreated', { recreatedCount, expected: savedCrossFileEdges.length });
|
|
323
323
|
}
|
|
324
|
-
console.
|
|
324
|
+
console.error('Graph generation completed:', result);
|
|
325
325
|
await debugLog('Neo4j import completed', result);
|
|
326
326
|
// Update Project node status to complete
|
|
327
327
|
await neo4jService.run(UPDATE_PROJECT_STATUS_QUERY, {
|
|
@@ -63,7 +63,6 @@ export const createSearchCodebaseTool = (server) => {
|
|
|
63
63
|
const sanitizedMaxNodesPerChain = sanitizeNumericInput(maxNodesPerChain, 5);
|
|
64
64
|
const sanitizedSkip = sanitizeNumericInput(skip, 0);
|
|
65
65
|
const sanitizedSnippetLength = sanitizeNumericInput(snippetLength, DEFAULTS.codeSnippetLength);
|
|
66
|
-
await debugLog('Search codebase started', { projectId: resolvedProjectId, query });
|
|
67
66
|
const embeddingsService = new EmbeddingsService();
|
|
68
67
|
const traversalHandler = new TraversalHandler(neo4jService);
|
|
69
68
|
const embedding = await embeddingsService.embedText(query);
|
|
@@ -75,7 +74,6 @@ export const createSearchCodebaseTool = (server) => {
|
|
|
75
74
|
minSimilarity,
|
|
76
75
|
});
|
|
77
76
|
if (vectorResults.length === 0) {
|
|
78
|
-
await debugLog('No relevant code found', { projectId: resolvedProjectId, query, minSimilarity });
|
|
79
77
|
return createSuccessResponse(`No code found with similarity >= ${minSimilarity}. ` +
|
|
80
78
|
`Try rephrasing your query or lowering the minSimilarity threshold. Query: "${query}"`);
|
|
81
79
|
}
|
|
@@ -84,26 +82,9 @@ export const createSearchCodebaseTool = (server) => {
|
|
|
84
82
|
const similarityScore = vectorResults[0].score;
|
|
85
83
|
// Check if best match meets threshold - prevents traversing low-relevance results
|
|
86
84
|
if (similarityScore < minSimilarity) {
|
|
87
|
-
await debugLog('Best match below similarity threshold', {
|
|
88
|
-
projectId: resolvedProjectId,
|
|
89
|
-
query,
|
|
90
|
-
score: similarityScore,
|
|
91
|
-
threshold: minSimilarity,
|
|
92
|
-
});
|
|
93
85
|
return createSuccessResponse(`No sufficiently relevant code found. Best match score: ${similarityScore.toFixed(3)} ` +
|
|
94
86
|
`(threshold: ${minSimilarity}). Try rephrasing your query.`);
|
|
95
87
|
}
|
|
96
|
-
await debugLog('Vector search completed, starting traversal', {
|
|
97
|
-
projectId: resolvedProjectId,
|
|
98
|
-
nodeId,
|
|
99
|
-
similarityScore,
|
|
100
|
-
resultsCount: vectorResults.length,
|
|
101
|
-
maxDepth: sanitizedMaxDepth,
|
|
102
|
-
maxNodesPerChain: sanitizedMaxNodesPerChain,
|
|
103
|
-
skip: sanitizedSkip,
|
|
104
|
-
includeCode,
|
|
105
|
-
snippetLength: sanitizedSnippetLength,
|
|
106
|
-
});
|
|
107
88
|
// Include similarity score in the title so users can see relevance
|
|
108
89
|
const scoreDisplay = typeof similarityScore === 'number' ? similarityScore.toFixed(3) : 'N/A';
|
|
109
90
|
return await traversalHandler.traverseFromNode(nodeId, embedding, {
|