gitnexus 1.3.9 → 1.3.11

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.
Files changed (42) hide show
  1. package/README.md +194 -194
  2. package/dist/cli/ai-context.js +105 -87
  3. package/dist/cli/analyze.js +8 -0
  4. package/dist/cli/index.js +15 -25
  5. package/dist/cli/lazy-action.d.ts +6 -0
  6. package/dist/cli/lazy-action.js +18 -0
  7. package/dist/cli/setup.js +17 -19
  8. package/dist/core/augmentation/engine.js +20 -20
  9. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  10. package/dist/core/ingestion/ast-cache.js +3 -2
  11. package/dist/core/ingestion/cluster-enricher.js +16 -16
  12. package/dist/core/ingestion/pipeline.js +23 -2
  13. package/dist/core/ingestion/tree-sitter-queries.js +484 -484
  14. package/dist/core/ingestion/workers/worker-pool.js +8 -0
  15. package/dist/core/kuzu/kuzu-adapter.js +11 -11
  16. package/dist/core/kuzu/schema.js +287 -287
  17. package/dist/core/search/bm25-index.js +7 -6
  18. package/dist/core/search/hybrid-search.js +3 -3
  19. package/dist/core/wiki/graph-queries.js +52 -52
  20. package/dist/core/wiki/html-viewer.js +192 -192
  21. package/dist/core/wiki/prompts.js +82 -82
  22. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  23. package/dist/mcp/compatible-stdio-transport.js +200 -0
  24. package/dist/mcp/core/kuzu-adapter.js +6 -18
  25. package/dist/mcp/local/local-backend.js +128 -128
  26. package/dist/mcp/resources.js +42 -42
  27. package/dist/mcp/server.js +18 -18
  28. package/dist/mcp/tools.js +86 -86
  29. package/hooks/claude/gitnexus-hook.cjs +238 -155
  30. package/hooks/claude/pre-tool-use.sh +79 -79
  31. package/hooks/claude/session-start.sh +42 -42
  32. package/package.json +96 -96
  33. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  34. package/skills/gitnexus-cli.md +82 -82
  35. package/skills/gitnexus-debugging.md +89 -89
  36. package/skills/gitnexus-exploring.md +78 -78
  37. package/skills/gitnexus-guide.md +64 -64
  38. package/skills/gitnexus-impact-analysis.md +97 -97
  39. package/skills/gitnexus-pr-review.md +163 -163
  40. package/skills/gitnexus-refactoring.md +121 -121
  41. package/vendor/leiden/index.cjs +355 -355
  42. package/vendor/leiden/utils.cjs +392 -392
