code-graph-context 2.6.0 → 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/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/parse-typescript-project.tool.js +5 -5
- package/package.json +1 -1
|
@@ -195,7 +195,7 @@ Provide ONLY the JSON response with no additional text, markdown formatting, or
|
|
|
195
195
|
this.schemaPath = schemaPath;
|
|
196
196
|
if (process.env.OPENAI_ASSISTANT_ID) {
|
|
197
197
|
this.assistantId = process.env.OPENAI_ASSISTANT_ID;
|
|
198
|
-
console.
|
|
198
|
+
console.error(`Using existing assistant with ID: ${this.assistantId}`);
|
|
199
199
|
return this.assistantId;
|
|
200
200
|
}
|
|
201
201
|
const schemaFile = await this.openai.files.create({
|
|
@@ -418,7 +418,7 @@ The query will be scoped to project: ${projectId}
|
|
|
418
418
|
Remember to include WHERE n.projectId = $projectId for all node patterns.
|
|
419
419
|
`;
|
|
420
420
|
// SECURITY: Only log prompt length, not full content which may contain sensitive data
|
|
421
|
-
console.
|
|
421
|
+
console.error(`NL-to-Cypher: Processing prompt (${prompt.length} chars) for project ${projectId}`);
|
|
422
422
|
const run = await this.openai.beta.threads.createAndRunPoll({
|
|
423
423
|
assistant_id: this.assistantId,
|
|
424
424
|
thread: {
|
|
@@ -432,7 +432,7 @@ Remember to include WHERE n.projectId = $projectId for all node patterns.
|
|
|
432
432
|
});
|
|
433
433
|
const threadId = run.thread_id;
|
|
434
434
|
// SECURITY: Log minimal info, avoid exposing full objects that may contain sensitive data
|
|
435
|
-
console.
|
|
435
|
+
console.error(`NL-to-Cypher: Thread ${threadId}, status: ${run.status}`);
|
|
436
436
|
// Validate run completed successfully
|
|
437
437
|
if (run.status !== 'completed') {
|
|
438
438
|
// SECURITY: Only log status and error, not full run object which may contain sensitive data
|
|
@@ -448,7 +448,7 @@ Remember to include WHERE n.projectId = $projectId for all node patterns.
|
|
|
448
448
|
`This may occur if the assistant is still initializing. Try setting OPENAI_ASSISTANT_ID in .env.`);
|
|
449
449
|
}
|
|
450
450
|
// SECURITY: Don't log full message content which may contain user data
|
|
451
|
-
console.
|
|
451
|
+
console.error(`NL-to-Cypher: Received message with ${latestMessage.content?.length ?? 0} content blocks`);
|
|
452
452
|
if (!latestMessage.content || latestMessage.content.length === 0) {
|
|
453
453
|
throw new Error(`Message has no content. Run status: ${run.status}. Thread: ${threadId}. ` +
|
|
454
454
|
`Message role: ${latestMessage.role}`);
|
|
@@ -464,7 +464,7 @@ Remember to include WHERE n.projectId = $projectId for all node patterns.
|
|
|
464
464
|
`Text content: ${JSON.stringify(textContent)}`);
|
|
465
465
|
}
|
|
466
466
|
// SECURITY: Don't log the full text value which may contain sensitive queries
|
|
467
|
-
console.
|
|
467
|
+
console.error(`NL-to-Cypher: Parsing response (${textValue.length} chars)`);
|
|
468
468
|
// Parse the response with proper error handling
|
|
469
469
|
let result;
|
|
470
470
|
try {
|
|
@@ -23,8 +23,8 @@ export class ParserFactory {
|
|
|
23
23
|
customFrameworkSchemas = [], excludePatterns = EXCLUDE_PATTERNS_REGEX, excludedNodeTypes = [CoreNodeType.PARAMETER_DECLARATION], projectId, lazyLoad = false, } = options;
|
|
24
24
|
// Select framework schemas based on project type
|
|
25
25
|
const frameworkSchemas = this.selectFrameworkSchemas(projectType, customFrameworkSchemas);
|
|
26
|
-
console.
|
|
27
|
-
console.
|
|
26
|
+
console.error(`📦 Creating parser for ${projectType} project`);
|
|
27
|
+
console.error(`📚 Framework schemas: ${frameworkSchemas.map((s) => s.name).join(', ')}`);
|
|
28
28
|
return new TypeScriptParser(workspacePath, tsConfigPath, CORE_TYPESCRIPT_SCHEMA, frameworkSchemas, {
|
|
29
29
|
excludePatterns,
|
|
30
30
|
excludedNodeTypes,
|
|
@@ -93,7 +93,7 @@ export class ParserFactory {
|
|
|
93
93
|
*/
|
|
94
94
|
static async createParserWithAutoDetection(workspacePath, tsConfigPath, projectId, lazyLoad = false) {
|
|
95
95
|
const projectType = await this.detectProjectType(workspacePath);
|
|
96
|
-
console.
|
|
96
|
+
console.error(`🔍 Auto-detected project type: ${projectType}`);
|
|
97
97
|
return this.createParser({
|
|
98
98
|
workspacePath,
|
|
99
99
|
tsConfigPath,
|
|
@@ -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
|
|
@@ -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, {
|
package/package.json
CHANGED