@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.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,
@@ -155,6 +156,10 @@ __export(index_exports, {
155
156
  loadMergedConfig: () => loadMergedConfig,
156
157
  loadScoreHistory: () => loadScoreHistory,
157
158
  mergeConfigWithDefaults: () => mergeConfigWithDefaults,
159
+ normalizeAnalysisResult: () => normalizeAnalysisResult,
160
+ normalizeIssue: () => normalizeIssue,
161
+ normalizeMetrics: () => normalizeMetrics,
162
+ normalizeSpokeOutput: () => normalizeSpokeOutput,
158
163
  normalizeToolName: () => normalizeToolName,
159
164
  parseFileExports: () => parseFileExports,
160
165
  parseWeightString: () => parseWeightString,
@@ -181,21 +186,21 @@ var Severity = /* @__PURE__ */ ((Severity2) => {
181
186
  return Severity2;
182
187
  })(Severity || {});
183
188
  var SeveritySchema = import_zod.z.nativeEnum(Severity);
184
- var ToolName = /* @__PURE__ */ ((ToolName2) => {
185
- ToolName2["PatternDetect"] = "pattern-detect";
186
- ToolName2["ContextAnalyzer"] = "context-analyzer";
187
- ToolName2["NamingConsistency"] = "naming-consistency";
188
- ToolName2["AiSignalClarity"] = "ai-signal-clarity";
189
- ToolName2["AgentGrounding"] = "agent-grounding";
190
- ToolName2["TestabilityIndex"] = "testability-index";
191
- ToolName2["DocDrift"] = "doc-drift";
192
- ToolName2["DependencyHealth"] = "dependency-health";
193
- ToolName2["ChangeAmplification"] = "change-amplification";
194
- ToolName2["CognitiveLoad"] = "cognitive-load";
195
- ToolName2["PatternEntropy"] = "pattern-entropy";
196
- ToolName2["ConceptCohesion"] = "concept-cohesion";
197
- ToolName2["SemanticDistance"] = "semantic-distance";
198
- 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;
199
204
  })(ToolName || {});
200
205
  var ToolNameSchema = import_zod.z.nativeEnum(ToolName);
201
206
  var FRIENDLY_TOOL_NAMES = {
@@ -331,6 +336,44 @@ var UnifiedReportSchema = import_zod.z.object({
331
336
  )
332
337
  }).optional()
333
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
+ });
334
377
 
335
378
  // src/types/business.ts
336
379
  var import_zod2 = require("zod");
@@ -447,51 +490,100 @@ var ParseError = class extends Error {
447
490
  }
448
491
  };
449
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
+
450
569
  // src/types/contract.ts
451
570
  function validateSpokeOutput(toolName, output) {
452
- const errors = [];
453
571
  if (!output) {
454
572
  return { valid: false, errors: ["Output is null or undefined"] };
455
573
  }
456
- if (!Array.isArray(output.results)) {
457
- errors.push(`${toolName}: 'results' must be an array`);
458
- } else {
459
- output.results.forEach((res, idx) => {
460
- const fileName = res.fileName || res.file || res.filePath;
461
- if (!fileName)
462
- errors.push(
463
- `${toolName}: results[${idx}] missing 'fileName', 'file' or 'filePath'`
464
- );
465
- const issues = res.issues;
466
- if (!Array.isArray(issues)) {
467
- errors.push(`${toolName}: results[${idx}] 'issues' must be an array`);
468
- } else if (issues.length > 0) {
469
- issues.forEach((issue, iidx) => {
470
- if (typeof issue === "string") return;
471
- if (!issue.type && !res.file)
472
- errors.push(
473
- `${toolName}: results[${idx}].issues[${iidx}] missing 'type'`
474
- );
475
- if (!issue.severity && !res.severity)
476
- errors.push(
477
- `${toolName}: results[${idx}].issues[${iidx}] missing 'severity'`
478
- );
479
- const severity = issue.severity || res.severity;
480
- if (severity && !["critical", "major", "minor", "info"].includes(severity)) {
481
- errors.push(
482
- `${toolName}: results[${idx}].issues[${iidx}] has invalid severity: ${severity}`
483
- );
484
- }
485
- });
486
- }
487
- });
488
- }
489
574
  if (!output.summary) {
490
- 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: [] };
491
581
  }
