gitnexus 1.1.0 → 1.1.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.
@@ -15,7 +15,7 @@ const DEFAULT_CONFIG = {
15
15
  maxTraceDepth: 10,
16
16
  maxBranching: 4,
17
17
  maxProcesses: 75,
18
- minSteps: 2,
18
+ minSteps: 3, // 3+ steps = genuine multi-hop flow (2-step is just "A calls B")
19
19
  };
20
20
  // ============================================================================
21
21
  // MAIN PROCESSOR
@@ -51,10 +51,13 @@ export const processProcesses = async (knowledgeGraph, memberships, onProgress,
51
51
  }
52
52
  }
53
53
  onProgress?.(`Found ${allTraces.length} traces, deduplicating...`, 60);
54
- // Step 3: Deduplicate similar traces
54
+ // Step 3: Deduplicate similar traces (subset removal)
55
55
  const uniqueTraces = deduplicateTraces(allTraces);
56
+ // Step 3b: Deduplicate by entry+terminal pair (keep longest path per pair)
57
+ const endpointDeduped = deduplicateByEndpoints(uniqueTraces);
58
+ onProgress?.(`Deduped ${uniqueTraces.length} → ${endpointDeduped.length} unique endpoint pairs`, 70);
56
59
  // Step 4: Limit to max processes (prioritize longer traces)
57
- const limitedTraces = uniqueTraces
60
+ const limitedTraces = endpointDeduped
58
61
  .sort((a, b) => b.length - a.length)
59
62
  .slice(0, cfg.maxProcesses);
60
63
  onProgress?.(`Creating ${limitedTraces.length} process nodes...`, 80);
@@ -266,6 +269,27 @@ const deduplicateTraces = (traces) => {
266
269
  return unique;
267
270
  };
268
271
  // ============================================================================
272
+ // HELPER: Deduplicate by entry+terminal endpoints
273
+ // ============================================================================
274
+ /**
275
+ * Keep only the longest trace per unique entry→terminal pair.
276
+ * Multiple paths between the same two endpoints are redundant for agents.
277
+ */
278
+ const deduplicateByEndpoints = (traces) => {
279
+ if (traces.length === 0)
280
+ return [];
281
+ const byEndpoints = new Map();
282
+ // Sort longest first so the first seen per key is the longest
283
+ const sorted = [...traces].sort((a, b) => b.length - a.length);
284
+ for (const trace of sorted) {
285
+ const key = `${trace[0]}::${trace[trace.length - 1]}`;
286
+ if (!byEndpoints.has(key)) {
287
+ byEndpoints.set(key, trace);
288
+ }
289
+ }
290
+ return Array.from(byEndpoints.values());
291
+ };
292
+ // ============================================================================
269
293
  // HELPER: String utilities
270
294
  // ============================================================================
