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.
- package/LICENSE +90 -90
- package/README.md +588 -588
- package/config/benchmark.config.example.json +81 -81
- package/config/benchmark.config.json +86 -86
- package/dist/db/ladybug-clusters.js +50 -50
- package/dist/db/ladybug-edges.js +140 -140
- package/dist/db/ladybug-feedback.js +66 -66
- package/dist/db/ladybug-memory.js +56 -56
- package/dist/db/ladybug-metrics.js +61 -61
- package/dist/db/ladybug-processes.js +55 -55
- package/dist/db/ladybug-repos.js +94 -94
- package/dist/db/ladybug-schema.js +236 -236
- package/dist/db/ladybug-symbols.js +280 -280
- package/dist/indexer/adapter/c.js +8 -8
- package/dist/indexer/adapter/kotlin.js +24 -24
- package/dist/indexer/adapter/php.js +41 -41
- package/dist/indexer/adapter/python.js +34 -34
- package/dist/indexer/adapter/rust.js +18 -18
- package/dist/indexer/treesitter/extractCalls.js +69 -69
- package/dist/indexer/treesitter/extractImports.js +6 -6
- package/dist/server.js +2 -2
- package/package.json +153 -153
package/dist/db/ladybug-edges.js
CHANGED
|
@@ -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();
|