492
582
  return {
493
- valid: errors.length === 0,
494
- errors
583
+ valid: false,
584
+ errors: result.error.issues.map(
585
+ (e) => `${toolName}: ${e.path.join(".")}: ${e.message}`
586
+ )
495
587
  };
496
588
  }
497
589
  function validateWithSchema(schema, data) {
@@ -1115,148 +1207,67 @@ var TypeScriptParser = class {
1115
1207
  this.extensions = [".ts", ".tsx", ".js", ".jsx"];
1116
1208
  }
1117
1209
  async initialize() {
1118
- return Promise.resolve();
1119
1210
  }
1120
- async getAST(code, filePath) {
1121
- return (0, import_typescript_estree.parse)(code, {
1122
- loc: true,
1123
- range: true,
1124
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1125
- filePath,
1126
- sourceType: "module",
1127
- ecmaVersion: "latest",
1128
- comment: true
1129
- });
1211
+ canHandle(filePath) {
1212
+ return this.extensions.some((ext) => filePath.endsWith(ext));
1130
1213
  }
1131
- analyzeMetadata(node, code) {
1132
- const metadata = {
1133
- isPure: true,
1134
- hasSideEffects: false
1135
- };
1136
- const start = node.range?.[0] ?? 0;
1137
- const preceding = code.slice(Math.max(0, start - 1e3), start);
1138
- const jsdocMatches = Array.from(
1139
- preceding.matchAll(/\/\*\*([\s\S]*?)\*\//g)
1140
- );
1141
- if (jsdocMatches.length > 0) {
1142
- const lastMatch = jsdocMatches[jsdocMatches.length - 1];
1143
- const matchEndIndex = (lastMatch.index || 0) + lastMatch[0].length;
1144
- const between = preceding.slice(matchEndIndex);
1145
- if (/^\s*$/.test(between)) {
1146
- const precedingStartOffset = Math.max(0, start - 1e3);
1147
- const absoluteStartOffset = precedingStartOffset + (lastMatch.index || 0);
1148
- const absoluteEndOffset = precedingStartOffset + matchEndIndex;
1149
- const codeBeforeStart = code.slice(0, absoluteStartOffset);
1150
- const startLines = codeBeforeStart.split("\n");
1151
- const startLine = startLines.length;
1152
- const startColumn = startLines[startLines.length - 1].length;
1153
- const codeBeforeEnd = code.slice(0, absoluteEndOffset);
1154
- const endLines = codeBeforeEnd.split("\n");
1155
- const endLine = endLines.length;
1156
- const endColumn = endLines[endLines.length - 1].length;
1157
- metadata.documentation = {
1158
- content: lastMatch[1].replace(/^\s*\*+/gm, "").trim(),
1159
- type: "jsdoc",
1160
- loc: {
1161
- start: { line: startLine, column: startColumn },
1162
- end: { line: endLine, column: endColumn }
1163
- }
1164
- };
1165
- }
1166
- }
1167
- const walk = (n) => {
1168
- if (!n) return;
1169
- if (n.type === "AssignmentExpression") {
1170
- metadata.isPure = false;
1171
- metadata.hasSideEffects = true;
1172
- }
1173
- if (n.type === "UpdateExpression") {
1174
- metadata.isPure = false;
1175
- metadata.hasSideEffects = true;
1176
- }
1177
- if (n.type === "CallExpression" && n.callee.type === "MemberExpression" && n.callee.object.type === "Identifier") {
1178
- const objName = n.callee.object.name;
1179
- if (["console", "process", "fs", "window", "document"].includes(objName)) {
1180
- metadata.isPure = false;
1181
- metadata.hasSideEffects = true;
1182
- }
1183
- }
1184
- if (n.type === "ThrowStatement") {
1185
- metadata.isPure = false;
1186
- metadata.hasSideEffects = true;
1187
- }
1188
- for (const key of Object.keys(n)) {
1189
- if (key === "parent") continue;
1190
- const child = n[key];
1191
- if (child && typeof child === "object") {
1192
- if (Array.isArray(child)) {
1193
- child.forEach((c) => c?.type && walk(c));
1194
- } else if (child.type) {
1195
- walk(child);
1196
- }
1197
- }
1198
- }
1199
- };
1200
- let nodeToAnalyze = node;
1201
- if (node.type === "ExportNamedDeclaration" && node.declaration) {
1202
- nodeToAnalyze = node.declaration;
1203
- } else if (node.type === "ExportDefaultDeclaration" && node.declaration) {
1204
- if (node.declaration.type !== "TSInterfaceDeclaration" && node.declaration.type !== "TSTypeAliasDeclaration") {
1205
- nodeToAnalyze = node.declaration;
1206
- }
1207
- }
1208
- if (nodeToAnalyze.type === "FunctionDeclaration" || nodeToAnalyze.type === "FunctionExpression" || nodeToAnalyze.type === "ArrowFunctionExpression") {
1209
- if (nodeToAnalyze.body) walk(nodeToAnalyze.body);
1210
- } else if (nodeToAnalyze.type === "ClassDeclaration" || nodeToAnalyze.type === "ClassExpression") {
1211
- 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
+ });
1212
1229
  }
1213
- return metadata;
1214
1230
  }
1215
1231
  parse(code, filePath) {
1216
1232
  try {
1217
- const isJavaScript = filePath.match(/\.jsx?$/i);
1218
1233
  const ast = (0, import_typescript_estree.parse)(code, {
1234
+ filePath,
1219
1235
  loc: true,
1220
1236
  range: true,
1221
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1222
- filePath,
1223
- sourceType: "module",
1224
- ecmaVersion: "latest",
1225
- comment: true
1237
+ tokens: true,
1238
+ comment: true,
1239
+ jsx: filePath.endsWith("x")
1226
1240
  });
1227
1241
  const imports = this.extractImports(ast);
1228
- const exports2 = this.extractExports(ast, imports, code);
1242
+ const exports2 = this.extractExports(ast, code);
1229
1243
  return {
1230
1244
  exports: exports2,
1231
1245
  imports,
1232
- language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
1233
- warnings: []
1246
+ language: this.language
1234
1247
  };
1235
1248
  } catch (error) {
1236
- const err = error;
1237
- throw new ParseError(
1238
- `Failed to parse ${filePath}: ${err.message}`,
1239
- filePath
1240
- );
1249
+ throw new ParseError(error.message, filePath, {
1250
+ line: error.lineNumber || 1,
1251
+ column: error.column || 0
1252
+ });
1241
1253
  }
1242
1254
  }
1243
1255
  getNamingConventions() {
1244
1256
  return {
1245
- // camelCase for variables and functions
1246
1257
  variablePattern: /^[a-z][a-zA-Z0-9]*$/,
1247
1258
  functionPattern: /^[a-z][a-zA-Z0-9]*$/,
1248
- // PascalCase for classes, types and interfaces
1249
1259
  classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1250
- typePattern: /^[A-Z][a-zA-Z0-9]*$/,
1251
- interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
1252
- // UPPER_CASE for constants
1253
1260
  constantPattern: /^[A-Z][A-Z0-9_]*$/,
1254
- // Common exceptions (React hooks, etc.)
1255
- exceptions: ["__filename", "__dirname", "__esModule"]
1261
+ typePattern: /^[A-Z][a-zA-Z0-9]*$/,
1262
+ interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
1256
1263
  };
1257
1264
  }
1258
- canHandle(filePath) {
1259
- 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
+ };
1260
1271
  }