271
295
  const capitalize = (s) => {
@@ -11,11 +11,11 @@ import { queryFTS } from '../kuzu/kuzu-adapter.js';
11
11
  */
12
12
  async function queryFTSViaExecutor(executor, tableName, indexName, query, limit) {
13
13
  const escapedQuery = query.replace(/'/g, "''");
14
- const cypher = `
15
- CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', '${escapedQuery}', conjunctive := false)
16
- RETURN node, score
17
- ORDER BY score DESC
18
- LIMIT ${limit}
14
+ const cypher = `
15
+ CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', '${escapedQuery}', conjunctive := false)
16
+ RETURN node, score
17
+ ORDER BY score DESC
18
+ LIMIT ${limit}
19
19
  `;
20
20
  try {
21
21
  const rows = await executor(cypher);
@@ -11,19 +11,9 @@ export interface CodebaseContext {
11
11
  stats: {
12
12
  fileCount: number;
13
13
  functionCount: number;
14
- classCount: number;
15
- interfaceCount: number;
16
- methodCount: number;
17
14
  communityCount: number;
18
15
  processCount: number;
19
16
  };
20
- hotspots: Array<{
21
- name: string;
22
- type: string;
23
- filePath: string;
24
- connections: number;
25
- }>;
26
- folderTree: string;
27
17
  }
28
18
  interface RepoHandle {
29
19
  id: string;
@@ -57,24 +47,10 @@ export declare class LocalBackend {
57
47
  */
58
48
  resolveRepo(repoParam?: string): RepoHandle;
59
49
  private ensureInitialized;
60
- get isReady(): boolean;
61
50
  /**
62
51
  * Get context for a specific repo (or the single repo if only one).
63
52
  */
64
53
  getContext(repoId?: string): CodebaseContext | null;
65
- /**
66
- * Backwards-compatible getter — returns context of single repo or null.
67
- */
68
- get context(): CodebaseContext | null;
69
- getRepoPath(repoId?: string): string | null;
70
- get repoPath(): string | null;
71
- getMeta(repoId?: string): {
72
- lastCommit: string;
73
- indexedAt: string;
74
- stats?: any;
75
- } | null;
76
- get meta(): any;
77
- get storagePath(): string | null;
78
54
  /**
79
55
  * List all registered repos with their metadata.
80
56
  */
@@ -96,6 +72,12 @@ export declare class LocalBackend {
96
72
  */
97
73
  private semanticSearch;
98
74
  private cypher;
75
+ /**
76
+ * Aggregate same-named clusters: group by heuristicLabel, sum symbols,
77
+ * weighted-average cohesion, filter out tiny clusters (<5 symbols).
78
+ * Raw communities stay intact in KuzuDB for Cypher queries.
79
+ */
80
+ private aggregateClusters;
99
81
  private overview;
100
82
  private explore;
101
83
  private impact;
@@ -57,14 +57,9 @@ export class LocalBackend {
57
57
  stats: {
58
58
  fileCount: s.files || 0,
59
59
  functionCount: s.nodes || 0,
60
- classCount: 0,
61
- interfaceCount: 0,
62
- methodCount: 0,
63
60
  communityCount: s.communities || 0,
64
61
  processCount: s.processes || 0,
65
62
  },
66
- hotspots: [],
67
- folderTree: '',
68
63
  });
69
64
  }
70
65
  return this.repos.size > 0;
@@ -137,9 +132,6 @@ export class LocalBackend {
137
132
  this.initializedRepos.add(repoId);
138
133
  }
139
134
  // ─── Public Getters ──────────────────────────────────────────────
140
- get isReady() {
141
- return this.repos.size > 0;
142
- }
143
135
  /**
144
136
  * Get context for a specific repo (or the single repo if only one).
145
137
  */
@@ -152,38 +144,6 @@ export class LocalBackend {
152
144
  }
153
145
  return null;
154
146
  }
155
- /**
156
- * Backwards-compatible getter — returns context of single repo or null.
157
- */
158
- get context() {
159
- return this.getContext();
160
- }
161
- getRepoPath(repoId) {
162
- if (repoId)
163
- return this.repos.get(repoId)?.repoPath ?? null;
164
- if (this.repos.size === 1)
165
- return this.repos.values().next().value?.repoPath ?? null;
166
- return null;
167
- }
168
- get repoPath() {
169
- return this.getRepoPath();
170
- }
171
- getMeta(repoId) {
172
- const handle = repoId
173
- ? this.repos.get(repoId)
174
- : this.repos.size === 1 ? this.repos.values().next().value : null;
175
- if (!handle)
176
- return null;
177
- return { lastCommit: handle.lastCommit, indexedAt: handle.indexedAt, stats: handle.stats };
178
- }
179
- get meta() {
180
- return this.getMeta();
181
- }
182
- get storagePath() {
183
- if (this.repos.size === 1)
184
- return this.repos.values().next().value?.storagePath ?? null;
185
- return null;
186
- }
187
147
  /**
188
148
  * List all registered repos with their metadata.
189
149
  */
@@ -274,10 +234,10 @@ export class LocalBackend {
274
234
  // Add cluster membership context for each result with a nodeId
275
235
  if (result.nodeId) {
276
236
  try {
277
- const clusterQuery = `
278
- MATCH (n {id: '${result.nodeId.replace(/'/g, "''")}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
279
- RETURN c.label AS label, c.heuristicLabel AS heuristicLabel
280
- LIMIT 1
237
+ const clusterQuery = `
238
+ MATCH (n {id: '${result.nodeId.replace(/'/g, "''")}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
239
+ RETURN c.label AS label, c.heuristicLabel AS heuristicLabel
240
+ LIMIT 1
281
241
  `;
282
242
  const clusters = await executeQuery(repo.id, clusterQuery);
283
243
  if (clusters.length > 0) {
@@ -294,10 +254,10 @@ export class LocalBackend {
294
254
  // Add relationships if depth is 'full' and we have a node ID
295
255
  if (depth === 'full' && result.nodeId) {
296
256
  try {
297
- const relQuery = `
298
- MATCH (n {id: '${result.nodeId.replace(/'/g, "''")}'})-[r:CodeRelation]->(m)
299
- RETURN r.type AS type, m.name AS targetName, m.filePath AS targetPath
300
- LIMIT 5
257
+ const relQuery = `
258
+ MATCH (n {id: '${result.nodeId.replace(/'/g, "''")}'})-[r:CodeRelation]->(m)
259
+ RETURN r.type AS type, m.name AS targetName, m.filePath AS targetPath
260
+ LIMIT 5
301
261
  `;
302
262
  const rels = await executeQuery(repo.id, relQuery);
303
263
  result.connections = rels.map((rel) => ({
@@ -324,11 +284,11 @@ export class LocalBackend {
324
284
  for (const bm25Result of bm25Results) {
325
285
  const fileName = bm25Result.filePath.split('/').pop() || bm25Result.filePath;
326
286
  try {
327
- const symbolQuery = `
328
- MATCH (n)
329
- WHERE n.filePath CONTAINS '${fileName.replace(/'/g, "''")}'
330
- 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
331
- LIMIT 3
287
+ const symbolQuery = `
288
+ MATCH (n)
289
+ WHERE n.filePath CONTAINS '${fileName.replace(/'/g, "''")}'
290
+ 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
291
+ LIMIT 3
332
292
  `;
333
293
  const symbols = await executeQuery(repo.id, symbolQuery);
334
294
  if (symbols.length > 0) {
@@ -372,14 +332,14 @@ export class LocalBackend {
372
332
  const queryVec = await embedQuery(query);
373
333
  const dims = getEmbeddingDims();
374
334
  const queryVecStr = `[${queryVec.join(',')}]`;
375
- const vectorQuery = `
376
- CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
377
- CAST(${queryVecStr} AS FLOAT[${dims}]), ${limit})
378
- YIELD node AS emb, distance
379
- WITH emb, distance
380
- WHERE distance < 0.6
381
- RETURN emb.nodeId AS nodeId, distance
382
- ORDER BY distance
335
+ const vectorQuery = `
336
+ CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx',
337
+ CAST(${queryVecStr} AS FLOAT[${dims}]), ${limit})
338
+ YIELD node AS emb, distance
339
+ WITH emb, distance
340
+ WHERE distance < 0.6
341
+ RETURN emb.nodeId AS nodeId, distance
342
+ ORDER BY distance
383
343
  `;
384
344
  const embResults = await executeQuery(repo.id, vectorQuery);
385
345
  if (embResults.length === 0)
@@ -430,6 +390,42 @@ export class LocalBackend {
430
390
  return { error: err.message || 'Query failed' };
431
391
  }
432
392
  }
393
+ /**
394
+ * Aggregate same-named clusters: group by heuristicLabel, sum symbols,
395
+ * weighted-average cohesion, filter out tiny clusters (<5 symbols).
396
+ * Raw communities stay intact in KuzuDB for Cypher queries.
397
+ */
398
+ aggregateClusters(clusters) {
399
+ const groups = new Map();
400
+ for (const c of clusters) {
401
+ const label = c.heuristicLabel || c.label || 'Unknown';
402
+ const symbols = c.symbolCount || 0;
403
+ const cohesion = c.cohesion || 0;
404
+ const existing = groups.get(label);
405
+ if (!existing) {
406
+ groups.set(label, { ids: [c.id], totalSymbols: symbols, weightedCohesion: cohesion * symbols, largest: c });
407
+ }
408
+ else {
409
+ existing.ids.push(c.id);
410
+ existing.totalSymbols += symbols;
411
+ existing.weightedCohesion += cohesion * symbols;
412
+ if (symbols > (existing.largest.symbolCount || 0)) {
413
+ existing.largest = c;
414
+ }
415
+ }
416
+ }
417
+ return Array.from(groups.entries())
418
+ .map(([label, g]) => ({
419
+ id: g.largest.id,
420
+ label,
421
+ heuristicLabel: label,
422
+ symbolCount: g.totalSymbols,
423
+ cohesion: g.totalSymbols > 0 ? g.weightedCohesion / g.totalSymbols : 0,
424
+ subCommunities: g.ids.length,
425
+ }))
426
+ .filter(c => c.symbolCount >= 5)
427
+ .sort((a, b) => b.symbolCount - a.symbolCount);
428
+ }
433
429
  async overview(repo, params) {
434
430
  await this.ensureInitialized(repo.id);
435
431
  const limit = params.limit || 20;
@@ -442,19 +438,22 @@ export class LocalBackend {
442
438
  };
443
439
  if (params.showClusters !== false) {
444
440
  try {
445
- const clusters = await executeQuery(repo.id, `
446
- MATCH (c:Community)
447
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
448
- ORDER BY c.symbolCount DESC
449
- LIMIT ${limit}
441
+ // Fetch more raw communities than the display limit so aggregation has enough data
442
+ const rawLimit = Math.max(limit * 5, 200);
443
+ const clusters = await executeQuery(repo.id, `
444
+ MATCH (c:Community)
445
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
446
+ ORDER BY c.symbolCount DESC
447
+ LIMIT ${rawLimit}
450
448
  `);
451
- result.clusters = clusters.map((c) => ({
449
+ const rawClusters = clusters.map((c) => ({
452
450
  id: c.id || c[0],
453
451
  label: c.label || c[1],
454
452
  heuristicLabel: c.heuristicLabel || c[2],
455
453
  cohesion: c.cohesion || c[3],
456
454
  symbolCount: c.symbolCount || c[4],
457
455
  }));
456
+ result.clusters = this.aggregateClusters(rawClusters).slice(0, limit);
458
457
  }
459
458
  catch {
460
459
  result.clusters = [];
@@ -462,11 +461,11 @@ export class LocalBackend {
462
461
  }
463
462
  if (params.showProcesses !== false) {
464
463
  try {
465
- const processes = await executeQuery(repo.id, `
466
- MATCH (p:Process)
467
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
468
- ORDER BY p.stepCount DESC
469
- LIMIT ${limit}
464
+ const processes = await executeQuery(repo.id, `
465
+ MATCH (p:Process)
466
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
467
+ ORDER BY p.stepCount DESC
468
+ LIMIT ${limit}
470
469
  `);
471
470
  result.processes = processes.map((p) => ({
472
471
  id: p.id || p[0],
@@ -486,33 +485,33 @@ export class LocalBackend {
486
485
  await this.ensureInitialized(repo.id);
487
486
  const { name, type } = params;
488
487
  if (type === 'symbol') {
489
- const symbolQuery = `
490
- MATCH (n)
491
- WHERE n.name = '${name.replace(/'/g, "''")}'
492
- 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
493
- LIMIT 1
488
+ const symbolQuery = `
489
+ MATCH (n)
490
+ WHERE n.name = '${name.replace(/'/g, "''")}'
491
+ 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
492
+ LIMIT 1
494
493
  `;
495
494
  const symbols = await executeQuery(repo.id, symbolQuery);
496
495
  if (symbols.length === 0)
497
496
  return { error: `Symbol '${name}' not found` };
498
497
  const sym = symbols[0];
499
498
  const symId = sym.id || sym[0];
500
- const callersQuery = `
501
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${symId}'})
502
- RETURN caller.name AS name, caller.filePath AS filePath
503
- LIMIT 10
499
+ const callersQuery = `
500
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${symId}'})
501
+ RETURN caller.name AS name, caller.filePath AS filePath
502
+ LIMIT 10
504
503
  `;
505
504
  const callers = await executeQuery(repo.id, callersQuery);
506
- const calleesQuery = `
507
- MATCH (n {id: '${symId}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
508
- RETURN callee.name AS name, callee.filePath AS filePath
509
- LIMIT 10
505
+ const calleesQuery = `
506
+ MATCH (n {id: '${symId}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
507
+ RETURN callee.name AS name, callee.filePath AS filePath
508
+ LIMIT 10
510
509
  `;
511
510
  const callees = await executeQuery(repo.id, calleesQuery);
512
- const communityQuery = `
513
- MATCH (n {id: '${symId}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
514
- RETURN c.label AS label, c.heuristicLabel AS heuristicLabel
515
- LIMIT 1
511
+ const communityQuery = `
512
+ MATCH (n {id: '${symId}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
513
+ RETURN c.label AS label, c.heuristicLabel AS heuristicLabel
514
+ LIMIT 1
516
515
  `;
517
516
  const communities = await executeQuery(repo.id, communityQuery);
518
517
  return {
@@ -533,30 +532,47 @@ export class LocalBackend {
533
532
  };
534
533
  }
535
534
  if (type === 'cluster') {
536
- const clusterQuery = `
537
- MATCH (c:Community)
538
- WHERE c.label = '${name.replace(/'/g, "''")}' OR c.heuristicLabel = '${name.replace(/'/g, "''")}'
539
- RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
540
- LIMIT 1
535
+ const escaped = name.replace(/'/g, "''");
536
+ // Find ALL communities with this label (not just one)
537
+ const clusterQuery = `
538
+ MATCH (c:Community)
539
+ WHERE c.label = '${escaped}' OR c.heuristicLabel = '${escaped}'
540
+ RETURN c.id AS id, c.label AS label, c.heuristicLabel AS heuristicLabel, c.cohesion AS cohesion, c.symbolCount AS symbolCount
541
541
  `;
542
542
  const clusters = await executeQuery(repo.id, clusterQuery);
543
543
  if (clusters.length === 0)
544
544
  return { error: `Cluster '${name}' not found` };
545
- const cluster = clusters[0];
546
- const clusterId = cluster.id || cluster[0];
547
- const membersQuery = `
548
- MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c {id: '${clusterId}'})
549
- RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
550
- LIMIT 20
545
+ const rawClusters = clusters.map((c) => ({
546
+ id: c.id || c[0],
547
+ label: c.label || c[1],
548
+ heuristicLabel: c.heuristicLabel || c[2],
549
+ cohesion: c.cohesion || c[3],
550
+ symbolCount: c.symbolCount || c[4],
551
+ }));
552
+ // Aggregate: sum symbols, weighted-average cohesion across sub-communities
553
+ let totalSymbols = 0;
554
+ let weightedCohesion = 0;
555
+ for (const c of rawClusters) {
556
+ const s = c.symbolCount || 0;
557
+ totalSymbols += s;
558
+ weightedCohesion += (c.cohesion || 0) * s;
559
+ }
560
+ // Fetch members from ALL matching sub-communities (DISTINCT to avoid dupes)
561
+ const membersQuery = `
562
+ MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
563
+ WHERE c.label = '${escaped}' OR c.heuristicLabel = '${escaped}'
564
+ RETURN DISTINCT n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
565
+ LIMIT 30
551
566
  `;
552
567
  const members = await executeQuery(repo.id, membersQuery);
553
568
  return {
554
569
  cluster: {
555
- id: clusterId,
556
- label: cluster.label || cluster[1],
557
- heuristicLabel: cluster.heuristicLabel || cluster[2],
558
- cohesion: cluster.cohesion || cluster[3],
559
- symbolCount: cluster.symbolCount || cluster[4],
570
+ id: rawClusters[0].id,
571
+ label: rawClusters[0].heuristicLabel || rawClusters[0].label,
572
+ heuristicLabel: rawClusters[0].heuristicLabel || rawClusters[0].label,
573
+ cohesion: totalSymbols > 0 ? weightedCohesion / totalSymbols : 0,
574
+ symbolCount: totalSymbols,
575
+ subCommunities: rawClusters.length,
560
576
  },
561
577
  members: members.map((m) => ({
562
578
  name: m.name || m[0],
@@ -566,21 +582,21 @@ export class LocalBackend {
566
582
  };
567
583
  }
568
584
  if (type === 'process') {
569
- const processQuery = `
570
- MATCH (p:Process)
571
- WHERE p.label = '${name.replace(/'/g, "''")}' OR p.heuristicLabel = '${name.replace(/'/g, "''")}'
572
- RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount, p.entryPointId AS entryPointId, p.terminalId AS terminalId
573
- LIMIT 1
585
+ const processQuery = `
586
+ MATCH (p:Process)
587
+ WHERE p.label = '${name.replace(/'/g, "''")}' OR p.heuristicLabel = '${name.replace(/'/g, "''")}'
588
+ RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount, p.entryPointId AS entryPointId, p.terminalId AS terminalId
589
+ LIMIT 1
574
590
  `;
575
591
  const processes = await executeQuery(repo.id, processQuery);
576
592
  if (processes.length === 0)
577
593
  return { error: `Process '${name}' not found` };
578
594
  const proc = processes[0];
579
595
  const procId = proc.id || proc[0];
580
- const stepsQuery = `
581
- MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: '${procId}'})
582
- RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
583
- ORDER BY r.step
596
+ const stepsQuery = `
597
+ MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: '${procId}'})
598
+ RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
599
+ ORDER BY r.step
584
600
  `;
585
601
  const steps = await executeQuery(repo.id, stepsQuery);
586
602
  return {
@@ -612,11 +628,11 @@ export class LocalBackend {
612
628
  const minConfidence = params.minConfidence ?? 0;
613
629
  const relTypeFilter = relationTypes.map(t => `'${t}'`).join(', ');
614
630
  const confidenceFilter = minConfidence > 0 ? ` AND r.confidence >= ${minConfidence}` : '';
615
- const targetQuery = `
616
- MATCH (n)
617
- WHERE n.name = '${target.replace(/'/g, "''")}'
618
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
619
- LIMIT 1
631
+ const targetQuery = `
632
+ MATCH (n)
633
+ WHERE n.name = '${target.replace(/'/g, "''")}'
634
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
635
+ LIMIT 1
620
636
  `;
621
637
  const targets = await executeQuery(repo.id, targetQuery);
622
638
  if (targets.length === 0)
@@ -772,14 +788,9 @@ export class LocalBackend {
772
788
  stats: {
773
789
  fileCount: newMeta.stats.files || 0,
774
790
  functionCount: newMeta.stats.nodes || 0,
775
- classCount: 0,
776
- interfaceCount: 0,
777
- methodCount: 0,
778
791
  communityCount: newMeta.stats.communities || 0,
779
792
  processCount: newMeta.stats.processes || 0,
780
793
  },
781
- hotspots: [],
782
- folderTree: '',
783
794
  });
784
795
  console.error('GitNexus: Indexing complete!');
785
796
  return {
@@ -2,8 +2,7 @@
2
2
  * MCP Resources (Multi-Repo)
3
3
  *
4
4
  * Provides structured on-demand data to AI agents.
5
- * Supports repo-scoped URIs (gitnexus://repo/{name}/context)
6
- * and backwards-compatible global URIs (gitnexus://context).
5
+ * All resources use repo-scoped URIs: gitnexus://repo/{name}/context
7
6
  */
8
7
  import type { LocalBackend } from './local/local-backend.js';
9
8
  export interface ResourceDefinition {