@aiready/core 0.9.32 → 0.9.33
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/dist/chunk-HFLFBA6F.mjs +410 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +22 -20
- package/dist/client.mjs +1 -1
- package/dist/index.js +299 -102
- package/dist/index.mjs +278 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -207,7 +207,9 @@ async function scanFiles(options) {
|
|
|
207
207
|
ignoreFromFile = [];
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
const finalExclude = [
|
|
210
|
+
const finalExclude = [
|
|
211
|
+
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
|
|
212
|
+
];
|
|
211
213
|
const files = await (0, import_glob.glob)(include, {
|
|
212
214
|
cwd: rootDir,
|
|
213
215
|
ignore: finalExclude,
|
|
@@ -455,7 +457,9 @@ async function loadConfig(rootDir) {
|
|
|
455
457
|
return config;
|
|
456
458
|
} catch (error) {
|
|
457
459
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
458
|
-
throw new Error(
|
|
460
|
+
throw new Error(
|
|
461
|
+
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
462
|
+
);
|
|
459
463
|
}
|
|
460
464
|
}
|
|
461
465
|
}
|
|
@@ -707,26 +711,26 @@ function generateHTML(graph) {
|
|
|
707
711
|
var DEFAULT_TOOL_WEIGHTS = {
|
|
708
712
|
"pattern-detect": 22,
|
|
709
713
|
"context-analyzer": 19,
|
|
710
|
-
|
|
714
|
+
consistency: 14,
|
|
711
715
|
"ai-signal-clarity": 11,
|
|
712
716
|
"agent-grounding": 10,
|
|
713
|
-
|
|
717
|
+
testability: 10,
|
|
714
718
|
"doc-drift": 8,
|
|
715
|
-
|
|
719
|
+
deps: 6
|
|
716
720
|
};
|
|
717
721
|
var TOOL_NAME_MAP = {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
722
|
+
patterns: "pattern-detect",
|
|
723
|
+
context: "context-analyzer",
|
|
724
|
+
consistency: "consistency",
|
|
721
725
|
"AI signal clarity": "ai-signal-clarity",
|
|
722
726
|
"ai-signal-clarity": "ai-signal-clarity",
|
|
723
|
-
|
|
727
|
+
grounding: "agent-grounding",
|
|
724
728
|
"agent-grounding": "agent-grounding",
|
|
725
|
-
|
|
726
|
-
|
|
729
|
+
testability: "testability",
|
|
730
|
+
tests: "testability",
|
|
727
731
|
"doc-drift": "doc-drift",
|
|
728
|
-
|
|
729
|
-
|
|
732
|
+
docs: "doc-drift",
|
|
733
|
+
deps: "deps"
|
|
730
734
|
};
|
|
731
735
|
var CONTEXT_TIER_THRESHOLDS = {
|
|
732
736
|
compact: { idealTokens: 3e3, criticalTokens: 1e4, idealDepth: 4 },
|
|
@@ -735,15 +739,15 @@ var CONTEXT_TIER_THRESHOLDS = {
|
|
|
735
739
|
frontier: { idealTokens: 5e4, criticalTokens: 15e4, idealDepth: 10 }
|
|
736
740
|
};
|
|
737
741
|
var SIZE_ADJUSTED_THRESHOLDS = {
|
|
738
|
-
|
|
742
|
+
xs: 80,
|
|
739
743
|
// < 50 files
|
|
740
|
-
|
|
744
|
+
small: 75,
|
|
741
745
|
// 50-200 files
|
|
742
|
-
|
|
746
|
+
medium: 70,
|
|
743
747
|
// 200-500 files
|
|
744
|
-
|
|
748
|
+
large: 65,
|
|
745
749
|
// 500-2000 files
|
|
746
|
-
|
|
750
|
+
enterprise: 58
|
|
747
751
|
// 2000+ files
|
|
748
752
|
};
|
|
749
753
|
function getProjectSizeTier(fileCount) {
|
|
@@ -816,10 +820,12 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
816
820
|
}
|
|
817
821
|
const overall = Math.round(weightedSum / totalWeight);
|
|
818
822
|
const rating = getRating(overall);
|
|
819
|
-
const formulaParts = Array.from(toolOutputs.entries()).map(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
+
const formulaParts = Array.from(toolOutputs.entries()).map(
|
|
824
|
+
([name, output]) => {
|
|
825
|
+
const w = weights.get(name) || 10;
|
|
826
|
+
return `(${output.score} \xD7 ${w})`;
|
|
827
|
+
}
|
|
828
|
+
);
|
|
823
829
|
const formulaStr = `[${formulaParts.join(" + ")}] / ${totalWeight} = ${overall}`;
|
|
824
830
|
return {
|
|
825
831
|
overall,
|
|
@@ -951,7 +957,7 @@ var MODEL_PRICING_PRESETS = {
|
|
|
951
957
|
contextTier: "frontier",
|
|
952
958
|
typicalQueriesPerDevPerDay: 150
|
|
953
959
|
},
|
|
954
|
-
|
|
960
|
+
copilot: {
|
|
955
961
|
name: "GitHub Copilot (subscription)",
|
|
956
962
|
// Amortized per-request cost for a $19/month plan at 80 queries/day
|
|
957
963
|
pricePer1KInputTokens: 1e-4,
|
|
@@ -1017,9 +1023,18 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
|
1017
1023
|
hourlyRate,
|
|
1018
1024
|
totalCost: Math.round(totalCost),
|
|
1019
1025
|
bySeverity: {
|
|
1020
|
-
critical: {
|
|
1021
|
-
|
|
1022
|
-
|
|
1026
|
+
critical: {
|
|
1027
|
+
hours: Math.round(hours.critical * 10) / 10,
|
|
1028
|
+
cost: Math.round(hours.critical * hourlyRate)
|
|
1029
|
+
},
|
|
1030
|
+
major: {
|
|
1031
|
+
hours: Math.round(hours.major * 10) / 10,
|
|
1032
|
+
cost: Math.round(hours.major * hourlyRate)
|
|
1033
|
+
},
|
|
1034
|
+
minor: {
|
|
1035
|
+
hours: Math.round(hours.minor * 10) / 10,
|
|
1036
|
+
cost: Math.round(hours.minor * hourlyRate)
|
|
1037
|
+
}
|
|
1023
1038
|
}
|
|
1024
1039
|
};
|
|
1025
1040
|
}
|
|
@@ -1077,12 +1092,18 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
|
|
|
1077
1092
|
const criticalBudget = tierThresholds.criticalTokens;
|
|
1078
1093
|
const idealDepth = tierThresholds.idealDepth;
|
|
1079
1094
|
const budgetRange = criticalBudget - idealBudget;
|
|
1080
|
-
const budgetFactor = Math.min(
|
|
1081
|
-
|
|
1082
|
-
(contextBudget - idealBudget) / budgetRange * 100
|
|
1083
|
-
)
|
|
1084
|
-
const depthFactor = Math.min(
|
|
1085
|
-
|
|
1095
|
+
const budgetFactor = Math.min(
|
|
1096
|
+
100,
|
|
1097
|
+
Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
|
|
1098
|
+
);
|
|
1099
|
+
const depthFactor = Math.min(
|
|
1100
|
+
100,
|
|
1101
|
+
Math.max(0, (importDepth - idealDepth) * 10)
|
|
1102
|
+
);
|
|
1103
|
+
const fragmentationFactor = Math.min(
|
|
1104
|
+
100,
|
|
1105
|
+
Math.max(0, (fragmentation - 0.3) * 250)
|
|
1106
|
+
);
|
|
1086
1107
|
const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
|
|
1087
1108
|
const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
|
|
1088
1109
|
const score = Math.round(
|
|
@@ -1162,8 +1183,12 @@ function calculateScoreTrend(history) {
|
|
|
1162
1183
|
const now = /* @__PURE__ */ new Date();
|
|
1163
1184
|
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
|
|
1164
1185
|
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
|
|
1165
|
-
const last30Days = history.filter(
|
|
1166
|
-
|
|
1186
|
+
const last30Days = history.filter(
|
|
1187
|
+
(e) => new Date(e.timestamp) >= thirtyDaysAgo
|
|
1188
|
+
);
|
|
1189
|
+
const last90Days = history.filter(
|
|
1190
|
+
(e) => new Date(e.timestamp) >= ninetyDaysAgo
|
|
1191
|
+
);
|
|
1167
1192
|
const currentScore = history[history.length - 1].overallScore;
|
|
1168
1193
|
const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
|
|
1169
1194
|
const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
|
|
@@ -1176,7 +1201,10 @@ function calculateScoreTrend(history) {
|
|
|
1176
1201
|
if (change30Days > 3) direction = "improving";
|
|
1177
1202
|
else if (change30Days < -3) direction = "degrading";
|
|
1178
1203
|
else direction = "stable";
|
|
1179
|
-
const projectedScore = Math.max(
|
|
1204
|
+
const projectedScore = Math.max(
|
|
1205
|
+
0,
|
|
1206
|
+
Math.min(100, currentScore + velocity * 4)
|
|
1207
|
+
);
|
|
1180
1208
|
return {
|
|
1181
1209
|
direction,
|
|
1182
1210
|
change30Days,
|
|
@@ -1237,9 +1265,13 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
1237
1265
|
recommendations: ["No files to analyze"]
|
|
1238
1266
|
};
|
|
1239
1267
|
}
|
|
1240
|
-
const orphanFiles = files.filter(
|
|
1268
|
+
const orphanFiles = files.filter(
|
|
1269
|
+
(f) => f.exports < 2 && f.imports < 2
|
|
1270
|
+
).length;
|
|
1241
1271
|
const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
|
|
1242
|
-
const uniqueConceptFiles = files.filter(
|
|
1272
|
+
const uniqueConceptFiles = files.filter(
|
|
1273
|
+
(f) => f.exports > avgExports * 2
|
|
1274
|
+
).length;
|
|
1243
1275
|
const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
|
|
1244
1276
|
const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
|
|
1245
1277
|
let singleAuthorFiles = 0;
|
|
@@ -1251,7 +1283,10 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
1251
1283
|
const orphanRisk = orphanFiles / files.length * 30;
|
|
1252
1284
|
const uniqueRisk = concentrationRatio * 40;
|
|
1253
1285
|
const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
|
|
1254
|
-
const score = Math.min(
|
|
1286
|
+
const score = Math.min(
|
|
1287
|
+
100,
|
|
1288
|
+
Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
|
|
1289
|
+
);
|
|
1255
1290
|
let rating;
|
|
1256
1291
|
if (score < 20) rating = "low";
|
|
1257
1292
|
else if (score < 40) rating = "moderate";
|
|
@@ -1259,13 +1294,19 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
1259
1294
|
else rating = "critical";
|
|
1260
1295
|
const recommendations = [];
|
|
1261
1296
|
if (orphanFiles > files.length * 0.2) {
|
|
1262
|
-
recommendations.push(
|
|
1297
|
+
recommendations.push(
|
|
1298
|
+
`Reduce ${orphanFiles} orphan files by connecting them to main modules`
|
|
1299
|
+
);
|
|
1263
1300
|
}
|
|
1264
1301
|
if (uniqueConceptFiles > files.length * 0.1) {
|
|
1265
|
-
recommendations.push(
|
|
1302
|
+
recommendations.push(
|
|
1303
|
+
"Distribute high-export files into more focused modules"
|
|
1304
|
+
);
|
|
1266
1305
|
}
|
|
1267
1306
|
if (authorData && singleAuthorFiles > files.length * 0.3) {
|
|
1268
|
-
recommendations.push(
|
|
1307
|
+
recommendations.push(
|
|
1308
|
+
"Increase knowledge sharing to reduce single-author dependencies"
|
|
1309
|
+
);
|
|
1269
1310
|
}
|
|
1270
1311
|
return {
|
|
1271
1312
|
score,
|
|
@@ -1419,7 +1460,10 @@ var TypeScriptParser = class {
|
|
|
1419
1460
|
specifiers,
|
|
1420
1461
|
isTypeOnly,
|
|
1421
1462
|
loc: node.loc ? {
|
|
1422
|
-
start: {
|
|
1463
|
+
start: {
|
|
1464
|
+
line: node.loc.start.line,
|
|
1465
|
+
column: node.loc.start.column
|
|
1466
|
+
},
|
|
1423
1467
|
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
1424
1468
|
} : void 0
|
|
1425
1469
|
});
|
|
@@ -1430,11 +1474,16 @@ var TypeScriptParser = class {
|
|
|
1430
1474
|
extractExports(ast, imports) {
|
|
1431
1475
|
const exports2 = [];
|
|
1432
1476
|
const importedNames = new Set(
|
|
1433
|
-
imports.flatMap(
|
|
1477
|
+
imports.flatMap(
|
|
1478
|
+
(imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
|
|
1479
|
+
)
|
|
1434
1480
|
);
|
|
1435
1481
|
for (const node of ast.body) {
|
|
1436
1482
|
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
1437
|
-
const extracted = this.extractFromDeclaration(
|
|
1483
|
+
const extracted = this.extractFromDeclaration(
|
|
1484
|
+
node.declaration,
|
|
1485
|
+
importedNames
|
|
1486
|
+
);
|
|
1438
1487
|
exports2.push(...extracted);
|
|
1439
1488
|
} else if (node.type === "ExportDefaultDeclaration") {
|
|
1440
1489
|
let name = "default";
|
|
@@ -1450,7 +1499,10 @@ var TypeScriptParser = class {
|
|
|
1450
1499
|
name,
|
|
1451
1500
|
type,
|
|
1452
1501
|
loc: node.loc ? {
|
|
1453
|
-
start: {
|
|
1502
|
+
start: {
|
|
1503
|
+
line: node.loc.start.line,
|
|
1504
|
+
column: node.loc.start.column
|
|
1505
|
+
},
|
|
1454
1506
|
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
1455
1507
|
} : void 0
|
|
1456
1508
|
});
|
|
@@ -1472,7 +1524,10 @@ var TypeScriptParser = class {
|
|
|
1472
1524
|
line: declaration.loc.start.line,
|
|
1473
1525
|
column: declaration.loc.start.column
|
|
1474
1526
|
},
|
|
1475
|
-
end: {
|
|
1527
|
+
end: {
|
|
1528
|
+
line: declaration.loc.end.line,
|
|
1529
|
+
column: declaration.loc.end.column
|
|
1530
|
+
}
|
|
1476
1531
|
} : void 0
|
|
1477
1532
|
});
|
|
1478
1533
|
} else if (declaration.type === "ClassDeclaration" && declaration.id) {
|
|
@@ -1484,7 +1539,10 @@ var TypeScriptParser = class {
|
|
|
1484
1539
|
line: declaration.loc.start.line,
|
|
1485
1540
|
column: declaration.loc.start.column
|
|
1486
1541
|
},
|
|
1487
|
-
end: {
|
|
1542
|
+
end: {
|
|
1543
|
+
line: declaration.loc.end.line,
|
|
1544
|
+
column: declaration.loc.end.column
|
|
1545
|
+
}
|
|
1488
1546
|
} : void 0
|
|
1489
1547
|
});
|
|
1490
1548
|
} else if (declaration.type === "VariableDeclaration") {
|
|
@@ -1515,7 +1573,10 @@ var TypeScriptParser = class {
|
|
|
1515
1573
|
line: declaration.loc.start.line,
|
|
1516
1574
|
column: declaration.loc.start.column
|
|
1517
1575
|
},
|
|
1518
|
-
end: {
|
|
1576
|
+
end: {
|
|
1577
|
+
line: declaration.loc.end.line,
|
|
1578
|
+
column: declaration.loc.end.column
|
|
1579
|
+
}
|
|
1519
1580
|
} : void 0
|
|
1520
1581
|
});
|
|
1521
1582
|
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
@@ -1527,7 +1588,10 @@ var TypeScriptParser = class {
|
|
|
1527
1588
|
line: declaration.loc.start.line,
|
|
1528
1589
|
column: declaration.loc.start.column
|
|
1529
1590
|
},
|
|
1530
|
-
end: {
|
|
1591
|
+
end: {
|
|
1592
|
+
line: declaration.loc.end.line,
|
|
1593
|
+
column: declaration.loc.end.column
|
|
1594
|
+
}
|
|
1531
1595
|
} : void 0
|
|
1532
1596
|
});
|
|
1533
1597
|
}
|
|
@@ -1552,7 +1616,9 @@ var PythonParser = class {
|
|
|
1552
1616
|
try {
|
|
1553
1617
|
this.initialized = true;
|
|
1554
1618
|
} catch (error) {
|
|
1555
|
-
throw new Error(
|
|
1619
|
+
throw new Error(
|
|
1620
|
+
`Failed to initialize Python parser: ${error.message}`
|
|
1621
|
+
);
|
|
1556
1622
|
}
|
|
1557
1623
|
}
|
|
1558
1624
|
parse(code, filePath) {
|
|
@@ -1563,7 +1629,9 @@ var PythonParser = class {
|
|
|
1563
1629
|
exports: exports2,
|
|
1564
1630
|
imports,
|
|
1565
1631
|
language: "python" /* Python */,
|
|
1566
|
-
warnings: [
|
|
1632
|
+
warnings: [
|
|
1633
|
+
"Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."
|
|
1634
|
+
]
|
|
1567
1635
|
};
|
|
1568
1636
|
} catch (error) {
|
|
1569
1637
|
throw new ParseError(
|
|
@@ -1658,7 +1726,7 @@ var PythonParser = class {
|
|
|
1658
1726
|
}
|
|
1659
1727
|
/**
|
|
1660
1728
|
* Regex-based export extraction (temporary implementation)
|
|
1661
|
-
*
|
|
1729
|
+
*
|
|
1662
1730
|
* Python doesn't have explicit exports like JavaScript.
|
|
1663
1731
|
* We extract:
|
|
1664
1732
|
* - Functions defined at module level (def)
|
|
@@ -1826,7 +1894,13 @@ function getSupportedLanguages() {
|
|
|
1826
1894
|
|
|
1827
1895
|
// src/future-proof-metrics.ts
|
|
1828
1896
|
function calculateCognitiveLoad(params) {
|
|
1829
|
-
const {
|
|
1897
|
+
const {
|
|
1898
|
+
linesOfCode,
|
|
1899
|
+
exportCount,
|
|
1900
|
+
importCount,
|
|
1901
|
+
uniqueConcepts,
|
|
1902
|
+
cyclomaticComplexity = 1
|
|
1903
|
+
} = params;
|
|
1830
1904
|
const sizeFactor = {
|
|
1831
1905
|
name: "Size Complexity",
|
|
1832
1906
|
score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
|
|
@@ -1852,7 +1926,12 @@ function calculateCognitiveLoad(params) {
|
|
|
1852
1926
|
weight: 0.2,
|
|
1853
1927
|
description: `${uniqueConcepts} unique concepts`
|
|
1854
1928
|
};
|
|
1855
|
-
const factors = [
|
|
1929
|
+
const factors = [
|
|
1930
|
+
sizeFactor,
|
|
1931
|
+
interfaceFactor,
|
|
1932
|
+
dependencyFactor,
|
|
1933
|
+
conceptFactor
|
|
1934
|
+
];
|
|
1856
1935
|
const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
|
|
1857
1936
|
let rating;
|
|
1858
1937
|
if (score < 20) rating = "trivial";
|
|
@@ -1875,7 +1954,10 @@ function calculateCognitiveLoad(params) {
|
|
|
1875
1954
|
function calculateSemanticDistance(params) {
|
|
1876
1955
|
const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
|
|
1877
1956
|
const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
|
|
1878
|
-
const importOverlap = sharedDependencies.length / Math.max(
|
|
1957
|
+
const importOverlap = sharedDependencies.length / Math.max(
|
|
1958
|
+
1,
|
|
1959
|
+
Math.min(params.file1Imports.length, params.file2Imports.length)
|
|
1960
|
+
);
|
|
1879
1961
|
const importDistance = 1 - importOverlap;
|
|
1880
1962
|
const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
|
|
1881
1963
|
let relationship;
|
|
@@ -1883,7 +1965,9 @@ function calculateSemanticDistance(params) {
|
|
|
1883
1965
|
else if (file1Domain === file2Domain) relationship = "same-domain";
|
|
1884
1966
|
else if (sharedDependencies.length > 0) relationship = "cross-domain";
|
|
1885
1967
|
else relationship = "unrelated";
|
|
1886
|
-
const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
|
|
1968
|
+
const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
|
|
1969
|
+
(s) => typeof s === "string" && s.length > 0
|
|
1970
|
+
);
|
|
1887
1971
|
return {
|
|
1888
1972
|
between: [file1, file2],
|
|
1889
1973
|
distance: Math.round(distance * 100) / 100,
|
|
@@ -1898,7 +1982,11 @@ function calculatePatternEntropy(files) {
|
|
|
1898
1982
|
domain: "unknown",
|
|
1899
1983
|
entropy: 0,
|
|
1900
1984
|
rating: "crystalline",
|
|
1901
|
-
distribution: {
|
|
1985
|
+
distribution: {
|
|
1986
|
+
locationCount: 0,
|
|
1987
|
+
dominantLocation: "",
|
|
1988
|
+
giniCoefficient: 0
|
|
1989
|
+
},
|
|
1902
1990
|
recommendations: ["No files to analyze"]
|
|
1903
1991
|
};
|
|
1904
1992
|
}
|
|
@@ -1938,10 +2026,14 @@ function calculatePatternEntropy(files) {
|
|
|
1938
2026
|
else rating = "chaotic";
|
|
1939
2027
|
const recommendations = [];
|
|
1940
2028
|
if (normalizedEntropy > 0.5) {
|
|
1941
|
-
recommendations.push(
|
|
2029
|
+
recommendations.push(
|
|
2030
|
+
`Consolidate ${files.length} files into fewer directories by domain`
|
|
2031
|
+
);
|
|
1942
2032
|
}
|
|
1943
2033
|
if (dirGroups.size > 5) {
|
|
1944
|
-
recommendations.push(
|
|
2034
|
+
recommendations.push(
|
|
2035
|
+
"Consider barrel exports to reduce directory navigation"
|
|
2036
|
+
);
|
|
1945
2037
|
}
|
|
1946
2038
|
if (gini > 0.5) {
|
|
1947
2039
|
recommendations.push("Redistribute files more evenly across directories");
|
|
@@ -1966,7 +2058,11 @@ function calculateConceptCohesion(params) {
|
|
|
1966
2058
|
return {
|
|
1967
2059
|
score: 1,
|
|
1968
2060
|
rating: "excellent",
|
|
1969
|
-
analysis: {
|
|
2061
|
+
analysis: {
|
|
2062
|
+
uniqueDomains: 0,
|
|
2063
|
+
domainConcentration: 0,
|
|
2064
|
+
exportPurposeClarity: 1
|
|
2065
|
+
}
|
|
1970
2066
|
};
|
|
1971
2067
|
}
|
|
1972
2068
|
const allDomains = [];
|
|
@@ -2072,7 +2168,10 @@ function calculateAiSignalClarity(params) {
|
|
|
2072
2168
|
recommendations: []
|
|
2073
2169
|
};
|
|
2074
2170
|
}
|
|
2075
|
-
const overloadRatio = Math.min(
|
|
2171
|
+
const overloadRatio = Math.min(
|
|
2172
|
+
1,
|
|
2173
|
+
overloadedSymbols / Math.max(1, totalSymbols)
|
|
2174
|
+
);
|
|
2076
2175
|
const overloadSignal = {
|
|
2077
2176
|
name: "Symbol Overloading",
|
|
2078
2177
|
count: overloadedSymbols,
|
|
@@ -2096,7 +2195,10 @@ function calculateAiSignalClarity(params) {
|
|
|
2096
2195
|
// 20% weight
|
|
2097
2196
|
description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
|
|
2098
2197
|
};
|
|
2099
|
-
const sideEffectRatio = Math.min(
|
|
2198
|
+
const sideEffectRatio = Math.min(
|
|
2199
|
+
1,
|
|
2200
|
+
implicitSideEffects / Math.max(1, totalExports)
|
|
2201
|
+
);
|
|
2100
2202
|
const sideEffectSignal = {
|
|
2101
2203
|
name: "Implicit Side Effects",
|
|
2102
2204
|
count: implicitSideEffects,
|
|
@@ -2104,7 +2206,10 @@ function calculateAiSignalClarity(params) {
|
|
|
2104
2206
|
// 15% weight
|
|
2105
2207
|
description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
|
|
2106
2208
|
};
|
|
2107
|
-
const callbackRatio = Math.min(
|
|
2209
|
+
const callbackRatio = Math.min(
|
|
2210
|
+
1,
|
|
2211
|
+
deepCallbacks / Math.max(1, totalSymbols * 0.1)
|
|
2212
|
+
);
|
|
2108
2213
|
const callbackSignal = {
|
|
2109
2214
|
name: "Callback Nesting",
|
|
2110
2215
|
count: deepCallbacks,
|
|
@@ -2112,7 +2217,10 @@ function calculateAiSignalClarity(params) {
|
|
|
2112
2217
|
// 10% weight
|
|
2113
2218
|
description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
|
|
2114
2219
|
};
|
|
2115
|
-
const ambiguousRatio = Math.min(
|
|
2220
|
+
const ambiguousRatio = Math.min(
|
|
2221
|
+
1,
|
|
2222
|
+
ambiguousNames / Math.max(1, totalSymbols)
|
|
2223
|
+
);
|
|
2116
2224
|
const ambiguousSignal = {
|
|
2117
2225
|
name: "Ambiguous Names",
|
|
2118
2226
|
count: ambiguousNames,
|
|
@@ -2120,7 +2228,10 @@ function calculateAiSignalClarity(params) {
|
|
|
2120
2228
|
// 10% weight
|
|
2121
2229
|
description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
|
|
2122
2230
|
};
|
|
2123
|
-
const undocRatio = Math.min(
|
|
2231
|
+
const undocRatio = Math.min(
|
|
2232
|
+
1,
|
|
2233
|
+
undocumentedExports / Math.max(1, totalExports)
|
|
2234
|
+
);
|
|
2124
2235
|
const undocSignal = {
|
|
2125
2236
|
name: "Undocumented Exports",
|
|
2126
2237
|
count: undocumentedExports,
|
|
@@ -2137,30 +2248,45 @@ function calculateAiSignalClarity(params) {
|
|
|
2137
2248
|
ambiguousSignal,
|
|
2138
2249
|
undocSignal
|
|
2139
2250
|
];
|
|
2140
|
-
const score = Math.min(
|
|
2251
|
+
const score = Math.min(
|
|
2252
|
+
100,
|
|
2253
|
+
signals.reduce((sum, s) => sum + s.riskContribution, 0)
|
|
2254
|
+
);
|
|
2141
2255
|
let rating;
|
|
2142
2256
|
if (score < 10) rating = "minimal";
|
|
2143
2257
|
else if (score < 25) rating = "low";
|
|
2144
2258
|
else if (score < 50) rating = "moderate";
|
|
2145
2259
|
else if (score < 75) rating = "high";
|
|
2146
2260
|
else rating = "severe";
|
|
2147
|
-
const topSignal = signals.reduce(
|
|
2261
|
+
const topSignal = signals.reduce(
|
|
2262
|
+
(a, b) => a.riskContribution > b.riskContribution ? a : b
|
|
2263
|
+
);
|
|
2148
2264
|
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
|
|
2149
2265
|
const recommendations = [];
|
|
2150
2266
|
if (overloadSignal.riskContribution > 5) {
|
|
2151
|
-
recommendations.push(
|
|
2267
|
+
recommendations.push(
|
|
2268
|
+
`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
|
|
2269
|
+
);
|
|
2152
2270
|
}
|
|
2153
2271
|
if (magicSignal.riskContribution > 5) {
|
|
2154
|
-
recommendations.push(
|
|
2272
|
+
recommendations.push(
|
|
2273
|
+
`Extract ${magicLiterals} magic literals into named constants`
|
|
2274
|
+
);
|
|
2155
2275
|
}
|
|
2156
2276
|
if (trapSignal.riskContribution > 5) {
|
|
2157
|
-
recommendations.push(
|
|
2277
|
+
recommendations.push(
|
|
2278
|
+
`Replace ${booleanTraps} boolean traps with named options objects`
|
|
2279
|
+
);
|
|
2158
2280
|
}
|
|
2159
2281
|
if (undocSignal.riskContribution > 5) {
|
|
2160
|
-
recommendations.push(
|
|
2282
|
+
recommendations.push(
|
|
2283
|
+
`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
|
|
2284
|
+
);
|
|
2161
2285
|
}
|
|
2162
2286
|
if (sideEffectSignal.riskContribution > 5) {
|
|
2163
|
-
recommendations.push(
|
|
2287
|
+
recommendations.push(
|
|
2288
|
+
"Mark functions with side effects explicitly in their names or docs"
|
|
2289
|
+
);
|
|
2164
2290
|
}
|
|
2165
2291
|
return {
|
|
2166
2292
|
score: Math.round(score),
|
|
@@ -2185,7 +2311,10 @@ function calculateAgentGrounding(params) {
|
|
|
2185
2311
|
domainVocabularySize
|
|
2186
2312
|
} = params;
|
|
2187
2313
|
const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
|
|
2188
|
-
const structureClarityScore = Math.max(
|
|
2314
|
+
const structureClarityScore = Math.max(
|
|
2315
|
+
0,
|
|
2316
|
+
Math.round(100 - deepDirRatio * 80)
|
|
2317
|
+
);
|
|
2189
2318
|
const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
|
|
2190
2319
|
const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
|
|
2191
2320
|
let entryPointScore = 60;
|
|
@@ -2197,7 +2326,10 @@ function calculateAgentGrounding(params) {
|
|
|
2197
2326
|
const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
|
|
2198
2327
|
const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
|
|
2199
2328
|
const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
|
|
2200
|
-
const domainConsistencyScore = Math.max(
|
|
2329
|
+
const domainConsistencyScore = Math.max(
|
|
2330
|
+
0,
|
|
2331
|
+
Math.round(100 - inconsistencyRatio * 80)
|
|
2332
|
+
);
|
|
2201
2333
|
const score = Math.round(
|
|
2202
2334
|
structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
|
|
2203
2335
|
);
|
|
@@ -2209,21 +2341,33 @@ function calculateAgentGrounding(params) {
|
|
|
2209
2341
|
else rating = "disorienting";
|
|
2210
2342
|
const recommendations = [];
|
|
2211
2343
|
if (structureClarityScore < 70) {
|
|
2212
|
-
recommendations.push(
|
|
2344
|
+
recommendations.push(
|
|
2345
|
+
`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
|
|
2346
|
+
);
|
|
2213
2347
|
}
|
|
2214
2348
|
if (selfDocumentationScore < 70) {
|
|
2215
|
-
recommendations.push(
|
|
2349
|
+
recommendations.push(
|
|
2350
|
+
`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
|
|
2351
|
+
);
|
|
2216
2352
|
}
|
|
2217
2353
|
if (!hasRootReadme) {
|
|
2218
|
-
recommendations.push(
|
|
2354
|
+
recommendations.push(
|
|
2355
|
+
"Add a root README.md so agents understand the project context immediately"
|
|
2356
|
+
);
|
|
2219
2357
|
} else if (!readmeIsFresh) {
|
|
2220
|
-
recommendations.push(
|
|
2358
|
+
recommendations.push(
|
|
2359
|
+
"Update README.md \u2014 stale entry-point documentation disorients agents"
|
|
2360
|
+
);
|
|
2221
2361
|
}
|
|
2222
2362
|
if (apiClarityScore < 70) {
|
|
2223
|
-
recommendations.push(
|
|
2363
|
+
recommendations.push(
|
|
2364
|
+
`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
|
|
2365
|
+
);
|
|
2224
2366
|
}
|
|
2225
2367
|
if (domainConsistencyScore < 70) {
|
|
2226
|
-
recommendations.push(
|
|
2368
|
+
recommendations.push(
|
|
2369
|
+
`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
|
|
2370
|
+
);
|
|
2227
2371
|
}
|
|
2228
2372
|
return {
|
|
2229
2373
|
score,
|
|
@@ -2256,7 +2400,9 @@ function calculateTestabilityIndex(params) {
|
|
|
2256
2400
|
const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
|
|
2257
2401
|
const purityScore = Math.round(purityRatio * 100);
|
|
2258
2402
|
const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
|
|
2259
|
-
const dependencyInjectionScore = Math.round(
|
|
2403
|
+
const dependencyInjectionScore = Math.round(
|
|
2404
|
+
Math.min(100, injectionRatio * 100)
|
|
2405
|
+
);
|
|
2260
2406
|
const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
|
|
2261
2407
|
const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
|
|
2262
2408
|
const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
|
|
@@ -2272,25 +2418,36 @@ function calculateTestabilityIndex(params) {
|
|
|
2272
2418
|
else rating = "unverifiable";
|
|
2273
2419
|
let aiChangeSafetyRating;
|
|
2274
2420
|
if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
|
|
2275
|
-
else if (rawCoverageRatio >= 0.2 && score >= 50)
|
|
2421
|
+
else if (rawCoverageRatio >= 0.2 && score >= 50)
|
|
2422
|
+
aiChangeSafetyRating = "moderate-risk";
|
|
2276
2423
|
else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
|
|
2277
2424
|
else aiChangeSafetyRating = "blind-risk";
|
|
2278
2425
|
const recommendations = [];
|
|
2279
2426
|
if (!hasTestFramework) {
|
|
2280
|
-
recommendations.push(
|
|
2427
|
+
recommendations.push(
|
|
2428
|
+
"Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
|
|
2429
|
+
);
|
|
2281
2430
|
}
|
|
2282
2431
|
if (rawCoverageRatio < 0.3) {
|
|
2283
2432
|
const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
|
|
2284
|
-
recommendations.push(
|
|
2433
|
+
recommendations.push(
|
|
2434
|
+
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
2435
|
+
);
|
|
2285
2436
|
}
|
|
2286
2437
|
if (purityScore < 50) {
|
|
2287
|
-
recommendations.push(
|
|
2438
|
+
recommendations.push(
|
|
2439
|
+
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
2440
|
+
);
|
|
2288
2441
|
}
|
|
2289
2442
|
if (dependencyInjectionScore < 50 && totalClasses > 0) {
|
|
2290
|
-
recommendations.push(
|
|
2443
|
+
recommendations.push(
|
|
2444
|
+
"Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
|
|
2445
|
+
);
|
|
2291
2446
|
}
|
|
2292
2447
|
if (externalStateMutations > totalFunctions * 0.3) {
|
|
2293
|
-
recommendations.push(
|
|
2448
|
+
recommendations.push(
|
|
2449
|
+
"Reduce direct state mutations \u2014 return values instead to improve observability"
|
|
2450
|
+
);
|
|
2294
2451
|
}
|
|
2295
2452
|
return {
|
|
2296
2453
|
score,
|
|
@@ -2307,7 +2464,12 @@ function calculateTestabilityIndex(params) {
|
|
|
2307
2464
|
};
|
|
2308
2465
|
}
|
|
2309
2466
|
function calculateDocDrift(params) {
|
|
2310
|
-
const {
|
|
2467
|
+
const {
|
|
2468
|
+
uncommentedExports,
|
|
2469
|
+
totalExports,
|
|
2470
|
+
outdatedComments,
|
|
2471
|
+
undocumentedComplexity
|
|
2472
|
+
} = params;
|
|
2311
2473
|
const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
|
|
2312
2474
|
const outdatedScore = Math.min(100, outdatedComments * 15);
|
|
2313
2475
|
const uncommentedScore = Math.min(100, uncommentedRatio * 100);
|
|
@@ -2324,13 +2486,19 @@ function calculateDocDrift(params) {
|
|
|
2324
2486
|
else rating = "severe";
|
|
2325
2487
|
const recommendations = [];
|
|
2326
2488
|
if (outdatedComments > 0) {
|
|
2327
|
-
recommendations.push(
|
|
2489
|
+
recommendations.push(
|
|
2490
|
+
`Update or remove ${outdatedComments} outdated comments that contradict the code.`
|
|
2491
|
+
);
|
|
2328
2492
|
}
|
|
2329
2493
|
if (uncommentedRatio > 0.3) {
|
|
2330
|
-
recommendations.push(
|
|
2494
|
+
recommendations.push(
|
|
2495
|
+
`Add JSDoc to ${uncommentedExports} uncommented exports.`
|
|
2496
|
+
);
|
|
2331
2497
|
}
|
|
2332
2498
|
if (undocumentedComplexity > 0) {
|
|
2333
|
-
recommendations.push(
|
|
2499
|
+
recommendations.push(
|
|
2500
|
+
`Explain the business logic for ${undocumentedComplexity} highly complex functions.`
|
|
2501
|
+
);
|
|
2334
2502
|
}
|
|
2335
2503
|
return {
|
|
2336
2504
|
score: finalScore,
|
|
@@ -2344,7 +2512,12 @@ function calculateDocDrift(params) {
|
|
|
2344
2512
|
};
|
|
2345
2513
|
}
|
|
2346
2514
|
function calculateDependencyHealth(params) {
|
|
2347
|
-
const {
|
|
2515
|
+
const {
|
|
2516
|
+
totalPackages,
|
|
2517
|
+
outdatedPackages,
|
|
2518
|
+
deprecatedPackages,
|
|
2519
|
+
trainingCutoffSkew
|
|
2520
|
+
} = params;
|
|
2348
2521
|
const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
|
|
2349
2522
|
const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
|
|
2350
2523
|
const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
|
|
@@ -2359,19 +2532,27 @@ function calculateDependencyHealth(params) {
|
|
|
2359
2532
|
else if (score >= 30) rating = "poor";
|
|
2360
2533
|
else rating = "hazardous";
|
|
2361
2534
|
let aiKnowledgeConfidence;
|
|
2362
|
-
if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
|
|
2363
|
-
|
|
2535
|
+
if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
|
|
2536
|
+
aiKnowledgeConfidence = "high";
|
|
2537
|
+
else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2)
|
|
2538
|
+
aiKnowledgeConfidence = "moderate";
|
|
2364
2539
|
else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
|
|
2365
2540
|
else aiKnowledgeConfidence = "blind";
|
|
2366
2541
|
const recommendations = [];
|
|
2367
2542
|
if (deprecatedPackages > 0) {
|
|
2368
|
-
recommendations.push(
|
|
2543
|
+
recommendations.push(
|
|
2544
|
+
`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
|
|
2545
|
+
);
|
|
2369
2546
|
}
|
|
2370
2547
|
if (outdatedRatio > 0.2) {
|
|
2371
|
-
recommendations.push(
|
|
2548
|
+
recommendations.push(
|
|
2549
|
+
`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
|
|
2550
|
+
);
|
|
2372
2551
|
}
|
|
2373
2552
|
if (trainingCutoffSkew > 0.5) {
|
|
2374
|
-
recommendations.push(
|
|
2553
|
+
recommendations.push(
|
|
2554
|
+
"High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
|
|
2555
|
+
);
|
|
2375
2556
|
}
|
|
2376
2557
|
return {
|
|
2377
2558
|
score,
|
|
@@ -2412,10 +2593,14 @@ function calculateChangeAmplification(params) {
|
|
|
2412
2593
|
else if (score < 90) rating = "contained";
|
|
2413
2594
|
const recommendations = [];
|
|
2414
2595
|
if (score < 70 && hotspots.length > 0) {
|
|
2415
|
-
recommendations.push(
|
|
2596
|
+
recommendations.push(
|
|
2597
|
+
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
|
|
2598
|
+
);
|
|
2416
2599
|
}
|
|
2417
2600
|
if (maxAmplification > 30) {
|
|
2418
|
-
recommendations.push(
|
|
2601
|
+
recommendations.push(
|
|
2602
|
+
`Break down key bottlenecks with amplification factor > 30.`
|
|
2603
|
+
);
|
|
2419
2604
|
}
|
|
2420
2605
|
return {
|
|
2421
2606
|
score: Math.round(score),
|
|
@@ -2497,7 +2682,11 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2497
2682
|
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
2498
2683
|
}
|
|
2499
2684
|
for (const rec of params.agentGrounding.recommendations) {
|
|
2500
|
-
recommendations.push({
|
|
2685
|
+
recommendations.push({
|
|
2686
|
+
action: rec,
|
|
2687
|
+
estimatedImpact: 6,
|
|
2688
|
+
priority: "medium"
|
|
2689
|
+
});
|
|
2501
2690
|
}
|
|
2502
2691
|
for (const rec of params.testability.recommendations) {
|
|
2503
2692
|
const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
|
|
@@ -2508,12 +2697,20 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2508
2697
|
}
|
|
2509
2698
|
if (params.docDrift) {
|
|
2510
2699
|
for (const rec of params.docDrift.recommendations) {
|
|
2511
|
-
recommendations.push({
|
|
2700
|
+
recommendations.push({
|
|
2701
|
+
action: rec,
|
|
2702
|
+
estimatedImpact: 8,
|
|
2703
|
+
priority: "high"
|
|
2704
|
+
});
|
|
2512
2705
|
}
|
|
2513
2706
|
}
|
|
2514
2707
|
if (params.dependencyHealth) {
|
|
2515
2708
|
for (const rec of params.dependencyHealth.recommendations) {
|
|
2516
|
-
recommendations.push({
|
|
2709
|
+
recommendations.push({
|
|
2710
|
+
action: rec,
|
|
2711
|
+
estimatedImpact: 7,
|
|
2712
|
+
priority: "medium"
|
|
2713
|
+
});
|
|
2517
2714
|
}
|
|
2518
2715
|
}
|
|
2519
2716
|
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|