1261
1272
  extractImports(ast) {
1262
1273
  const imports = [];
@@ -1294,168 +1305,165 @@ var TypeScriptParser = class {
1294
1305
  }
1295
1306
  return imports;
1296
1307
  }
1297
- extractExports(ast, imports, code) {
1308
+ extractExports(ast, code) {
1298
1309
  const exports2 = [];
1299
- const importedNames = new Set(
1300
- imports.flatMap(
1301
- (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1302
- )
1303
- );
1304
1310
  for (const node of ast.body) {
1305
- if (node.type === "ExportNamedDeclaration" && node.declaration) {
1306
- const extracted = this.extractFromDeclaration(
1307
- node.declaration,
1308
- importedNames,
1309
- code,
1310
- node
1311
- // Pass the ExportNamedDeclaration as parent for metadata
1312
- );
1313
- exports2.push(...extracted);
1314
- } else if (node.type === "ExportDefaultDeclaration") {
1315
- const metadata = this.analyzeMetadata(node, code);
1316
- let name = "default";
1317
- let type = "default";
1318
- if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
1319
- name = node.declaration.id.name;
1320
- type = "function";
1321
- } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
1322
- name = node.declaration.id.name;
1323
- 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
+ }
1324
1370
  }
1325
- exports2.push({
1326
- name,
1327
- type,
1328
- loc: node.loc ? {
1329
- start: {
1330
- line: node.loc.start.line,
1331
- column: node.loc.start.column
1332
- },
1333
- end: { line: node.loc.end.line, column: node.loc.end.column }
1334
- } : void 0,
1335
- ...metadata
1336
- });
1371
+ } else if (node.type === "ExportDefaultDeclaration") {
1372
+ exports2.push(this.createExport("default", "default", node, code));
1337
1373
  }
1338
1374
  }
