@aiready/core 0.23.6 → 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.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AIReadyConfigSchema: () => AIReadyConfigSchema,
33
34
  AnalysisResultSchema: () => AnalysisResultSchema,
34
35
  AnalysisStatus: () => AnalysisStatus,
35
36
  AnalysisStatusSchema: () => AnalysisStatusSchema,
@@ -111,8 +112,6 @@ __export(index_exports, {
111
112
  estimateCostFromBudget: () => estimateCostFromBudget,
112
113
  estimateTokens: () => estimateTokens,
113
114
  exportHistory: () => exportHistory,
114
- extractFunctions: () => extractFunctions,
115
- extractImports: () => extractImports,
116
115
  findLatestReport: () => findLatestReport,
117
116
  findLatestScanReport: () => findLatestScanReport,
118
117
  formatAcceptanceRate: () => formatAcceptanceRate,
@@ -157,8 +156,11 @@ __export(index_exports, {
157
156
  loadMergedConfig: () => loadMergedConfig,
158
157
  loadScoreHistory: () => loadScoreHistory,
159
158
  mergeConfigWithDefaults: () => mergeConfigWithDefaults,
159
+ normalizeAnalysisResult: () => normalizeAnalysisResult,
160
+ normalizeIssue: () => normalizeIssue,
161
+ normalizeMetrics: () => normalizeMetrics,
162
+ normalizeSpokeOutput: () => normalizeSpokeOutput,
160
163
  normalizeToolName: () => normalizeToolName,
161
- parseCode: () => parseCode,
162
164
  parseFileExports: () => parseFileExports,
163
165
  parseWeightString: () => parseWeightString,
164
166
  predictAcceptanceRate: () => predictAcceptanceRate,
@@ -184,21 +186,21 @@ var Severity = /* @__PURE__ */ ((Severity2) => {
184
186
  return Severity2;
185
187
  })(Severity || {});
186
188
  var SeveritySchema = import_zod.z.nativeEnum(Severity);
187
- var ToolName = /* @__PURE__ */ ((ToolName2) => {
188
- ToolName2["PatternDetect"] = "pattern-detect";
189
- ToolName2["ContextAnalyzer"] = "context-analyzer";
190
- ToolName2["NamingConsistency"] = "naming-consistency";
191
- ToolName2["AiSignalClarity"] = "ai-signal-clarity";
192
- ToolName2["AgentGrounding"] = "agent-grounding";
193
- ToolName2["TestabilityIndex"] = "testability-index";
194
- ToolName2["DocDrift"] = "doc-drift";
195
- ToolName2["DependencyHealth"] = "dependency-health";
196
- ToolName2["ChangeAmplification"] = "change-amplification";
197
- ToolName2["CognitiveLoad"] = "cognitive-load";
198
- ToolName2["PatternEntropy"] = "pattern-entropy";
199
- ToolName2["ConceptCohesion"] = "concept-cohesion";
200
- ToolName2["SemanticDistance"] = "semantic-distance";
201
- return ToolName2;
189
+ var ToolName = /* @__PURE__ */ ((ToolName3) => {
190
+ ToolName3["PatternDetect"] = "pattern-detect";
191
+ ToolName3["ContextAnalyzer"] = "context-analyzer";
192
+ ToolName3["NamingConsistency"] = "naming-consistency";
193
+ ToolName3["AiSignalClarity"] = "ai-signal-clarity";
194
+ ToolName3["AgentGrounding"] = "agent-grounding";
195
+ ToolName3["TestabilityIndex"] = "testability-index";
196
+ ToolName3["DocDrift"] = "doc-drift";
197
+ ToolName3["DependencyHealth"] = "dependency-health";
198
+ ToolName3["ChangeAmplification"] = "change-amplification";
199
+ ToolName3["CognitiveLoad"] = "cognitive-load";
200
+ ToolName3["PatternEntropy"] = "pattern-entropy";
201
+ ToolName3["ConceptCohesion"] = "concept-cohesion";
202
+ ToolName3["SemanticDistance"] = "semantic-distance";
203
+ return ToolName3;
202
204
  })(ToolName || {});
203
205
  var ToolNameSchema = import_zod.z.nativeEnum(ToolName);
204
206
  var FRIENDLY_TOOL_NAMES = {
@@ -334,6 +336,44 @@ var UnifiedReportSchema = import_zod.z.object({
334
336
  )
335
337
  }).optional()
336
338
  }).catchall(import_zod.z.any());
339
+ var AIReadyConfigSchema = import_zod.z.object({
340
+ /** Target score threshold (0-100) */
341
+ threshold: import_zod.z.number().optional(),
342
+ /** Files or directories to include in scan */
343
+ include: import_zod.z.array(import_zod.z.string()).optional(),
344
+ /** Files or directories to exclude from scan */
345
+ exclude: import_zod.z.array(import_zod.z.string()).optional(),
346
+ /** Scan-specific configuration */
347
+ scan: import_zod.z.object({
348
+ include: import_zod.z.array(import_zod.z.string()).optional(),
349
+ exclude: import_zod.z.array(import_zod.z.string()).optional(),
350
+ parallel: import_zod.z.boolean().optional(),
351
+ deep: import_zod.z.boolean().optional(),
352
+ tools: import_zod.z.array(import_zod.z.string()).optional()
353
+ }).optional(),
354
+ /** Output-specific configuration */
355
+ output: import_zod.z.object({
356
+ /** Output format (json, console, html) */
357
+ format: import_zod.z.enum(["json", "console", "html"]).optional(),
358
+ /** Output file path */
359
+ path: import_zod.z.string().optional(),
360
+ /** Output directory */
361
+ saveTo: import_zod.z.string().optional(),
362
+ /** Whether to show score breakdown in console */
363
+ showBreakdown: import_zod.z.boolean().optional(),
364
+ /** Baseline report to compare against */
365
+ compareBaseline: import_zod.z.string().optional()
366
+ }).optional(),
367
+ /** Tool-specific configuration overrides (Strictly ToolName -> Config) */
368
+ tools: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(),
369
+ /** Scoring profile and weights */
370
+ scoring: import_zod.z.object({
371
+ /** Name of the scoring profile (e.g. "strict", "balanced") */
372
+ profile: import_zod.z.string().optional(),
373
+ /** Custom weights for tools and metrics */
374
+ weights: import_zod.z.record(import_zod.z.string(), import_zod.z.number()).optional()
375
+ }).optional()
376
+ });
337
377
 
338
378
  // src/types/business.ts
339
379
  var import_zod2 = require("zod");
@@ -450,51 +490,100 @@ var ParseError = class extends Error {
450
490
  }
451
491
  };
452
492
 
493
+ // src/utils/normalization.ts
494
+ function normalizeIssue(raw) {
495
+ return {
496
+ type: raw.type ?? "pattern-inconsistency" /* PatternInconsistency */,
497
+ severity: raw.severity ?? raw.severityLevel ?? "info" /* Info */,
498
+ message: raw.message ?? "Unknown issue",
499
+ location: raw.location ?? {
500
+ file: raw.fileName ?? raw.file ?? raw.filePath ?? "unknown",
501
+ line: raw.line ?? 1,
502
+ column: raw.column
503
+ },
504
+ suggestion: raw.suggestion
505
+ };
506
+ }
507
+ function normalizeMetrics(raw) {
508
+ return {
509
+ tokenCost: raw.tokenCost ?? 0,
510
+ complexityScore: raw.complexityScore ?? 0,
511
+ consistencyScore: raw.consistencyScore,
512
+ docFreshnessScore: raw.docFreshnessScore,
513
+ aiSignalClarityScore: raw.aiSignalClarityScore,
514
+ agentGroundingScore: raw.agentGroundingScore,
515
+ testabilityScore: raw.testabilityScore,
516
+ docDriftScore: raw.docDriftScore,
517
+ dependencyHealthScore: raw.dependencyHealthScore,
518
+ modelContextTier: raw.modelContextTier,
519
+ estimatedMonthlyCost: raw.estimatedMonthlyCost,
520
+ estimatedDeveloperHours: raw.estimatedDeveloperHours,
521
+ comprehensionDifficultyIndex: raw.comprehensionDifficultyIndex,
522
+ totalSymbols: raw.totalSymbols,
523
+ totalExports: raw.totalExports
524
+ };
525
+ }
526
+ function normalizeAnalysisResult(raw) {
527
+ const fileName = raw.fileName ?? raw.file ?? raw.filePath ?? "unknown";
528
+ const rawIssues = Array.isArray(raw.issues) ? raw.issues : [];
529
+ return {
530
+ fileName,
531
+ issues: rawIssues.map((issue) => {
532
+ if (typeof issue === "string") {
533
+ return {
534
+ type: "pattern-inconsistency" /* PatternInconsistency */,
535
+ // Default fallback
536
+ severity: raw.severity ?? "info" /* Info */,
537
+ message: issue,
538
+ location: { file: fileName, line: 1 }
539
+ };
540
+ }
541
+ return normalizeIssue({
542
+ ...issue,
543
+ fileName: issue.fileName ?? fileName,
544
+ severity: issue.severity ?? raw.severity
545
+ });
546
+ }),
547
+ metrics: normalizeMetrics(raw.metrics ?? {})
548
+ };
549
+ }
550
+ function normalizeSpokeOutput(raw, toolName) {
551
+ const rawResults = Array.isArray(raw.results) ? raw.results : [];
552
+ return {
553
+ results: rawResults.map(normalizeAnalysisResult),
554
+ summary: raw.summary ?? {
555
+ totalFiles: rawResults.length,
556
+ totalIssues: 0,
557
+ criticalIssues: 0,
558
+ majorIssues: 0
559
+ },
560
+ metadata: {
561
+ toolName: raw.metadata?.toolName ?? toolName,
562
+ version: raw.metadata?.version,
563
+ timestamp: raw.metadata?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
564
+ config: raw.metadata?.config
565
+ }
566
+ };
567
+ }
568
+
453
569
  // src/types/contract.ts
454
570
  function validateSpokeOutput(toolName, output) {
455
- const errors = [];
456
571
  if (!output) {
457
572
  return { valid: false, errors: ["Output is null or undefined"] };
458
573
  }
459
- if (!Array.isArray(output.results)) {
460
- errors.push(`${toolName}: 'results' must be an array`);
461
- } else {
462
- output.results.forEach((res, idx) => {
463
- const fileName = res.fileName || res.file || res.filePath;
464
- if (!fileName)
465
- errors.push(
466
- `${toolName}: results[${idx}] missing 'fileName', 'file' or 'filePath'`
467
- );
468
- const issues = res.issues;
469
- if (!Array.isArray(issues)) {
470
- errors.push(`${toolName}: results[${idx}] 'issues' must be an array`);
471
- } else if (issues.length > 0) {
472
- issues.forEach((issue, iidx) => {
473
- if (typeof issue === "string") return;
474
- if (!issue.type && !res.file)
475
- errors.push(
476
- `${toolName}: results[${idx}].issues[${iidx}] missing 'type'`
477
- );
478
- if (!issue.severity && !res.severity)
479
- errors.push(
480
- `${toolName}: results[${idx}].issues[${iidx}] missing 'severity'`
481
- );
482
- const severity = issue.severity || res.severity;
483
- if (severity && !["critical", "major", "minor", "info"].includes(severity)) {
484
- errors.push(
485
- `${toolName}: results[${idx}].issues[${iidx}] has invalid severity: ${severity}`
486
- );
487
- }
488
- });
489
- }
490
- });
491
- }
492
574
  if (!output.summary) {
493
- errors.push(`${toolName}: missing 'summary'`);
575
+ return { valid: false, errors: [`${toolName}: missing 'summary'`] };
576
+ }
577
+ const normalized = normalizeSpokeOutput(output, toolName);
578
+ const result = SpokeOutputSchema.safeParse(normalized);
579
+ if (result.success) {
580
+ return { valid: true, errors: [] };
494
581
  }
495
582
  return {
496
- valid: errors.length === 0,
497
- errors
583
+ valid: false,
584
+ errors: result.error.issues.map(
585
+ (e) => `${toolName}: ${e.path.join(".")}: ${e.message}`
586
+ )
498
587
  };
499
588
  }
500
589
  function validateWithSchema(schema, data) {
@@ -857,7 +946,7 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
857
946
  if ((0, import_fs2.statSync)(workingDir).isFile()) {
858
947
  baseDir = (0, import_path2.dirname)(workingDir);
859
948
  }
860
- } catch (e) {
949
+ } catch {
861
950
  }
862
951
  const aireadyDir = (0, import_path2.join)(baseDir, ".aiready");
863
952
  outputPath = (0, import_path2.join)(aireadyDir, defaultFilename);
@@ -1029,8 +1118,8 @@ function findLatestScanReport(scanReportsDir, reportFilePrefix) {
1029
1118
  return idB - idA;
1030
1119
  });
1031
1120
  return (0, import_path2.join)(scanReportsDir, reportFiles[0]);
1032
- } catch (e) {
1033
- console.error("Error while finding latest scan report:", e);
1121
+ } catch {
1122
+ console.error("Error while finding latest scan report");
1034
1123
  return null;
1035
1124
  }
1036
1125
  }
