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.
@@ -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.log(`Using existing assistant with ID: ${this.assistantId} `);
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.log(`NL-to-Cypher: Processing prompt (${prompt.length} chars) for project ${projectId}`);
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.log(`NL-to-Cypher: Thread ${threadId}, status: ${run.status}`);
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.log(`NL-to-Cypher: Received message with ${latestMessage.content?.length ?? 0} content blocks`);
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.log(`NL-to-Cypher: Parsing response (${textValue.length} chars)`);
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.log(`📦 Creating parser for ${projectType} project`);
27
- console.log(`📚 Framework schemas: ${frameworkSchemas.map((s) => s.name).join(', ')}`);
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.log(`🔍 Auto-detected project type: ${projectType}`);
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.log(`Generating graph from JSON file: ${graphJsonPath}`);
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.log(`Generating graph with ${nodes.length} nodes and ${edges.length} edges`);
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.log(`Clearing existing graph data for project: ${this.projectId}...`);
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.log('Clearing ALL existing graph data (no projectId set)...');
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.log('Creating project indexes...');
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.log(`Importing ${nodes.length} nodes with embeddings...`);
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.log(`Created ${result[0].created} nodes in batch ${i + 1}-${batchEnd}`);
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.log(`Importing ${edges.length} edges using APOC...`);
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.log(`Created ${result[0].created} edges in batch ${i + 1}-${batchEnd}`);
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.log('Creating vector indexes...');
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.log(`No source nodes to explore at depth ${depth}`);
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.log(`No connections found at depth ${depth}`);
168
+ console.error(`No connections found at depth ${depth}`);
169
169
  break;
170
170
  }
171
171
  // Collect node IDs for next depth exploration
@@ -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.log(`[JobManager] Cleaned up ${cleaned} old jobs`);
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.log(`[JobManager] Auto-cleaned ${cleaned} old jobs to make room for new job`);
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.log(`[WatchManager] Incremental parse completed for ${state.projectId}: ` +
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.log(`[WatchManager] Synced missed changes for ${state.projectId}: ` +
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.log(`[WatchManager] Stopped watching project: ${projectId}`);
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.log(`[WatchManager] Stopped all ${projectIds.length} watchers`);
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.log(`📊 Project has ${totalFiles} files. Streaming: ${shouldUseStreaming ? 'enabled' : 'disabled'}`);
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.log(`🚀 Using streaming import with chunk size ${chunkSize}`);
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.log(`Parsed ${nodes.length} nodes / ${edges.length} edges for project ${finalProjectId}`);
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.log(`Graph data written to ${outputPath}`);
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.log('Graph generation completed:', result);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-context",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "MCP server that builds code graphs to provide rich context to LLMs",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/drewdrewH/code-graph-context#readme",