1339
1375
  return exports2;
1340
1376
  }
1341
- extractFromDeclaration(declaration, importedNames, code, parentNode) {
1342
- const exports2 = [];
1343
- const metadata = this.analyzeMetadata(parentNode || declaration, code);
1344
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
1345
- exports2.push({
1346
- name: declaration.id.name,
1347
- type: "function",
1348
- 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) => {
1349
1416
  if (p.type === "Identifier") return p.name;
1350
- if (p.type === "AssignmentPattern" && p.left.type === "Identifier")
1351
- return p.left.name;
1352
- if (p.type === "RestElement" && p.argument.type === "Identifier")
1353
- return p.argument.name;
1354
- return "unknown";
1355
- }),
1356
- loc: declaration.loc ? {
1357
- start: {
1358
- line: declaration.loc.start.line,
1359
- column: declaration.loc.start.column
1360
- },
1361
- end: {
1362
- line: declaration.loc.end.line,
1363
- column: declaration.loc.end.column
1364
- }
1365
- } : void 0,
1366
- ...metadata
1367
- });
1368
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
1369
- const body = declaration.body.body;
1370
- const methods = body.filter((m) => m.type === "MethodDefinition");
1371
- const properties = body.filter((m) => m.type === "PropertyDefinition");
1372
- const constructor = methods.find(
1373
- (m) => m.kind === "constructor"
1374
- );
1375
- const parameters = constructor ? constructor.value.params.map((p) => {
1376
- if (p.type === "Identifier") return p.name;
1377
- if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier")
1378
- return p.parameter.name;
1379
- return "unknown";
1380
- }) : [];
1381
- exports2.push({
1382
- name: declaration.id.name,
1383
- type: "class",
1384
- methodCount: methods.length,
1385
- propertyCount: properties.length,
1386
- parameters,
1387
- loc: declaration.loc ? {
1388
- start: {
1389
- line: declaration.loc.start.line,
1390
- column: declaration.loc.start.column
1391
- },
1392
- end: {
1393
- line: declaration.loc.end.line,
1394
- column: declaration.loc.end.column
1395
- }
1396
- } : void 0,
1397
- ...metadata
1398
- });
1399
- } else if (declaration.type === "VariableDeclaration") {
1400
- for (const declarator of declaration.declarations) {
1401
- if (declarator.id.type === "Identifier") {
1402
- exports2.push({
1403
- name: declarator.id.name,
1404
- type: "const",
1405
- loc: declarator.loc ? {
1406
- start: {
1407
- line: declarator.loc.start.line,
1408
- column: declarator.loc.start.column
1409
- },
1410
- end: {
1411
- line: declarator.loc.end.line,
1412
- column: declarator.loc.end.column
1413
- }
1414
- } : void 0,
1415
- ...metadata
1416
- });
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;
1417
1461
  }
1462
+ return true;
1418
1463
  }
1419
- } else if (declaration.type === "TSTypeAliasDeclaration") {
1420
- exports2.push({
1421
- name: declaration.id.name,
1422
- type: "type",
1423
- loc: declaration.loc ? {
1424
- start: {
1425
- line: declaration.loc.start.line,
1426
- column: declaration.loc.start.column
1427
- },
1428
- end: {
1429
- line: declaration.loc.end.line,
1430
- column: declaration.loc.end.column
1431
- }
1432
- } : void 0,
1433
- ...metadata
1434
- });
1435
- } else if (declaration.type === "TSInterfaceDeclaration") {
1436
- const body = declaration.body.body;
1437
- const methods = body.filter((m) => m.type === "TSMethodSignature");
1438
- const properties = body.filter((m) => m.type === "TSPropertySignature");
1439
- exports2.push({
1440
- name: declaration.id.name,
1441
- type: "interface",
1442
- methodCount: methods.length,
1443
- propertyCount: properties.length || body.length,
1444
- // Fallback to body.length
1445
- loc: declaration.loc ? {
1446
- start: {
1447
- line: declaration.loc.start.line,
1448
- column: declaration.loc.start.column
1449
- },
1450
- end: {
1451
- line: declaration.loc.end.line,
1452
- column: declaration.loc.end.column
1453
- }
1454
- } : void 0,
1455
- ...metadata
1456
- });
1464
+ return true;
1457
1465
  }
