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.
@@ -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
  }