@aiready/core 0.23.7 → 0.23.8

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/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-Q55AMEFV.mjs";
53
+ } from "./chunk-TJXR2CHZ.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.push(`${toolName}: missing 'summary'`);
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: errors.length === 0,
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
- async getAST(code, filePath) {
731
- return parse(code, {
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
- analyzeMetadata(node, code) {
742
- const metadata = {
743
- isPure: true,
744
- hasSideEffects: false
745
- };
746
- const start = node.range?.[0] ?? 0;
747
- const preceding = code.slice(Math.max(0, start - 1e3), start);
748
- const jsdocMatches = Array.from(
749
- preceding.matchAll(/\/\*\*([\s\S]*?)\*\//g)
750
- );
751
- if (jsdocMatches.length > 0) {
752
- const lastMatch = jsdocMatches[jsdocMatches.length - 1];
753
- const matchEndIndex = (lastMatch.index || 0) + lastMatch[0].length;
754
- const between = preceding.slice(matchEndIndex);
755
- if (/^\s*$/.test(between)) {
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
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
832
- filePath,
833
- sourceType: "module",
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, imports, code);
810
+ const exports = this.extractExports(ast, code);
839
811
  return {
840
812
  exports,
841
813
  imports,
842
- language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
843
- warnings: []
814
+ language: this.language
844
815
  };
845
816
  } catch (error) {
846
- const err = error;
847
- throw new ParseError(
848
- `Failed to parse ${filePath}: ${err.message}`,
849
- filePath
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
- // Common exceptions (React hooks, etc.)
865
- exceptions: ["__filename", "__dirname", "__esModule"]
829
+ typePattern: /^[A-Z][a-zA-Z0-9]*$/,
830
+ interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
866
831
  };
867
832
  }
868
- canHandle(filePath) {
869
- return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
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, imports, code) {
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" && node.declaration) {
916
- const extracted = this.extractFromDeclaration(
917
- node.declaration,
918
- importedNames,
919
- code,
920
- node
921
- // Pass the ExportNamedDeclaration as parent for metadata
922
- );
923
- exports.push(...extracted);
924
- } else if (node.type === "ExportDefaultDeclaration") {
925
- const metadata = this.analyzeMetadata(node, code);
926
- let name = "default";
927
- let type = "default";
928
- if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
929
- name = node.declaration.id.name;
930
- type = "function";
931
- } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
932
- name = node.declaration.id.name;
933
- type = "class";
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
- exports.push({
936
- name,
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
- extractFromDeclaration(declaration, importedNames, code, parentNode) {
952
- const exports = [];
953
- const metadata = this.analyzeMetadata(parentNode || declaration, code);
954
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
955
- exports.push({
956
- name: declaration.id.name,
957
- type: "function",
958
- parameters: declaration.params.map((p) => {
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
- if (p.type === "AssignmentPattern" && p.left.type === "Identifier")
961
- return p.left.name;
962
- if (p.type === "RestElement" && p.argument.type === "Identifier")
963
- return p.argument.name;
964
- return "unknown";
965
- }),
966
- loc: declaration.loc ? {
967
- start: {
968
- line: declaration.loc.start.line,
969
- column: declaration.loc.start.column
970
- },
971
- end: {
972
- line: declaration.loc.end.line,
973
- column: declaration.loc.end.column
974
- }
975
- } : void 0,
976
- ...metadata
977
- });
978
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
979
- const body = declaration.body.body;
980
- const methods = body.filter((m) => m.type === "MethodDefinition");
981
- const properties = body.filter((m) => m.type === "PropertyDefinition");
982
- const constructor = methods.find(
983
- (m) => m.kind === "constructor"
984
- );
985
- const parameters = constructor ? constructor.value.params.map((p) => {
986
- if (p.type === "Identifier") return p.name;
987
- if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier")
988
- return p.parameter.name;
989
- return "unknown";
990
- }) : [];
991
- exports.push({
992
- name: declaration.id.name,
993
- type: "class",
994
- methodCount: methods.length,
995
- propertyCount: properties.length,
996
- parameters,
997
- loc: declaration.loc ? {
998
- start: {
999
- line: declaration.loc.start.line,
1000
- column: declaration.loc.start.column
1001
- },
1002
- end: {
1003
- line: declaration.loc.end.line,
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
- } else if (declaration.type === "TSTypeAliasDeclaration") {
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 exports;
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 ImportInfo objects.
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 ImportInfo objects.
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 ImportInfo objects.
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 ImportInfo objects.
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
- if (typeof config !== "object" || config === null) {
2889
- throw new Error("Config must be an object");
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
- const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
2920
- if (toolOverrides) {
2900
+ if (userConfig.tools) {
2921
2901
  if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
2922
- for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
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["claude-4.6"];
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("claude-3.5-sonnet");
2991
+ const preset = getModelPreset("gpt-5.4-mini");
3020
2992
  return estimateCostFromBudget(budget, preset, config);
3021
2993
  }
3022
2994
  function calculateTokenBudget(params) {
@@ -3862,12 +3834,12 @@ function calculateTestabilityIndex(params) {
3862
3834
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
3863
3835
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
3864
3836
  const purityScore = Math.round(
3865
- (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
3837
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
3866
3838
  );
3867
3839
  const dependencyInjectionScore = Math.round(
3868
3840
  Math.min(
3869
3841
  100,
3870
- (totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
3842
+ (totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
3871
3843
  )
3872
3844
  );
3873
3845
  const interfaceFocusScore = Math.max(
@@ -3948,9 +3920,9 @@ function calculateDocDrift(params) {
3948
3920
  const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
3949
3921
  const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
3950
3922
  const driftRatio = totalExports > 0 ? actualDrift / totalExports : 0;
3951
- const DRIFT_THRESHOLD = 0.2;
3952
- const OUTDATED_THRESHOLD = 0.4;
3953
- const COMPLEXITY_THRESHOLD = 0.2;
3923
+ const DRIFT_THRESHOLD = 0.35;
3924
+ const OUTDATED_THRESHOLD = 0.5;
3925
+ const COMPLEXITY_THRESHOLD = 0.3;
3954
3926
  const UNCOMMENTED_THRESHOLD = 0.8;
3955
3927
  const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
3956
3928
  const outdatedRisk = Math.min(
@@ -4415,6 +4387,7 @@ function emitIssuesAsAnnotations(issues) {
4415
4387
  });
4416
4388
  }
4417
4389
  export {
4390
+ AIReadyConfigSchema,
4418
4391
  AnalysisResultSchema,
4419
4392
  AnalysisStatus,
4420
4393
  AnalysisStatusSchema,
@@ -4540,6 +4513,10 @@ export {
4540
4513
  loadMergedConfig,
4541
4514
  loadScoreHistory,
4542
4515
  mergeConfigWithDefaults,
4516
+ normalizeAnalysisResult,
4517
+ normalizeIssue,
4518
+ normalizeMetrics,
4519
+ normalizeSpokeOutput,
4543
4520
  normalizeToolName,
4544
4521
  parseFileExports,
4545
4522
  parseWeightString,