@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.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
getToolWeight,
|
|
19
19
|
normalizeToolName,
|
|
20
20
|
parseWeightString
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HFLFBA6F.mjs";
|
|
22
22
|
|
|
23
23
|
// src/utils/file-scanner.ts
|
|
24
24
|
import { glob } from "glob";
|
|
@@ -88,7 +88,9 @@ async function scanFiles(options) {
|
|
|
88
88
|
ignoreFromFile = [];
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
const finalExclude = [
|
|
91
|
+
const finalExclude = [
|
|
92
|
+
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
|
|
93
|
+
];
|
|
92
94
|
const files = await glob(include, {
|
|
93
95
|
cwd: rootDir,
|
|
94
96
|
ignore: finalExclude,
|
|
@@ -336,7 +338,9 @@ async function loadConfig(rootDir) {
|
|
|
336
338
|
return config;
|
|
337
339
|
} catch (error) {
|
|
338
340
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
339
|
-
throw new Error(
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
343
|
+
);
|
|
340
344
|
}
|
|
341
345
|
}
|
|
342
346
|
}
|
|
@@ -476,7 +480,7 @@ var MODEL_PRICING_PRESETS = {
|
|
|
476
480
|
contextTier: "frontier",
|
|
477
481
|
typicalQueriesPerDevPerDay: 150
|
|
478
482
|
},
|
|
479
|
-
|
|
483
|
+
copilot: {
|
|
480
484
|
name: "GitHub Copilot (subscription)",
|
|
481
485
|
// Amortized per-request cost for a $19/month plan at 80 queries/day
|
|
482
486
|
pricePer1KInputTokens: 1e-4,
|
|
@@ -542,9 +546,18 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
|
542
546
|
hourlyRate,
|
|
543
547
|
totalCost: Math.round(totalCost),
|
|
544
548
|
bySeverity: {
|
|
545
|
-
critical: {
|
|
546
|
-
|
|
547
|
-
|
|
549
|
+
critical: {
|
|
550
|
+
hours: Math.round(hours.critical * 10) / 10,
|
|
551
|
+
cost: Math.round(hours.critical * hourlyRate)
|
|
552
|
+
},
|
|
553
|
+
major: {
|
|
554
|
+
hours: Math.round(hours.major * 10) / 10,
|
|
555
|
+
cost: Math.round(hours.major * hourlyRate)
|
|
556
|
+
},
|
|
557
|
+
minor: {
|
|
558
|
+
hours: Math.round(hours.minor * 10) / 10,
|
|
559
|
+
cost: Math.round(hours.minor * hourlyRate)
|
|
560
|
+
}
|
|
548
561
|
}
|
|
549
562
|
};
|
|
550
563
|
}
|
|
@@ -602,12 +615,18 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
|
|
|
602
615
|
const criticalBudget = tierThresholds.criticalTokens;
|
|
603
616
|
const idealDepth = tierThresholds.idealDepth;
|
|
604
617
|
const budgetRange = criticalBudget - idealBudget;
|
|
605
|
-
const budgetFactor = Math.min(
|
|
606
|
-
|
|
607
|
-
(contextBudget - idealBudget) / budgetRange * 100
|
|
608
|
-
)
|
|
609
|
-
const depthFactor = Math.min(
|
|
610
|
-
|
|
618
|
+
const budgetFactor = Math.min(
|
|
619
|
+
100,
|
|
620
|
+
Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
|
|
621
|
+
);
|
|
622
|
+
const depthFactor = Math.min(
|
|
623
|
+
100,
|
|
624
|
+
Math.max(0, (importDepth - idealDepth) * 10)
|
|
625
|
+
);
|
|
626
|
+
const fragmentationFactor = Math.min(
|
|
627
|
+
100,
|
|
628
|
+
Math.max(0, (fragmentation - 0.3) * 250)
|
|
629
|
+
);
|
|
611
630
|
const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
|
|
612
631
|
const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
|
|
613
632
|
const score = Math.round(
|
|
@@ -687,8 +706,12 @@ function calculateScoreTrend(history) {
|
|
|
687
706
|
const now = /* @__PURE__ */ new Date();
|
|
688
707
|
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
|
|
689
708
|
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
|
|
690
|
-
const last30Days = history.filter(
|
|
691
|
-
|
|
709
|
+
const last30Days = history.filter(
|
|
710
|
+
(e) => new Date(e.timestamp) >= thirtyDaysAgo
|
|
711
|
+
);
|
|
712
|
+
const last90Days = history.filter(
|
|
713
|
+
(e) => new Date(e.timestamp) >= ninetyDaysAgo
|
|
714
|
+
);
|
|
692
715
|
const currentScore = history[history.length - 1].overallScore;
|
|
693
716
|
const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
|
|
694
717
|
const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
|
|
@@ -701,7 +724,10 @@ function calculateScoreTrend(history) {
|
|
|
701
724
|
if (change30Days > 3) direction = "improving";
|
|
702
725
|
else if (change30Days < -3) direction = "degrading";
|
|
703
726
|
else direction = "stable";
|
|
704
|
-
const projectedScore = Math.max(
|
|
727
|
+
const projectedScore = Math.max(
|
|
728
|
+
0,
|
|
729
|
+
Math.min(100, currentScore + velocity * 4)
|
|
730
|
+
);
|
|
705
731
|
return {
|
|
706
732
|
direction,
|
|
707
733
|
change30Days,
|
|
@@ -762,9 +788,13 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
762
788
|
recommendations: ["No files to analyze"]
|
|
763
789
|
};
|
|
764
790
|
}
|
|
765
|
-
const orphanFiles = files.filter(
|
|
791
|
+
const orphanFiles = files.filter(
|
|
792
|
+
(f) => f.exports < 2 && f.imports < 2
|
|
793
|
+
).length;
|
|
766
794
|
const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
|
|
767
|
-
const uniqueConceptFiles = files.filter(
|
|
795
|
+
const uniqueConceptFiles = files.filter(
|
|
796
|
+
(f) => f.exports > avgExports * 2
|
|
797
|
+
).length;
|
|
768
798
|
const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
|
|
769
799
|
const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
|
|
770
800
|
let singleAuthorFiles = 0;
|
|
@@ -776,7 +806,10 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
776
806
|
const orphanRisk = orphanFiles / files.length * 30;
|
|
777
807
|
const uniqueRisk = concentrationRatio * 40;
|
|
778
808
|
const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
|
|
779
|
-
const score = Math.min(
|
|
809
|
+
const score = Math.min(
|
|
810
|
+
100,
|
|
811
|
+
Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
|
|
812
|
+
);
|
|
780
813
|
let rating;
|
|
781
814
|
if (score < 20) rating = "low";
|
|
782
815
|
else if (score < 40) rating = "moderate";
|
|
@@ -784,13 +817,19 @@ function calculateKnowledgeConcentration(files, authorData) {
|
|
|
784
817
|
else rating = "critical";
|
|
785
818
|
const recommendations = [];
|
|
786
819
|
if (orphanFiles > files.length * 0.2) {
|
|
787
|
-
recommendations.push(
|
|
820
|
+
recommendations.push(
|
|
821
|
+
`Reduce ${orphanFiles} orphan files by connecting them to main modules`
|
|
822
|
+
);
|
|
788
823
|
}
|
|
789
824
|
if (uniqueConceptFiles > files.length * 0.1) {
|
|
790
|
-
recommendations.push(
|
|
825
|
+
recommendations.push(
|
|
826
|
+
"Distribute high-export files into more focused modules"
|
|
827
|
+
);
|
|
791
828
|
}
|
|
792
829
|
if (authorData && singleAuthorFiles > files.length * 0.3) {
|
|
793
|
-
recommendations.push(
|
|
830
|
+
recommendations.push(
|
|
831
|
+
"Increase knowledge sharing to reduce single-author dependencies"
|
|
832
|
+
);
|
|
794
833
|
}
|
|
795
834
|
return {
|
|
796
835
|
score,
|
|
@@ -944,7 +983,10 @@ var TypeScriptParser = class {
|
|
|
944
983
|
specifiers,
|
|
945
984
|
isTypeOnly,
|
|
946
985
|
loc: node.loc ? {
|
|
947
|
-
start: {
|
|
986
|
+
start: {
|
|
987
|
+
line: node.loc.start.line,
|
|
988
|
+
column: node.loc.start.column
|
|
989
|
+
},
|
|
948
990
|
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
949
991
|
} : void 0
|
|
950
992
|
});
|
|
@@ -955,11 +997,16 @@ var TypeScriptParser = class {
|
|
|
955
997
|
extractExports(ast, imports) {
|
|
956
998
|
const exports = [];
|
|
957
999
|
const importedNames = new Set(
|
|
958
|
-
imports.flatMap(
|
|
1000
|
+
imports.flatMap(
|
|
1001
|
+
(imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
|
|
1002
|
+
)
|
|
959
1003
|
);
|
|
960
1004
|
for (const node of ast.body) {
|
|
961
1005
|
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
962
|
-
const extracted = this.extractFromDeclaration(
|
|
1006
|
+
const extracted = this.extractFromDeclaration(
|
|
1007
|
+
node.declaration,
|
|
1008
|
+
importedNames
|
|
1009
|
+
);
|
|
963
1010
|
exports.push(...extracted);
|
|
964
1011
|
} else if (node.type === "ExportDefaultDeclaration") {
|
|
965
1012
|
let name = "default";
|
|
@@ -975,7 +1022,10 @@ var TypeScriptParser = class {
|
|
|
975
1022
|
name,
|
|
976
1023
|
type,
|
|
977
1024
|
loc: node.loc ? {
|
|
978
|
-
start: {
|
|
1025
|
+
start: {
|
|
1026
|
+
line: node.loc.start.line,
|
|
1027
|
+
column: node.loc.start.column
|
|
1028
|
+
},
|
|
979
1029
|
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
980
1030
|
} : void 0
|
|
981
1031
|
});
|
|
@@ -997,7 +1047,10 @@ var TypeScriptParser = class {
|
|
|
997
1047
|
line: declaration.loc.start.line,
|
|
998
1048
|
column: declaration.loc.start.column
|
|
999
1049
|
},
|
|
1000
|
-
end: {
|
|
1050
|
+
end: {
|
|
1051
|
+
line: declaration.loc.end.line,
|
|
1052
|
+
column: declaration.loc.end.column
|
|
1053
|
+
}
|
|
1001
1054
|
} : void 0
|
|
1002
1055
|
});
|
|
1003
1056
|
} else if (declaration.type === "ClassDeclaration" && declaration.id) {
|
|
@@ -1009,7 +1062,10 @@ var TypeScriptParser = class {
|
|
|
1009
1062
|
line: declaration.loc.start.line,
|
|
1010
1063
|
column: declaration.loc.start.column
|
|
1011
1064
|
},
|
|
1012
|
-
end: {
|
|
1065
|
+
end: {
|
|
1066
|
+
line: declaration.loc.end.line,
|
|
1067
|
+
column: declaration.loc.end.column
|
|
1068
|
+
}
|
|
1013
1069
|
} : void 0
|
|
1014
1070
|
});
|
|
1015
1071
|
} else if (declaration.type === "VariableDeclaration") {
|
|
@@ -1040,7 +1096,10 @@ var TypeScriptParser = class {
|
|
|
1040
1096
|
line: declaration.loc.start.line,
|
|
1041
1097
|
column: declaration.loc.start.column
|
|
1042
1098
|
},
|
|
1043
|
-
end: {
|
|
1099
|
+
end: {
|
|
1100
|
+
line: declaration.loc.end.line,
|
|
1101
|
+
column: declaration.loc.end.column
|
|
1102
|
+
}
|
|
1044
1103
|
} : void 0
|
|
1045
1104
|
});
|
|
1046
1105
|
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
@@ -1052,7 +1111,10 @@ var TypeScriptParser = class {
|
|
|
1052
1111
|
line: declaration.loc.start.line,
|
|
1053
1112
|
column: declaration.loc.start.column
|
|
1054
1113
|
},
|
|
1055
|
-
end: {
|
|
1114
|
+
end: {
|
|
1115
|
+
line: declaration.loc.end.line,
|
|
1116
|
+
column: declaration.loc.end.column
|
|
1117
|
+
}
|
|
1056
1118
|
} : void 0
|
|
1057
1119
|
});
|
|
1058
1120
|
}
|
|
@@ -1077,7 +1139,9 @@ var PythonParser = class {
|
|
|
1077
1139
|
try {
|
|
1078
1140
|
this.initialized = true;
|
|
1079
1141
|
} catch (error) {
|
|
1080
|
-
throw new Error(
|
|
1142
|
+
throw new Error(
|
|
1143
|
+
`Failed to initialize Python parser: ${error.message}`
|
|
1144
|
+
);
|
|
1081
1145
|
}
|
|
1082
1146
|
}
|
|
1083
1147
|
parse(code, filePath) {
|
|
@@ -1088,7 +1152,9 @@ var PythonParser = class {
|
|
|
1088
1152
|
exports,
|
|
1089
1153
|
imports,
|
|
1090
1154
|
language: "python" /* Python */,
|
|
1091
|
-
warnings: [
|
|
1155
|
+
warnings: [
|
|
1156
|
+
"Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."
|
|
1157
|
+
]
|
|
1092
1158
|
};
|
|
1093
1159
|
} catch (error) {
|
|
1094
1160
|
throw new ParseError(
|
|
@@ -1183,7 +1249,7 @@ var PythonParser = class {
|
|
|
1183
1249
|
}
|
|
1184
1250
|
/**
|
|
1185
1251
|
* Regex-based export extraction (temporary implementation)
|
|
1186
|
-
*
|
|
1252
|
+
*
|
|
1187
1253
|
* Python doesn't have explicit exports like JavaScript.
|
|
1188
1254
|
* We extract:
|
|
1189
1255
|
* - Functions defined at module level (def)
|
|
@@ -1351,7 +1417,13 @@ function getSupportedLanguages() {
|
|
|
1351
1417
|
|
|
1352
1418
|
// src/future-proof-metrics.ts
|
|
1353
1419
|
function calculateCognitiveLoad(params) {
|
|
1354
|
-
const {
|
|
1420
|
+
const {
|
|
1421
|
+
linesOfCode,
|
|
1422
|
+
exportCount,
|
|
1423
|
+
importCount,
|
|
1424
|
+
uniqueConcepts,
|
|
1425
|
+
cyclomaticComplexity = 1
|
|
1426
|
+
} = params;
|
|
1355
1427
|
const sizeFactor = {
|
|
1356
1428
|
name: "Size Complexity",
|
|
1357
1429
|
score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
|
|
@@ -1377,7 +1449,12 @@ function calculateCognitiveLoad(params) {
|
|
|
1377
1449
|
weight: 0.2,
|
|
1378
1450
|
description: `${uniqueConcepts} unique concepts`
|
|
1379
1451
|
};
|
|
1380
|
-
const factors = [
|
|
1452
|
+
const factors = [
|
|
1453
|
+
sizeFactor,
|
|
1454
|
+
interfaceFactor,
|
|
1455
|
+
dependencyFactor,
|
|
1456
|
+
conceptFactor
|
|
1457
|
+
];
|
|
1381
1458
|
const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
|
|
1382
1459
|
let rating;
|
|
1383
1460
|
if (score < 20) rating = "trivial";
|
|
@@ -1400,7 +1477,10 @@ function calculateCognitiveLoad(params) {
|
|
|
1400
1477
|
function calculateSemanticDistance(params) {
|
|
1401
1478
|
const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
|
|
1402
1479
|
const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
|
|
1403
|
-
const importOverlap = sharedDependencies.length / Math.max(
|
|
1480
|
+
const importOverlap = sharedDependencies.length / Math.max(
|
|
1481
|
+
1,
|
|
1482
|
+
Math.min(params.file1Imports.length, params.file2Imports.length)
|
|
1483
|
+
);
|
|
1404
1484
|
const importDistance = 1 - importOverlap;
|
|
1405
1485
|
const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
|
|
1406
1486
|
let relationship;
|
|
@@ -1408,7 +1488,9 @@ function calculateSemanticDistance(params) {
|
|
|
1408
1488
|
else if (file1Domain === file2Domain) relationship = "same-domain";
|
|
1409
1489
|
else if (sharedDependencies.length > 0) relationship = "cross-domain";
|
|
1410
1490
|
else relationship = "unrelated";
|
|
1411
|
-
const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
|
|
1491
|
+
const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
|
|
1492
|
+
(s) => typeof s === "string" && s.length > 0
|
|
1493
|
+
);
|
|
1412
1494
|
return {
|
|
1413
1495
|
between: [file1, file2],
|
|
1414
1496
|
distance: Math.round(distance * 100) / 100,
|
|
@@ -1423,7 +1505,11 @@ function calculatePatternEntropy(files) {
|
|
|
1423
1505
|
domain: "unknown",
|
|
1424
1506
|
entropy: 0,
|
|
1425
1507
|
rating: "crystalline",
|
|
1426
|
-
distribution: {
|
|
1508
|
+
distribution: {
|
|
1509
|
+
locationCount: 0,
|
|
1510
|
+
dominantLocation: "",
|
|
1511
|
+
giniCoefficient: 0
|
|
1512
|
+
},
|
|
1427
1513
|
recommendations: ["No files to analyze"]
|
|
1428
1514
|
};
|
|
1429
1515
|
}
|
|
@@ -1463,10 +1549,14 @@ function calculatePatternEntropy(files) {
|
|
|
1463
1549
|
else rating = "chaotic";
|
|
1464
1550
|
const recommendations = [];
|
|
1465
1551
|
if (normalizedEntropy > 0.5) {
|
|
1466
|
-
recommendations.push(
|
|
1552
|
+
recommendations.push(
|
|
1553
|
+
`Consolidate ${files.length} files into fewer directories by domain`
|
|
1554
|
+
);
|
|
1467
1555
|
}
|
|
1468
1556
|
if (dirGroups.size > 5) {
|
|
1469
|
-
recommendations.push(
|
|
1557
|
+
recommendations.push(
|
|
1558
|
+
"Consider barrel exports to reduce directory navigation"
|
|
1559
|
+
);
|
|
1470
1560
|
}
|
|
1471
1561
|
if (gini > 0.5) {
|
|
1472
1562
|
recommendations.push("Redistribute files more evenly across directories");
|
|
@@ -1491,7 +1581,11 @@ function calculateConceptCohesion(params) {
|
|
|
1491
1581
|
return {
|
|
1492
1582
|
score: 1,
|
|
1493
1583
|
rating: "excellent",
|
|
1494
|
-
analysis: {
|
|
1584
|
+
analysis: {
|
|
1585
|
+
uniqueDomains: 0,
|
|
1586
|
+
domainConcentration: 0,
|
|
1587
|
+
exportPurposeClarity: 1
|
|
1588
|
+
}
|
|
1495
1589
|
};
|
|
1496
1590
|
}
|
|
1497
1591
|
const allDomains = [];
|
|
@@ -1597,7 +1691,10 @@ function calculateAiSignalClarity(params) {
|
|
|
1597
1691
|
recommendations: []
|
|
1598
1692
|
};
|
|
1599
1693
|
}
|
|
1600
|
-
const overloadRatio = Math.min(
|
|
1694
|
+
const overloadRatio = Math.min(
|
|
1695
|
+
1,
|
|
1696
|
+
overloadedSymbols / Math.max(1, totalSymbols)
|
|
1697
|
+
);
|
|
1601
1698
|
const overloadSignal = {
|
|
1602
1699
|
name: "Symbol Overloading",
|
|
1603
1700
|
count: overloadedSymbols,
|
|
@@ -1621,7 +1718,10 @@ function calculateAiSignalClarity(params) {
|
|
|
1621
1718
|
// 20% weight
|
|
1622
1719
|
description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
|
|
1623
1720
|
};
|
|
1624
|
-
const sideEffectRatio = Math.min(
|
|
1721
|
+
const sideEffectRatio = Math.min(
|
|
1722
|
+
1,
|
|
1723
|
+
implicitSideEffects / Math.max(1, totalExports)
|
|
1724
|
+
);
|
|
1625
1725
|
const sideEffectSignal = {
|
|
1626
1726
|
name: "Implicit Side Effects",
|
|
1627
1727
|
count: implicitSideEffects,
|
|
@@ -1629,7 +1729,10 @@ function calculateAiSignalClarity(params) {
|
|
|
1629
1729
|
// 15% weight
|
|
1630
1730
|
description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
|
|
1631
1731
|
};
|
|
1632
|
-
const callbackRatio = Math.min(
|
|
1732
|
+
const callbackRatio = Math.min(
|
|
1733
|
+
1,
|
|
1734
|
+
deepCallbacks / Math.max(1, totalSymbols * 0.1)
|
|
1735
|
+
);
|
|
1633
1736
|
const callbackSignal = {
|
|
1634
1737
|
name: "Callback Nesting",
|
|
1635
1738
|
count: deepCallbacks,
|
|
@@ -1637,7 +1740,10 @@ function calculateAiSignalClarity(params) {
|
|
|
1637
1740
|
// 10% weight
|
|
1638
1741
|
description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
|
|
1639
1742
|
};
|
|
1640
|
-
const ambiguousRatio = Math.min(
|
|
1743
|
+
const ambiguousRatio = Math.min(
|
|
1744
|
+
1,
|
|
1745
|
+
ambiguousNames / Math.max(1, totalSymbols)
|
|
1746
|
+
);
|
|
1641
1747
|
const ambiguousSignal = {
|
|
1642
1748
|
name: "Ambiguous Names",
|
|
1643
1749
|
count: ambiguousNames,
|
|
@@ -1645,7 +1751,10 @@ function calculateAiSignalClarity(params) {
|
|
|
1645
1751
|
// 10% weight
|
|
1646
1752
|
description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
|
|
1647
1753
|
};
|
|
1648
|
-
const undocRatio = Math.min(
|
|
1754
|
+
const undocRatio = Math.min(
|
|
1755
|
+
1,
|
|
1756
|
+
undocumentedExports / Math.max(1, totalExports)
|
|
1757
|
+
);
|
|
1649
1758
|
const undocSignal = {
|
|
1650
1759
|
name: "Undocumented Exports",
|
|
1651
1760
|
count: undocumentedExports,
|
|
@@ -1662,30 +1771,45 @@ function calculateAiSignalClarity(params) {
|
|
|
1662
1771
|
ambiguousSignal,
|
|
1663
1772
|
undocSignal
|
|
1664
1773
|
];
|
|
1665
|
-
const score = Math.min(
|
|
1774
|
+
const score = Math.min(
|
|
1775
|
+
100,
|
|
1776
|
+
signals.reduce((sum, s) => sum + s.riskContribution, 0)
|
|
1777
|
+
);
|
|
1666
1778
|
let rating;
|
|
1667
1779
|
if (score < 10) rating = "minimal";
|
|
1668
1780
|
else if (score < 25) rating = "low";
|
|
1669
1781
|
else if (score < 50) rating = "moderate";
|
|
1670
1782
|
else if (score < 75) rating = "high";
|
|
1671
1783
|
else rating = "severe";
|
|
1672
|
-
const topSignal = signals.reduce(
|
|
1784
|
+
const topSignal = signals.reduce(
|
|
1785
|
+
(a, b) => a.riskContribution > b.riskContribution ? a : b
|
|
1786
|
+
);
|
|
1673
1787
|
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
|
|
1674
1788
|
const recommendations = [];
|
|
1675
1789
|
if (overloadSignal.riskContribution > 5) {
|
|
1676
|
-
recommendations.push(
|
|
1790
|
+
recommendations.push(
|
|
1791
|
+
`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
|
|
1792
|
+
);
|
|
1677
1793
|
}
|
|
1678
1794
|
if (magicSignal.riskContribution > 5) {
|
|
1679
|
-
recommendations.push(
|
|
1795
|
+
recommendations.push(
|
|
1796
|
+
`Extract ${magicLiterals} magic literals into named constants`
|
|
1797
|
+
);
|
|
1680
1798
|
}
|
|
1681
1799
|
if (trapSignal.riskContribution > 5) {
|
|
1682
|
-
recommendations.push(
|
|
1800
|
+
recommendations.push(
|
|
1801
|
+
`Replace ${booleanTraps} boolean traps with named options objects`
|
|
1802
|
+
);
|
|
1683
1803
|
}
|
|
1684
1804
|
if (undocSignal.riskContribution > 5) {
|
|
1685
|
-
recommendations.push(
|
|
1805
|
+
recommendations.push(
|
|
1806
|
+
`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
|
|
1807
|
+
);
|
|
1686
1808
|
}
|
|
1687
1809
|
if (sideEffectSignal.riskContribution > 5) {
|
|
1688
|
-
recommendations.push(
|
|
1810
|
+
recommendations.push(
|
|
1811
|
+
"Mark functions with side effects explicitly in their names or docs"
|
|
1812
|
+
);
|
|
1689
1813
|
}
|
|
1690
1814
|
return {
|
|
1691
1815
|
score: Math.round(score),
|
|
@@ -1710,7 +1834,10 @@ function calculateAgentGrounding(params) {
|
|
|
1710
1834
|
domainVocabularySize
|
|
1711
1835
|
} = params;
|
|
1712
1836
|
const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
|
|
1713
|
-
const structureClarityScore = Math.max(
|
|
1837
|
+
const structureClarityScore = Math.max(
|
|
1838
|
+
0,
|
|
1839
|
+
Math.round(100 - deepDirRatio * 80)
|
|
1840
|
+
);
|
|
1714
1841
|
const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
|
|
1715
1842
|
const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
|
|
1716
1843
|
let entryPointScore = 60;
|
|
@@ -1722,7 +1849,10 @@ function calculateAgentGrounding(params) {
|
|
|
1722
1849
|
const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
|
|
1723
1850
|
const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
|
|
1724
1851
|
const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
|
|
1725
|
-
const domainConsistencyScore = Math.max(
|
|
1852
|
+
const domainConsistencyScore = Math.max(
|
|
1853
|
+
0,
|
|
1854
|
+
Math.round(100 - inconsistencyRatio * 80)
|
|
1855
|
+
);
|
|
1726
1856
|
const score = Math.round(
|
|
1727
1857
|
structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
|
|
1728
1858
|
);
|
|
@@ -1734,21 +1864,33 @@ function calculateAgentGrounding(params) {
|
|
|
1734
1864
|
else rating = "disorienting";
|
|
1735
1865
|
const recommendations = [];
|
|
1736
1866
|
if (structureClarityScore < 70) {
|
|
1737
|
-
recommendations.push(
|
|
1867
|
+
recommendations.push(
|
|
1868
|
+
`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
|
|
1869
|
+
);
|
|
1738
1870
|
}
|
|
1739
1871
|
if (selfDocumentationScore < 70) {
|
|
1740
|
-
recommendations.push(
|
|
1872
|
+
recommendations.push(
|
|
1873
|
+
`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
|
|
1874
|
+
);
|
|
1741
1875
|
}
|
|
1742
1876
|
if (!hasRootReadme) {
|
|
1743
|
-
recommendations.push(
|
|
1877
|
+
recommendations.push(
|
|
1878
|
+
"Add a root README.md so agents understand the project context immediately"
|
|
1879
|
+
);
|
|
1744
1880
|
} else if (!readmeIsFresh) {
|
|
1745
|
-
recommendations.push(
|
|
1881
|
+
recommendations.push(
|
|
1882
|
+
"Update README.md \u2014 stale entry-point documentation disorients agents"
|
|
1883
|
+
);
|
|
1746
1884
|
}
|
|
1747
1885
|
if (apiClarityScore < 70) {
|
|
1748
|
-
recommendations.push(
|
|
1886
|
+
recommendations.push(
|
|
1887
|
+
`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
|
|
1888
|
+
);
|
|
1749
1889
|
}
|
|
1750
1890
|
if (domainConsistencyScore < 70) {
|
|
1751
|
-
recommendations.push(
|
|
1891
|
+
recommendations.push(
|
|
1892
|
+
`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
|
|
1893
|
+
);
|
|
1752
1894
|
}
|
|
1753
1895
|
return {
|
|
1754
1896
|
score,
|
|
@@ -1781,7 +1923,9 @@ function calculateTestabilityIndex(params) {
|
|
|
1781
1923
|
const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
|
|
1782
1924
|
const purityScore = Math.round(purityRatio * 100);
|
|
1783
1925
|
const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
|
|
1784
|
-
const dependencyInjectionScore = Math.round(
|
|
1926
|
+
const dependencyInjectionScore = Math.round(
|
|
1927
|
+
Math.min(100, injectionRatio * 100)
|
|
1928
|
+
);
|
|
1785
1929
|
const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
|
|
1786
1930
|
const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
|
|
1787
1931
|
const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
|
|
@@ -1797,25 +1941,36 @@ function calculateTestabilityIndex(params) {
|
|
|
1797
1941
|
else rating = "unverifiable";
|
|
1798
1942
|
let aiChangeSafetyRating;
|
|
1799
1943
|
if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
|
|
1800
|
-
else if (rawCoverageRatio >= 0.2 && score >= 50)
|
|
1944
|
+
else if (rawCoverageRatio >= 0.2 && score >= 50)
|
|
1945
|
+
aiChangeSafetyRating = "moderate-risk";
|
|
1801
1946
|
else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
|
|
1802
1947
|
else aiChangeSafetyRating = "blind-risk";
|
|
1803
1948
|
const recommendations = [];
|
|
1804
1949
|
if (!hasTestFramework) {
|
|
1805
|
-
recommendations.push(
|
|
1950
|
+
recommendations.push(
|
|
1951
|
+
"Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
|
|
1952
|
+
);
|
|
1806
1953
|
}
|
|
1807
1954
|
if (rawCoverageRatio < 0.3) {
|
|
1808
1955
|
const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
|
|
1809
|
-
recommendations.push(
|
|
1956
|
+
recommendations.push(
|
|
1957
|
+
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
1958
|
+
);
|
|
1810
1959
|
}
|
|
1811
1960
|
if (purityScore < 50) {
|
|
1812
|
-
recommendations.push(
|
|
1961
|
+
recommendations.push(
|
|
1962
|
+
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
1963
|
+
);
|
|
1813
1964
|
}
|
|
1814
1965
|
if (dependencyInjectionScore < 50 && totalClasses > 0) {
|
|
1815
|
-
recommendations.push(
|
|
1966
|
+
recommendations.push(
|
|
1967
|
+
"Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
|
|
1968
|
+
);
|
|
1816
1969
|
}
|
|
1817
1970
|
if (externalStateMutations > totalFunctions * 0.3) {
|
|
1818
|
-
recommendations.push(
|
|
1971
|
+
recommendations.push(
|
|
1972
|
+
"Reduce direct state mutations \u2014 return values instead to improve observability"
|
|
1973
|
+
);
|
|
1819
1974
|
}
|
|
1820
1975
|
return {
|
|
1821
1976
|
score,
|
|
@@ -1832,7 +1987,12 @@ function calculateTestabilityIndex(params) {
|
|
|
1832
1987
|
};
|
|
1833
1988
|
}
|
|
1834
1989
|
function calculateDocDrift(params) {
|
|
1835
|
-
const {
|
|
1990
|
+
const {
|
|
1991
|
+
uncommentedExports,
|
|
1992
|
+
totalExports,
|
|
1993
|
+
outdatedComments,
|
|
1994
|
+
undocumentedComplexity
|
|
1995
|
+
} = params;
|
|
1836
1996
|
const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
|
|
1837
1997
|
const outdatedScore = Math.min(100, outdatedComments * 15);
|
|
1838
1998
|
const uncommentedScore = Math.min(100, uncommentedRatio * 100);
|
|
@@ -1849,13 +2009,19 @@ function calculateDocDrift(params) {
|
|
|
1849
2009
|
else rating = "severe";
|
|
1850
2010
|
const recommendations = [];
|
|
1851
2011
|
if (outdatedComments > 0) {
|
|
1852
|
-
recommendations.push(
|
|
2012
|
+
recommendations.push(
|
|
2013
|
+
`Update or remove ${outdatedComments} outdated comments that contradict the code.`
|
|
2014
|
+
);
|
|
1853
2015
|
}
|
|
1854
2016
|
if (uncommentedRatio > 0.3) {
|
|
1855
|
-
recommendations.push(
|
|
2017
|
+
recommendations.push(
|
|
2018
|
+
`Add JSDoc to ${uncommentedExports} uncommented exports.`
|
|
2019
|
+
);
|
|
1856
2020
|
}
|
|
1857
2021
|
if (undocumentedComplexity > 0) {
|
|
1858
|
-
recommendations.push(
|
|
2022
|
+
recommendations.push(
|
|
2023
|
+
`Explain the business logic for ${undocumentedComplexity} highly complex functions.`
|
|
2024
|
+
);
|
|
1859
2025
|
}
|
|
1860
2026
|
return {
|
|
1861
2027
|
score: finalScore,
|
|
@@ -1869,7 +2035,12 @@ function calculateDocDrift(params) {
|
|
|
1869
2035
|
};
|
|
1870
2036
|
}
|
|
1871
2037
|
function calculateDependencyHealth(params) {
|
|
1872
|
-
const {
|
|
2038
|
+
const {
|
|
2039
|
+
totalPackages,
|
|
2040
|
+
outdatedPackages,
|
|
2041
|
+
deprecatedPackages,
|
|
2042
|
+
trainingCutoffSkew
|
|
2043
|
+
} = params;
|
|
1873
2044
|
const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
|
|
1874
2045
|
const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
|
|
1875
2046
|
const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
|
|
@@ -1884,19 +2055,27 @@ function calculateDependencyHealth(params) {
|
|
|
1884
2055
|
else if (score >= 30) rating = "poor";
|
|
1885
2056
|
else rating = "hazardous";
|
|
1886
2057
|
let aiKnowledgeConfidence;
|
|
1887
|
-
if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
|
|
1888
|
-
|
|
2058
|
+
if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
|
|
2059
|
+
aiKnowledgeConfidence = "high";
|
|
2060
|
+
else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2)
|
|
2061
|
+
aiKnowledgeConfidence = "moderate";
|
|
1889
2062
|
else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
|
|
1890
2063
|
else aiKnowledgeConfidence = "blind";
|
|
1891
2064
|
const recommendations = [];
|
|
1892
2065
|
if (deprecatedPackages > 0) {
|
|
1893
|
-
recommendations.push(
|
|
2066
|
+
recommendations.push(
|
|
2067
|
+
`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
|
|
2068
|
+
);
|
|
1894
2069
|
}
|
|
1895
2070
|
if (outdatedRatio > 0.2) {
|
|
1896
|
-
recommendations.push(
|
|
2071
|
+
recommendations.push(
|
|
2072
|
+
`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
|
|
2073
|
+
);
|
|
1897
2074
|
}
|
|
1898
2075
|
if (trainingCutoffSkew > 0.5) {
|
|
1899
|
-
recommendations.push(
|
|
2076
|
+
recommendations.push(
|
|
2077
|
+
"High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
|
|
2078
|
+
);
|
|
1900
2079
|
}
|
|
1901
2080
|
return {
|
|
1902
2081
|
score,
|
|
@@ -1937,10 +2116,14 @@ function calculateChangeAmplification(params) {
|
|
|
1937
2116
|
else if (score < 90) rating = "contained";
|
|
1938
2117
|
const recommendations = [];
|
|
1939
2118
|
if (score < 70 && hotspots.length > 0) {
|
|
1940
|
-
recommendations.push(
|
|
2119
|
+
recommendations.push(
|
|
2120
|
+
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
|
|
2121
|
+
);
|
|
1941
2122
|
}
|
|
1942
2123
|
if (maxAmplification > 30) {
|
|
1943
|
-
recommendations.push(
|
|
2124
|
+
recommendations.push(
|
|
2125
|
+
`Break down key bottlenecks with amplification factor > 30.`
|
|
2126
|
+
);
|
|
1944
2127
|
}
|
|
1945
2128
|
return {
|
|
1946
2129
|
score: Math.round(score),
|
|
@@ -2022,7 +2205,11 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2022
2205
|
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
2023
2206
|
}
|
|
2024
2207
|
for (const rec of params.agentGrounding.recommendations) {
|
|
2025
|
-
recommendations.push({
|
|
2208
|
+
recommendations.push({
|
|
2209
|
+
action: rec,
|
|
2210
|
+
estimatedImpact: 6,
|
|
2211
|
+
priority: "medium"
|
|
2212
|
+
});
|
|
2026
2213
|
}
|
|
2027
2214
|
for (const rec of params.testability.recommendations) {
|
|
2028
2215
|
const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
|
|
@@ -2033,12 +2220,20 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2033
2220
|
}
|
|
2034
2221
|
if (params.docDrift) {
|
|
2035
2222
|
for (const rec of params.docDrift.recommendations) {
|
|
2036
|
-
recommendations.push({
|
|
2223
|
+
recommendations.push({
|
|
2224
|
+
action: rec,
|
|
2225
|
+
estimatedImpact: 8,
|
|
2226
|
+
priority: "high"
|
|
2227
|
+
});
|
|
2037
2228
|
}
|
|
2038
2229
|
}
|
|
2039
2230
|
if (params.dependencyHealth) {
|
|
2040
2231
|
for (const rec of params.dependencyHealth.recommendations) {
|
|
2041
|
-
recommendations.push({
|
|
2232
|
+
recommendations.push({
|
|
2233
|
+
action: rec,
|
|
2234
|
+
estimatedImpact: 7,
|
|
2235
|
+
priority: "medium"
|
|
2236
|
+
});
|
|
2042
2237
|
}
|
|
2043
2238
|
}
|
|
2044
2239
|
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|