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
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "1.0",
|
|
3
|
-
"description": "Benchmark threshold configuration for CI regression detection",
|
|
4
|
-
"thresholds": {
|
|
5
|
-
"indexing": {
|
|
6
|
-
"indexTimePerFile": {
|
|
7
|
-
"maxMs": 200,
|
|
8
|
-
"trend": "lower-is-better",
|
|
9
|
-
"allowableIncreasePercent": 10
|
|
10
|
-
},
|
|
11
|
-
"indexTimePerSymbol": {
|
|
12
|
-
"maxMs": 10,
|
|
13
|
-
"trend": "lower-is-better",
|
|
14
|
-
"allowableIncreasePercent": 10
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"quality": {
|
|
18
|
-
"symbolsPerFile": {
|
|
19
|
-
"minValue": 5,
|
|
20
|
-
"trend": "higher-is-better",
|
|
21
|
-
"allowableDecreasePercent": 5
|
|
22
|
-
},
|
|
23
|
-
"edgesPerSymbol": {
|
|
24
|
-
"minValue": 1,
|
|
25
|
-
"trend": "higher-is-better",
|
|
26
|
-
"allowableDecreasePercent": 5
|
|
27
|
-
},
|
|
28
|
-
"graphConnectivity": {
|
|
29
|
-
"minValue": 0.3,
|
|
30
|
-
"trend": "higher-is-better",
|
|
31
|
-
"allowableDecreasePercent": 5
|
|
32
|
-
},
|
|
33
|
-
"exportedSymbolRatio": {
|
|
34
|
-
"minValue": 0.2,
|
|
35
|
-
"trend": "higher-is-better",
|
|
36
|
-
"allowableDecreasePercent": 10
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"performance": {
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"description": "Benchmark threshold configuration for CI regression detection",
|
|
4
|
+
"thresholds": {
|
|
5
|
+
"indexing": {
|
|
6
|
+
"indexTimePerFile": {
|
|
7
|
+
"maxMs": 200,
|
|
8
|
+
"trend": "lower-is-better",
|
|
9
|
+
"allowableIncreasePercent": 10
|
|
10
|
+
},
|
|
11
|
+
"indexTimePerSymbol": {
|
|
12
|
+
"maxMs": 10,
|
|
13
|
+
"trend": "lower-is-better",
|
|
14
|
+
"allowableIncreasePercent": 10
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"quality": {
|
|
18
|
+
"symbolsPerFile": {
|
|
19
|
+
"minValue": 5,
|
|
20
|
+
"trend": "higher-is-better",
|
|
21
|
+
"allowableDecreasePercent": 5
|
|
22
|
+
},
|
|
23
|
+
"edgesPerSymbol": {
|
|
24
|
+
"minValue": 1,
|
|
25
|
+
"trend": "higher-is-better",
|
|
26
|
+
"allowableDecreasePercent": 5
|
|
27
|
+
},
|
|
28
|
+
"graphConnectivity": {
|
|
29
|
+
"minValue": 0.3,
|
|
30
|
+
"trend": "higher-is-better",
|
|
31
|
+
"allowableDecreasePercent": 5
|
|
32
|
+
},
|
|
33
|
+
"exportedSymbolRatio": {
|
|
34
|
+
"minValue": 0.2,
|
|
35
|
+
"trend": "higher-is-better",
|
|
36
|
+
"allowableDecreasePercent": 10
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"performance": {
|
|
40
40
|
"sliceBuildTimeMs": {
|
|
41
41
|
"maxMs": 200,
|
|
42
42
|
"trend": "lower-is-better",
|
|
43
43
|
"allowableIncreasePercent": 25
|
|
44
44
|
},
|
|
45
|
-
"avgSkeletonTimeMs": {
|
|
46
|
-
"maxMs": 5,
|
|
47
|
-
"trend": "lower-is-better",
|
|
48
|
-
"allowableIncreasePercent": 15
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"tokenEfficiency": {
|
|
52
|
-
"avgCardTokens": {
|
|
53
|
-
"maxTokens": 250,
|
|
54
|
-
"trend": "lower-is-better",
|
|
55
|
-
"allowableIncreasePercent": 10
|
|
56
|
-
},
|
|
57
|
-
"avgSkeletonTokens": {
|
|
58
|
-
"maxTokens": 200,
|
|
59
|
-
"trend": "lower-is-better",
|
|
60
|
-
"allowableIncreasePercent": 10
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"coverage": {
|
|
64
|
-
"callEdgeCoverage": {
|
|
65
|
-
"minPercent": 0.1,
|
|
66
|
-
"trend": "higher-is-better",
|
|
67
|
-
"allowableDecreasePercent": 10
|
|
68
|
-
},
|
|
69
|
-
"importEdgeCoverage": {
|
|
70
|
-
"minPercent": 0.5,
|
|
71
|
-
"trend": "higher-is-better",
|
|
72
|
-
"allowableDecreasePercent": 10
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"smoothing": {
|
|
77
|
-
"warmupRuns": 2,
|
|
78
|
-
"sampleRuns": 3,
|
|
79
|
-
"outlierMethod": "iqr",
|
|
80
|
-
"iqrMultiplier": 1.5
|
|
81
|
-
},
|
|
82
|
-
"baseline": {
|
|
83
|
-
"filePath": ".benchmark/baseline.json",
|
|
84
|
-
"autoUpdateOnPass": false
|
|
85
|
-
}
|
|
86
|
-
}
|
|
45
|
+
"avgSkeletonTimeMs": {
|
|
46
|
+
"maxMs": 5,
|
|
47
|
+
"trend": "lower-is-better",
|
|
48
|
+
"allowableIncreasePercent": 15
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"tokenEfficiency": {
|
|
52
|
+
"avgCardTokens": {
|
|
53
|
+
"maxTokens": 250,
|
|
54
|
+
"trend": "lower-is-better",
|
|
55
|
+
"allowableIncreasePercent": 10
|
|
56
|
+
},
|
|
57
|
+
"avgSkeletonTokens": {
|
|
58
|
+
"maxTokens": 200,
|
|
59
|
+
"trend": "lower-is-better",
|
|
60
|
+
"allowableIncreasePercent": 10
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"coverage": {
|
|
64
|
+
"callEdgeCoverage": {
|
|
65
|
+
"minPercent": 0.1,
|
|
66
|
+
"trend": "higher-is-better",
|
|
67
|
+
"allowableDecreasePercent": 10
|
|
68
|
+
},
|
|
69
|
+
"importEdgeCoverage": {
|
|
70
|
+
"minPercent": 0.5,
|
|
71
|
+
"trend": "higher-is-better",
|
|
72
|
+
"allowableDecreasePercent": 10
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"smoothing": {
|
|
77
|
+
"warmupRuns": 2,
|
|
78
|
+
"sampleRuns": 3,
|
|
79
|
+
"outlierMethod": "iqr",
|
|
80
|
+
"iqrMultiplier": 1.5
|
|
81
|
+
},
|
|
82
|
+
"baseline": {
|
|
83
|
+
"filePath": ".benchmark/baseline.json",
|
|
84
|
+
"autoUpdateOnPass": false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "1.0",
|
|
3
|
-
"description": "Benchmark threshold configuration for CI regression detection",
|
|
4
|
-
"thresholds": {
|
|
5
|
-
"indexing": {
|
|
6
|
-
"indexTimePerFile": {
|
|
7
|
-
"maxMs": 450,
|
|
8
|
-
"trend": "lower-is-better",
|
|
9
|
-
"allowableIncreasePercent": 15
|
|
10
|
-
},
|
|
11
|
-
"indexTimePerSymbol": {
|
|
12
|
-
"maxMs": 20,
|
|
13
|
-
"trend": "lower-is-better",
|
|
14
|
-
"allowableIncreasePercent": 60
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"quality": {
|
|
18
|
-
"symbolsPerFile": {
|
|
19
|
-
"minValue": 5,
|
|
20
|
-
"trend": "higher-is-better",
|
|
21
|
-
"allowableDecreasePercent": 5
|
|
22
|
-
},
|
|
23
|
-
"edgesPerSymbol": {
|
|
24
|
-
"minValue": 1,
|
|
25
|
-
"trend": "higher-is-better",
|
|
26
|
-
"allowableDecreasePercent": 5
|
|
27
|
-
},
|
|
28
|
-
"graphConnectivity": {
|
|
29
|
-
"minValue": 0.3,
|
|
30
|
-
"trend": "higher-is-better",
|
|
31
|
-
"allowableDecreasePercent": 5
|
|
32
|
-
},
|
|
33
|
-
"exportedSymbolRatio": {
|
|
34
|
-
"minValue": 0.2,
|
|
35
|
-
"trend": "higher-is-better",
|
|
36
|
-
"allowableDecreasePercent": 15
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"performance": {
|
|
40
|
-
"sliceBuildTimeMs": {
|
|
41
|
-
"maxMs": 350,
|
|
42
|
-
"trend": "lower-is-better",
|
|
43
|
-
"allowableIncreasePercent": 125
|
|
44
|
-
},
|
|
45
|
-
"avgSkeletonTimeMs": {
|
|
46
|
-
"maxMs": 5,
|
|
47
|
-
"trend": "lower-is-better",
|
|
48
|
-
"allowableIncreasePercent": 30
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"tokenEfficiency": {
|
|
52
|
-
"avgCardTokens": {
|
|
53
|
-
"maxTokens": 250,
|
|
54
|
-
"trend": "lower-is-better",
|
|
55
|
-
"allowableIncreasePercent": 10
|
|
56
|
-
},
|
|
57
|
-
"avgSkeletonTokens": {
|
|
58
|
-
"maxTokens": 200,
|
|
59
|
-
"trend": "lower-is-better",
|
|
60
|
-
"allowableIncreasePercent": 10
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"coverage": {
|
|
64
|
-
"callEdgeCoverage": {
|
|
65
|
-
"minPercent": 0.1,
|
|
66
|
-
"trend": "higher-is-better",
|
|
67
|
-
"allowableDecreasePercent": 10
|
|
68
|
-
},
|
|
69
|
-
"importEdgeCoverage": {
|
|
70
|
-
"minPercent": 0.5,
|
|
71
|
-
"trend": "higher-is-better",
|
|
72
|
-
"allowableDecreasePercent": 10
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"smoothing": {
|
|
77
|
-
"warmupRuns": 2,
|
|
78
|
-
"sampleRuns": 3,
|
|
79
|
-
"outlierMethod": "iqr",
|
|
80
|
-
"iqrMultiplier": 1.5
|
|
81
|
-
},
|
|
82
|
-
"baseline": {
|
|
83
|
-
"filePath": ".benchmark/baseline.json",
|
|
84
|
-
"autoUpdateOnPass": false
|
|
85
|
-
}
|
|
86
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"description": "Benchmark threshold configuration for CI regression detection",
|
|
4
|
+
"thresholds": {
|
|
5
|
+
"indexing": {
|
|
6
|
+
"indexTimePerFile": {
|
|
7
|
+
"maxMs": 450,
|
|
8
|
+
"trend": "lower-is-better",
|
|
9
|
+
"allowableIncreasePercent": 15
|
|
10
|
+
},
|
|
11
|
+
"indexTimePerSymbol": {
|
|
12
|
+
"maxMs": 20,
|
|
13
|
+
"trend": "lower-is-better",
|
|
14
|
+
"allowableIncreasePercent": 60
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"quality": {
|
|
18
|
+
"symbolsPerFile": {
|
|
19
|
+
"minValue": 5,
|
|
20
|
+
"trend": "higher-is-better",
|
|
21
|
+
"allowableDecreasePercent": 5
|
|
22
|
+
},
|
|
23
|
+
"edgesPerSymbol": {
|
|
24
|
+
"minValue": 1,
|
|
25
|
+
"trend": "higher-is-better",
|
|
26
|
+
"allowableDecreasePercent": 5
|
|
27
|
+
},
|
|
28
|
+
"graphConnectivity": {
|
|
29
|
+
"minValue": 0.3,
|
|
30
|
+
"trend": "higher-is-better",
|
|
31
|
+
"allowableDecreasePercent": 5
|
|
32
|
+
},
|
|
33
|
+
"exportedSymbolRatio": {
|
|
34
|
+
"minValue": 0.2,
|
|
35
|
+
"trend": "higher-is-better",
|
|
36
|
+
"allowableDecreasePercent": 15
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"performance": {
|
|
40
|
+
"sliceBuildTimeMs": {
|
|
41
|
+
"maxMs": 350,
|
|
42
|
+
"trend": "lower-is-better",
|
|
43
|
+
"allowableIncreasePercent": 125
|
|
44
|
+
},
|
|
45
|
+
"avgSkeletonTimeMs": {
|
|
46
|
+
"maxMs": 5,
|
|
47
|
+
"trend": "lower-is-better",
|
|
48
|
+
"allowableIncreasePercent": 30
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"tokenEfficiency": {
|
|
52
|
+
"avgCardTokens": {
|
|
53
|
+
"maxTokens": 250,
|
|
54
|
+
"trend": "lower-is-better",
|
|
55
|
+
"allowableIncreasePercent": 10
|
|
56
|
+
},
|
|
57
|
+
"avgSkeletonTokens": {
|
|
58
|
+
"maxTokens": 200,
|
|
59
|
+
"trend": "lower-is-better",
|
|
60
|
+
"allowableIncreasePercent": 10
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"coverage": {
|
|
64
|
+
"callEdgeCoverage": {
|
|
65
|
+
"minPercent": 0.1,
|
|
66
|
+
"trend": "higher-is-better",
|
|
67
|
+
"allowableDecreasePercent": 10
|
|
68
|
+
},
|
|
69
|
+
"importEdgeCoverage": {
|
|
70
|
+
"minPercent": 0.5,
|
|
71
|
+
"trend": "higher-is-better",
|
|
72
|
+
"allowableDecreasePercent": 10
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"smoothing": {
|
|
77
|
+
"warmupRuns": 2,
|
|
78
|
+
"sampleRuns": 3,
|
|
79
|
+
"outlierMethod": "iqr",
|
|
80
|
+
"iqrMultiplier": 1.5
|
|
81
|
+
},
|
|
82
|
+
"baseline": {
|
|
83
|
+
"filePath": ".benchmark/baseline.json",
|
|
84
|
+
"autoUpdateOnPass": false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { exec, queryAll, querySingle, toNumber, withTransaction, } from "./ladybug-core.js";
|
|
2
2
|
export async function upsertCluster(conn, row) {
|
|
3
|
-
await exec(conn, `MATCH (r:Repo {repoId: $repoId})
|
|
4
|
-
MERGE (c:Cluster {clusterId: $clusterId})
|
|
5
|
-
SET c.repoId = $repoId,
|
|
6
|
-
c.label = $label,
|
|
7
|
-
c.symbolCount = $symbolCount,
|
|
8
|
-
c.cohesionScore = $cohesionScore,
|
|
9
|
-
c.versionId = $versionId,
|
|
10
|
-
c.createdAt = $createdAt
|
|
3
|
+
await exec(conn, `MATCH (r:Repo {repoId: $repoId})
|
|
4
|
+
MERGE (c:Cluster {clusterId: $clusterId})
|
|
5
|
+
SET c.repoId = $repoId,
|
|
6
|
+
c.label = $label,
|
|
7
|
+
c.symbolCount = $symbolCount,
|
|
8
|
+
c.cohesionScore = $cohesionScore,
|
|
9
|
+
c.versionId = $versionId,
|
|
10
|
+
c.createdAt = $createdAt
|
|
11
11
|
MERGE (c)-[:CLUSTER_IN_REPO]->(r)`, {
|
|
12
12
|
clusterId: row.clusterId,
|
|
13
13
|
repoId: row.repoId,
|
|
@@ -19,9 +19,9 @@ export async function upsertCluster(conn, row) {
|
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
export async function upsertClusterMember(conn, row) {
|
|
22
|
-
await exec(conn, `MATCH (s:Symbol {symbolId: $symbolId})
|
|
23
|
-
MATCH (c:Cluster {clusterId: $clusterId})
|
|
24
|
-
MERGE (s)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
22
|
+
await exec(conn, `MATCH (s:Symbol {symbolId: $symbolId})
|
|
23
|
+
MATCH (c:Cluster {clusterId: $clusterId})
|
|
24
|
+
MERGE (s)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
25
25
|
SET m.membershipScore = $membershipScore`, {
|
|
26
26
|
symbolId: row.symbolId,
|
|
27
27
|
clusterId: row.clusterId,
|
|
@@ -33,9 +33,9 @@ export async function upsertClusterMembersBatch(conn, members) {
|
|
|
33
33
|
return;
|
|
34
34
|
await withTransaction(conn, async (txConn) => {
|
|
35
35
|
for (const member of members) {
|
|
36
|
-
await exec(txConn, `MATCH (s:Symbol {symbolId: $symbolId})
|
|
37
|
-
MATCH (c:Cluster {clusterId: $clusterId})
|
|
38
|
-
MERGE (s)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
36
|
+
await exec(txConn, `MATCH (s:Symbol {symbolId: $symbolId})
|
|
37
|
+
MATCH (c:Cluster {clusterId: $clusterId})
|
|
38
|
+
MERGE (s)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
39
39
|
SET m.membershipScore = $membershipScore`, {
|
|
40
40
|
symbolId: member.symbolId,
|
|
41
41
|
clusterId: member.clusterId,
|
|
@@ -45,10 +45,10 @@ export async function upsertClusterMembersBatch(conn, members) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
export async function getClusterForSymbol(conn, symbolId) {
|
|
48
|
-
const row = await querySingle(conn, `MATCH (s:Symbol {symbolId: $symbolId})-[m:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
49
|
-
RETURN c.clusterId AS clusterId,
|
|
50
|
-
c.label AS label,
|
|
51
|
-
c.symbolCount AS symbolCount,
|
|
48
|
+
const row = await querySingle(conn, `MATCH (s:Symbol {symbolId: $symbolId})-[m:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
49
|
+
RETURN c.clusterId AS clusterId,
|
|
50
|
+
c.label AS label,
|
|
51
|
+
c.symbolCount AS symbolCount,
|
|
52
52
|
m.membershipScore AS membershipScore`, { symbolId });
|
|
53
53
|
if (!row)
|
|
54
54
|
return null;
|
|
@@ -62,13 +62,13 @@ export async function getClusterForSymbol(conn, symbolId) {
|
|
|
62
62
|
export async function getClustersForSymbols(conn, symbolIds) {
|
|
63
63
|
if (symbolIds.length === 0)
|
|
64
64
|
return new Map();
|
|
65
|
-
const rows = await queryAll(conn, `MATCH (s:Symbol)-[m:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
66
|
-
WHERE s.symbolId IN $symbolIds
|
|
67
|
-
RETURN s.symbolId AS symbolId,
|
|
68
|
-
c.clusterId AS clusterId,
|
|
69
|
-
c.label AS label,
|
|
70
|
-
c.symbolCount AS symbolCount,
|
|
71
|
-
m.membershipScore AS membershipScore
|
|
65
|
+
const rows = await queryAll(conn, `MATCH (s:Symbol)-[m:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
66
|
+
WHERE s.symbolId IN $symbolIds
|
|
67
|
+
RETURN s.symbolId AS symbolId,
|
|
68
|
+
c.clusterId AS clusterId,
|
|
69
|
+
c.label AS label,
|
|
70
|
+
c.symbolCount AS symbolCount,
|
|
71
|
+
m.membershipScore AS membershipScore
|
|
72
72
|
ORDER BY symbolId ASC, clusterId ASC`, { symbolIds });
|
|
73
73
|
const map = new Map();
|
|
74
74
|
for (const row of rows) {
|
|
@@ -84,13 +84,13 @@ export async function getClustersForSymbols(conn, symbolIds) {
|
|
|
84
84
|
return map;
|
|
85
85
|
}
|
|
86
86
|
export async function getClustersForRepo(conn, repoId) {
|
|
87
|
-
const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
88
|
-
RETURN c.clusterId AS clusterId,
|
|
89
|
-
c.label AS label,
|
|
90
|
-
c.symbolCount AS symbolCount,
|
|
91
|
-
c.cohesionScore AS cohesionScore,
|
|
92
|
-
c.versionId AS versionId,
|
|
93
|
-
c.createdAt AS createdAt
|
|
87
|
+
const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
88
|
+
RETURN c.clusterId AS clusterId,
|
|
89
|
+
c.label AS label,
|
|
90
|
+
c.symbolCount AS symbolCount,
|
|
91
|
+
c.cohesionScore AS cohesionScore,
|
|
92
|
+
c.versionId AS versionId,
|
|
93
|
+
c.createdAt AS createdAt
|
|
94
94
|
ORDER BY c.clusterId`, { repoId });
|
|
95
95
|
return rows.map((row) => ({
|
|
96
96
|
clusterId: row.clusterId,
|
|
@@ -103,19 +103,19 @@ export async function getClustersForRepo(conn, repoId) {
|
|
|
103
103
|
}));
|
|
104
104
|
}
|
|
105
105
|
export async function getClusterMembersForRepo(conn, repoId) {
|
|
106
|
-
const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
106
|
+
const rows = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:SYMBOL_IN_REPO]-(s:Symbol)-[:BELONGS_TO_CLUSTER]->(c:Cluster)
|
|
107
107
|
RETURN s.symbolId AS symbolId, c.clusterId AS clusterId`, { repoId });
|
|
108
108
|
return rows;
|
|
109
109
|
}
|
|
110
110
|
export async function getClusterOverviewStats(conn, repoId) {
|
|
111
|
-
const agg = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
112
|
-
RETURN COUNT(c) AS totalClusters,
|
|
111
|
+
const agg = await querySingle(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
112
|
+
RETURN COUNT(c) AS totalClusters,
|
|
113
113
|
AVG(c.symbolCount) AS averageClusterSize`, { repoId });
|
|
114
|
-
const top = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
115
|
-
RETURN c.clusterId AS clusterId,
|
|
116
|
-
c.label AS label,
|
|
117
|
-
c.symbolCount AS size
|
|
118
|
-
ORDER BY size DESC, clusterId ASC
|
|
114
|
+
const top = await queryAll(conn, `MATCH (r:Repo {repoId: $repoId})<-[:CLUSTER_IN_REPO]-(c:Cluster)
|
|
115
|
+
RETURN c.clusterId AS clusterId,
|
|
116
|
+
c.label AS label,
|
|
117
|
+
c.symbolCount AS size
|
|
118
|
+
ORDER BY size DESC, clusterId ASC
|
|
119
119
|
LIMIT 5`, { repoId });
|
|
120
120
|
return {
|
|
121
121
|
totalClusters: toNumber(agg?.totalClusters ?? 0),
|
|
@@ -128,9 +128,9 @@ export async function getClusterOverviewStats(conn, repoId) {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
export async function getClusterMembers(conn, clusterId) {
|
|
131
|
-
const rows = await queryAll(conn, `MATCH (s:Symbol)-[m:BELONGS_TO_CLUSTER]->(c:Cluster {clusterId: $clusterId})
|
|
132
|
-
RETURN s.symbolId AS symbolId,
|
|
133
|
-
m.membershipScore AS membershipScore
|
|
131
|
+
const rows = await queryAll(conn, `MATCH (s:Symbol)-[m:BELONGS_TO_CLUSTER]->(c:Cluster {clusterId: $clusterId})
|
|
132
|
+
RETURN s.symbolId AS symbolId,
|
|
133
|
+
m.membershipScore AS membershipScore
|
|
134
134
|
ORDER BY symbolId`, { clusterId });
|
|
135
135
|
return rows.map((row) => ({
|
|
136
136
|
symbolId: row.symbolId,
|
|
@@ -138,9 +138,9 @@ export async function getClusterMembers(conn, clusterId) {
|
|
|
138
138
|
}));
|
|
139
139
|
}
|
|
140
140
|
export async function getRelatedClusters(conn, clusterId, limit = 20) {
|
|
141
|
-
const rows = await queryAll(conn, `MATCH (c1:Cluster {clusterId: $clusterId})<-[:BELONGS_TO_CLUSTER]-(s:Symbol)
|
|
142
|
-
MATCH (s)-[d:DEPENDS_ON]->(t:Symbol)-[:BELONGS_TO_CLUSTER]->(c2:Cluster)
|
|
143
|
-
WHERE c2.clusterId <> $clusterId
|
|
141
|
+
const rows = await queryAll(conn, `MATCH (c1:Cluster {clusterId: $clusterId})<-[:BELONGS_TO_CLUSTER]-(s:Symbol)
|
|
142
|
+
MATCH (s)-[d:DEPENDS_ON]->(t:Symbol)-[:BELONGS_TO_CLUSTER]->(c2:Cluster)
|
|
143
|
+
WHERE c2.clusterId <> $clusterId
|
|
144
144
|
RETURN c2.clusterId AS clusterId`, { clusterId });
|
|
145
145
|
const edgeCounts = new Map();
|
|
146
146
|
for (const row of rows) {
|
|
@@ -155,12 +155,12 @@ export async function getRelatedClusters(conn, clusterId, limit = 20) {
|
|
|
155
155
|
}
|
|
156
156
|
export async function deleteClustersByRepo(conn, repoId) {
|
|
157
157
|
await withTransaction(conn, async (txConn) => {
|
|
158
|
-
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})
|
|
159
|
-
OPTIONAL MATCH (:Symbol)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
158
|
+
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})
|
|
159
|
+
OPTIONAL MATCH (:Symbol)-[m:BELONGS_TO_CLUSTER]->(c)
|
|
160
160
|
DELETE m`, { repoId });
|
|
161
|
-
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})-[rel:CLUSTER_IN_REPO]->(:Repo {repoId: $repoId})
|
|
161
|
+
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})-[rel:CLUSTER_IN_REPO]->(:Repo {repoId: $repoId})
|
|
162
162
|
DELETE rel`, { repoId });
|
|
163
|
-
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})
|
|
163
|
+
await exec(txConn, `MATCH (c:Cluster {repoId: $repoId})
|
|
164
164
|
DELETE c`, { repoId });
|
|
165
165
|
});
|
|
166
166
|
}
|