@@ -1095,7 +1184,19 @@ function createProvider(config) {
1095
1184
  };
1096
1185
  }
1097
1186
 
1098
- // src/utils/ast-parser.ts
1187
+ // src/utils/similarity-utils.ts
1188
+ function calculateImportSimilarity(export1, export2) {
1189
+ if (export1.imports.length === 0 && export2.imports.length === 0) {
1190
+ return 1;
1191
+ }
1192
+ const set1 = new Set(export1.imports);
1193
+ const set2 = new Set(export2.imports);
1194
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
1195
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
1196
+ return intersection.size / union.size;
1197
+ }
1198
+
1199
+ // src/utils/dependency-analyzer.ts
1099
1200
  var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
1100
1201
 
1101
1202
  // src/parsers/typescript-parser.ts
@@ -1106,148 +1207,67 @@ var TypeScriptParser = class {
1106
1207
  this.extensions = [".ts", ".tsx", ".js", ".jsx"];
1107
1208
  }
1108
1209
  async initialize() {
1109
- return Promise.resolve();
1110
1210
  }
1111
- async getAST(code, filePath) {
1112
- return (0, import_typescript_estree.parse)(code, {
1113
- loc: true,
1114
- range: true,
1115
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1116
- filePath,
1117
- sourceType: "module",
1118
- ecmaVersion: "latest",
1119
- comment: true
1120
- });
1211
+ canHandle(filePath) {
1212
+ return this.extensions.some((ext) => filePath.endsWith(ext));
1121
1213
  }
1122
- analyzeMetadata(node, code) {
1123
- const metadata = {
1124
- isPure: true,
1125
- hasSideEffects: false
1126
- };
1127
- const start = node.range?.[0] ?? 0;
1128
- const preceding = code.slice(Math.max(0, start - 1e3), start);
1129
- const jsdocMatches = Array.from(
1130
- preceding.matchAll(/\/\*\*([\s\S]*?)\*\//g)
1131
- );
1132
- if (jsdocMatches.length > 0) {
1133
- const lastMatch = jsdocMatches[jsdocMatches.length - 1];
1134
- const matchEndIndex = (lastMatch.index || 0) + lastMatch[0].length;
1135
- const between = preceding.slice(matchEndIndex);
1136
- if (/^\s*$/.test(between)) {
1137
- const precedingStartOffset = Math.max(0, start - 1e3);
1138
- const absoluteStartOffset = precedingStartOffset + (lastMatch.index || 0);
1139
- const absoluteEndOffset = precedingStartOffset + matchEndIndex;
1140
- const codeBeforeStart = code.slice(0, absoluteStartOffset);
1141
- const startLines = codeBeforeStart.split("\n");
1142
- const startLine = startLines.length;
1143
- const startColumn = startLines[startLines.length - 1].length;
1144
- const codeBeforeEnd = code.slice(0, absoluteEndOffset);
1145
- const endLines = codeBeforeEnd.split("\n");
1146
- const endLine = endLines.length;
1147
- const endColumn = endLines[endLines.length - 1].length;
1148
- metadata.documentation = {
1149
- content: lastMatch[1].replace(/^\s*\*+/gm, "").trim(),
1150
- type: "jsdoc",
1151
- loc: {
1152
- start: { line: startLine, column: startColumn },
1153
- end: { line: endLine, column: endColumn }
1154
- }
1155
- };
1156
- }
1157
- }
1158
- const walk = (n) => {
1159
- if (!n) return;
1160
- if (n.type === "AssignmentExpression") {
1161
- metadata.isPure = false;
1162
- metadata.hasSideEffects = true;
1163
- }
1164
- if (n.type === "UpdateExpression") {
1165
- metadata.isPure = false;
1166
- metadata.hasSideEffects = true;
1167
- }
1168
- if (n.type === "CallExpression" && n.callee.type === "MemberExpression" && n.callee.object.type === "Identifier") {
1169
- const objName = n.callee.object.name;
1170
- if (["console", "process", "fs", "window", "document"].includes(objName)) {
1171
- metadata.isPure = false;
1172
- metadata.hasSideEffects = true;
1173
- }
1174
- }
1175
- if (n.type === "ThrowStatement") {
1176
- metadata.isPure = false;
1177
- metadata.hasSideEffects = true;
1178
- }
1179
- for (const key of Object.keys(n)) {
1180
- if (key === "parent") continue;
1181
- const child = n[key];
1182
- if (child && typeof child === "object") {
1183
- if (Array.isArray(child)) {
1184
- child.forEach((c) => c?.type && walk(c));
1185
- } else if (child.type) {
1186
- walk(child);
1187
- }
1188
- }
1189
- }
1190
- };
1191
- let nodeToAnalyze = node;
1192
- if (node.type === "ExportNamedDeclaration" && node.declaration) {
1193
- nodeToAnalyze = node.declaration;
1194
- } else if (node.type === "ExportDefaultDeclaration" && node.declaration) {
1195
- if (node.declaration.type !== "TSInterfaceDeclaration" && node.declaration.type !== "TSTypeAliasDeclaration") {
1196
- nodeToAnalyze = node.declaration;
1197
- }
1198
- }
1199
- if (nodeToAnalyze.type === "FunctionDeclaration" || nodeToAnalyze.type === "FunctionExpression" || nodeToAnalyze.type === "ArrowFunctionExpression") {
1200
- if (nodeToAnalyze.body) walk(nodeToAnalyze.body);
1201
- } else if (nodeToAnalyze.type === "ClassDeclaration" || nodeToAnalyze.type === "ClassExpression") {
1202
- walk(nodeToAnalyze.body);
1214
+ async getAST(code, filePath) {
1215
+ try {
1216
+ return (0, import_typescript_estree.parse)(code, {
1217
+ filePath,
1218
+ loc: true,
1219
+ range: true,
1220
+ tokens: true,
1221
+ comment: true,
1222
+ jsx: filePath.endsWith("x")
1223
+ });
1224
+ } catch (error) {
1225
+ throw new ParseError(error.message, filePath, {
1226
+ line: error.lineNumber || 1,
1227
+ column: error.column || 0
1228
+ });
1203
1229
  }
1204
- return metadata;
1205
1230
  }
1206
1231
  parse(code, filePath) {
1207
1232
  try {
1208
- const isJavaScript = filePath.match(/\.jsx?$/i);
1209
1233
  const ast = (0, import_typescript_estree.parse)(code, {
1234
+ filePath,
1210
1235
  loc: true,
1211
1236
  range: true,
1212
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1213
- filePath,
1214
- sourceType: "module",
1215
- ecmaVersion: "latest",
1216
- comment: true
1237
+ tokens: true,
1238
+ comment: true,
1239
+ jsx: filePath.endsWith("x")
1217
1240
  });
1218
1241
  const imports = this.extractImports(ast);
1219
- const exports2 = this.extractExports(ast, imports, code);
1242
+ const exports2 = this.extractExports(ast, code);
1220
1243
  return {
1221
1244
  exports: exports2,
1222
1245
  imports,
1223
- language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
1224
- warnings: []
1246
+ language: this.language
1225
1247
  };
1226
1248
  } catch (error) {
1227
- const err = error;
1228
- throw new ParseError(
1229
- `Failed to parse ${filePath}: ${err.message}`,
1230
- filePath
1231
- );
1249
+ throw new ParseError(error.message, filePath, {
1250
+ line: error.lineNumber || 1,
1251
+ column: error.column || 0
1252
+ });
1232
1253
  }
1233
1254
  }
1234
1255
  getNamingConventions() {
1235
1256
  return {
1236
- // camelCase for variables and functions
1237
1257
  variablePattern: /^[a-z][a-zA-Z0-9]*$/,
1238
1258
  functionPattern: /^[a-z][a-zA-Z0-9]*$/,
1239
- // PascalCase for classes, types and interfaces
1240
1259
  classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1241
- typePattern: /^[A-Z][a-zA-Z0-9]*$/,
1242
- interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
1243
- // UPPER_CASE for constants
1244
1260
  constantPattern: /^[A-Z][A-Z0-9_]*$/,
1245
- // Common exceptions (React hooks, etc.)
1246
- exceptions: ["__filename", "__dirname", "__esModule"]
1261
+ typePattern: /^[A-Z][a-zA-Z0-9]*$/,
1262
+ interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
1247
1263
  };
1248
1264
  }
1249
- canHandle(filePath) {
1250
- return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
1265
+ analyzeMetadata(node, code) {
1266
+ if (!code) return {};
1267
+ return {
1268
+ isPure: this.isLikelyPure(node),
1269
+ hasSideEffects: !this.isLikelyPure(node)
1270
+ };
1251
1271
  }
1252
1272
  extractImports(ast) {
1253
1273
  const imports = [];
@@ -1285,168 +1305,165 @@ var TypeScriptParser = class {
1285
1305
  }
1286
1306
  return imports;
1287
1307
  }
1288
- extractExports(ast, imports, code) {
1308
+ extractExports(ast, code) {
1289
1309
  const exports2 = [];
1290
- const importedNames = new Set(
1291
- imports.flatMap(
1292
- (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1293
- )
1294
- );
1295
1310
  for (const node of ast.body) {
1296
- if (node.type === "ExportNamedDeclaration" && node.declaration) {
1297
- const extracted = this.extractFromDeclaration(
1298
- node.declaration,
1299
- importedNames,
1300
- code,
1301
- node
1302
- // Pass the ExportNamedDeclaration as parent for metadata
1303
- );
1304
- exports2.push(...extracted);
1305
- } else if (node.type === "ExportDefaultDeclaration") {
1306
- const metadata = this.analyzeMetadata(node, code);
1307
- let name = "default";
1308
- let type = "default";
1309
- if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
1310
- name = node.declaration.id.name;
1311
- type = "function";
1312
- } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
1313
- name = node.declaration.id.name;
1314
- type = "class";
1311
+ if (node.type === "ExportNamedDeclaration") {
1312
+ if (node.declaration) {
1313
+ const declaration = node.declaration;
1314
+ if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
1315
+ exports2.push(
1316
+ this.createExport(
1317
+ declaration.id.name,
1318
+ "function",
1319
+ node,
1320
+ // Pass the outer ExportNamedDeclaration
1321
+ code
1322
+ )
1323
+ );
1324
+ } else if (declaration.type === "ClassDeclaration" && declaration.id) {
1325
+ exports2.push(
1326
+ this.createExport(
1327
+ declaration.id.name,
1328
+ "class",
1329
+ node,
1330
+ // Pass the outer ExportNamedDeclaration
1331
+ code
1332
+ )
1333
+ );
1334
+ } else if (declaration.type === "TSTypeAliasDeclaration") {
1335
+ exports2.push(
1336
+ this.createExport(
1337
+ declaration.id.name,
1338
+ "type",
1339
+ node,
1340
+ // Pass the outer ExportNamedDeclaration
1341
+ code
1342
+ )
1343
+ );
1344
+ } else if (declaration.type === "TSInterfaceDeclaration") {
1345
+ exports2.push(
1346
+ this.createExport(
1347
+ declaration.id.name,
1348
+ "interface",
1349
+ node,
1350
+ // Pass the outer ExportNamedDeclaration
1351
+ code
1352
+ )
1353
+ );
1354
+ } else if (declaration.type === "VariableDeclaration") {
1355
+ for (const decl of declaration.declarations) {
1356
+ if (decl.id.type === "Identifier") {
1357
+ exports2.push(
1358
+ this.createExport(
1359
+ decl.id.name,
1360
+ "const",
1361
+ node,
1362
+ // Pass the outer ExportNamedDeclaration
1363
+ code,
1364
+ decl.init
1365
+ )
1366
+ );
1367
+ }
1368
+ }
1369
+ }
1315
1370
  }
1316
- exports2.push({
1317
- name,
1318
- type,
1319
- loc: node.loc ? {
1320
- start: {
1321
- line: node.loc.start.line,
1322
- column: node.loc.start.column
1323
- },
1324
- end: { line: node.loc.end.line, column: node.loc.end.column }
1325
- } : void 0,
1326
- ...metadata
1327
- });
1371
+ } else if (node.type === "ExportDefaultDeclaration") {
1372
+ exports2.push(this.createExport("default", "default", node, code));
1328
1373
  }
1329
1374
  }
1330
1375
  return exports2;
1331
1376
  }
1332
- extractFromDeclaration(declaration, importedNames, code, parentNode) {
1333
- const exports2 = [];
1334
- const metadata = this.analyzeMetadata(parentNode || declaration, code);
1335
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
1336
- exports2.push({
1337
- name: declaration.id.name,
1338
- type: "function",
1339
- parameters: declaration.params.map((p) => {
1377
+ createExport(name, type, node, code, initializer) {
1378
+ const documentation = this.extractDocumentation(node, code);
1379
+ let methodCount;
1380
+ let propertyCount;
1381
+ let parameters;
1382
+ let isPrimitive = false;
1383
+ if (initializer) {
1384
+ if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
1385
+ isPrimitive = true;
1386
+ }
1387
+ }
1388
+ const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1389
+ if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
1390
+ const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
1391
+ methodCount = body.filter(
1392
+ (m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
1393
+ ).length;
1394
+ propertyCount = body.filter(
1395
+ (m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
1396
+ ).length;
1397
+ if (structNode.type === "ClassDeclaration") {
1398
+ const constructor = body.find(
1399
+ (m) => m.type === "MethodDefinition" && m.kind === "constructor"
1400
+ );
1401
+ if (constructor && constructor.value && constructor.value.params) {
1402
+ parameters = constructor.value.params.map((p) => {
1403
+ if (p.type === "Identifier") return p.name;
1404
+ if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
1405
+ return p.parameter.name;
1406
+ }
1407
+ return void 0;
1408
+ }).filter(Boolean);
1409
+ }
1410
+ }
1411
+ }
1412
+ if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
1413
+ const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
1414
+ if (funcNode && funcNode.params) {
1415
+ parameters = funcNode.params.map((p) => {
1340
1416
  if (p.type === "Identifier") return p.name;
1341
- if (p.type === "AssignmentPattern" && p.left.type === "Identifier")
1342
- return p.left.name;
1343
- if (p.type === "RestElement" && p.argument.type === "Identifier")
1344
- return p.argument.name;
1345
- return "unknown";
1346
- }),
1347
- loc: declaration.loc ? {
1348
- start: {
1349
- line: declaration.loc.start.line,
1350
- column: declaration.loc.start.column
1351
- },
1352
- end: {
1353
- line: declaration.loc.end.line,
1354
- column: declaration.loc.end.column
1355
- }
1356
- } : void 0,
1357
- ...metadata
1358
- });
1359
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
1360
- const body = declaration.body.body;
1361
- const methods = body.filter((m) => m.type === "MethodDefinition");
1362
- const properties = body.filter((m) => m.type === "PropertyDefinition");
1363
- const constructor = methods.find(
1364
- (m) => m.kind === "constructor"
1365
- );
1366
- const parameters = constructor ? constructor.value.params.map((p) => {
1367
- if (p.type === "Identifier") return p.name;
1368
- if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier")
1369
- return p.parameter.name;
1370
- return "unknown";
1371
- }) : [];
1372
- exports2.push({
1373
- name: declaration.id.name,
1374
- type: "class",
1375
- methodCount: methods.length,
1376
- propertyCount: properties.length,
1377
- parameters,
1378
- loc: declaration.loc ? {
1379
- start: {
1380
- line: declaration.loc.start.line,
1381
- column: declaration.loc.start.column
1382
- },
1383
- end: {
1384
- line: declaration.loc.end.line,
1385
- column: declaration.loc.end.column
1386
- }
1387
- } : void 0,
1388
- ...metadata
1389
- });
1390
- } else if (declaration.type === "VariableDeclaration") {
1391
- for (const declarator of declaration.declarations) {
1392
- if (declarator.id.type === "Identifier") {
1393
- exports2.push({
1394
- name: declarator.id.name,
1395
- type: "const",
1396
- loc: declarator.loc ? {
1397
- start: {
1398
- line: declarator.loc.start.line,
1399
- column: declarator.loc.start.column
1400
- },
1401
- end: {
1402
- line: declarator.loc.end.line,
1403
- column: declarator.loc.end.column
1404
- }
1405
- } : void 0,
1406
- ...metadata
1407
- });
1417
+ return void 0;
1418
+ }).filter(Boolean);
1419
+ }
1420
+ }
1421
+ return {
1422
+ name,
1423
+ type,
1424
+ isPrimitive,
1425
+ loc: node.loc ? {
1426
+ start: { line: node.loc.start.line, column: node.loc.start.column },
1427
+ end: { line: node.loc.end.line, column: node.loc.end.column }
1428
+ } : void 0,
1429
+ documentation,
1430
+ methodCount,
1431
+ propertyCount,
1432
+ parameters,
1433
+ isPure: this.isLikelyPure(node),
1434
+ hasSideEffects: !this.isLikelyPure(node)
1435
+ };
1436
+ }
1437
+ extractDocumentation(node, code) {
1438
+ if (node.range) {
1439
+ const start = node.range[0];
1440
+ const precedingCode = code.substring(0, start);
1441
+ const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1442
+ if (jsdocMatch) {
1443
+ return {
1444
+ content: jsdocMatch[1].trim(),
1445
+ type: "jsdoc"
1446
+ };
1447
+ }
1448
+ }
1449
+ return void 0;
1450
+ }
1451
+ isLikelyPure(node) {
1452
+ const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1453
+ if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
1454
+ return true;
1455
+ if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
1456
+ const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
1457
+ if (body && body.type === "BlockStatement") {
1458
+ const bodyContent = JSON.stringify(body);
1459
+ if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
1460
+ return false;
1408
1461
  }
1462
+ return true;
1409
1463
  }
1410
- } else if (declaration.type === "TSTypeAliasDeclaration") {
1411
- exports2.push({
1412
- name: declaration.id.name,
1413
- type: "type",
1414
- loc: declaration.loc ? {
1415
- start: {
1416
- line: declaration.loc.start.line,
1417
- column: declaration.loc.start.column
1418
- },
1419
- end: {
1420
- line: declaration.loc.end.line,
1421
- column: declaration.loc.end.column
1422
- }
1423
- } : void 0,
1424
- ...metadata
1425
- });
1426
- } else if (declaration.type === "TSInterfaceDeclaration") {
1427
- const body = declaration.body.body;
1428
- const methods = body.filter((m) => m.type === "TSMethodSignature");
1429
- const properties = body.filter((m) => m.type === "TSPropertySignature");
1430
- exports2.push({
1431
- name: declaration.id.name,
1432
- type: "interface",
1433
- methodCount: methods.length,
1434
- propertyCount: properties.length || body.length,
1435
- // Fallback to body.length
1436
- loc: declaration.loc ? {
1437
- start: {
1438
- line: declaration.loc.start.line,
1439
- column: declaration.loc.start.column
1440
- },
1441
- end: {
1442
- line: declaration.loc.end.line,
1443
- column: declaration.loc.end.column
1444
- }
1445
- } : void 0,
1446
- ...metadata
1447
- });
1464
+ return true;
1448
1465
  }
1449
- return exports2;
1466
+ return false;
1450
1467
  }
1451
1468
  };
1452
1469
 
@@ -1720,11 +1737,24 @@ var PythonParser = class extends BaseLanguageParser {
1720
1737
  getParserName() {
1721
1738
  return "python";
1722
1739
  }
1740
+ /**
1741
+ * Analyze metadata for a Python node (purity, side effects).
1742
+ *
1743
+ * @param node - Tree-sitter node to analyze.
1744
+ * @param code - Source code for context.
1745
+ * @returns Partial ExportInfo containing discovered metadata.
1746
+ */
1723
1747
  analyzeMetadata(node, code) {
1724
1748
  return analyzeNodeMetadata(node, code, {
1725
1749
  sideEffectSignatures: ["print(", "input(", "open("]
1726
1750
  });
1727
1751
  }
1752
+ /**
1753
+ * Extract import information using AST walk.
1754
+ *
1755
+ * @param rootNode - Root node of the Python AST.
1756
+ * @returns Array of discovered FileImport objects.
1757
+ */
1728
1758
  extractImportsAST(rootNode) {
1729
1759
  const imports = [];
1730
1760
  const processImportNode = (node) => {
@@ -1806,6 +1836,13 @@ var PythonParser = class extends BaseLanguageParser {
1806
1836
  }
1807
1837
  return imports;
1808
1838
  }
1839
+ /**
1840
+ * Extract export information using AST walk.
1841
+ *
1842
+ * @param rootNode - Root node of the Python AST.
1843
+ * @param code - Source code for documentation extraction.
1844
+ * @returns Array of discovered ExportInfo objects.
1845
+ */
1809
1846
  extractExportsAST(rootNode, code) {
1810
1847
  const exports2 = [];
1811
1848
  for (const node of rootNode.children) {
@@ -1884,6 +1921,12 @@ var PythonParser = class extends BaseLanguageParser {
1884
1921
  }
1885
1922
  return exports2;
1886
1923
  }
1924
+ /**
1925
+ * Extract parameter names from a function definition node.
1926
+ *
1927
+ * @param node - Function definition node.
1928
+ * @returns Array of parameter name strings.
1929
+ */
1887
1930
  extractParameters(node) {
1888
1931
  const paramsNode = node.childForFieldName("parameters");
1889
1932
  if (!paramsNode) return [];
@@ -1897,6 +1940,13 @@ var PythonParser = class extends BaseLanguageParser {
1897
1940
  return "unknown";
1898
1941
  });
1899
1942
  }
1943
+ /**
1944
+ * Fallback regex-based parsing when tree-sitter is unavailable.
1945
+ *
1946
+ * @param code - Source code content.
1947
+ * @param filePath - Path to the file being parsed.
1948
+ * @returns Consolidated ParseResult.
1949
+ */
1900
1950
  parseRegex(code, filePath) {
1901
1951
  try {
1902
1952
  const imports = this.extractImportsRegex(code, filePath);
@@ -2170,6 +2220,13 @@ var JavaParser = class extends BaseLanguageParser {
2170
2220
  getParserName() {
2171
2221
  return "java";
2172
2222
  }
2223
+ /**
2224
+ * Analyze metadata for a Java node (purity, side effects).
2225
+ *
2226
+ * @param node - Tree-sitter node to analyze.
2227
+ * @param code - Source code for context.
2228
+ * @returns Partial ExportInfo containing discovered metadata.
2229
+ */
2173
2230
  analyzeMetadata(node, code) {
2174
2231
  return analyzeGeneralMetadata(node, code, {
2175
2232
  sideEffectSignatures: [
@@ -2249,6 +2306,12 @@ var JavaParser = class extends BaseLanguageParser {
2249
2306
  warnings: ["Parser falling back to regex-based analysis"]
2250
2307
  };
2251
2308
  }
2309
+ /**
2310
+ * Extract import information using AST walk.
2311
+ *
2312
+ * @param rootNode - Root node of the Java AST.
2313
+ * @returns Array of discovered FileImport objects.
2314
+ */
2252
2315
  extractImportsAST(rootNode) {
2253
2316
  const imports = [];
2254
2317
  for (const node of rootNode.children) {
@@ -2282,6 +2345,13 @@ var JavaParser = class extends BaseLanguageParser {
2282
2345
  }
2283
2346
  return imports;
2284
2347
  }
2348
+ /**
2349
+ * Extract export information (classes, interfaces, methods) using AST walk.
2350
+ *
2351
+ * @param rootNode - Root node of the Java AST.
2352
+ * @param code - Source code for documentation extraction.
2353
+ * @returns Array of discovered ExportInfo objects.
2354
+ */
2285
2355
  extractExportsAST(rootNode, code) {
2286
2356
  const exports2 = [];
2287
2357
  for (const node of rootNode.children) {
@@ -2312,11 +2382,25 @@ var JavaParser = class extends BaseLanguageParser {
2312
2382
  }
2313
2383
  return exports2;
2314
2384
  }
2385
+ /**
2386
+ * Extract modifiers (visibility, static, etc.) from a node.
2387
+ *
2388
+ * @param node - AST node to extract modifiers from.
2389
+ * @returns Array of modifier strings.
2390
+ */
2315
2391
  getModifiers(node) {
2316
2392
  const modifiersNode = node.children.find((c) => c.type === "modifiers");
2317
2393
  if (!modifiersNode) return [];
2318
2394
  return modifiersNode.children.map((c) => c.text);
2319
2395
  }
2396
+ /**
2397
+ * Extract methods and nested exports from a class or interface body.
2398
+ *
2399
+ * @param parentNode - Class or interface declaration node.
2400
+ * @param parentName - Name of the parent class/interface.
2401
+ * @param exports - Array to collect discovered exports into.
2402
+ * @param code - Source code for context.
2403
+ */
2320
2404
  extractSubExports(parentNode, parentName, exports2, code) {
2321
2405
  const bodyNode = parentNode.children.find((c) => c.type === "class_body");
2322
2406
  if (!bodyNode) return;
@@ -2375,11 +2459,24 @@ var CSharpParser = class extends BaseLanguageParser {
2375
2459
  getParserName() {
2376
2460
  return "c_sharp";
2377
2461
  }
2462
+ /**
2463
+ * Analyze metadata for a C# node (purity, side effects).
2464
+ *
2465
+ * @param node - Tree-sitter node to analyze.
2466
+ * @param code - Source code for context.
2467
+ * @returns Partial ExportInfo containing discovered metadata.
2468
+ */
2378
2469
  analyzeMetadata(node, code) {
2379
2470
  return analyzeGeneralMetadata(node, code, {
2380
2471
  sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
2381
2472
  });
2382
2473
  }
2474
+ /**
2475
+ * Fallback regex-based parsing when tree-sitter is unavailable.
2476
+ *
2477
+ * @param code - Source code content.
2478
+ * @returns Consolidated ParseResult.
2479
+ */
2383
2480
  parseRegex(code) {
2384
2481
  const lines = code.split("\n");
2385
2482
  const exports2 = [];
@@ -2441,6 +2538,12 @@ var CSharpParser = class extends BaseLanguageParser {
2441
2538
  warnings: ["Parser falling back to regex-based analysis"]
2442
2539
  };
2443
2540
  }
2541
+ /**
2542
+ * Extract import information (usings) using AST walk.
2543
+ *
2544
+ * @param rootNode - Root node of the C# AST.
2545
+ * @returns Array of discovered FileImport objects.
2546
+ */
2444
2547
  extractImportsAST(rootNode) {
2445
2548
  const imports = [];
2446
2549
  const findUsings = (node) => {
@@ -2474,6 +2577,14 @@ var CSharpParser = class extends BaseLanguageParser {
2474
2577
  findUsings(rootNode);
2475
2578
  return imports;
2476
2579
  }
2580
+ /**
2581
+ * Extract export information (classes, methods, properties) using AST walk.
2582
+ * Handles nested namespaces and classes.
2583
+ *
2584
+ * @param rootNode - Root node of the C# AST.
2585
+ * @param code - Source code for documentation extraction.
2586
+ * @returns Array of discovered ExportInfo objects.
2587
+ */
2477
2588
  extractExportsAST(rootNode, code) {
2478
2589
  const exports2 = [];
2479
2590
  const traverse = (node, currentNamespace, currentClass) => {
@@ -2585,11 +2696,24 @@ var GoParser = class extends BaseLanguageParser {
2585
2696
  getParserName() {
2586
2697
  return "go";
2587
2698
  }
2699
+ /**
2700
+ * Analyze metadata for a Go node (purity, side effects).
2701
+ *
2702
+ * @param node - Tree-sitter node to analyze.
2703
+ * @param code - Source code for context.
2704
+ * @returns Partial ExportInfo containing discovered metadata.
2705
+ */
2588
2706
  analyzeMetadata(node, code) {
2589
2707
  return analyzeGeneralMetadata(node, code, {
2590
2708
  sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
2591
2709
  });
2592
2710
  }
2711
+ /**
2712
+ * Fallback regex-based parsing when tree-sitter is unavailable.
2713
+ *
2714
+ * @param code - Source code content.
2715
+ * @returns Consolidated ParseResult.
2716
+ */
2593
2717
  parseRegex(code) {
2594
2718
  const lines = code.split("\n");
2595
2719
  const exports2 = [];
@@ -2667,6 +2791,12 @@ var GoParser = class extends BaseLanguageParser {
2667
2791
  warnings: ["Parser falling back to regex-based analysis"]
2668
2792
  };
2669
2793
  }
2794
+ /**
2795
+ * Extract import information using AST walk.
2796
+ *
2797
+ * @param rootNode - Root node of the Go AST.
2798
+ * @returns Array of discovered FileImport objects.
2799
+ */
2670
2800
  extractImportsAST(rootNode) {
2671
2801
  const imports = [];
2672
2802
  const findImports = (node) => {
@@ -2700,6 +2830,13 @@ var GoParser = class extends BaseLanguageParser {
2700
2830
  findImports(rootNode);
2701
2831
  return imports;
2702
2832
  }
2833
+ /**
2834
+ * Extract export information (functions, types, vars) using AST walk.
2835
+ *
2836
+ * @param rootNode - Root node of the Go AST.
2837
+ * @param code - Source code for documentation extraction.
2838
+ * @returns Array of discovered ExportInfo objects.
2839
+ */
2703
2840
  extractExportsAST(rootNode, code) {
2704
2841
  const exports2 = [];
2705
2842
  const isExported = (name) => {
@@ -3056,7 +3193,7 @@ function extractTypeReferences(node) {
3056
3193
  return Array.from(types);
3057
3194
  }
3058
3195
 
3059
- // src/utils/ast-parser.ts
3196
+ // src/utils/dependency-analyzer.ts
3060
3197
  function parseFileExports(code, filePath) {
3061
3198
  const parser = getParser(filePath);
3062
3199
  if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
@@ -3098,29 +3235,6 @@ function parseFileExports(code, filePath) {
3098
3235
  return { exports: [], imports: [] };
3099
3236
  }
3100
3237
  }
3101
- function calculateImportSimilarity(export1, export2) {
3102
- if (export1.imports.length === 0 && export2.imports.length === 0) {
3103
- return 1;
3104
- }
3105
- const set1 = new Set(export1.imports);
3106
- const set2 = new Set(export2.imports);
3107
- const intersection = new Set([...set1].filter((x) => set2.has(x)));
3108
- const union = /* @__PURE__ */ new Set([...set1, ...set2]);
3109
- return intersection.size / union.size;
3110
- }
3111
- function parseCode(_code, _language) {
3112
- void _code;
3113
- void _language;
3114
- return null;
3115
- }
3116
- function extractFunctions(_ast) {
3117
- void _ast;
3118
- return [];
3119
- }
3120
- function extractImports(_ast) {
3121
- void _ast;
3122
- return [];
3123
- }
3124
3238
 
3125
3239
  // src/utils/metrics.ts
3126
3240
  function estimateTokens(text) {
@@ -3169,10 +3283,25 @@ async function loadConfig(rootDir) {
3169
3283
  const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
3170
3284
  config = JSON.parse(content);
3171
3285
  }
3172
- if (typeof config !== "object" || config === null) {
3173
- throw new Error("Config must be an object");
3286
+ const legacyKeys = ["toolConfigs"];
3287
+ const rootLevelTools = [
3288
+ "pattern-detect",
3289
+ "context-analyzer",
3290
+ "naming-consistency",
3291
+ "ai-signal-clarity"
3292
+ ];
3293
+ const allKeys = Object.keys(config);
3294
+ const foundLegacy = allKeys.filter(
3295
+ (k) => legacyKeys.includes(k) || rootLevelTools.includes(k)
3296
+ );
3297
+ if (foundLegacy.length > 0) {
3298
+ console.warn(
3299
+ `\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(
3300
+ ", "
3301
+ )}. These are deprecated and should be moved under the "tools" key.`
3302
+ );
3174
3303
  }
3175
- return config;
3304
+ return AIReadyConfigSchema.parse(config);
3176
3305
  } catch (error) {
3177
3306
  const errorMessage = error instanceof Error ? error.message : String(error);
3178
3307
  const configError = new Error(
@@ -3200,12 +3329,10 @@ function mergeConfigWithDefaults(userConfig, defaults) {
3200
3329
  if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
3201
3330
  if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
3202
3331
  }
3203
- const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
3204
- if (toolOverrides) {
3332
+ if (userConfig.tools) {
3205
3333
  if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
3206
- for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
3334
+ for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
3207
3335
  if (typeof toolConfig === "object" && toolConfig !== null) {
3208
- mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
3209
3336
  mergedConfig.toolConfigs[toolName] = {
3210
3337
  ...mergedConfig.toolConfigs[toolName],
3211
3338
  ...toolConfig
@@ -3507,12 +3634,12 @@ function getRecommendedThreshold(fileCount, modelTier = "standard") {
3507
3634
  return base + modelBonus;
3508
3635
  }
3509
3636
  function normalizeToolName(shortName) {
3510
- return TOOL_NAME_MAP[shortName.toLowerCase()] || shortName;
3637
+ return TOOL_NAME_MAP[shortName.toLowerCase()] ?? shortName;
3511
3638
  }
3512
3639
  function getToolWeight(toolName, toolConfig, cliOverride, profile = "default" /* Default */) {
3513
3640
  if (cliOverride !== void 0) return cliOverride;
3514
3641
  if (toolConfig?.scoreWeight !== void 0) return toolConfig.scoreWeight;
3515
- const profileWeights = SCORING_PROFILES[profile] || DEFAULT_TOOL_WEIGHTS;
3642
+ const profileWeights = SCORING_PROFILES[profile] ?? DEFAULT_TOOL_WEIGHTS;
3516
3643
  return profileWeights[toolName] ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 5;
3517
3644
  }
3518
3645
  function parseWeightString(weightStr) {
@@ -3549,7 +3676,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3549
3676
  const toolsUsed = [];
3550
3677
  const calculationWeights = {};
3551
3678
  for (const [toolName, output] of toolOutputs.entries()) {
3552
- const weight = weights.get(toolName) || 5;
3679
+ const weight = weights.get(toolName) ?? 5;
3553
3680
  weightedSum += output.score * weight;
3554
3681
  totalWeight += weight;
3555
3682
  toolsUsed.push(toolName);
@@ -3560,7 +3687,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3560
3687
  const rating = getRating(overall);
3561
3688
  const formulaParts = Array.from(toolOutputs.entries()).map(
3562
3689
  ([name, output]) => {
3563
- const w = weights.get(name) || 5;
3690
+ const w = weights.get(name) ?? 5;
3564
3691
  return `(${output.score} \xD7 ${w})`;
3565
3692
  }
3566
3693
  );
@@ -3653,6 +3780,13 @@ function formatToolScore(output) {
3653
3780
 
3654
3781
  // src/business/pricing-models.ts
3655
3782
  var MODEL_PRICING_PRESETS = {
3783
+ "gpt-5.4-mini": {
3784
+ name: "GPT-5.4 Mini",
3785
+ pricePer1KInputTokens: 1e-4,
3786
+ pricePer1KOutputTokens: 4e-4,
3787
+ contextTier: "extended",
3788
+ typicalQueriesPerDevPerDay: 200
3789
+ },
3656
3790
  "gpt-5.3": {
3657
3791
  name: "GPT-5.3",
3658
3792
  pricePer1KInputTokens: 2e-3,
@@ -3674,20 +3808,6 @@ var MODEL_PRICING_PRESETS = {
3674
3808
  contextTier: "frontier",
3675
3809
  typicalQueriesPerDevPerDay: 120
3676
3810
  },
3677
- "gpt-4o": {
3678
- name: "GPT-4o (legacy)",
3679
- pricePer1KInputTokens: 5e-3,
3680
- pricePer1KOutputTokens: 0.015,
3681
- contextTier: "extended",
3682
- typicalQueriesPerDevPerDay: 60
3683
- },
3684
- "claude-3-5-sonnet": {
3685
- name: "Claude 3.5 Sonnet (legacy)",
3686
- pricePer1KInputTokens: 3e-3,
3687
- pricePer1KOutputTokens: 0.015,
3688
- contextTier: "extended",
3689
- typicalQueriesPerDevPerDay: 80
3690
- },
3691
3811
  "gemini-1-5-pro": {
3692
3812
  name: "Gemini 1.5 Pro (legacy)",
3693
3813
  pricePer1KInputTokens: 125e-5,
@@ -3711,7 +3831,7 @@ var MODEL_PRICING_PRESETS = {
3711
3831
  }
3712
3832
  };
3713
3833
  function getModelPreset(modelId) {
3714
- return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
3834
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-5.4-mini"];
3715
3835
  }
3716
3836
 
3717
3837
  // src/business/cost-metrics.ts
@@ -3732,7 +3852,7 @@ function calculateMonthlyCost(tokenWaste, config = {}) {
3732
3852
  // Added baseline chattiness
3733
3853
  }
3734
3854
  });
3735
- const preset = getModelPreset("claude-3.5-sonnet");
3855
+ const preset = getModelPreset("gpt-5.4-mini");
3736
3856
  return estimateCostFromBudget(budget, preset, config);
3737
3857
  }
3738
3858
  function calculateTokenBudget(params) {
@@ -4578,12 +4698,12 @@ function calculateTestabilityIndex(params) {
4578
4698
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
4579
4699
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
4580
4700
  const purityScore = Math.round(
4581
- (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
4701
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
4582
4702
  );
4583
4703
  const dependencyInjectionScore = Math.round(
4584
4704
  Math.min(
4585
4705
  100,
4586
- (totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
4706
+ (totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
4587
4707
  )
4588
4708
  );
4589
4709
  const interfaceFocusScore = Math.max(
@@ -4664,9 +4784,9 @@ function calculateDocDrift(params) {
4664
4784
  const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
4665
4785
  const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
4666
4786
  const driftRatio = totalExports > 0 ? actualDrift / totalExports : 0;
4667
- const DRIFT_THRESHOLD = 0.2;
4668
- const OUTDATED_THRESHOLD = 0.4;
4669
- const COMPLEXITY_THRESHOLD = 0.2;
4787
+ const DRIFT_THRESHOLD = 0.35;
4788
+ const OUTDATED_THRESHOLD = 0.5;
4789
+ const COMPLEXITY_THRESHOLD = 0.3;
4670
4790
  const UNCOMMENTED_THRESHOLD = 0.8;
4671
4791
  const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
4672
4792
  const outdatedRisk = Math.min(
@@ -5132,6 +5252,7 @@ function emitIssuesAsAnnotations(issues) {
5132
5252
  }
5133
5253
  // Annotate the CommonJS export names for ESM import in node:
5134
5254
  0 && (module.exports = {
5255
+ AIReadyConfigSchema,
5135
5256
  AnalysisResultSchema,
5136
5257
  AnalysisStatus,
5137
5258
  AnalysisStatusSchema,
@@ -5213,8 +5334,6 @@ function emitIssuesAsAnnotations(issues) {
5213
5334
  estimateCostFromBudget,
5214
5335
  estimateTokens,
5215
5336
  exportHistory,
5216
- extractFunctions,
5217
- extractImports,
5218
5337
  findLatestReport,
5219
5338
  findLatestScanReport,
5220
5339
  formatAcceptanceRate,
@@ -5259,8 +5378,11 @@ function emitIssuesAsAnnotations(issues) {
5259
5378
  loadMergedConfig,
5260
5379
  loadScoreHistory,
5261
5380
  mergeConfigWithDefaults,
5381
+ normalizeAnalysisResult,
5382
+ normalizeIssue,
5383
+ normalizeMetrics,
5384
+ normalizeSpokeOutput,
5262
5385
  normalizeToolName,
5263
- parseCode,
5264
5386
  parseFileExports,
5265
5387
  parseWeightString,
5266
5388
  predictAcceptanceRate,