@@ -335,9 +335,9 @@ export class LocalBackend {
335
335
  // Find processes this symbol participates in
336
336
  let processRows = [];
337
337
  try {
338
- processRows = await executeParameterized(repo.id, `
339
- MATCH (n {id: $nodeId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
340
- RETURN p.id AS pid, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount, r.step AS step
338
+ processRows = await executeParameterized(repo.id, `
339
+ MATCH (n {id: $nodeId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
340
+ RETURN p.id AS pid, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount, r.step AS step
341
341
  `, { nodeId: sym.nodeId });
342
342
  }
343
343
  catch (e) {
@@ -347,10 +347,10 @@ export class LocalBackend {
347
347
  let cohesion = 0;
348
348
  let module;
349
349
  try {
350
- const cohesionRows = await executeParameterized(repo.id, `
351
- MATCH (n {id: $nodeId})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
352
- RETURN c.cohesion AS cohesion, c.heuristicLabel AS module
353
- LIMIT 1
350
+ const cohesionRows = await executeParameterized(repo.id, `
351
+ MATCH (n {id: $nodeId})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
352
+ RETURN c.cohesion AS cohesion, c.heuristicLabel AS module
353
+ LIMIT 1
354
354
  `, { nodeId: sym.nodeId });
355
355
  if (cohesionRows.length > 0) {
356
356
  cohesion = (cohesionRows[0].cohesion ?? cohesionRows[0][0]) || 0;
@@ -364,9 +364,9 @@ export class LocalBackend {
364
364
  let content;
365
365
  if (includeContent) {
366
366
  try {
367
- const contentRows = await executeParameterized(repo.id, `
368
- MATCH (n {id: $nodeId})
369
- RETURN n.content AS content
367
+ const contentRows = await executeParameterized(repo.id, `
368
+ MATCH (n {id: $nodeId})
369
+ RETURN n.content AS content
370
370
  `, { nodeId: sym.nodeId });
371
371
  if (contentRows.length > 0) {
372
372
  content = contentRows[0].content ?? contentRows[0][0];
@@ -474,11 +474,11 @@ export class LocalBackend {
474
474
  for (const bm25Result of bm25Results) {
475
475
  const fullPath = bm25Result.filePath;
476
476
  try {
477
- const symbols = await executeParameterized(repo.id, `
478
- MATCH (n)
479
- WHERE n.filePath = $filePath
480
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine
481
- LIMIT 3
477
+ const symbols = await executeParameterized(repo.id, `
478
+ MATCH (n)
479
+ WHERE n.filePath = $filePath
480
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine
481
+ LIMIT 3
482
482
  `, { filePath: fullPath });
483
483
  if (symbols.length > 0) {
484
484
  for (const sym of symbols) {
@@ -528,14 +528,14 @@ export class LocalBackend {
528
528
  const queryVec = await embedQuery(query);
529
529
  const dims = getEmbeddingDims();
530
530
  const queryVecStr = `[${queryVec.join(',')}]`;
531
- const vectorQuery = `
532
- CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
533
- CAST(${queryVecStr} AS FLOAT[${dims}]), ${limit})
534
- YIELD node AS emb, distance
535
- WITH emb, distance
536
- WHERE distance < 0.6
537
- RETURN emb.nodeId AS nodeId, distance
538
- ORDER BY distance
531
+ const vectorQuery = `
532
+ CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
533
+ CAST(${queryVecStr} AS FLOAT[${dims}]), ${limit})
534
+ YIELD node AS emb, distance
535
+ WITH emb, distance
536
+ WHERE distance < 0.6
537
+ RETURN emb.nodeId AS nodeId, distance
538
+ ORDER BY distance
539
539
  `;
540
540
  const embResults = await executeQuery(repo.id, vectorQuery);
541
541
  if (embResults.length === 0)
@@ -675,11 +675,11 @@ export class LocalBackend {
675
675
  try {
676
676
  // Fetch more raw communities than the display limit so aggregation has enough data
677
677
  const rawLimit = Math.max(limit * 5, 200);
678
- const clusters = await executeQuery(repo.id, `
679
- MATCH (c:Community)
680
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
681
- ORDER BY c.symbolCount DESC
682
- LIMIT ${rawLimit}
678
+ const clusters = await executeQuery(repo.id, `
679
+ MATCH (c:Community)
680
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
681
+ ORDER BY c.symbolCount DESC
682
+ LIMIT ${rawLimit}
683
683
  `);
684
684
  const rawClusters = clusters.map((c) => ({
685
685
  id: c.id || c[0],
@@ -696,11 +696,11 @@ export class LocalBackend {
696
696
  }
697
697
  if (params.showProcesses !== false) {
698
698
  try {
699
- const processes = await executeQuery(repo.id, `
700
- MATCH (p:Process)
701
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
702
- ORDER BY p.stepCount DESC
703
- LIMIT ${limit}
699
+ const processes = await executeQuery(repo.id, `
700
+ MATCH (p:Process)
701
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
702
+ ORDER BY p.stepCount DESC
703
+ LIMIT ${limit}
704
704
  `);
705
705
  result.processes = processes.map((p) => ({
706
706
  id: p.id || p[0],
@@ -730,10 +730,10 @@ export class LocalBackend {
730
730
  // Step 1: Find the symbol
731
731
  let symbols;
732
732
  if (uid) {
733
- symbols = await executeParameterized(repo.id, `
734
- MATCH (n {id: $uid})
735
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine${include_content ? ', n.content AS content' : ''}
736
- LIMIT 1
733
+ symbols = await executeParameterized(repo.id, `
734
+ MATCH (n {id: $uid})
735
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine${include_content ? ', n.content AS content' : ''}
736
+ LIMIT 1
737
737
  `, { uid });
738
738
  }
739
739
  else {
@@ -752,10 +752,10 @@ export class LocalBackend {
752
752
  whereClause = `WHERE n.name = $symName`;
753
753
  queryParams = { symName: name };
754
754
  }
755
- symbols = await executeParameterized(repo.id, `
756
- MATCH (n) ${whereClause}
757
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine${include_content ? ', n.content AS content' : ''}
758
- LIMIT 10
755
+ symbols = await executeParameterized(repo.id, `
756
+ MATCH (n) ${whereClause}
757
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine${include_content ? ', n.content AS content' : ''}
758
+ LIMIT 10
759
759
  `, queryParams);
760
760
  }
761
761
  if (symbols.length === 0) {
@@ -779,25 +779,25 @@ export class LocalBackend {
779
779
  const sym = symbols[0];
780
780
  const symId = sym.id || sym[0];
781
781
  // Categorized incoming refs
782
- const incomingRows = await executeParameterized(repo.id, `
783
- MATCH (caller)-[r:CodeRelation]->(n {id: $symId})
784
- WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
785
- RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
786
- LIMIT 30
782
+ const incomingRows = await executeParameterized(repo.id, `
783
+ MATCH (caller)-[r:CodeRelation]->(n {id: $symId})
784
+ WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
785
+ RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
786
+ LIMIT 30
787
787
  `, { symId });
788
788
  // Categorized outgoing refs
789
- const outgoingRows = await executeParameterized(repo.id, `
790
- MATCH (n {id: $symId})-[r:CodeRelation]->(target)
791
- WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
792
- RETURN r.type AS relType, target.id AS uid, target.name AS name, target.filePath AS filePath, labels(target)[0] AS kind
793
- LIMIT 30
789
+ const outgoingRows = await executeParameterized(repo.id, `
790
+ MATCH (n {id: $symId})-[r:CodeRelation]->(target)
791
+ WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
792
+ RETURN r.type AS relType, target.id AS uid, target.name AS name, target.filePath AS filePath, labels(target)[0] AS kind
793
+ LIMIT 30
794
794
  `, { symId });
795
795
  // Process participation
796
796
  let processRows = [];
797
797
  try {
798
- processRows = await executeParameterized(repo.id, `
799
- MATCH (n {id: $symId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
800
- RETURN p.id AS pid, p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
798
+ processRows = await executeParameterized(repo.id, `
799
+ MATCH (n {id: $symId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
800
+ RETURN p.id AS pid, p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
801
801
  `, { symId });
802
802
  }
803
803
  catch (e) {
@@ -852,10 +852,10 @@ export class LocalBackend {
852
852
  return this.context(repo, { name });
853
853
  }
854
854
  if (type === 'cluster') {
855
- const clusters = await executeParameterized(repo.id, `
856
- MATCH (c:Community)
857
- WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
858
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
855
+ const clusters = await executeParameterized(repo.id, `
856
+ MATCH (c:Community)
857
+ WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
858
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
859
859
  `, { clusterName: name });
860
860
  if (clusters.length === 0)
861
861
  return { error: `Cluster '${name}' not found` };
@@ -869,11 +869,11 @@ export class LocalBackend {
869
869
  totalSymbols += s;
870
870
  weightedCohesion += (c.cohesion || 0) * s;
871
871
  }
872
- const members = await executeParameterized(repo.id, `
873
- MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
874
- WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
875
- RETURN DISTINCT n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
876
- LIMIT 30
872
+ const members = await executeParameterized(repo.id, `
873
+ MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
874
+ WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
875
+ RETURN DISTINCT n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
876
+ LIMIT 30
877
877
  `, { clusterName: name });
878
878
  return {
879
879
  cluster: {
@@ -890,20 +890,20 @@ export class LocalBackend {
890
890
  };
891
891
  }
892
892
  if (type === 'process') {
893
- const processes = await executeParameterized(repo.id, `
894
- MATCH (p:Process)
895
- WHERE p.label = $processName OR p.heuristicLabel = $processName
896
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
897
- LIMIT 1
893
+ const processes = await executeParameterized(repo.id, `
894
+ MATCH (p:Process)
895
+ WHERE p.label = $processName OR p.heuristicLabel = $processName
896
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
897
+ LIMIT 1
898
898
  `, { processName: name });
899
899
  if (processes.length === 0)
900
900
  return { error: `Process '${name}' not found` };
901
901
  const proc = processes[0];
902
902
  const procId = proc.id || proc[0];
903
- const steps = await executeParameterized(repo.id, `
904
- MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
905
- RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
906
- ORDER BY r.step
903
+ const steps = await executeParameterized(repo.id, `
904
+ MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
905
+ RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
906
+ ORDER BY r.step
907
907
  `, { procId });
908
908
  return {
909
909
  process: {
@@ -964,10 +964,10 @@ export class LocalBackend {
964
964
  for (const file of changedFiles) {
965
965
  const normalizedFile = file.replace(/\\/g, '/');
966
966
  try {
967
- const symbols = await executeParameterized(repo.id, `
968
- MATCH (n) WHERE n.filePath CONTAINS $filePath
969
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
970
- LIMIT 20
967
+ const symbols = await executeParameterized(repo.id, `
968
+ MATCH (n) WHERE n.filePath CONTAINS $filePath
969
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
970
+ LIMIT 20
971
971
  `, { filePath: normalizedFile });
972
972
  for (const sym of symbols) {
973
973
  changedSymbols.push({
@@ -987,9 +987,9 @@ export class LocalBackend {
987
987
  const affectedProcesses = new Map();
988
988
  for (const sym of changedSymbols) {
989
989
  try {
990
- const procs = await executeParameterized(repo.id, `
991
- MATCH (n {id: $nodeId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
992
- RETURN p.id AS pid, p.heuristicLabel AS label, p.processType AS processType, p.stepCount AS stepCount, r.step AS step
990
+ const procs = await executeParameterized(repo.id, `
991
+ MATCH (n {id: $nodeId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
992
+ RETURN p.id AS pid, p.heuristicLabel AS label, p.processType AS processType, p.stepCount AS stepCount, r.step AS step
993
993
  `, { nodeId: sym.id });
994
994
  for (const proc of procs) {
995
995
  const pid = proc.pid || proc[0];
@@ -1193,11 +1193,11 @@ export class LocalBackend {
1193
1193
  const minConfidence = params.minConfidence ?? 0;
1194
1194
  const relTypeFilter = relationTypes.map(t => `'${t}'`).join(', ');
1195
1195
  const confidenceFilter = minConfidence > 0 ? ` AND r.confidence >= ${minConfidence}` : '';
1196
- const targets = await executeParameterized(repo.id, `
1197
- MATCH (n)
1198
- WHERE n.name = $targetName
1199
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
1200
- LIMIT 1
1196
+ const targets = await executeParameterized(repo.id, `
1197
+ MATCH (n)
1198
+ WHERE n.name = $targetName
1199
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
1200
+ LIMIT 1
1201
1201
  `, { targetName: target });
1202
1202
  if (targets.length === 0)
1203
1203
  return { error: `Target '${target}' not found` };
@@ -1255,24 +1255,24 @@ export class LocalBackend {
1255
1255
  const d1Ids = (grouped[1] || []).map((i) => `'${i.id.replace(/'/g, "''")}'`).join(', ');
1256
1256
  // Affected processes: which execution flows are broken and at which step
1257
1257
  const [processRows, moduleRows, directModuleRows] = await Promise.all([
1258
- executeQuery(repo.id, `
1259
- MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
1260
- WHERE s.id IN [${allIds}]
1261
- RETURN p.heuristicLabel AS name, COUNT(DISTINCT s.id) AS hits, MIN(r.step) AS minStep, p.stepCount AS stepCount
1262
- ORDER BY hits DESC
1263
- LIMIT 20
1258
+ executeQuery(repo.id, `
1259
+ MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
1260
+ WHERE s.id IN [${allIds}]
1261
+ RETURN p.heuristicLabel AS name, COUNT(DISTINCT s.id) AS hits, MIN(r.step) AS minStep, p.stepCount AS stepCount
1262
+ ORDER BY hits DESC
1263
+ LIMIT 20
1264
1264
  `).catch(() => []),
1265
- executeQuery(repo.id, `
1266
- MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1267
- WHERE s.id IN [${allIds}]
1268
- RETURN c.heuristicLabel AS name, COUNT(DISTINCT s.id) AS hits
1269
- ORDER BY hits DESC
1270
- LIMIT 20
1265
+ executeQuery(repo.id, `
1266
+ MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1267
+ WHERE s.id IN [${allIds}]
1268
+ RETURN c.heuristicLabel AS name, COUNT(DISTINCT s.id) AS hits
1269
+ ORDER BY hits DESC
1270
+ LIMIT 20
1271
1271
  `).catch(() => []),
1272
- d1Ids ? executeQuery(repo.id, `
1273
- MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1274
- WHERE s.id IN [${d1Ids}]
1275
- RETURN DISTINCT c.heuristicLabel AS name
1272
+ d1Ids ? executeQuery(repo.id, `
1273
+ MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1274
+ WHERE s.id IN [${d1Ids}]
1275
+ RETURN DISTINCT c.heuristicLabel AS name
1276
1276
  `).catch(() => []) : Promise.resolve([]),
1277
1277
  ]);
1278
1278
  affectedProcesses = processRows.map((r) => ({
@@ -1334,11 +1334,11 @@ export class LocalBackend {
1334
1334
  await this.ensureInitialized(repo.id);
1335
1335
  try {
1336
1336
  const rawLimit = Math.max(limit * 5, 200);
1337
- const clusters = await executeQuery(repo.id, `
1338
- MATCH (c:Community)
1339
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
1340
- ORDER BY c.symbolCount DESC
1341
- LIMIT ${rawLimit}
1337
+ const clusters = await executeQuery(repo.id, `
1338
+ MATCH (c:Community)
1339
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
1340
+ ORDER BY c.symbolCount DESC
1341
+ LIMIT ${rawLimit}
1342
1342
  `);
1343
1343
  const rawClusters = clusters.map((c) => ({
1344
1344
  id: c.id || c[0],
@@ -1361,11 +1361,11 @@ export class LocalBackend {
1361
1361
  const repo = await this.resolveRepo(repoName);
1362
1362
  await this.ensureInitialized(repo.id);
1363
1363
  try {
1364
- const processes = await executeQuery(repo.id, `
1365
- MATCH (p:Process)
1366
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
1367
- ORDER BY p.stepCount DESC
1368
- LIMIT ${limit}
1364
+ const processes = await executeQuery(repo.id, `
1365
+ MATCH (p:Process)
1366
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
1367
+ ORDER BY p.stepCount DESC
1368
+ LIMIT ${limit}
1369
1369
  `);
1370
1370
  return {
1371
1371
  processes: processes.map((p) => ({
@@ -1388,10 +1388,10 @@ export class LocalBackend {
1388
1388
  async queryClusterDetail(name, repoName) {
1389
1389
  const repo = await this.resolveRepo(repoName);
1390
1390
  await this.ensureInitialized(repo.id);
1391
- const clusters = await executeParameterized(repo.id, `
1392
- MATCH (c:Community)
1393
- WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
1394
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
1391
+ const clusters = await executeParameterized(repo.id, `
1392
+ MATCH (c:Community)
1393
+ WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
1394
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
1395
1395
  `, { clusterName: name });
1396
1396
  if (clusters.length === 0)
1397
1397
  return { error: `Cluster '${name}' not found` };
@@ -1405,11 +1405,11 @@ export class LocalBackend {
1405
1405
  totalSymbols += s;
1406
1406
  weightedCohesion += (c.cohesion || 0) * s;
1407
1407
  }
1408
- const members = await executeParameterized(repo.id, `
1409
- MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1410
- WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
1411
- RETURN DISTINCT n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
1412
- LIMIT 30
1408
+ const members = await executeParameterized(repo.id, `
1409
+ MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1410
+ WHERE c.label = $clusterName OR c.heuristicLabel = $clusterName
1411
+ RETURN DISTINCT n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
1412
+ LIMIT 30
1413
1413
  `, { clusterName: name });
1414
1414
  return {
1415
1415
  cluster: {
@@ -1432,20 +1432,20 @@ export class LocalBackend {
1432
1432
  async queryProcessDetail(name, repoName) {
1433
1433
  const repo = await this.resolveRepo(repoName);
1434
1434
  await this.ensureInitialized(repo.id);
1435
- const processes = await executeParameterized(repo.id, `
1436
- MATCH (p:Process)
1437
- WHERE p.label = $processName OR p.heuristicLabel = $processName
1438
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
1439
- LIMIT 1
1435
+ const processes = await executeParameterized(repo.id, `
1436
+ MATCH (p:Process)
1437
+ WHERE p.label = $processName OR p.heuristicLabel = $processName
1438
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
1439
+ LIMIT 1
1440
1440
  `, { processName: name });
1441
1441
  if (processes.length === 0)
1442
1442
  return { error: `Process '${name}' not found` };
1443
1443
  const proc = processes[0];
1444
1444
  const procId = proc.id || proc[0];
1445
- const steps = await executeParameterized(repo.id, `
1446
- MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
1447
- RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
1448
- ORDER BY r.step
1445
+ const steps = await executeParameterized(repo.id, `
1446
+ MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
1447
+ RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
1448
+ ORDER BY r.step
1449
1449
  `, { procId });
1450
1450
  return {
1451
1451
  process: {
@@ -256,48 +256,48 @@ async function getProcessesResource(backend, repoName) {
256
256
  * Schema resource — graph structure for Cypher queries
257
257
  */
258
258
  function getSchemaResource() {
259
- return `# GitNexus Graph Schema
260
-
261
- nodes:
262
- - File: Source code files
263
- - Folder: Directory containers
264
- - Function: Functions and arrow functions
265
- - Class: Class definitions
266
- - Interface: Interface/type definitions
267
- - Method: Class methods
268
- - CodeElement: Catch-all for other code elements
269
- - Community: Auto-detected functional area (Leiden algorithm)
270
- - Process: Execution flow trace
271
-
272
- additional_node_types: "Multi-language: Struct, Enum, Macro, Typedef, Union, Namespace, Trait, Impl, TypeAlias, Const, Static, Property, Record, Delegate, Annotation, Constructor, Template, Module (use backticks in queries: \`Struct\`, \`Enum\`, etc.)"
273
-
274
- relationships:
275
- - CONTAINS: File/Folder contains child
276
- - DEFINES: File defines a symbol
277
- - CALLS: Function/method invocation
278
- - IMPORTS: Module imports
279
- - EXTENDS: Class inheritance
280
- - IMPLEMENTS: Interface implementation
281
- - MEMBER_OF: Symbol belongs to community
282
- - STEP_IN_PROCESS: Symbol is step N in process
283
-
284
- relationship_table: "All relationships use a single CodeRelation table with a 'type' property. Properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)"
285
-
286
- example_queries:
287
- find_callers: |
288
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
289
- RETURN caller.name, caller.filePath
290
-
291
- find_community_members: |
292
- MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
293
- WHERE c.heuristicLabel = "Auth"
294
- RETURN s.name, labels(s)[0] AS type
295
-
296
- trace_process: |
297
- MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
298
- WHERE p.heuristicLabel = "LoginFlow"
299
- RETURN s.name, r.step
300
- ORDER BY r.step
259
+ return `# GitNexus Graph Schema
260
+
261
+ nodes:
262
+ - File: Source code files
263
+ - Folder: Directory containers
264
+ - Function: Functions and arrow functions
265
+ - Class: Class definitions
266
+ - Interface: Interface/type definitions
267
+ - Method: Class methods
268
+ - CodeElement: Catch-all for other code elements
269
+ - Community: Auto-detected functional area (Leiden algorithm)
270
+ - Process: Execution flow trace
271
+
272
+ additional_node_types: "Multi-language: Struct, Enum, Macro, Typedef, Union, Namespace, Trait, Impl, TypeAlias, Const, Static, Property, Record, Delegate, Annotation, Constructor, Template, Module (use backticks in queries: \`Struct\`, \`Enum\`, etc.)"
273
+
274
+ relationships:
275
+ - CONTAINS: File/Folder contains child
276
+ - DEFINES: File defines a symbol
277
+ - CALLS: Function/method invocation
278
+ - IMPORTS: Module imports
279
+ - EXTENDS: Class inheritance
280
+ - IMPLEMENTS: Interface implementation
281
+ - MEMBER_OF: Symbol belongs to community
282
+ - STEP_IN_PROCESS: Symbol is step N in process
283
+
284
+ relationship_table: "All relationships use a single CodeRelation table with a 'type' property. Properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)"
285
+
286
+ example_queries:
287
+ find_callers: |
288
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
289
+ RETURN caller.name, caller.filePath
290
+
291
+ find_community_members: |
292
+ MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
293
+ WHERE c.heuristicLabel = "Auth"
294
+ RETURN s.name, labels(s)[0] AS type
295
+
296
+ trace_process: |
297
+ MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
298
+ WHERE p.heuristicLabel = "LoginFlow"
299
+ RETURN s.name, r.step
300
+ ORDER BY r.step
301
301
  `;
302
302
  }
303
303
  /**
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { createRequire } from 'module';
14
14
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
+ import { CompatibleStdioServerTransport } from './compatible-stdio-transport.js';
16
16
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
17
17
  import { GITNEXUS_TOOLS } from './tools.js';
18
18
  import { getResourceDefinitions, getResourceTemplates, readResource } from './resources.js';
@@ -192,14 +192,14 @@ export function createMCPServer(backend) {
192
192
  role: 'user',
193
193
  content: {
194
194
  type: 'text',
195
- text: `Analyze the impact of my current code changes before committing.
196
-
197
- Follow these steps:
198
- 1. Run \`detect_changes(${JSON.stringify({ scope, ...(baseRef ? { base_ref: baseRef } : {}) })})\` to find what changed and affected processes
199
- 2. For each changed symbol in critical processes, run \`context({name: "<symbol>"})\` to see its full reference graph
200
- 3. For any high-risk items (many callers or cross-process), run \`impact({target: "<symbol>", direction: "upstream"})\` for blast radius
201
- 4. Summarize: changes, affected processes, risk level, and recommended actions
202
-
195
+ text: `Analyze the impact of my current code changes before committing.
196
+
197
+ Follow these steps:
198
+ 1. Run \`detect_changes(${JSON.stringify({ scope, ...(baseRef ? { base_ref: baseRef } : {}) })})\` to find what changed and affected processes
199
+ 2. For each changed symbol in critical processes, run \`context({name: "<symbol>"})\` to see its full reference graph
200
+ 3. For any high-risk items (many callers or cross-process), run \`impact({target: "<symbol>", direction: "upstream"})\` for blast radius
201
+ 4. Summarize: changes, affected processes, risk level, and recommended actions
202
+
203
203
  Present the analysis as a clear risk report.`,
204
204
  },
205
205
  },
@@ -214,14 +214,14 @@ Present the analysis as a clear risk report.`,
214
214
  role: 'user',
215
215
  content: {
216
216
  type: 'text',
217
- text: `Generate architecture documentation for this codebase using the knowledge graph.
218
-
219
- Follow these steps:
220
- 1. READ \`gitnexus://repo/${repo || '{name}'}/context\` for codebase stats
221
- 2. READ \`gitnexus://repo/${repo || '{name}'}/clusters\` to see all functional areas
222
- 3. READ \`gitnexus://repo/${repo || '{name}'}/processes\` to see all execution flows
223
- 4. For the top 5 most important processes, READ \`gitnexus://repo/${repo || '{name}'}/process/{name}\` for step-by-step traces
224
- 5. Generate a mermaid architecture diagram showing the major areas and their connections
217
+ text: `Generate architecture documentation for this codebase using the knowledge graph.
218
+
219
+ Follow these steps:
220
+ 1. READ \`gitnexus://repo/${repo || '{name}'}/context\` for codebase stats
221
+ 2. READ \`gitnexus://repo/${repo || '{name}'}/clusters\` to see all functional areas
222
+ 3. READ \`gitnexus://repo/${repo || '{name}'}/processes\` to see all execution flows
223
+ 4. For the top 5 most important processes, READ \`gitnexus://repo/${repo || '{name}'}/process/{name}\` for step-by-step traces
224
+ 5. Generate a mermaid architecture diagram showing the major areas and their connections
225
225
  6. Write an ARCHITECTURE.md file with: overview, functional areas, key execution flows, and the mermaid diagram`,
226
226
  },
227
227
  },
@@ -238,7 +238,7 @@ Follow these steps:
238
238
  export async function startMCPServer(backend) {
239
239
  const server = createMCPServer(backend);
240
240
  // Connect to stdio transport
241
- const transport = new StdioServerTransport();
241
+ const transport = new CompatibleStdioServerTransport();
242
242
  await server.connect(transport);
243
243
  // Graceful shutdown helper
244
244
  let shuttingDown = false;