code-graph-context 3.0.8 → 3.0.9

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.
@@ -199,7 +199,10 @@ Actions: send (post or broadcast), read (retrieve), acknowledge (mark read). Cat
199
199
  export const DEFAULTS = {
200
200
  traversalDepth: 3,
201
201
  skipOffset: 0,
202
- batchSize: 500,
202
+ /** Batch size for node imports (larger payloads due to embeddings) */
203
+ nodeBatchSize: 500,
204
+ /** Batch size for edge imports (lightweight — just IDs and type) */
205
+ edgeBatchSize: 2000,
203
206
  maxResultsDisplayed: 30,
204
207
  codeSnippetLength: 500, // Reduced from 1000 to control output size
205
208
  chainSnippetLength: 700,
@@ -18,6 +18,7 @@ export class GraphGeneratorHandler {
18
18
  neo4jService;
19
19
  embeddingsService;
20
20
  static EMBEDDED_LABEL = 'Embedded';
21
+ static GRAPH_NODE_LABEL = 'GraphNode';
21
22
  projectId = null;
22
23
  constructor(neo4jService, embeddingsService) {
23
24
  this.neo4jService = neo4jService;
@@ -29,10 +30,10 @@ export class GraphGeneratorHandler {
29
30
  setProjectId(projectId) {
30
31
  this.projectId = projectId;
31
32
  }
32
- async generateGraph(graphJsonPath, batchSize = DEFAULTS.batchSize, clearExisting = true) {
33
+ async generateGraph(graphJsonPath, clearExisting = true) {
33
34
  console.error(`Generating graph from JSON file: ${graphJsonPath}`);
34
35
  const graphData = await this.loadGraphData(graphJsonPath);
35
- return this.generateGraphFromData(graphData.nodes, graphData.edges, batchSize, clearExisting, graphData.metadata);
36
+ return this.generateGraphFromData(graphData.nodes, graphData.edges, clearExisting, graphData.metadata);
36
37
  }
37
38
  /**
38
39
  * Import nodes and edges directly from in-memory data.
@@ -41,11 +42,12 @@ export class GraphGeneratorHandler {
41
42
  * @param skipIndexes - When true, skips index creation (caller manages indexes).
42
43
  * Use this for chunked imports where indexes are created once before/after all chunks.
43
44
  */
44
- async generateGraphFromData(nodes, edges, batchSize = DEFAULTS.batchSize, clearExisting = true, metadata = {}, skipIndexes = false) {
45
+ async generateGraphFromData(nodes, edges, clearExisting = true, metadata = {}, skipIndexes = false) {
45
46
  await debugLog('Starting graph generation', {
46
47
  nodeCount: nodes.length,
47
48
  edgeCount: edges.length,
48
- batchSize,
49
+ nodeBatchSize: DEFAULTS.nodeBatchSize,
50
+ edgeBatchSize: DEFAULTS.edgeBatchSize,
49
51
  clearExisting,
50
52
  skipIndexes,
51
53
  projectId: this.projectId,
@@ -58,8 +60,8 @@ export class GraphGeneratorHandler {
58
60
  if (!skipIndexes) {
59
61
  await this.createProjectIndexes();
60
62
  }
61
- await this.importNodes(nodes, batchSize);
62
- await this.importEdges(edges, batchSize);
63
+ await this.importNodes(nodes);
64
+ await this.importEdges(edges);
63
65
  if (!skipIndexes) {
64
66
  await this.createVectorIndexes();
65
67
  }
@@ -77,7 +79,7 @@ export class GraphGeneratorHandler {
77
79
  throw error;
78
80
  }
79
81
  }
80
- async ingestConfigFiles(projectPath, projectId, globs, excludeGlobs, maxFileSizeBytes, batchSize = DEFAULTS.batchSize) {
82
+ async ingestConfigFiles(projectPath, projectId, globs, excludeGlobs, maxFileSizeBytes) {
81
83
  const files = await glob(globs, {
82
84
  cwd: projectPath,
83
85
  ignore: [...EXCLUDE_PATTERNS_GLOB, ...excludeGlobs],
@@ -129,7 +131,7 @@ export class GraphGeneratorHandler {
129
131
  });
130
132
  }
131
133
  console.error(`[config-ingest] Importing ${nodes.length} config file nodes`);
132
- await this.importNodes(nodes, batchSize);
134
+ await this.importNodes(nodes);
133
135
  return { nodesCreated: nodes.length };
134
136
  }
135
137
  /**
@@ -161,14 +163,16 @@ export class GraphGeneratorHandler {
161
163
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_INDEX_SOURCEFILE);
162
164
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_ID_INDEX_EMBEDDED);
163
165
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_ID_INDEX_SOURCEFILE);
166
+ await this.neo4jService.run(QUERIES.CREATE_PROJECT_ID_INDEX_GRAPHNODE);
164
167
  await this.neo4jService.run(QUERIES.CREATE_NORMALIZED_HASH_INDEX);
165
168
  await this.neo4jService.run(QUERIES.CREATE_SESSION_BOOKMARK_INDEX);
166
169
  await this.neo4jService.run(QUERIES.CREATE_SESSION_NOTE_INDEX);
167
170
  await this.neo4jService.run(QUERIES.CREATE_SESSION_NOTE_CATEGORY_INDEX);
168
171
  await debugLog('Project indexes created');
169
172
  }
170
- async importNodes(nodes, batchSize) {
171
- console.error(`Importing ${nodes.length} nodes with embeddings...`);
173
+ async importNodes(nodes) {
174
+ const batchSize = DEFAULTS.nodeBatchSize;
175
+ console.error(`Importing ${nodes.length} nodes with embeddings (batch size: ${batchSize})...`);
172
176
  // Pipelined: write batch N to Neo4j while embedding batch N+1.
173
177
  // This overlaps GPU work with Neo4j I/O.
174
178
  let pendingWrite = null;
@@ -218,7 +222,7 @@ export class GraphGeneratorHandler {
218
222
  // Node doesn't need embedding - prepare it immediately
219
223
  nodeResults[index] = {
220
224
  ...node,
221
- labels: node.labels,
225
+ labels: [...node.labels, GraphGeneratorHandler.GRAPH_NODE_LABEL],
222
226
  properties: {
223
227
  ...this.flattenProperties(node.properties),
224
228
  embedding: null,
@@ -242,7 +246,7 @@ export class GraphGeneratorHandler {
242
246
  embeddedCount++;
243
247
  nodeResults[item.index] = {
244
248
  ...item.node,
245
- labels: embedding ? [...item.node.labels, GraphGeneratorHandler.EMBEDDED_LABEL] : item.node.labels,
249
+ labels: embedding ? [...item.node.labels, GraphGeneratorHandler.EMBEDDED_LABEL, GraphGeneratorHandler.GRAPH_NODE_LABEL] : [...item.node.labels, GraphGeneratorHandler.GRAPH_NODE_LABEL],
246
250
  properties: {
247
251
  ...this.flattenProperties(item.node.properties),
248
252
  embedding,
@@ -265,8 +269,9 @@ export class GraphGeneratorHandler {
265
269
  }
266
270
  return nodeResults;
267
271
  }
268
- async importEdges(edges, batchSize) {
269
- console.error(`Importing ${edges.length} edges using APOC...`);
272
+ async importEdges(edges) {
273
+ const batchSize = DEFAULTS.edgeBatchSize;
274
+ console.error(`Importing ${edges.length} edges using APOC (batch size: ${batchSize})...`);
270
275
  for (let i = 0; i < edges.length; i += batchSize) {
271
276
  const batch = edges.slice(i, i + batchSize).map((edge) => ({
272
277
  ...edge,
@@ -10,7 +10,7 @@ import { ParserFactory } from '../../core/parsers/parser-factory.js';
10
10
  import { detectChangedFiles } from '../../core/utils/file-change-detection.js';
11
11
  import { resolveProjectId, getProjectName, UPSERT_PROJECT_QUERY } from '../../core/utils/project-id.js';
12
12
  import { Neo4jService, QUERIES } from '../../storage/neo4j/neo4j.service.js';
13
- import { DEFAULTS, FILE_PATHS, LOG_CONFIG } from '../constants.js';
13
+ import { FILE_PATHS, LOG_CONFIG } from '../constants.js';
14
14
  import { debugLog } from '../utils.js';
15
15
  import { deleteSourceFileSubgraphs, loadExistingNodesForEdgeDetection, getCrossFileEdges, } from './cross-file-edge.helpers.js';
16
16
  import { GraphGeneratorHandler } from './graph-generator.handler.js';
@@ -109,7 +109,7 @@ export const performIncrementalParse = async (projectPath, projectId, tsconfigPa
109
109
  await debugLog('Incremental parse: starting graph import', {});
110
110
  graphHandler.setProjectId(resolvedId);
111
111
  try {
112
- const result = await graphHandler.generateGraph(outputPath, DEFAULTS.batchSize, false);
112
+ const result = await graphHandler.generateGraph(outputPath, false);
113
113
  nodesImported = result.nodesImported;
114
114
  edgesImported = result.edgesImported;
115
115
  await debugLog('Incremental parse: graph import completed', { nodesImported, edgesImported });
@@ -117,6 +117,6 @@ export class ParallelImportHandler {
117
117
  async importToNeo4j(nodes, edges) {
118
118
  if (nodes.length === 0 && edges.length === 0)
119
119
  return;
120
- await this.graphGeneratorHandler.generateGraphFromData(nodes, edges, 100, false, {}, true);
120
+ await this.graphGeneratorHandler.generateGraphFromData(nodes, edges, false, {}, true);
121
121
  }
122
122
  }
@@ -3,7 +3,6 @@
3
3
  * Orchestrates chunked parsing and import for large codebases
4
4
  */
5
5
  import { ProgressReporter } from '../../core/utils/progress-reporter.js';
6
- import { DEFAULTS } from '../constants.js';
7
6
  import { debugLog } from '../utils.js';
8
7
  export class StreamingImportHandler {
9
8
  graphGeneratorHandler;
@@ -120,11 +119,11 @@ export class StreamingImportHandler {
120
119
  return result;
121
120
  }
122
121
  async importChunkToNeo4j(nodes, edges) {
123
- await this.graphGeneratorHandler.generateGraphFromData(nodes, edges, DEFAULTS.batchSize, false, {}, true);
122
+ await this.graphGeneratorHandler.generateGraphFromData(nodes, edges, false, {}, true);
124
123
  }
125
124
  async importEdgesToNeo4j(edges) {
126
125
  if (edges.length === 0)
127
126
  return;
128
- await this.graphGeneratorHandler.generateGraphFromData([], edges, DEFAULTS.batchSize, false, {}, true);
127
+ await this.graphGeneratorHandler.generateGraphFromData([], edges, false, {}, true);
129
128
  }
130
129
  }
@@ -16,7 +16,7 @@ import { ParserFactory } from '../../core/parsers/parser-factory.js';
16
16
  import { detectChangedFiles } from '../../core/utils/file-change-detection.js';
17
17
  import { resolveProjectId, getProjectName, UPSERT_PROJECT_QUERY, UPDATE_PROJECT_STATUS_QUERY, } from '../../core/utils/project-id.js';
18
18
  import { Neo4jService, QUERIES } from '../../storage/neo4j/neo4j.service.js';
19
- import { TOOL_NAMES, TOOL_METADATA, DEFAULTS, FILE_PATHS, LOG_CONFIG, PARSING, CONFIG_FILE_PATTERNS, } from '../constants.js';
19
+ import { TOOL_NAMES, TOOL_METADATA, FILE_PATHS, LOG_CONFIG, PARSING, CONFIG_FILE_PATTERNS, } from '../constants.js';
20
20
  import { deleteSourceFileSubgraphs, loadExistingNodesForEdgeDetection, getCrossFileEdges, } from '../handlers/cross-file-edge.helpers.js';
21
21
  import { GraphGeneratorHandler } from '../handlers/graph-generator.handler.js';
22
22
  import { StreamingImportHandler } from '../handlers/streaming-import.handler.js';
@@ -315,7 +315,7 @@ export const createParseTypescriptProjectTool = (server) => {
315
315
  try {
316
316
  // Set projectId for project-scoped operations (clear, indexes)
317
317
  graphGeneratorHandler.setProjectId(finalProjectId);
318
- const result = await graphGeneratorHandler.generateGraph(outputPath, DEFAULTS.batchSize, clearExisting);
318
+ const result = await graphGeneratorHandler.generateGraph(outputPath, clearExisting);
319
319
  // Recreate cross-file edges after incremental parse
320
320
  if (!clearExisting && savedCrossFileEdges.length > 0) {
321
321
  await debugLog('Recreating cross-file edges', { edgesToRecreate: savedCrossFileEdges.length });
@@ -87,6 +87,8 @@ export const QUERIES = {
87
87
  // Create composite indexes on projectId + id for efficient lookups
88
88
  CREATE_PROJECT_ID_INDEX_EMBEDDED: 'CREATE INDEX project_id_embedded_idx IF NOT EXISTS FOR (n:Embedded) ON (n.projectId, n.id)',
89
89
  CREATE_PROJECT_ID_INDEX_SOURCEFILE: 'CREATE INDEX project_id_sourcefile_idx IF NOT EXISTS FOR (n:SourceFile) ON (n.projectId, n.id)',
90
+ // Create composite index on GraphNode for efficient edge resolution lookups
91
+ CREATE_PROJECT_ID_INDEX_GRAPHNODE: 'CREATE INDEX project_id_graphnode_idx IF NOT EXISTS FOR (n:GraphNode) ON (n.projectId, n.id)',
90
92
  // Create index on normalizedHash for efficient structural duplicate detection
91
93
  CREATE_NORMALIZED_HASH_INDEX: 'CREATE INDEX normalized_hash_idx IF NOT EXISTS FOR (n:Embedded) ON (n.normalizedHash)',
92
94
  CREATE_NODE: `
@@ -96,8 +98,8 @@ export const QUERIES = {
96
98
  `,
97
99
  CREATE_RELATIONSHIP: `
98
100
  UNWIND $edges AS edgeData
99
- MATCH (start) WHERE start.id = edgeData.startNodeId AND start.projectId = $projectId
100
- MATCH (end) WHERE end.id = edgeData.endNodeId AND end.projectId = $projectId
101
+ MATCH (start:GraphNode) WHERE start.id = edgeData.startNodeId AND start.projectId = $projectId
102
+ MATCH (end:GraphNode) WHERE end.id = edgeData.endNodeId AND end.projectId = $projectId
101
103
  WITH start, end, edgeData
102
104
  CALL apoc.create.relationship(start, edgeData.type, edgeData.properties, end) YIELD rel
103
105
  RETURN count(*) as created
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-context",
3
- "version": "3.0.8",
3
+ "version": "3.0.9",
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/andrew-hernandez-paragon/code-graph-context#readme",