1458
- return exports2;
1466
+ return false;
1459
1467
  }
1460
1468
  };
1461
1469
 
@@ -1745,7 +1753,7 @@ var PythonParser = class extends BaseLanguageParser {
1745
1753
  * Extract import information using AST walk.
1746
1754
  *
1747
1755
  * @param rootNode - Root node of the Python AST.
1748
- * @returns Array of discovered ImportInfo objects.
1756
+ * @returns Array of discovered FileImport objects.
1749
1757
  */
1750
1758
  extractImportsAST(rootNode) {
1751
1759
  const imports = [];
@@ -2302,7 +2310,7 @@ var JavaParser = class extends BaseLanguageParser {
2302
2310
  * Extract import information using AST walk.
2303
2311
  *
2304
2312
  * @param rootNode - Root node of the Java AST.
2305
- * @returns Array of discovered ImportInfo objects.
2313
+ * @returns Array of discovered FileImport objects.
2306
2314
  */
2307
2315
  extractImportsAST(rootNode) {
2308
2316
  const imports = [];
@@ -2534,7 +2542,7 @@ var CSharpParser = class extends BaseLanguageParser {
2534
2542
  * Extract import information (usings) using AST walk.
2535
2543
  *
2536
2544
  * @param rootNode - Root node of the C# AST.
2537
- * @returns Array of discovered ImportInfo objects.
2545
+ * @returns Array of discovered FileImport objects.
2538
2546
  */
2539
2547
  extractImportsAST(rootNode) {
2540
2548
  const imports = [];
@@ -2787,7 +2795,7 @@ var GoParser = class extends BaseLanguageParser {
2787
2795
  * Extract import information using AST walk.
2788
2796
  *
2789
2797
  * @param rootNode - Root node of the Go AST.
2790
- * @returns Array of discovered ImportInfo objects.
2798
+ * @returns Array of discovered FileImport objects.
2791
2799
  */
2792
2800
  extractImportsAST(rootNode) {
2793
2801
  const imports = [];
@@ -3275,10 +3283,25 @@ async function loadConfig(rootDir) {
3275
3283
  const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
3276
3284
  config = JSON.parse(content);
3277
3285
  }
3278
- if (typeof config !== "object" || config === null) {
3279
- 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
+ );
3280
3303
  }
3281
- return config;
3304
+ return AIReadyConfigSchema.parse(config);
3282
3305
  } catch (error) {
3283
3306
  const errorMessage = error instanceof Error ? error.message : String(error);
3284
3307
  const configError = new Error(
@@ -3306,12 +3329,10 @@ function mergeConfigWithDefaults(userConfig, defaults) {
3306
3329
  if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
3307
3330
  if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
3308
3331
  }
3309
- const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
3310
- if (toolOverrides) {
3332
+ if (userConfig.tools) {
3311
3333
  if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
3312
- for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
3334
+ for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
3313
3335
  if (typeof toolConfig === "object" && toolConfig !== null) {
3314
- mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
3315
3336
  mergedConfig.toolConfigs[toolName] = {
3316
3337
  ...mergedConfig.toolConfigs[toolName],
3317
3338
  ...toolConfig
@@ -3613,12 +3634,12 @@ function getRecommendedThreshold(fileCount, modelTier = "standard") {
3613
3634
  return base + modelBonus;
3614
3635
  }
3615
3636
  function normalizeToolName(shortName) {
3616
- return TOOL_NAME_MAP[shortName.toLowerCase()] || shortName;
3637
+ return TOOL_NAME_MAP[shortName.toLowerCase()] ?? shortName;
3617
3638
  }
3618
3639
  function getToolWeight(toolName, toolConfig, cliOverride, profile = "default" /* Default */) {
3619
3640
  if (cliOverride !== void 0) return cliOverride;
3620
3641
  if (toolConfig?.scoreWeight !== void 0) return toolConfig.scoreWeight;
3621
- const profileWeights = SCORING_PROFILES[profile] || DEFAULT_TOOL_WEIGHTS;
3642
+ const profileWeights = SCORING_PROFILES[profile] ?? DEFAULT_TOOL_WEIGHTS;
3622
3643
  return profileWeights[toolName] ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 5;
3623
3644
  }
3624
3645
  function parseWeightString(weightStr) {
@@ -3655,7 +3676,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3655
3676
  const toolsUsed = [];
3656
3677
  const calculationWeights = {};
3657
3678
  for (const [toolName, output] of toolOutputs.entries()) {
3658
- const weight = weights.get(toolName) || 5;
3679
+ const weight = weights.get(toolName) ?? 5;
3659
3680
  weightedSum += output.score * weight;
3660
3681
  totalWeight += weight;
3661
3682
  toolsUsed.push(toolName);
@@ -3666,7 +3687,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3666
3687
  const rating = getRating(overall);
3667
3688
  const formulaParts = Array.from(toolOutputs.entries()).map(
3668
3689
  ([name, output]) => {
3669
- const w = weights.get(name) || 5;
3690
+ const w = weights.get(name) ?? 5;
3670
3691
  return `(${output.score} \xD7 ${w})`;
3671
3692
  }
3672
3693
  );
@@ -3759,6 +3780,13 @@ function formatToolScore(output) {
3759
3780
 
3760
3781
  // src/business/pricing-models.ts
3761
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
+ },
3762
3790
  "gpt-5.3": {
3763
3791
  name: "GPT-5.3",
3764
3792
  pricePer1KInputTokens: 2e-3,
@@ -3780,20 +3808,6 @@ var MODEL_PRICING_PRESETS = {
3780
3808
  contextTier: "frontier",
3781
3809
  typicalQueriesPerDevPerDay: 120
3782
3810
  },
3783
- "gpt-4o": {
3784
- name: "GPT-4o (legacy)",
3785
- pricePer1KInputTokens: 5e-3,
3786
- pricePer1KOutputTokens: 0.015,
3787
- contextTier: "extended",
3788
- typicalQueriesPerDevPerDay: 60
3789
- },
3790
- "claude-3-5-sonnet": {
3791
- name: "Claude 3.5 Sonnet (legacy)",
3792
- pricePer1KInputTokens: 3e-3,
3793
- pricePer1KOutputTokens: 0.015,
3794
- contextTier: "extended",
3795
- typicalQueriesPerDevPerDay: 80
3796
- },
3797
3811
  "gemini-1-5-pro": {
3798
3812
  name: "Gemini 1.5 Pro (legacy)",
3799
3813
  pricePer1KInputTokens: 125e-5,
@@ -3817,7 +3831,7 @@ var MODEL_PRICING_PRESETS = {
3817
3831
  }
3818
3832
  };
3819
3833
  function getModelPreset(modelId) {
3820
- return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
3834
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-5.4-mini"];
3821
3835
  }
3822
3836
 
3823
3837
  // src/business/cost-metrics.ts
@@ -3838,7 +3852,7 @@ function calculateMonthlyCost(tokenWaste, config = {}) {
3838
3852
  // Added baseline chattiness
3839
3853
  }
3840
3854
  });
3841
- const preset = getModelPreset("claude-3.5-sonnet");
3855
+ const preset = getModelPreset("gpt-5.4-mini");
3842
3856
  return estimateCostFromBudget(budget, preset, config);
3843
3857
  }
3844
3858
  function calculateTokenBudget(params) {
@@ -4684,12 +4698,12 @@ function calculateTestabilityIndex(params) {
4684
4698
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
4685
4699
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
4686
4700
  const purityScore = Math.round(
4687
- (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
4701
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
4688
4702
  );
4689
4703
  const dependencyInjectionScore = Math.round(
4690
4704
  Math.min(
4691
4705
  100,
4692
- (totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
4706
+ (totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
4693
4707
  )
4694
4708
  );
4695
4709
  const interfaceFocusScore = Math.max(
@@ -4770,9 +4784,9 @@ function calculateDocDrift(params) {
4770
4784
  const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
4771
4785
  const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
4772
4786
  const driftRatio = totalExports > 0 ? actualDrift / totalExports : 0;
4773
- const DRIFT_THRESHOLD = 0.2;
4774
- const OUTDATED_THRESHOLD = 0.4;
4775
- const COMPLEXITY_THRESHOLD = 0.2;
4787
+ const DRIFT_THRESHOLD = 0.35;
4788
+ const OUTDATED_THRESHOLD = 0.5;
4789
+ const COMPLEXITY_THRESHOLD = 0.3;
4776
4790
  const UNCOMMENTED_THRESHOLD = 0.8;
4777
4791
  const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
4778
4792
  const outdatedRisk = Math.min(
@@ -5238,6 +5252,7 @@ function emitIssuesAsAnnotations(issues) {
5238
5252
  }
5239
5253
  // Annotate the CommonJS export names for ESM import in node:
5240
5254
  0 && (module.exports = {
5255
+ AIReadyConfigSchema,
5241
5256
  AnalysisResultSchema,
5242
5257
  AnalysisStatus,
5243
5258
  AnalysisStatusSchema,
@@ -5363,6 +5378,10 @@ function emitIssuesAsAnnotations(issues) {
5363
5378
  loadMergedConfig,
5364
5379
  loadScoreHistory,
5365
5380
  mergeConfigWithDefaults,
5381
+ normalizeAnalysisResult,
5382
+ normalizeIssue,
5383
+ normalizeMetrics,
5384
+ normalizeSpokeOutput,
5366
5385
  normalizeToolName,
5367
5386
  parseFileExports,
5368
5387
  parseWeightString,