sdl-mcp 0.9.2 → 0.9.3

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.
@@ -40,18 +40,18 @@ export async function insertEdge(conn, edge) {
40
40
  // and SYMBOL_IN_REPO edge when the target hasn't been indexed yet. This is
41
41
  // intentional — stubs are populated when the target file is indexed, and
42
42
  // queries that need full symbol data filter via SYMBOL_IN_FILE joins.
43
- await exec(conn, `MATCH (r:Repo {repoId: $repoId})
44
- MERGE (a:Symbol {symbolId: $fromSymbolId})
45
- MERGE (b:Symbol {symbolId: $toSymbolId})
46
- MERGE (a)-[:SYMBOL_IN_REPO]->(r)
47
- MERGE (b)-[:SYMBOL_IN_REPO]->(r)
48
- MERGE (a)-[d:DEPENDS_ON {edgeType: $edgeType}]->(b)
49
- SET d.weight = $weight,
50
- d.confidence = $confidence,
51
- d.resolution = $resolution,
52
- d.resolverId = $resolverId,
53
- d.resolutionPhase = $resolutionPhase,
54
- d.provenance = $provenance,
43
+ await exec(conn, `MATCH (r:Repo {repoId: $repoId})
44
+ MERGE (a:Symbol {symbolId: $fromSymbolId})
45
+ MERGE (b:Symbol {symbolId: $toSymbolId})
46
+ MERGE (a)-[:SYMBOL_IN_REPO]->(r)
47
+ MERGE (b)-[:SYMBOL_IN_REPO]->(r)
48
+ MERGE (a)-[d:DEPENDS_ON {edgeType: $edgeType}]->(b)
49
+ SET d.weight = $weight,
50
+ d.confidence = $confidence,
51
+ d.resolution = $resolution,
52
+ d.resolverId = $resolverId,
53
+ d.resolutionPhase = $resolutionPhase,
54
+ d.provenance = $provenance,
55
55
  d.createdAt = $createdAt`, {
56
56
  repoId: edge.repoId,
57
57
  fromSymbolId: edge.fromSymbolId,
@@ -81,20 +81,20 @@ export async function insertEdges(conn, edges) {
81
81
  for (const [repoId, repoEdges] of edgesByRepo) {
82
82
  const fromSymbolIds = [...new Set(repoEdges.map((e) => e.fromSymbolId))];
83
83
  for (const symbolId of fromSymbolIds) {
84
- await exec(txConn, `MATCH (r:Repo {repoId: $repoId})
85
- MERGE (s:Symbol {symbolId: $symbolId})
84
+ await exec(txConn, `MATCH (r:Repo {repoId: $repoId})
85
+ MERGE (s:Symbol {symbolId: $symbolId})
86
86
  MERGE (s)-[:SYMBOL_IN_REPO]->(r)`, { repoId, symbolId });
87
87
  }
88
88
  for (const edge of repoEdges) {
89
- await exec(txConn, `MERGE (a:Symbol {symbolId: $fromSymbolId})
90
- MERGE (b:Symbol {symbolId: $toSymbolId})
91
- MERGE (a)-[d:DEPENDS_ON {edgeType: $edgeType}]->(b)
92
- SET d.weight = $weight,
93
- d.confidence = $confidence,
94
- d.resolution = $resolution,
95
- d.resolverId = $resolverId,
96
- d.resolutionPhase = $resolutionPhase,
97
- d.provenance = $provenance,
89
+ await exec(txConn, `MERGE (a:Symbol {symbolId: $fromSymbolId})
90
+ MERGE (b:Symbol {symbolId: $toSymbolId})
91
+ MERGE (a)-[d:DEPENDS_ON {edgeType: $edgeType}]->(b)
92
+ SET d.weight = $weight,
93
+ d.confidence = $confidence,
94
+ d.resolution = $resolution,
95
+ d.resolverId = $resolverId,
96
+ d.resolutionPhase = $resolutionPhase,
97
+ d.provenance = $provenance,
98
98
  d.createdAt = $createdAt`, {
99
99
  fromSymbolId: edge.fromSymbolId,
100
100
  toSymbolId: edge.toSymbolId,
@@ -112,8 +112,8 @@ export async function insertEdges(conn, edges) {
112
112
  });
113
113
  }
114
114
  export async function deleteEdge(conn, edge) {
115
- await exec(conn, `MATCH (a:Symbol {symbolId: $fromSymbolId})-[d:DEPENDS_ON]->(b:Symbol {symbolId: $toSymbolId})
116
- WHERE d.edgeType = $edgeType
115
+ await exec(conn, `MATCH (a:Symbol {symbolId: $fromSymbolId})-[d:DEPENDS_ON]->(b:Symbol {symbolId: $toSymbolId})
116
+ WHERE d.edgeType = $edgeType
117
117
  DELETE d`, {
118
118
  fromSymbolId: edge.fromSymbolId,
119
119
  toSymbolId: edge.toSymbolId,
@@ -122,19 +122,19 @@ export async function deleteEdge(conn, edge) {
122
122
  }
123
123
  export async function getEdgesFrom(conn, symbolId, options) {
124
124
  const minCallConfidenceClause = buildMinCallConfidenceClause("d", options?.minCallConfidence);
125
- const rows = await queryAll(conn, `MATCH (a:Symbol {symbolId: $symbolId})-[d:DEPENDS_ON]->(b:Symbol)
126
- WHERE 1 = 1${minCallConfidenceClause}
127
- MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
128
- RETURN r.repoId AS repoId,
129
- a.symbolId AS fromSymbolId,
130
- b.symbolId AS toSymbolId,
131
- d.edgeType AS edgeType,
132
- d.weight AS weight,
133
- d.confidence AS confidence,
134
- d.resolution AS resolution,
135
- d.resolverId AS resolverId,
136
- d.resolutionPhase AS resolutionPhase,
137
- d.provenance AS provenance,
125
+ const rows = await queryAll(conn, `MATCH (a:Symbol {symbolId: $symbolId})-[d:DEPENDS_ON]->(b:Symbol)
126
+ WHERE 1 = 1${minCallConfidenceClause}
127
+ MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
128
+ RETURN r.repoId AS repoId,
129
+ a.symbolId AS fromSymbolId,
130
+ b.symbolId AS toSymbolId,
131
+ d.edgeType AS edgeType,
132
+ d.weight AS weight,
133
+ d.confidence AS confidence,
134
+ d.resolution AS resolution,
135
+ d.resolverId AS resolverId,
136
+ d.resolutionPhase AS resolutionPhase,
137
+ d.provenance AS provenance,
138
138
  d.createdAt AS createdAt`, {
139
139
  symbolId,
140
140
  ...(options?.minCallConfidence !== undefined
@@ -162,19 +162,19 @@ export async function getEdgesFromSymbols(conn, symbolIds, options) {
162
162
  for (const id of symbolIds)
163
163
  result.set(id, []);
164
164
  const minCallConfidenceClause = buildMinCallConfidenceClause("d", options?.minCallConfidence);
165
- const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
166
- WHERE a.symbolId IN $symbolIds${minCallConfidenceClause}
167
- MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
168
- RETURN r.repoId AS repoId,
169
- a.symbolId AS fromSymbolId,
170
- b.symbolId AS toSymbolId,
171
- d.edgeType AS edgeType,
172
- d.weight AS weight,
173
- d.confidence AS confidence,
174
- d.resolution AS resolution,
175
- d.resolverId AS resolverId,
176
- d.resolutionPhase AS resolutionPhase,
177
- d.provenance AS provenance,
165
+ const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
166
+ WHERE a.symbolId IN $symbolIds${minCallConfidenceClause}
167
+ MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
168
+ RETURN r.repoId AS repoId,
169
+ a.symbolId AS fromSymbolId,
170
+ b.symbolId AS toSymbolId,
171
+ d.edgeType AS edgeType,
172
+ d.weight AS weight,
173
+ d.confidence AS confidence,
174
+ d.resolution AS resolution,
175
+ d.resolverId AS resolverId,
176
+ d.resolutionPhase AS resolutionPhase,
177
+ d.provenance AS provenance,
178
178
  d.createdAt AS createdAt`, {
179
179
  symbolIds,
180
180
  ...(options?.minCallConfidence !== undefined
@@ -208,15 +208,15 @@ export async function getEdgesFromSymbolsForSlice(conn, symbolIds, options) {
208
208
  for (const id of symbolIds)
209
209
  result.set(id, []);
210
210
  const minCallConfidenceClause = buildMinCallConfidenceClause("d", options?.minCallConfidence);
211
- const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
212
- WHERE a.symbolId IN $symbolIds${minCallConfidenceClause}
213
- RETURN a.symbolId AS fromSymbolId,
214
- b.symbolId AS toSymbolId,
215
- d.edgeType AS edgeType,
216
- d.weight AS weight,
217
- d.confidence AS confidence,
218
- d.resolution AS resolution,
219
- d.resolverId AS resolverId,
211
+ const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
212
+ WHERE a.symbolId IN $symbolIds${minCallConfidenceClause}
213
+ RETURN a.symbolId AS fromSymbolId,
214
+ b.symbolId AS toSymbolId,
215
+ d.edgeType AS edgeType,
216
+ d.weight AS weight,
217
+ d.confidence AS confidence,
218
+ d.resolution AS resolution,
219
+ d.resolverId AS resolverId,
220
220
  d.resolutionPhase AS resolutionPhase`, {
221
221
  symbolIds,
222
222
  ...(options?.minCallConfidence !== undefined
@@ -246,10 +246,10 @@ export async function getEdgesFromSymbolsLite(conn, symbolIds) {
246
246
  const result = new Map();
247
247
  for (const id of symbolIds)
248
248
  result.set(id, []);
249
- const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
250
- WHERE a.symbolId IN $symbolIds
251
- RETURN a.symbolId AS fromSymbolId,
252
- b.symbolId AS toSymbolId,
249
+ const rows = await queryAll(conn, `MATCH (a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
250
+ WHERE a.symbolId IN $symbolIds
251
+ RETURN a.symbolId AS fromSymbolId,
252
+ b.symbolId AS toSymbolId,
253
253
  d.edgeType AS edgeType`, { symbolIds });
254
254
  for (const row of rows) {
255
255
  const bucket = result.get(row.fromSymbolId);
@@ -270,41 +270,41 @@ export async function getEdgesToSymbols(conn, symbolIds, options) {
270
270
  for (const id of symbolIds)
271
271
  result.set(id, []);
272
272
  const minCallConfidenceClause = buildMinCallConfidenceClause("d", options?.minCallConfidence);
273
- const queryWithHint = `MATCH (b:Symbol)
274
- WHERE b.symbolId IN $symbolIds
275
- MATCH (a:Symbol)-[d:DEPENDS_ON]->(b)
276
- WHERE 1 = 1${minCallConfidenceClause}
277
- MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
278
- WITH b, d, a, r
279
- HINT (b JOIN (d JOIN a))
280
- RETURN r.repoId AS repoId,
281
- a.symbolId AS fromSymbolId,
282
- b.symbolId AS toSymbolId,
283
- d.edgeType AS edgeType,
284
- d.weight AS weight,
285
- d.confidence AS confidence,
286
- d.resolution AS resolution,
287
- d.resolverId AS resolverId,
288
- d.resolutionPhase AS resolutionPhase,
289
- d.provenance AS provenance,
290
- d.createdAt AS createdAt
273
+ const queryWithHint = `MATCH (b:Symbol)
274
+ WHERE b.symbolId IN $symbolIds
275
+ MATCH (a:Symbol)-[d:DEPENDS_ON]->(b)
276
+ WHERE 1 = 1${minCallConfidenceClause}
277
+ MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
278
+ WITH b, d, a, r
279
+ HINT (b JOIN (d JOIN a))
280
+ RETURN r.repoId AS repoId,
281
+ a.symbolId AS fromSymbolId,
282
+ b.symbolId AS toSymbolId,
283
+ d.edgeType AS edgeType,
284
+ d.weight AS weight,
285
+ d.confidence AS confidence,
286
+ d.resolution AS resolution,
287
+ d.resolverId AS resolverId,
288
+ d.resolutionPhase AS resolutionPhase,
289
+ d.provenance AS provenance,
290
+ d.createdAt AS createdAt
291
291
  ORDER BY a.symbolId`;
292
- const queryWithoutHint = `MATCH (b:Symbol)
293
- WHERE b.symbolId IN $symbolIds
294
- MATCH (a:Symbol)-[d:DEPENDS_ON]->(b)
295
- WHERE 1 = 1${minCallConfidenceClause}
296
- MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
297
- RETURN r.repoId AS repoId,
298
- a.symbolId AS fromSymbolId,
299
- b.symbolId AS toSymbolId,
300
- d.edgeType AS edgeType,
301
- d.weight AS weight,
302
- d.confidence AS confidence,
303
- d.resolution AS resolution,
304
- d.resolverId AS resolverId,
305
- d.resolutionPhase AS resolutionPhase,
306
- d.provenance AS provenance,
307
- d.createdAt AS createdAt
292
+ const queryWithoutHint = `MATCH (b:Symbol)
293
+ WHERE b.symbolId IN $symbolIds
294
+ MATCH (a:Symbol)-[d:DEPENDS_ON]->(b)
295
+ WHERE 1 = 1${minCallConfidenceClause}
296
+ MATCH (a)-[:SYMBOL_IN_REPO]->(r:Repo)
297
+ RETURN r.repoId AS repoId,
298
+ a.symbolId AS fromSymbolId,
299
+ b.symbolId AS toSymbolId,
300
+ d.edgeType AS edgeType,
301
+ d.weight AS weight,
302
+ d.confidence AS confidence,
303
+ d.resolution AS resolution,
304
+ d.resolverId AS resolverId,
305
+ d.resolutionPhase AS resolutionPhase,
306
+ d.provenance AS provenance,
307
+ d.createdAt AS createdAt
308
308
  ORDER BY a.symbolId`;
309
309
  const useJoinHint = await getJoinHintSupported(conn);
310
310
  const rows = await queryAll(conn, useJoinHint ? queryWithHint : queryWithoutHint, {
@@ -334,17 +334,17 @@ export async function getEdgesToSymbols(conn, symbolIds, options) {
334
334
  return result;
335
335
  }
336
336
  export async function getEdgesByRepo(conn, repoId) {
337
- const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
338
- RETURN r.repoId AS repoId,
339
- a.symbolId AS fromSymbolId,
340
- b.symbolId AS toSymbolId,
341
- d.edgeType AS edgeType,
342
- d.weight AS weight,
343
- d.confidence AS confidence,
344
- d.resolution AS resolution,
345
- d.resolverId AS resolverId,
346
- d.resolutionPhase AS resolutionPhase,
347
- d.provenance AS provenance,
337
+ const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(a:Symbol)-[d:DEPENDS_ON]->(b:Symbol)
338
+ RETURN r.repoId AS repoId,
339
+ a.symbolId AS fromSymbolId,
340
+ b.symbolId AS toSymbolId,
341
+ d.edgeType AS edgeType,
342
+ d.weight AS weight,
343
+ d.confidence AS confidence,
344
+ d.resolution AS resolution,
345
+ d.resolverId AS resolverId,
346
+ d.resolutionPhase AS resolutionPhase,
347
+ d.provenance AS provenance,
348
348
  d.createdAt AS createdAt`, { repoId });
349
349
  return rows.map((row) => ({
350
350
  repoId: row.repoId,
@@ -363,48 +363,48 @@ export async function getEdgesByRepo(conn, repoId) {
363
363
  export async function getCallersOfSymbols(conn, repoId, symbolIds) {
364
364
  if (symbolIds.length === 0)
365
365
  return [];
366
- const queryWithHint = `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(to:Symbol)
367
- WHERE to.symbolId IN $symbolIds
368
- MATCH (from:Symbol)-[d:DEPENDS_ON]->(to)
369
- MATCH (from)-[:SYMBOL_IN_REPO]->(r)
370
- WHERE d.edgeType IN ['call', 'import']
371
- WITH r, to, d, from
372
- HINT (to JOIN (d JOIN from))
373
- RETURN DISTINCT from.symbolId AS symbolId
366
+ const queryWithHint = `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(to:Symbol)
367
+ WHERE to.symbolId IN $symbolIds
368
+ MATCH (from:Symbol)-[d:DEPENDS_ON]->(to)
369
+ MATCH (from)-[:SYMBOL_IN_REPO]->(r)
370
+ WHERE d.edgeType IN ['call', 'import']
371
+ WITH r, to, d, from
372
+ HINT (to JOIN (d JOIN from))
373
+ RETURN DISTINCT from.symbolId AS symbolId
374
374
  ORDER BY symbolId`;
375
- const queryWithoutHint = `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(to:Symbol)
376
- WHERE to.symbolId IN $symbolIds
377
- MATCH (from:Symbol)-[d:DEPENDS_ON]->(to)
378
- MATCH (from)-[:SYMBOL_IN_REPO]->(r)
379
- WHERE d.edgeType IN ['call', 'import']
380
- RETURN DISTINCT from.symbolId AS symbolId
375
+ const queryWithoutHint = `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(to:Symbol)
376
+ WHERE to.symbolId IN $symbolIds
377
+ MATCH (from:Symbol)-[d:DEPENDS_ON]->(to)
378
+ MATCH (from)-[:SYMBOL_IN_REPO]->(r)
379
+ WHERE d.edgeType IN ['call', 'import']
380
+ RETURN DISTINCT from.symbolId AS symbolId
381
381
  ORDER BY symbolId`;
382
382
  const useJoinHint = await getJoinHintSupported(conn);
383
383
  const rows = await queryAll(conn, useJoinHint ? queryWithHint : queryWithoutHint, { repoId, symbolIds });
384
384
  return rows.map((row) => row.symbolId);
385
385
  }
386
386
  export async function deleteEdgesByFileId(conn, fileId) {
387
- const symbolRows = await queryAll(conn, `MATCH (f:File {fileId: $fileId})<-[:SYMBOL_IN_FILE]-(s:Symbol)
387
+ const symbolRows = await queryAll(conn, `MATCH (f:File {fileId: $fileId})<-[:SYMBOL_IN_FILE]-(s:Symbol)
388
388
  RETURN s.symbolId AS symbolId`, { fileId });
389
389
  const symbolIds = symbolRows.map((r) => r.symbolId);
390
390
  if (symbolIds.length === 0)
391
391
  return;
392
- await exec(conn, `MATCH (s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
393
- WHERE s.symbolId IN $symbolIds
392
+ await exec(conn, `MATCH (s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
393
+ WHERE s.symbolId IN $symbolIds
394
394
  DELETE d`, { symbolIds });
395
- await exec(conn, `MATCH (:Symbol)-[d:DEPENDS_ON]->(s:Symbol)
396
- WHERE s.symbolId IN $symbolIds
395
+ await exec(conn, `MATCH (:Symbol)-[d:DEPENDS_ON]->(s:Symbol)
396
+ WHERE s.symbolId IN $symbolIds
397
397
  DELETE d`, { symbolIds });
398
398
  }
399
399
  export async function deleteOutgoingEdgesByTypeForSymbols(conn, symbolIds, edgeType) {
400
400
  if (symbolIds.length === 0)
401
401
  return;
402
- await exec(conn, `MATCH (s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
403
- WHERE s.symbolId IN $symbolIds AND d.edgeType = $edgeType
402
+ await exec(conn, `MATCH (s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
403
+ WHERE s.symbolId IN $symbolIds AND d.edgeType = $edgeType
404
404
  DELETE d`, { symbolIds, edgeType });
405
405
  }
406
406
  export async function getEdgeCountsByType(conn, repoId) {
407
- const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
407
+ const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
408
408
  RETURN d.edgeType AS edgeType, count(d) AS count`, { repoId });
409
409
  const result = {};
410
410
  for (const row of rows) {
@@ -413,24 +413,24 @@ export async function getEdgeCountsByType(conn, repoId) {
413
413
  return result;
414
414
  }
415
415
  export async function getEdgeCount(conn, repoId) {
416
- const row = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
416
+ const row = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
417
417
  RETURN count(d) AS count`, { repoId });
418
418
  return row ? toNumber(row.count) : 0;
419
419
  }
420
420
  export async function getCallEdgeResolutionCounts(conn, repoId) {
421
- const total = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
422
- WHERE d.edgeType = 'call'
421
+ const total = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
422
+ WHERE d.edgeType = 'call'
423
423
  RETURN count(d) AS count`, { repoId });
424
- const resolved = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(t:Symbol)
425
- WHERE d.edgeType = 'call' AND NOT (t.symbolId STARTS WITH 'unresolved:')
424
+ const resolved = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(t:Symbol)
425
+ WHERE d.edgeType = 'call' AND NOT (t.symbolId STARTS WITH 'unresolved:')
426
426
  RETURN count(d) AS count`, { repoId });
427
- const exact = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
428
- WHERE d.edgeType = 'call' AND (d.resolution = 'exact' OR d.confidence >= 0.9)
427
+ const exact = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(:Symbol)
428
+ WHERE d.edgeType = 'call' AND (d.resolution = 'exact' OR d.confidence >= 0.9)
429
429
  RETURN count(d) AS count`, { repoId });
430
430
  // Count of call edges that target repo-internal symbols (resolvable)
431
431
  // Excludes edges targeting 'unresolved:call:' stubs (external/builtin calls)
432
- const resolvable = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(t:Symbol)
433
- WHERE d.edgeType = 'call' AND NOT (t.symbolId STARTS WITH 'unresolved:call:')
432
+ const resolvable = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[d:DEPENDS_ON]->(t:Symbol)
433
+ WHERE d.edgeType = 'call' AND NOT (t.symbolId STARTS WITH 'unresolved:call:')
434
434
  RETURN count(d) AS count`, { repoId });
435
435
  return {
436
436
  totalCallEdges: total ? toNumber(total.count) : 0,
@@ -1,12 +1,12 @@
1
1
  import { exec, queryAll, querySingle, assertSafeInt } from "./ladybug-core.js";
2
2
  import { safeJsonParse, StringArraySchema } from "../util/safeJson.js";
3
3
  export async function insertAuditEvent(conn, row) {
4
- await exec(conn, `MERGE (a:Audit {eventId: $eventId})
5
- SET a.timestamp = $timestamp,
6
- a.tool = $tool,
7
- a.decision = $decision,
8
- a.repoId = $repoId,
9
- a.symbolId = $symbolId,
4
+ await exec(conn, `MERGE (a:Audit {eventId: $eventId})
5
+ SET a.timestamp = $timestamp,
6
+ a.tool = $tool,
7
+ a.decision = $decision,
8
+ a.repoId = $repoId,
9
+ a.symbolId = $symbolId,
10
10
  a.detailsJson = $detailsJson`, {
11
11
  eventId: row.eventId,
12
12
  timestamp: row.timestamp,
@@ -36,29 +36,29 @@ export async function getAuditEvents(conn, options = {}) {
36
36
  params.untilTimestamp = options.untilTimestamp;
37
37
  }
38
38
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
39
- const rows = await queryAll(conn, `MATCH (a:Audit)
40
- ${whereClause}
41
- RETURN a.eventId AS eventId,
42
- a.timestamp AS timestamp,
43
- a.tool AS tool,
44
- a.decision AS decision,
45
- a.repoId AS repoId,
46
- a.symbolId AS symbolId,
47
- a.detailsJson AS detailsJson
48
- ORDER BY a.timestamp DESC
39
+ const rows = await queryAll(conn, `MATCH (a:Audit)
40
+ ${whereClause}
41
+ RETURN a.eventId AS eventId,
42
+ a.timestamp AS timestamp,
43
+ a.tool AS tool,
44
+ a.decision AS decision,
45
+ a.repoId AS repoId,
46
+ a.symbolId AS symbolId,
47
+ a.detailsJson AS detailsJson
48
+ ORDER BY a.timestamp DESC
49
49
  LIMIT $limit`, { ...params, limit: maxFetch });
50
50
  return rows;
51
51
  }
52
52
  export async function upsertAgentFeedback(conn, row) {
53
- await exec(conn, `MERGE (f:AgentFeedback {feedbackId: $feedbackId})
54
- SET f.repoId = $repoId,
55
- f.versionId = $versionId,
56
- f.sliceHandle = $sliceHandle,
57
- f.usefulSymbolsJson = $usefulSymbolsJson,
58
- f.missingSymbolsJson = $missingSymbolsJson,
59
- f.taskTagsJson = $taskTagsJson,
60
- f.taskType = $taskType,
61
- f.taskText = $taskText,
53
+ await exec(conn, `MERGE (f:AgentFeedback {feedbackId: $feedbackId})
54
+ SET f.repoId = $repoId,
55
+ f.versionId = $versionId,
56
+ f.sliceHandle = $sliceHandle,
57
+ f.usefulSymbolsJson = $usefulSymbolsJson,
58
+ f.missingSymbolsJson = $missingSymbolsJson,
59
+ f.taskTagsJson = $taskTagsJson,
60
+ f.taskType = $taskType,
61
+ f.taskText = $taskText,
62
62
  f.createdAt = $createdAt`, {
63
63
  feedbackId: row.feedbackId,
64
64
  repoId: row.repoId,
@@ -73,16 +73,16 @@ export async function upsertAgentFeedback(conn, row) {
73
73
  });
74
74
  }
75
75
  export async function getAgentFeedback(conn, feedbackId) {
76
- const row = await querySingle(conn, `MATCH (f:AgentFeedback {feedbackId: $feedbackId})
77
- RETURN f.feedbackId AS feedbackId,
78
- f.repoId AS repoId,
79
- f.versionId AS versionId,
80
- f.sliceHandle AS sliceHandle,
81
- f.usefulSymbolsJson AS usefulSymbolsJson,
82
- f.missingSymbolsJson AS missingSymbolsJson,
83
- f.taskTagsJson AS taskTagsJson,
84
- f.taskType AS taskType,
85
- f.taskText AS taskText,
76
+ const row = await querySingle(conn, `MATCH (f:AgentFeedback {feedbackId: $feedbackId})
77
+ RETURN f.feedbackId AS feedbackId,
78
+ f.repoId AS repoId,
79
+ f.versionId AS versionId,
80
+ f.sliceHandle AS sliceHandle,
81
+ f.usefulSymbolsJson AS usefulSymbolsJson,
82
+ f.missingSymbolsJson AS missingSymbolsJson,
83
+ f.taskTagsJson AS taskTagsJson,
84
+ f.taskType AS taskType,
85
+ f.taskText AS taskText,
86
86
  f.createdAt AS createdAt`, { feedbackId });
87
87
  return row ?? null;
88
88
  }
@@ -90,32 +90,32 @@ export async function getAgentFeedbackByRepo(conn, repoId, limit, since) {
90
90
  assertSafeInt(limit, "limit");
91
91
  const maxFetch = Math.min(Math.max(0, limit), 10000);
92
92
  const query = since
93
- ? `MATCH (f:AgentFeedback {repoId: $repoId})
94
- WHERE f.createdAt >= $since
95
- RETURN f.feedbackId AS feedbackId,
96
- f.repoId AS repoId,
97
- f.versionId AS versionId,
98
- f.sliceHandle AS sliceHandle,
99
- f.usefulSymbolsJson AS usefulSymbolsJson,
100
- f.missingSymbolsJson AS missingSymbolsJson,
101
- f.taskTagsJson AS taskTagsJson,
102
- f.taskType AS taskType,
103
- f.taskText AS taskText,
104
- f.createdAt AS createdAt
105
- ORDER BY f.createdAt DESC
93
+ ? `MATCH (f:AgentFeedback {repoId: $repoId})
94
+ WHERE f.createdAt >= $since
95
+ RETURN f.feedbackId AS feedbackId,
96
+ f.repoId AS repoId,
97
+ f.versionId AS versionId,
98
+ f.sliceHandle AS sliceHandle,
99
+ f.usefulSymbolsJson AS usefulSymbolsJson,
100
+ f.missingSymbolsJson AS missingSymbolsJson,
101
+ f.taskTagsJson AS taskTagsJson,
102
+ f.taskType AS taskType,
103
+ f.taskText AS taskText,
104
+ f.createdAt AS createdAt
105
+ ORDER BY f.createdAt DESC
106
106
  LIMIT $limit`
107
- : `MATCH (f:AgentFeedback {repoId: $repoId})
108
- RETURN f.feedbackId AS feedbackId,
109
- f.repoId AS repoId,
110
- f.versionId AS versionId,
111
- f.sliceHandle AS sliceHandle,
112
- f.usefulSymbolsJson AS usefulSymbolsJson,
113
- f.missingSymbolsJson AS missingSymbolsJson,
114
- f.taskTagsJson AS taskTagsJson,
115
- f.taskType AS taskType,
116
- f.taskText AS taskText,
117
- f.createdAt AS createdAt
118
- ORDER BY f.createdAt DESC
107
+ : `MATCH (f:AgentFeedback {repoId: $repoId})
108
+ RETURN f.feedbackId AS feedbackId,
109
+ f.repoId AS repoId,
110
+ f.versionId AS versionId,
111
+ f.sliceHandle AS sliceHandle,
112
+ f.usefulSymbolsJson AS usefulSymbolsJson,
113
+ f.missingSymbolsJson AS missingSymbolsJson,
114
+ f.taskTagsJson AS taskTagsJson,
115
+ f.taskType AS taskType,
116
+ f.taskText AS taskText,
117
+ f.createdAt AS createdAt
118
+ ORDER BY f.createdAt DESC
119
119
  LIMIT $limit`;
120
120
  const rows = await queryAll(conn, query, since ? { repoId, since, limit: maxFetch } : { repoId, limit: maxFetch });
121
121
  return rows;
@@ -161,12 +161,12 @@ export async function getAggregatedFeedback(conn, repoId, sinceTimestamp) {
161
161
  conditions.push("f.createdAt >= $sinceTimestamp");
162
162
  params.sinceTimestamp = sinceTimestamp;
163
163
  }
164
- const rows = await queryAll(conn, `MATCH (f:AgentFeedback)
165
- WHERE ${conditions.join(" AND ")}
166
- RETURN f.usefulSymbolsJson AS usefulSymbolsJson,
167
- f.missingSymbolsJson AS missingSymbolsJson,
168
- f.taskTagsJson AS taskTagsJson,
169
- f.taskType AS taskType
164
+ const rows = await queryAll(conn, `MATCH (f:AgentFeedback)
165
+ WHERE ${conditions.join(" AND ")}
166
+ RETURN f.usefulSymbolsJson AS usefulSymbolsJson,
167
+ f.missingSymbolsJson AS missingSymbolsJson,
168
+ f.taskTagsJson AS taskTagsJson,
169
+ f.taskType AS taskType
170
170
  ORDER BY f.createdAt DESC`, params);
171
171
  const symbolPositiveCounts = new Map();
172
172
  const symbolNegativeCounts = new Map();