@aiready/core 0.23.7 → 0.23.9
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-5SHLHMH7.mjs +760 -0
- package/dist/chunk-CGOS2J6T.mjs +807 -0
- package/dist/chunk-TJXR2CHZ.mjs +799 -0
- package/dist/client-BEoUYNLp.d.mts +1191 -0
- package/dist/client-BEoUYNLp.d.ts +1191 -0
- package/dist/client-CLulBnie.d.mts +1182 -0
- package/dist/client-CLulBnie.d.ts +1182 -0
- package/dist/client-CQwvp8ep.d.mts +1182 -0
- package/dist/client-CQwvp8ep.d.ts +1182 -0
- package/dist/client-PFPdeo-z.d.mts +1186 -0
- package/dist/client-PFPdeo-z.d.ts +1186 -0
- package/dist/client-wk2fgk1q.d.mts +1184 -0
- package/dist/client-wk2fgk1q.d.ts +1184 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +50 -4
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +61 -21
- package/dist/index.d.ts +61 -21
- package/dist/index.js +457 -361
- package/dist/index.mjs +389 -343
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AIReadyConfigSchema,
|
|
2
3
|
AnalysisResultSchema,
|
|
3
4
|
AnalysisStatus,
|
|
4
5
|
AnalysisStatusSchema,
|
|
@@ -49,53 +50,102 @@ import {
|
|
|
49
50
|
getToolWeight,
|
|
50
51
|
normalizeToolName,
|
|
51
52
|
parseWeightString
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-CGOS2J6T.mjs";
|
|
54
|
+
|
|
55
|
+
// src/utils/normalization.ts
|
|
56
|
+
function normalizeIssue(raw) {
|
|
57
|
+
return {
|
|
58
|
+
type: raw.type ?? "pattern-inconsistency" /* PatternInconsistency */,
|
|
59
|
+
severity: raw.severity ?? raw.severityLevel ?? "info" /* Info */,
|
|
60
|
+
message: raw.message ?? "Unknown issue",
|
|
61
|
+
location: raw.location ?? {
|
|
62
|
+
file: raw.fileName ?? raw.file ?? raw.filePath ?? "unknown",
|
|
63
|
+
line: raw.line ?? 1,
|
|
64
|
+
column: raw.column
|
|
65
|
+
},
|
|
66
|
+
suggestion: raw.suggestion
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function normalizeMetrics(raw) {
|
|
70
|
+
return {
|
|
71
|
+
tokenCost: raw.tokenCost ?? 0,
|
|
72
|
+
complexityScore: raw.complexityScore ?? 0,
|
|
73
|
+
consistencyScore: raw.consistencyScore,
|
|
74
|
+
docFreshnessScore: raw.docFreshnessScore,
|
|
75
|
+
aiSignalClarityScore: raw.aiSignalClarityScore,
|
|
76
|
+
agentGroundingScore: raw.agentGroundingScore,
|
|
77
|
+
testabilityScore: raw.testabilityScore,
|
|
78
|
+
docDriftScore: raw.docDriftScore,
|
|
79
|
+
dependencyHealthScore: raw.dependencyHealthScore,
|
|
80
|
+
modelContextTier: raw.modelContextTier,
|
|
81
|
+
estimatedMonthlyCost: raw.estimatedMonthlyCost,
|
|
82
|
+
estimatedDeveloperHours: raw.estimatedDeveloperHours,
|
|
83
|
+
comprehensionDifficultyIndex: raw.comprehensionDifficultyIndex,
|
|
84
|
+
totalSymbols: raw.totalSymbols,
|
|
85
|
+
totalExports: raw.totalExports
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function normalizeAnalysisResult(raw) {
|
|
89
|
+
const fileName = raw.fileName ?? raw.file ?? raw.filePath ?? "unknown";
|
|
90
|
+
const rawIssues = Array.isArray(raw.issues) ? raw.issues : [];
|
|
91
|
+
return {
|
|
92
|
+
fileName,
|
|
93
|
+
issues: rawIssues.map((issue) => {
|
|
94
|
+
if (typeof issue === "string") {
|
|
95
|
+
return {
|
|
96
|
+
type: "pattern-inconsistency" /* PatternInconsistency */,
|
|
97
|
+
// Default fallback
|
|
98
|
+
severity: raw.severity ?? "info" /* Info */,
|
|
99
|
+
message: issue,
|
|
100
|
+
location: { file: fileName, line: 1 }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return normalizeIssue({
|
|
104
|
+
...issue,
|
|
105
|
+
fileName: issue.fileName ?? fileName,
|
|
106
|
+
severity: issue.severity ?? raw.severity
|
|
107
|
+
});
|
|
108
|
+
}),
|
|
109
|
+
metrics: normalizeMetrics(raw.metrics ?? {})
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function normalizeSpokeOutput(raw, toolName) {
|
|
113
|
+
const rawResults = Array.isArray(raw.results) ? raw.results : [];
|
|
114
|
+
return {
|
|
115
|
+
results: rawResults.map(normalizeAnalysisResult),
|
|
116
|
+
summary: raw.summary ?? {
|
|
117
|
+
totalFiles: rawResults.length,
|
|
118
|
+
totalIssues: 0,
|
|
119
|
+
criticalIssues: 0,
|
|
120
|
+
majorIssues: 0
|
|
121
|
+
},
|
|
122
|
+
metadata: {
|
|
123
|
+
toolName: raw.metadata?.toolName ?? toolName,
|
|
124
|
+
version: raw.metadata?.version,
|
|
125
|
+
timestamp: raw.metadata?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
126
|
+
config: raw.metadata?.config
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
53
130
|
|
|
54
131
|
// src/types/contract.ts
|
|
55
132
|
function validateSpokeOutput(toolName, output) {
|
|
56
|
-
const errors = [];
|
|
57
133
|
if (!output) {
|
|
58
134
|
return { valid: false, errors: ["Output is null or undefined"] };
|
|
59
135
|
}
|
|
60
|
-
if (!Array.isArray(output.results)) {
|
|
61
|
-
errors.push(`${toolName}: 'results' must be an array`);
|
|
62
|
-
} else {
|
|
63
|
-
output.results.forEach((res, idx) => {
|
|
64
|
-
const fileName = res.fileName || res.file || res.filePath;
|
|
65
|
-
if (!fileName)
|
|
66
|
-
errors.push(
|
|
67
|
-
`${toolName}: results[${idx}] missing 'fileName', 'file' or 'filePath'`
|
|
68
|
-
);
|
|
69
|
-
const issues = res.issues;
|
|
70
|
-
if (!Array.isArray(issues)) {
|
|
71
|
-
errors.push(`${toolName}: results[${idx}] 'issues' must be an array`);
|
|
72
|
-
} else if (issues.length > 0) {
|
|
73
|
-
issues.forEach((issue, iidx) => {
|
|
74
|
-
if (typeof issue === "string") return;
|
|
75
|
-
if (!issue.type && !res.file)
|
|
76
|
-
errors.push(
|
|
77
|
-
`${toolName}: results[${idx}].issues[${iidx}] missing 'type'`
|
|
78
|
-
);
|
|
79
|
-
if (!issue.severity && !res.severity)
|
|
80
|
-
errors.push(
|
|
81
|
-
`${toolName}: results[${idx}].issues[${iidx}] missing 'severity'`
|
|
82
|
-
);
|
|
83
|
-
const severity = issue.severity || res.severity;
|
|
84
|
-
if (severity && !["critical", "major", "minor", "info"].includes(severity)) {
|
|
85
|
-
errors.push(
|
|
86
|
-
`${toolName}: results[${idx}].issues[${iidx}] has invalid severity: ${severity}`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
136
|
if (!output.summary) {
|
|
94
|
-
errors
|
|
137
|
+
return { valid: false, errors: [`${toolName}: missing 'summary'`] };
|
|
138
|
+
}
|
|
139
|
+
const normalized = normalizeSpokeOutput(output, toolName);
|
|
140
|
+
const result = SpokeOutputSchema.safeParse(normalized);
|
|
141
|
+
if (result.success) {
|
|
142
|
+
return { valid: true, errors: [] };
|
|
95
143
|
}
|
|
96
144
|
return {
|
|
97
|
-
valid:
|
|
98
|
-
errors
|
|
145
|
+
valid: false,
|
|
146
|
+
errors: result.error.issues.map(
|
|
147
|
+
(e) => `${toolName}: ${e.path.join(".")}: ${e.message}`
|
|
148
|
+
)
|
|
99
149
|
};
|
|
100
150
|
}
|
|
101
151
|
function validateWithSchema(schema, data) {
|
|
@@ -725,148 +775,67 @@ var TypeScriptParser = class {
|
|
|
725
775
|
this.extensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
726
776
|
}
|
|
727
777
|
async initialize() {
|
|
728
|
-
return Promise.resolve();
|
|
729
778
|
}
|
|
730
|
-
|
|
731
|
-
return
|
|
732
|
-
loc: true,
|
|
733
|
-
range: true,
|
|
734
|
-
jsx: filePath.match(/\.[jt]sx$/i) !== null,
|
|
735
|
-
filePath,
|
|
736
|
-
sourceType: "module",
|
|
737
|
-
ecmaVersion: "latest",
|
|
738
|
-
comment: true
|
|
739
|
-
});
|
|
779
|
+
canHandle(filePath) {
|
|
780
|
+
return this.extensions.some((ext) => filePath.endsWith(ext));
|
|
740
781
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const precedingStartOffset = Math.max(0, start - 1e3);
|
|
757
|
-
const absoluteStartOffset = precedingStartOffset + (lastMatch.index || 0);
|
|
758
|
-
const absoluteEndOffset = precedingStartOffset + matchEndIndex;
|
|
759
|
-
const codeBeforeStart = code.slice(0, absoluteStartOffset);
|
|
760
|
-
const startLines = codeBeforeStart.split("\n");
|
|
761
|
-
const startLine = startLines.length;
|
|
762
|
-
const startColumn = startLines[startLines.length - 1].length;
|
|
763
|
-
const codeBeforeEnd = code.slice(0, absoluteEndOffset);
|
|
764
|
-
const endLines = codeBeforeEnd.split("\n");
|
|
765
|
-
const endLine = endLines.length;
|
|
766
|
-
const endColumn = endLines[endLines.length - 1].length;
|
|
767
|
-
metadata.documentation = {
|
|
768
|
-
content: lastMatch[1].replace(/^\s*\*+/gm, "").trim(),
|
|
769
|
-
type: "jsdoc",
|
|
770
|
-
loc: {
|
|
771
|
-
start: { line: startLine, column: startColumn },
|
|
772
|
-
end: { line: endLine, column: endColumn }
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
const walk = (n) => {
|
|
778
|
-
if (!n) return;
|
|
779
|
-
if (n.type === "AssignmentExpression") {
|
|
780
|
-
metadata.isPure = false;
|
|
781
|
-
metadata.hasSideEffects = true;
|
|
782
|
-
}
|
|
783
|
-
if (n.type === "UpdateExpression") {
|
|
784
|
-
metadata.isPure = false;
|
|
785
|
-
metadata.hasSideEffects = true;
|
|
786
|
-
}
|
|
787
|
-
if (n.type === "CallExpression" && n.callee.type === "MemberExpression" && n.callee.object.type === "Identifier") {
|
|
788
|
-
const objName = n.callee.object.name;
|
|
789
|
-
if (["console", "process", "fs", "window", "document"].includes(objName)) {
|
|
790
|
-
metadata.isPure = false;
|
|
791
|
-
metadata.hasSideEffects = true;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
if (n.type === "ThrowStatement") {
|
|
795
|
-
metadata.isPure = false;
|
|
796
|
-
metadata.hasSideEffects = true;
|
|
797
|
-
}
|
|
798
|
-
for (const key of Object.keys(n)) {
|
|
799
|
-
if (key === "parent") continue;
|
|
800
|
-
const child = n[key];
|
|
801
|
-
if (child && typeof child === "object") {
|
|
802
|
-
if (Array.isArray(child)) {
|
|
803
|
-
child.forEach((c) => c?.type && walk(c));
|
|
804
|
-
} else if (child.type) {
|
|
805
|
-
walk(child);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
let nodeToAnalyze = node;
|
|
811
|
-
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
812
|
-
nodeToAnalyze = node.declaration;
|
|
813
|
-
} else if (node.type === "ExportDefaultDeclaration" && node.declaration) {
|
|
814
|
-
if (node.declaration.type !== "TSInterfaceDeclaration" && node.declaration.type !== "TSTypeAliasDeclaration") {
|
|
815
|
-
nodeToAnalyze = node.declaration;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
if (nodeToAnalyze.type === "FunctionDeclaration" || nodeToAnalyze.type === "FunctionExpression" || nodeToAnalyze.type === "ArrowFunctionExpression") {
|
|
819
|
-
if (nodeToAnalyze.body) walk(nodeToAnalyze.body);
|
|
820
|
-
} else if (nodeToAnalyze.type === "ClassDeclaration" || nodeToAnalyze.type === "ClassExpression") {
|
|
821
|
-
walk(nodeToAnalyze.body);
|
|
782
|
+
async getAST(code, filePath) {
|
|
783
|
+
try {
|
|
784
|
+
return parse(code, {
|
|
785
|
+
filePath,
|
|
786
|
+
loc: true,
|
|
787
|
+
range: true,
|
|
788
|
+
tokens: true,
|
|
789
|
+
comment: true,
|
|
790
|
+
jsx: filePath.endsWith("x")
|
|
791
|
+
});
|
|
792
|
+
} catch (error) {
|
|
793
|
+
throw new ParseError(error.message, filePath, {
|
|
794
|
+
line: error.lineNumber || 1,
|
|
795
|
+
column: error.column || 0
|
|
796
|
+
});
|
|
822
797
|
}
|
|
823
|
-
return metadata;
|
|
824
798
|
}
|
|
825
799
|
parse(code, filePath) {
|
|
826
800
|
try {
|
|
827
|
-
const isJavaScript = filePath.match(/\.jsx?$/i);
|
|
828
801
|
const ast = parse(code, {
|
|
802
|
+
filePath,
|
|
829
803
|
loc: true,
|
|
830
804
|
range: true,
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
ecmaVersion: "latest",
|
|
835
|
-
comment: true
|
|
805
|
+
tokens: true,
|
|
806
|
+
comment: true,
|
|
807
|
+
jsx: filePath.endsWith("x")
|
|
836
808
|
});
|
|
837
809
|
const imports = this.extractImports(ast);
|
|
838
|
-
const exports = this.extractExports(ast,
|
|
810
|
+
const exports = this.extractExports(ast, code);
|
|
839
811
|
return {
|
|
840
812
|
exports,
|
|
841
813
|
imports,
|
|
842
|
-
language:
|
|
843
|
-
warnings: []
|
|
814
|
+
language: this.language
|
|
844
815
|
};
|
|
845
816
|
} catch (error) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
);
|
|
817
|
+
throw new ParseError(error.message, filePath, {
|
|
818
|
+
line: error.lineNumber || 1,
|
|
819
|
+
column: error.column || 0
|
|
820
|
+
});
|
|
851
821
|
}
|
|
852
822
|
}
|
|
853
823
|
getNamingConventions() {
|
|
854
824
|
return {
|
|
855
|
-
// camelCase for variables and functions
|
|
856
825
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
857
826
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
858
|
-
// PascalCase for classes, types and interfaces
|
|
859
827
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
860
|
-
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
861
|
-
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
862
|
-
// UPPER_CASE for constants
|
|
863
828
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
864
|
-
|
|
865
|
-
|
|
829
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
830
|
+
interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
|
|
866
831
|
};
|
|
867
832
|
}
|
|
868
|
-
|
|
869
|
-
|
|
833
|
+
analyzeMetadata(node, code) {
|
|
834
|
+
if (!code) return {};
|
|
835
|
+
return {
|
|
836
|
+
isPure: this.isLikelyPure(node),
|
|
837
|
+
hasSideEffects: !this.isLikelyPure(node)
|
|
838
|
+
};
|
|
870
839
|
}
|
|
871
840
|
extractImports(ast) {
|
|
872
841
|
const imports = [];
|
|
@@ -904,168 +873,165 @@ var TypeScriptParser = class {
|
|
|
904
873
|
}
|
|
905
874
|
return imports;
|
|
906
875
|
}
|
|
907
|
-
extractExports(ast,
|
|
876
|
+
extractExports(ast, code) {
|
|
908
877
|
const exports = [];
|
|
909
|
-
const importedNames = new Set(
|
|
910
|
-
imports.flatMap(
|
|
911
|
-
(imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
|
|
912
|
-
)
|
|
913
|
-
);
|
|
914
878
|
for (const node of ast.body) {
|
|
915
|
-
if (node.type === "ExportNamedDeclaration"
|
|
916
|
-
|
|
917
|
-
node.declaration
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
879
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
880
|
+
if (node.declaration) {
|
|
881
|
+
const declaration = node.declaration;
|
|
882
|
+
if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
|
|
883
|
+
exports.push(
|
|
884
|
+
this.createExport(
|
|
885
|
+
declaration.id.name,
|
|
886
|
+
"function",
|
|
887
|
+
node,
|
|
888
|
+
// Pass the outer ExportNamedDeclaration
|
|
889
|
+
code
|
|
890
|
+
)
|
|
891
|
+
);
|
|
892
|
+
} else if (declaration.type === "ClassDeclaration" && declaration.id) {
|
|
893
|
+
exports.push(
|
|
894
|
+
this.createExport(
|
|
895
|
+
declaration.id.name,
|
|
896
|
+
"class",
|
|
897
|
+
node,
|
|
898
|
+
// Pass the outer ExportNamedDeclaration
|
|
899
|
+
code
|
|
900
|
+
)
|
|
901
|
+
);
|
|
902
|
+
} else if (declaration.type === "TSTypeAliasDeclaration") {
|
|
903
|
+
exports.push(
|
|
904
|
+
this.createExport(
|
|
905
|
+
declaration.id.name,
|
|
906
|
+
"type",
|
|
907
|
+
node,
|
|
908
|
+
// Pass the outer ExportNamedDeclaration
|
|
909
|
+
code
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
913
|
+
exports.push(
|
|
914
|
+
this.createExport(
|
|
915
|
+
declaration.id.name,
|
|
916
|
+
"interface",
|
|
917
|
+
node,
|
|
918
|
+
// Pass the outer ExportNamedDeclaration
|
|
919
|
+
code
|
|
920
|
+
)
|
|
921
|
+
);
|
|
922
|
+
} else if (declaration.type === "VariableDeclaration") {
|
|
923
|
+
for (const decl of declaration.declarations) {
|
|
924
|
+
if (decl.id.type === "Identifier") {
|
|
925
|
+
exports.push(
|
|
926
|
+
this.createExport(
|
|
927
|
+
decl.id.name,
|
|
928
|
+
"const",
|
|
929
|
+
node,
|
|
930
|
+
// Pass the outer ExportNamedDeclaration
|
|
931
|
+
code,
|
|
932
|
+
decl.init
|
|
933
|
+
)
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
934
938
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
type,
|
|
938
|
-
loc: node.loc ? {
|
|
939
|
-
start: {
|
|
940
|
-
line: node.loc.start.line,
|
|
941
|
-
column: node.loc.start.column
|
|
942
|
-
},
|
|
943
|
-
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
944
|
-
} : void 0,
|
|
945
|
-
...metadata
|
|
946
|
-
});
|
|
939
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
940
|
+
exports.push(this.createExport("default", "default", node, code));
|
|
947
941
|
}
|
|
948
942
|
}
|
|
949
943
|
return exports;
|
|
950
944
|
}
|
|
951
|
-
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
945
|
+
createExport(name, type, node, code, initializer) {
|
|
946
|
+
const documentation = this.extractDocumentation(node, code);
|
|
947
|
+
let methodCount;
|
|
948
|
+
let propertyCount;
|
|
949
|
+
let parameters;
|
|
950
|
+
let isPrimitive = false;
|
|
951
|
+
if (initializer) {
|
|
952
|
+
if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
|
|
953
|
+
isPrimitive = true;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
957
|
+
if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
|
|
958
|
+
const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
|
|
959
|
+
methodCount = body.filter(
|
|
960
|
+
(m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
|
|
961
|
+
).length;
|
|
962
|
+
propertyCount = body.filter(
|
|
963
|
+
(m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
|
|
964
|
+
).length;
|
|
965
|
+
if (structNode.type === "ClassDeclaration") {
|
|
966
|
+
const constructor = body.find(
|
|
967
|
+
(m) => m.type === "MethodDefinition" && m.kind === "constructor"
|
|
968
|
+
);
|
|
969
|
+
if (constructor && constructor.value && constructor.value.params) {
|
|
970
|
+
parameters = constructor.value.params.map((p) => {
|
|
971
|
+
if (p.type === "Identifier") return p.name;
|
|
972
|
+
if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
|
|
973
|
+
return p.parameter.name;
|
|
974
|
+
}
|
|
975
|
+
return void 0;
|
|
976
|
+
}).filter(Boolean);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
|
|
981
|
+
const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
|
|
982
|
+
if (funcNode && funcNode.params) {
|
|
983
|
+
parameters = funcNode.params.map((p) => {
|
|
959
984
|
if (p.type === "Identifier") return p.name;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
);
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
column: declaration.loc.end.column
|
|
1005
|
-
}
|
|
1006
|
-
} : void 0,
|
|
1007
|
-
...metadata
|
|
1008
|
-
});
|
|
1009
|
-
} else if (declaration.type === "VariableDeclaration") {
|
|
1010
|
-
for (const declarator of declaration.declarations) {
|
|
1011
|
-
if (declarator.id.type === "Identifier") {
|
|
1012
|
-
exports.push({
|
|
1013
|
-
name: declarator.id.name,
|
|
1014
|
-
type: "const",
|
|
1015
|
-
loc: declarator.loc ? {
|
|
1016
|
-
start: {
|
|
1017
|
-
line: declarator.loc.start.line,
|
|
1018
|
-
column: declarator.loc.start.column
|
|
1019
|
-
},
|
|
1020
|
-
end: {
|
|
1021
|
-
line: declarator.loc.end.line,
|
|
1022
|
-
column: declarator.loc.end.column
|
|
1023
|
-
}
|
|
1024
|
-
} : void 0,
|
|
1025
|
-
...metadata
|
|
1026
|
-
});
|
|
985
|
+
return void 0;
|
|
986
|
+
}).filter(Boolean);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
name,
|
|
991
|
+
type,
|
|
992
|
+
isPrimitive,
|
|
993
|
+
loc: node.loc ? {
|
|
994
|
+
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
995
|
+
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
996
|
+
} : void 0,
|
|
997
|
+
documentation,
|
|
998
|
+
methodCount,
|
|
999
|
+
propertyCount,
|
|
1000
|
+
parameters,
|
|
1001
|
+
isPure: this.isLikelyPure(node),
|
|
1002
|
+
hasSideEffects: !this.isLikelyPure(node)
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
extractDocumentation(node, code) {
|
|
1006
|
+
if (node.range) {
|
|
1007
|
+
const start = node.range[0];
|
|
1008
|
+
const precedingCode = code.substring(0, start);
|
|
1009
|
+
const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
|
|
1010
|
+
if (jsdocMatch) {
|
|
1011
|
+
return {
|
|
1012
|
+
content: jsdocMatch[1].trim(),
|
|
1013
|
+
type: "jsdoc"
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return void 0;
|
|
1018
|
+
}
|
|
1019
|
+
isLikelyPure(node) {
|
|
1020
|
+
const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
1021
|
+
if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
|
|
1022
|
+
return true;
|
|
1023
|
+
if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
|
|
1024
|
+
const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
|
|
1025
|
+
if (body && body.type === "BlockStatement") {
|
|
1026
|
+
const bodyContent = JSON.stringify(body);
|
|
1027
|
+
if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
|
|
1028
|
+
return false;
|
|
1027
1029
|
}
|
|
1030
|
+
return true;
|
|
1028
1031
|
}
|
|
1029
|
-
|
|
1030
|
-
exports.push({
|
|
1031
|
-
name: declaration.id.name,
|
|
1032
|
-
type: "type",
|
|
1033
|
-
loc: declaration.loc ? {
|
|
1034
|
-
start: {
|
|
1035
|
-
line: declaration.loc.start.line,
|
|
1036
|
-
column: declaration.loc.start.column
|
|
1037
|
-
},
|
|
1038
|
-
end: {
|
|
1039
|
-
line: declaration.loc.end.line,
|
|
1040
|
-
column: declaration.loc.end.column
|
|
1041
|
-
}
|
|
1042
|
-
} : void 0,
|
|
1043
|
-
...metadata
|
|
1044
|
-
});
|
|
1045
|
-
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
1046
|
-
const body = declaration.body.body;
|
|
1047
|
-
const methods = body.filter((m) => m.type === "TSMethodSignature");
|
|
1048
|
-
const properties = body.filter((m) => m.type === "TSPropertySignature");
|
|
1049
|
-
exports.push({
|
|
1050
|
-
name: declaration.id.name,
|
|
1051
|
-
type: "interface",
|
|
1052
|
-
methodCount: methods.length,
|
|
1053
|
-
propertyCount: properties.length || body.length,
|
|
1054
|
-
// Fallback to body.length
|
|
1055
|
-
loc: declaration.loc ? {
|
|
1056
|
-
start: {
|
|
1057
|
-
line: declaration.loc.start.line,
|
|
1058
|
-
column: declaration.loc.start.column
|
|
1059
|
-
},
|
|
1060
|
-
end: {
|
|
1061
|
-
line: declaration.loc.end.line,
|
|
1062
|
-
column: declaration.loc.end.column
|
|
1063
|
-
}
|
|
1064
|
-
} : void 0,
|
|
1065
|
-
...metadata
|
|
1066
|
-
});
|
|
1032
|
+
return true;
|
|
1067
1033
|
}
|
|
1068
|
-
return
|
|
1034
|
+
return false;
|
|
1069
1035
|
}
|
|
1070
1036
|
};
|
|
1071
1037
|
|
|
@@ -1355,7 +1321,7 @@ var PythonParser = class extends BaseLanguageParser {
|
|
|
1355
1321
|
* Extract import information using AST walk.
|
|
1356
1322
|
*
|
|
1357
1323
|
* @param rootNode - Root node of the Python AST.
|
|
1358
|
-
* @returns Array of discovered
|
|
1324
|
+
* @returns Array of discovered FileImport objects.
|
|
1359
1325
|
*/
|
|
1360
1326
|
extractImportsAST(rootNode) {
|
|
1361
1327
|
const imports = [];
|
|
@@ -1912,7 +1878,7 @@ var JavaParser = class extends BaseLanguageParser {
|
|
|
1912
1878
|
* Extract import information using AST walk.
|
|
1913
1879
|
*
|
|
1914
1880
|
* @param rootNode - Root node of the Java AST.
|
|
1915
|
-
* @returns Array of discovered
|
|
1881
|
+
* @returns Array of discovered FileImport objects.
|
|
1916
1882
|
*/
|
|
1917
1883
|
extractImportsAST(rootNode) {
|
|
1918
1884
|
const imports = [];
|
|
@@ -2144,7 +2110,7 @@ var CSharpParser = class extends BaseLanguageParser {
|
|
|
2144
2110
|
* Extract import information (usings) using AST walk.
|
|
2145
2111
|
*
|
|
2146
2112
|
* @param rootNode - Root node of the C# AST.
|
|
2147
|
-
* @returns Array of discovered
|
|
2113
|
+
* @returns Array of discovered FileImport objects.
|
|
2148
2114
|
*/
|
|
2149
2115
|
extractImportsAST(rootNode) {
|
|
2150
2116
|
const imports = [];
|
|
@@ -2397,7 +2363,7 @@ var GoParser = class extends BaseLanguageParser {
|
|
|
2397
2363
|
* Extract import information using AST walk.
|
|
2398
2364
|
*
|
|
2399
2365
|
* @param rootNode - Root node of the Go AST.
|
|
2400
|
-
* @returns Array of discovered
|
|
2366
|
+
* @returns Array of discovered FileImport objects.
|
|
2401
2367
|
*/
|
|
2402
2368
|
extractImportsAST(rootNode) {
|
|
2403
2369
|
const imports = [];
|
|
@@ -2885,10 +2851,25 @@ async function loadConfig(rootDir) {
|
|
|
2885
2851
|
const content = readFileSync(configPath, "utf-8");
|
|
2886
2852
|
config = JSON.parse(content);
|
|
2887
2853
|
}
|
|
2888
|
-
|
|
2889
|
-
|
|
2854
|
+
const legacyKeys = ["toolConfigs"];
|
|
2855
|
+
const rootLevelTools = [
|
|
2856
|
+
"pattern-detect",
|
|
2857
|
+
"context-analyzer",
|
|
2858
|
+
"naming-consistency",
|
|
2859
|
+
"ai-signal-clarity"
|
|
2860
|
+
];
|
|
2861
|
+
const allKeys = Object.keys(config);
|
|
2862
|
+
const foundLegacy = allKeys.filter(
|
|
2863
|
+
(k) => legacyKeys.includes(k) || rootLevelTools.includes(k)
|
|
2864
|
+
);
|
|
2865
|
+
if (foundLegacy.length > 0) {
|
|
2866
|
+
console.warn(
|
|
2867
|
+
`\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(
|
|
2868
|
+
", "
|
|
2869
|
+
)}. These are deprecated and should be moved under the "tools" key.`
|
|
2870
|
+
);
|
|
2890
2871
|
}
|
|
2891
|
-
return config;
|
|
2872
|
+
return AIReadyConfigSchema.parse(config);
|
|
2892
2873
|
} catch (error) {
|
|
2893
2874
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2894
2875
|
const configError = new Error(
|
|
@@ -2916,12 +2897,10 @@ function mergeConfigWithDefaults(userConfig, defaults) {
|
|
|
2916
2897
|
if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
|
|
2917
2898
|
if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
|
|
2918
2899
|
}
|
|
2919
|
-
|
|
2920
|
-
if (toolOverrides) {
|
|
2900
|
+
if (userConfig.tools) {
|
|
2921
2901
|
if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
|
|
2922
|
-
for (const [toolName, toolConfig] of Object.entries(
|
|
2902
|
+
for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
|
|
2923
2903
|
if (typeof toolConfig === "object" && toolConfig !== null) {
|
|
2924
|
-
mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
|
|
2925
2904
|
mergedConfig.toolConfigs[toolName] = {
|
|
2926
2905
|
...mergedConfig.toolConfigs[toolName],
|
|
2927
2906
|
...toolConfig
|
|
@@ -2937,6 +2916,13 @@ function mergeConfigWithDefaults(userConfig, defaults) {
|
|
|
2937
2916
|
|
|
2938
2917
|
// src/business/pricing-models.ts
|
|
2939
2918
|
var MODEL_PRICING_PRESETS = {
|
|
2919
|
+
"gpt-5.4-mini": {
|
|
2920
|
+
name: "GPT-5.4 Mini",
|
|
2921
|
+
pricePer1KInputTokens: 1e-4,
|
|
2922
|
+
pricePer1KOutputTokens: 4e-4,
|
|
2923
|
+
contextTier: "extended",
|
|
2924
|
+
typicalQueriesPerDevPerDay: 200
|
|
2925
|
+
},
|
|
2940
2926
|
"gpt-5.3": {
|
|
2941
2927
|
name: "GPT-5.3",
|
|
2942
2928
|
pricePer1KInputTokens: 2e-3,
|
|
@@ -2958,20 +2944,6 @@ var MODEL_PRICING_PRESETS = {
|
|
|
2958
2944
|
contextTier: "frontier",
|
|
2959
2945
|
typicalQueriesPerDevPerDay: 120
|
|
2960
2946
|
},
|
|
2961
|
-
"gpt-4o": {
|
|
2962
|
-
name: "GPT-4o (legacy)",
|
|
2963
|
-
pricePer1KInputTokens: 5e-3,
|
|
2964
|
-
pricePer1KOutputTokens: 0.015,
|
|
2965
|
-
contextTier: "extended",
|
|
2966
|
-
typicalQueriesPerDevPerDay: 60
|
|
2967
|
-
},
|
|
2968
|
-
"claude-3-5-sonnet": {
|
|
2969
|
-
name: "Claude 3.5 Sonnet (legacy)",
|
|
2970
|
-
pricePer1KInputTokens: 3e-3,
|
|
2971
|
-
pricePer1KOutputTokens: 0.015,
|
|
2972
|
-
contextTier: "extended",
|
|
2973
|
-
typicalQueriesPerDevPerDay: 80
|
|
2974
|
-
},
|
|
2975
2947
|
"gemini-1-5-pro": {
|
|
2976
2948
|
name: "Gemini 1.5 Pro (legacy)",
|
|
2977
2949
|
pricePer1KInputTokens: 125e-5,
|
|
@@ -2995,7 +2967,7 @@ var MODEL_PRICING_PRESETS = {
|
|
|
2995
2967
|
}
|
|
2996
2968
|
};
|
|
2997
2969
|
function getModelPreset(modelId) {
|
|
2998
|
-
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["
|
|
2970
|
+
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-5.4-mini"];
|
|
2999
2971
|
}
|
|
3000
2972
|
|
|
3001
2973
|
// src/business/cost-metrics.ts
|
|
@@ -3016,7 +2988,7 @@ function calculateMonthlyCost(tokenWaste, config = {}) {
|
|
|
3016
2988
|
// Added baseline chattiness
|
|
3017
2989
|
}
|
|
3018
2990
|
});
|
|
3019
|
-
const preset = getModelPreset("
|
|
2991
|
+
const preset = getModelPreset("gpt-5.4-mini");
|
|
3020
2992
|
return estimateCostFromBudget(budget, preset, config);
|
|
3021
2993
|
}
|
|
3022
2994
|
function calculateTokenBudget(params) {
|
|
@@ -3846,6 +3818,40 @@ function calculateAgentGrounding(params) {
|
|
|
3846
3818
|
}
|
|
3847
3819
|
|
|
3848
3820
|
// src/metrics/testability-index.ts
|
|
3821
|
+
function isLikelyEntryPoint(filePath) {
|
|
3822
|
+
const basename = filePath.split("/").pop() || "";
|
|
3823
|
+
const lowerBasename = basename.toLowerCase();
|
|
3824
|
+
const entryPointPatterns = [
|
|
3825
|
+
"cli",
|
|
3826
|
+
"main",
|
|
3827
|
+
"bin",
|
|
3828
|
+
"index",
|
|
3829
|
+
// often used as entry point
|
|
3830
|
+
"run",
|
|
3831
|
+
"serve",
|
|
3832
|
+
"start",
|
|
3833
|
+
"boot",
|
|
3834
|
+
"init"
|
|
3835
|
+
];
|
|
3836
|
+
const nameWithoutExt = lowerBasename.replace(
|
|
3837
|
+
/\.(ts|js|tsx|jsx|mjs|cjs)$/,
|
|
3838
|
+
""
|
|
3839
|
+
);
|
|
3840
|
+
if (entryPointPatterns.some(
|
|
3841
|
+
(p) => nameWithoutExt === p || nameWithoutExt.endsWith(`-${p}`) || nameWithoutExt.startsWith(`${p}-`)
|
|
3842
|
+
)) {
|
|
3843
|
+
return true;
|
|
3844
|
+
}
|
|
3845
|
+
const cliDirPatterns = ["/bin/", "/cli/", "/cmd/", "/commands/"];
|
|
3846
|
+
if (cliDirPatterns.some((p) => filePath.includes(p))) {
|
|
3847
|
+
return true;
|
|
3848
|
+
}
|
|
3849
|
+
return false;
|
|
3850
|
+
}
|
|
3851
|
+
function calculateFilePurityScore(pureFunctions, totalFunctions) {
|
|
3852
|
+
if (totalFunctions === 0) return 100;
|
|
3853
|
+
return Math.round(pureFunctions / totalFunctions * 100);
|
|
3854
|
+
}
|
|
3849
3855
|
function calculateTestabilityIndex(params) {
|
|
3850
3856
|
const {
|
|
3851
3857
|
testFiles,
|
|
@@ -3857,17 +3863,18 @@ function calculateTestabilityIndex(params) {
|
|
|
3857
3863
|
bloatedInterfaces,
|
|
3858
3864
|
totalInterfaces,
|
|
3859
3865
|
externalStateMutations,
|
|
3860
|
-
hasTestFramework
|
|
3866
|
+
hasTestFramework,
|
|
3867
|
+
fileDetails
|
|
3861
3868
|
} = params;
|
|
3862
3869
|
const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
|
|
3863
3870
|
const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
|
|
3864
3871
|
const purityScore = Math.round(
|
|
3865
|
-
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.
|
|
3872
|
+
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
|
|
3866
3873
|
);
|
|
3867
3874
|
const dependencyInjectionScore = Math.round(
|
|
3868
3875
|
Math.min(
|
|
3869
3876
|
100,
|
|
3870
|
-
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.
|
|
3877
|
+
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
|
|
3871
3878
|
)
|
|
3872
3879
|
);
|
|
3873
3880
|
const interfaceFocusScore = Math.max(
|
|
@@ -3883,7 +3890,39 @@ function calculateTestabilityIndex(params) {
|
|
|
3883
3890
|
)
|
|
3884
3891
|
);
|
|
3885
3892
|
const frameworkWeight = hasTestFramework ? 1 : 0.8;
|
|
3886
|
-
|
|
3893
|
+
let fileMetrics;
|
|
3894
|
+
let libraryPureFunctions = pureFunctions;
|
|
3895
|
+
let libraryTotalFunctions = totalFunctions;
|
|
3896
|
+
let entryPointCount = 0;
|
|
3897
|
+
if (fileDetails && fileDetails.length > 0) {
|
|
3898
|
+
fileMetrics = fileDetails.map((file) => {
|
|
3899
|
+
const isEntryPoint = isLikelyEntryPoint(file.filePath);
|
|
3900
|
+
const purityScore2 = calculateFilePurityScore(
|
|
3901
|
+
file.pureFunctions,
|
|
3902
|
+
file.totalFunctions
|
|
3903
|
+
);
|
|
3904
|
+
if (isEntryPoint) {
|
|
3905
|
+
entryPointCount++;
|
|
3906
|
+
libraryPureFunctions -= file.pureFunctions;
|
|
3907
|
+
libraryTotalFunctions -= file.totalFunctions;
|
|
3908
|
+
}
|
|
3909
|
+
return {
|
|
3910
|
+
filePath: file.filePath,
|
|
3911
|
+
score: purityScore2,
|
|
3912
|
+
// Simplified - just purity for file-level
|
|
3913
|
+
purityScore: purityScore2,
|
|
3914
|
+
isEntryPoint
|
|
3915
|
+
};
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
const libraryPurityScore = Math.max(
|
|
3919
|
+
0,
|
|
3920
|
+
Math.round(
|
|
3921
|
+
(libraryTotalFunctions > 0 ? libraryPureFunctions / libraryTotalFunctions : 0.7) * 100
|
|
3922
|
+
)
|
|
3923
|
+
);
|
|
3924
|
+
const effectivePurityScore = fileDetails && fileDetails.length > 0 ? libraryPurityScore : purityScore;
|
|
3925
|
+
const rawScore = (testCoverageRatio * 0.3 + effectivePurityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
|
|
3887
3926
|
const score = Math.max(0, Math.min(100, Math.round(rawScore)));
|
|
3888
3927
|
let rating;
|
|
3889
3928
|
if (score >= 85) rating = "excellent";
|
|
@@ -3908,9 +3947,9 @@ function calculateTestabilityIndex(params) {
|
|
|
3908
3947
|
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
3909
3948
|
);
|
|
3910
3949
|
}
|
|
3911
|
-
if (
|
|
3950
|
+
if (effectivePurityScore < 50)
|
|
3912
3951
|
recommendations.push(
|
|
3913
|
-
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
3952
|
+
entryPointCount > 0 ? `Extract pure functions from library code (${entryPointCount} entry point files excluded) \u2014 pure functions are trivially AI-testable` : "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
3914
3953
|
);
|
|
3915
3954
|
if (dependencyInjectionScore < 50 && totalClasses > 0)
|
|
3916
3955
|
recommendations.push(
|
|
@@ -3925,13 +3964,15 @@ function calculateTestabilityIndex(params) {
|
|
|
3925
3964
|
rating,
|
|
3926
3965
|
dimensions: {
|
|
3927
3966
|
testCoverageRatio,
|
|
3928
|
-
purityScore,
|
|
3967
|
+
purityScore: effectivePurityScore,
|
|
3929
3968
|
dependencyInjectionScore,
|
|
3930
3969
|
interfaceFocusScore,
|
|
3931
3970
|
observabilityScore
|
|
3932
3971
|
},
|
|
3933
3972
|
aiChangeSafetyRating,
|
|
3934
|
-
recommendations
|
|
3973
|
+
recommendations,
|
|
3974
|
+
fileMetrics,
|
|
3975
|
+
libraryScore: Math.max(0, fileMetrics ? libraryPurityScore : purityScore)
|
|
3935
3976
|
};
|
|
3936
3977
|
}
|
|
3937
3978
|
|
|
@@ -3948,9 +3989,9 @@ function calculateDocDrift(params) {
|
|
|
3948
3989
|
const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
|
|
3949
3990
|
const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
|
|
3950
3991
|
const driftRatio = totalExports > 0 ? actualDrift / totalExports : 0;
|
|
3951
|
-
const DRIFT_THRESHOLD = 0.
|
|
3952
|
-
const OUTDATED_THRESHOLD = 0.
|
|
3953
|
-
const COMPLEXITY_THRESHOLD = 0.
|
|
3992
|
+
const DRIFT_THRESHOLD = 0.35;
|
|
3993
|
+
const OUTDATED_THRESHOLD = 0.5;
|
|
3994
|
+
const COMPLEXITY_THRESHOLD = 0.3;
|
|
3954
3995
|
const UNCOMMENTED_THRESHOLD = 0.8;
|
|
3955
3996
|
const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
|
|
3956
3997
|
const outdatedRisk = Math.min(
|
|
@@ -4415,6 +4456,7 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
4415
4456
|
});
|
|
4416
4457
|
}
|
|
4417
4458
|
export {
|
|
4459
|
+
AIReadyConfigSchema,
|
|
4418
4460
|
AnalysisResultSchema,
|
|
4419
4461
|
AnalysisStatus,
|
|
4420
4462
|
AnalysisStatusSchema,
|
|
@@ -4540,6 +4582,10 @@ export {
|
|
|
4540
4582
|
loadMergedConfig,
|
|
4541
4583
|
loadScoreHistory,
|
|
4542
4584
|
mergeConfigWithDefaults,
|
|
4585
|
+
normalizeAnalysisResult,
|
|
4586
|
+
normalizeIssue,
|
|
4587
|
+
normalizeMetrics,
|
|
4588
|
+
normalizeSpokeOutput,
|
|
4543
4589
|
normalizeToolName,
|
|
4544
4590
|
parseFileExports,
|
|
4545
4591
|
parseWeightString,
|