code-graph-context 2.4.0 → 2.4.2

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/cli.js CHANGED
@@ -246,7 +246,7 @@ program
246
246
  .option('-p, --port <port>', 'Neo4j Bolt port', '7687')
247
247
  .option('--http-port <port>', 'Neo4j Browser port', '7474')
248
248
  .option('--password <password>', 'Neo4j password', 'PASSWORD')
249
- .option('-m, --memory <size>', 'Max heap memory (e.g., 2G, 4G)', '2G')
249
+ .option('-m, --memory <size>', 'Max heap memory (e.g., 2G, 4G)', '4G')
250
250
  .option('-f, --force', 'Recreate container even if exists')
251
251
  .action(runInit);
252
252
  program.command('status').description('Check Neo4j and Docker status').action(runStatus);
@@ -525,13 +525,11 @@ export const FAIRSQUARE_FRAMEWORK_SCHEMA = {
525
525
  {
526
526
  type: 'function',
527
527
  pattern: (parsedNode) => {
528
- const node = parsedNode.sourceNode;
529
- if (!node || !Node.isVariableDeclaration(node))
530
- return false;
531
- const name = node.getName();
532
- const typeNode = node.getTypeNode();
528
+ // Use pre-extracted properties (works after AST cleanup in streaming/chunking)
529
+ const name = parsedNode.properties.name;
530
+ const typeAnnotation = parsedNode.properties.typeAnnotation;
533
531
  // Check if variable name ends with "Routes" AND has type ModuleRoute[]
534
- return !!name.endsWith('Routes') && !!typeNode?.getText().includes('ModuleRoute');
532
+ return !!name?.endsWith('Routes') && !!typeAnnotation?.includes('ModuleRoute');
535
533
  },
536
534
  confidence: 1.0,
537
535
  priority: 10,
@@ -696,14 +694,8 @@ export const FAIRSQUARE_FRAMEWORK_SCHEMA = {
696
694
  if (matchingRoutes.length === 0)
697
695
  return false;
698
696
  // CRITICAL FIX: Verify the method belongs to the correct controller
699
- // Find the parent class of this method by checking the AST node
700
- const targetNode = parsedTargetNode.sourceNode;
701
- if (!targetNode || !Node.isMethodDeclaration(targetNode))
702
- return false;
703
- const parentClass = targetNode.getParent();
704
- if (!parentClass || !Node.isClassDeclaration(parentClass))
705
- return false;
706
- const parentClassName = parentClass.getName();
697
+ // Use pre-extracted parentClassName property (works after AST cleanup in streaming/chunking)
698
+ const parentClassName = parsedTargetNode.properties.parentClassName;
707
699
  if (!parentClassName)
708
700
  return false;
709
701
  // Check if any matching route's controller name matches the parent class
@@ -434,7 +434,8 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
434
434
  },
435
435
  {
436
436
  type: 'function',
437
- pattern: (node) => node.getName()?.endsWith('Service'),
437
+ // Use pre-extracted name property (works after AST cleanup in streaming/chunking)
438
+ pattern: (parsedNode) => parsedNode.properties?.name?.endsWith('Service'),
438
439
  confidence: 0.7,
439
440
  priority: 7,
440
441
  },
@@ -516,10 +517,11 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
516
517
  detectionPatterns: [
517
518
  {
518
519
  type: 'function',
519
- pattern: (node) => {
520
- const decorators = node.getDecorators?.() ?? [];
520
+ // Use pre-extracted decoratorNames from context (works after AST cleanup in streaming/chunking)
521
+ pattern: (parsedNode) => {
522
+ const decoratorNames = parsedNode.properties?.context?.decoratorNames ?? [];
521
523
  const messageDecorators = ['MessagePattern', 'EventPattern'];
522
- return decorators.some((d) => messageDecorators.includes(d.getName()));
524
+ return messageDecorators.some((d) => decoratorNames.includes(d));
523
525
  },
524
526
  confidence: 0.98,
525
527
  priority: 15,
@@ -567,10 +569,11 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
567
569
  detectionPatterns: [
568
570
  {
569
571
  type: 'function',
570
- pattern: (node) => {
571
- const decorators = node.getDecorators?.() ?? [];
572
+ // Use pre-extracted decoratorNames from context (works after AST cleanup in streaming/chunking)
573
+ pattern: (parsedNode) => {
574
+ const decoratorNames = parsedNode.properties?.context?.decoratorNames ?? [];
572
575
  const httpDecorators = ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Head', 'Options'];
573
- return decorators.some((d) => httpDecorators.includes(d.getName()));
576
+ return httpDecorators.some((d) => decoratorNames.includes(d));
574
577
  },
575
578
  confidence: 0.98,
576
579
  priority: 15,
@@ -723,16 +726,11 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
723
726
  if (parsedSourceNode.properties?.filePath !== parsedTargetNode.properties?.filePath) {
724
727
  return false;
725
728
  }
726
- // Access AST nodes to check parent relationship
727
- const sourceNode = parsedSourceNode.sourceNode;
728
- const targetNode = parsedTargetNode.sourceNode;
729
- if (sourceNode && targetNode) {
730
- const methodParent = targetNode.getParent();
731
- if (methodParent === sourceNode) {
732
- return true;
733
- }
734
- }
735
- return false;
729
+ // CRITICAL FIX: Use pre-extracted parentClassName property (works after AST cleanup in streaming/chunking)
730
+ // The method's parentClassName should match the controller's name
731
+ const targetParentClassName = parsedTargetNode.properties?.parentClassName;
732
+ const sourceName = parsedSourceNode.properties?.name;
733
+ return !!targetParentClassName && targetParentClassName === sourceName;
736
734
  },
737
735
  contextExtractor: (parsedSourceNode, parsedTargetNode) => ({
738
736
  endpointType: 'RPC',
@@ -755,16 +753,11 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
755
753
  if (parsedSourceNode.properties?.filePath !== parsedTargetNode.properties?.filePath) {
756
754
  return false;
757
755
  }
758
- // Access AST nodes to check parent relationship
759
- const sourceNode = parsedSourceNode.sourceNode;
760
- const targetNode = parsedTargetNode.sourceNode;
761
- if (sourceNode && targetNode) {
762
- const methodParent = targetNode.getParent();
763
- if (methodParent === sourceNode) {
764
- return true;
765
- }
766
- }
767
- return false;
756
+ // CRITICAL FIX: Use pre-extracted parentClassName property (works after AST cleanup in streaming/chunking)
757
+ // The method's parentClassName should match the controller's name
758
+ const targetParentClassName = parsedTargetNode.properties?.parentClassName;
759
+ const sourceName = parsedSourceNode.properties?.name;
760
+ return !!targetParentClassName && targetParentClassName === sourceName;
768
761
  },
769
762
  contextExtractor: (parsedSourceNode, parsedTargetNode) => ({
770
763
  httpMethod: parsedTargetNode.properties?.context?.httpMethod ?? '',
@@ -480,6 +480,12 @@ export const CORE_TYPESCRIPT_SCHEMA = {
480
480
  extraction: { method: 'static', defaultValue: false }, // We'll set this manually
481
481
  neo4j: { indexed: true, unique: false, required: true },
482
482
  },
483
+ {
484
+ name: 'typeAnnotation',
485
+ type: 'string',
486
+ extraction: { method: 'ast', source: 'getTypeNode', transform: 'getText' },
487
+ neo4j: { indexed: false, unique: false, required: false },
488
+ },
483
489
  ],
484
490
  relationships: [],
485
491
  children: {},
@@ -277,16 +277,24 @@ Provide ONLY the JSON response with no additional text, markdown formatting, or
277
277
  frameworkHint = '\nFRAMEWORK DETECTED: React/functional codebase. Use Function nodes for components.';
278
278
  }
279
279
  return `
280
- ACTUAL GRAPH SCHEMA (use these exact labels):
280
+ === VALID NODE LABELS (use ONLY these after the colon) ===
281
+ ${nodeTypes}
281
282
 
282
- Node Types: ${nodeTypes}
283
- Relationship Types: ${relTypes}
284
- Semantic Types: ${semTypes}
283
+ === VALID RELATIONSHIP TYPES ===
284
+ ${relTypes}
285
+
286
+ === SEMANTIC TYPES (these are PROPERTY values, NOT labels) ===
287
+ ${semTypes}
288
+ Query semantic types via property: WHERE n.semanticType = 'TypeName'
285
289
  ${frameworkHint}
286
- CRITICAL: Use ONLY these node labels. Do NOT invent labels like :DbService, :UserService, etc.
287
- For queries about specific classes/services, use: (n:Class {name: 'ClassName'})
288
- For inheritance: (child:Class)-[:EXTENDS]->(parent:Class {name: 'ParentName'})
289
- For decorator-based queries: Use semanticType property with values from the discovered semantic types above.
290
+
291
+ === CRITICAL RULES ===
292
+ 1. Use ONLY the labels listed above after the colon (:Label)
293
+ 2. Semantic types are PROPERTY values, NOT labels
294
+ 3. Class/service names are PROPERTY values, NOT labels
295
+ 4. WRONG: (n:MyService), (n:MyController) - names as labels
296
+ 5. CORRECT: (n:Service {name: 'MyService'}), (n:Controller {name: 'MyController'})
297
+ 6. CORRECT: (n:Class) WHERE n.semanticType = 'Service'
290
298
  `.trim();
291
299
  }
292
300
  catch (error) {
@@ -507,7 +515,7 @@ Remember to include WHERE n.projectId = $projectId for all node patterns.
507
515
  }
508
516
  /**
509
517
  * Load valid labels dynamically from the schema file.
510
- * Returns all keys from rawSchema which represent actual Neo4j labels.
518
+ * Returns all keys from rawSchema AND discoveredSchema.nodeTypes which represent actual Neo4j labels.
511
519
  */
512
520
  loadValidLabelsFromSchema() {
513
521
  // Fallback to core TypeScript labels if schema not available
@@ -535,12 +543,21 @@ Remember to include WHERE n.projectId = $projectId for all node patterns.
535
543
  try {
536
544
  const content = fs.readFileSync(this.schemaPath, 'utf-8');
537
545
  const schema = JSON.parse(content);
538
- if (!schema.rawSchema?.records?.[0]?._fields?.[0]) {
539
- return coreLabels;
546
+ const allLabels = new Set(coreLabels);
547
+ // Extract labels from rawSchema keys
548
+ if (schema.rawSchema?.records?.[0]?._fields?.[0]) {
549
+ const schemaLabels = Object.keys(schema.rawSchema.records[0]._fields[0]);
550
+ schemaLabels.forEach((label) => allLabels.add(label));
551
+ }
552
+ // Also extract labels from discoveredSchema.nodeTypes (includes framework labels)
553
+ if (schema.discoveredSchema?.nodeTypes) {
554
+ for (const nodeType of schema.discoveredSchema.nodeTypes) {
555
+ if (nodeType.label) {
556
+ allLabels.add(nodeType.label);
557
+ }
558
+ }
540
559
  }
541
- // Extract all keys from rawSchema - these are the valid labels
542
- const schemaLabels = Object.keys(schema.rawSchema.records[0]._fields[0]);
543
- return new Set([...coreLabels, ...schemaLabels]);
560
+ return allLabels;
544
561
  }
545
562
  catch {
546
563
  return coreLabels;
@@ -542,6 +542,11 @@ export class TypeScriptParser {
542
542
  return astNode.getName();
543
543
  }
544
544
  break;
545
+ case CoreNodeType.VARIABLE_DECLARATION:
546
+ if (Node.isVariableDeclaration(astNode)) {
547
+ return astNode.getName();
548
+ }
549
+ break;
545
550
  default:
546
551
  return astNode.getKindName();
547
552
  }
@@ -552,13 +557,18 @@ export class TypeScriptParser {
552
557
  return 'Unknown';
553
558
  }
554
559
  extractProperty(astNode, propDef) {
555
- const { method, source, defaultValue } = propDef.extraction;
560
+ const { method, source, defaultValue, transform } = propDef.extraction;
556
561
  try {
557
562
  switch (method) {
558
563
  case 'ast':
559
564
  if (typeof source === 'string') {
560
565
  const fn = astNode[source];
561
- return typeof fn === 'function' ? fn.call(astNode) : defaultValue;
566
+ let result = typeof fn === 'function' ? fn.call(astNode) : defaultValue;
567
+ // Apply transform if specified (e.g., 'getText' on a returned node)
568
+ if (result && transform && typeof result[transform] === 'function') {
569
+ result = result[transform]();
570
+ }
571
+ return result ?? defaultValue;
562
572
  }
563
573
  return defaultValue;
564
574
  case 'function':
@@ -1284,11 +1294,9 @@ export class TypeScriptParser {
1284
1294
  }
1285
1295
  case 'function':
1286
1296
  if (typeof pattern.pattern === 'function') {
1287
- // Pass the AST sourceNode to pattern functions, not the ParsedNode wrapper
1288
- const astNode = node.sourceNode;
1289
- if (!astNode)
1290
- return false;
1291
- return pattern.pattern(astNode);
1297
+ // Pass the ParsedNode to pattern functions
1298
+ // Patterns should use pre-extracted properties for cross-chunk compatibility
1299
+ return pattern.pattern(node);
1292
1300
  }
1293
1301
  return false;
1294
1302
  case 'classname':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-context",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
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",