@aiready/core 0.23.7 → 0.23.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,52 @@ 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
+ /** Visualizer settings (interactive graph) */
377
+ visualizer: import_zod.z.object({
378
+ groupingDirs: import_zod.z.array(import_zod.z.string()).optional(),
379
+ graph: import_zod.z.object({
380
+ maxNodes: import_zod.z.number().optional(),
381
+ maxEdges: import_zod.z.number().optional()
382
+ }).optional()
383
+ }).optional()
384
+ }).catchall(import_zod.z.any());
334
385
 
335
386
  // src/types/business.ts
336
387
  var import_zod2 = require("zod");
@@ -447,51 +498,100 @@ var ParseError = class extends Error {
447
498
  }
448
499
  };
449
500
 
501
+ // src/utils/normalization.ts
502
+ function normalizeIssue(raw) {
503
+ return {
504
+ type: raw.type ?? "pattern-inconsistency" /* PatternInconsistency */,
505
+ severity: raw.severity ?? raw.severityLevel ?? "info" /* Info */,
506
+ message: raw.message ?? "Unknown issue",
507
+ location: raw.location ?? {
508
+ file: raw.fileName ?? raw.file ?? raw.filePath ?? "unknown",
509
+ line: raw.line ?? 1,
510
+ column: raw.column
511
+ },
512
+ suggestion: raw.suggestion
513
+ };
514
+ }
515
+ function normalizeMetrics(raw) {
516
+ return {
517
+ tokenCost: raw.tokenCost ?? 0,
518
+ complexityScore: raw.complexityScore ?? 0,
519
+ consistencyScore: raw.consistencyScore,
520
+ docFreshnessScore: raw.docFreshnessScore,
521
+ aiSignalClarityScore: raw.aiSignalClarityScore,
522
+ agentGroundingScore: raw.agentGroundingScore,
523
+ testabilityScore: raw.testabilityScore,
524
+ docDriftScore: raw.docDriftScore,
525
+ dependencyHealthScore: raw.dependencyHealthScore,
526
+ modelContextTier: raw.modelContextTier,
527
+ estimatedMonthlyCost: raw.estimatedMonthlyCost,
528
+ estimatedDeveloperHours: raw.estimatedDeveloperHours,
529
+ comprehensionDifficultyIndex: raw.comprehensionDifficultyIndex,
530
+ totalSymbols: raw.totalSymbols,
531
+ totalExports: raw.totalExports
532
+ };
533
+ }
534
+ function normalizeAnalysisResult(raw) {
535
+ const fileName = raw.fileName ?? raw.file ?? raw.filePath ?? "unknown";
536
+ const rawIssues = Array.isArray(raw.issues) ? raw.issues : [];
537
+ return {
538
+ fileName,
539
+ issues: rawIssues.map((issue) => {
540
+ if (typeof issue === "string") {
541
+ return {
542
+ type: "pattern-inconsistency" /* PatternInconsistency */,
543
+ // Default fallback
544
+ severity: raw.severity ?? "info" /* Info */,
545
+ message: issue,
546
+ location: { file: fileName, line: 1 }
547
+ };
548
+ }
549
+ return normalizeIssue({
550
+ ...issue,
551
+ fileName: issue.fileName ?? fileName,
552
+ severity: issue.severity ?? raw.severity
553
+ });
554
+ }),
555
+ metrics: normalizeMetrics(raw.metrics ?? {})
556
+ };
557
+ }
558
+ function normalizeSpokeOutput(raw, toolName) {
559
+ const rawResults = Array.isArray(raw.results) ? raw.results : [];
560
+ return {
561
+ results: rawResults.map(normalizeAnalysisResult),
562
+ summary: raw.summary ?? {
563
+ totalFiles: rawResults.length,
564
+ totalIssues: 0,
565
+ criticalIssues: 0,
566
+ majorIssues: 0
567
+ },
568
+ metadata: {
569
+ toolName: raw.metadata?.toolName ?? toolName,
570
+ version: raw.metadata?.version,
571
+ timestamp: raw.metadata?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
572
+ config: raw.metadata?.config
573
+ }
574
+ };
575
+ }
576
+
450
577
  // src/types/contract.ts
451
578
  function validateSpokeOutput(toolName, output) {
452
- const errors = [];
453
579
  if (!output) {
454
580
  return { valid: false, errors: ["Output is null or undefined"] };
455
581
  }
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
582
  if (!output.summary) {
490
- errors.push(`${toolName}: missing 'summary'`);
583
+ return { valid: false, errors: [`${toolName}: missing 'summary'`] };
584
+ }
585
+ const normalized = normalizeSpokeOutput(output, toolName);
586
+ const result = SpokeOutputSchema.safeParse(normalized);
587
+ if (result.success) {
588
+ return { valid: true, errors: [] };
491
589
  }
492
590
  return {
493
- valid: errors.length === 0,
494
- errors
591
+ valid: false,
592
+ errors: result.error.issues.map(
593
+ (e) => `${toolName}: ${e.path.join(".")}: ${e.message}`
594
+ )
495
595
  };
496
596
  }
497
597
  function validateWithSchema(schema, data) {
@@ -1115,148 +1215,67 @@ var TypeScriptParser = class {
1115
1215
  this.extensions = [".ts", ".tsx", ".js", ".jsx"];
1116
1216
  }
1117
1217
  async initialize() {
1118
- return Promise.resolve();
1119
1218
  }
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
- });
1219
+ canHandle(filePath) {
1220
+ return this.extensions.some((ext) => filePath.endsWith(ext));
1130
1221
  }
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);
1222
+ async getAST(code, filePath) {
1223
+ try {
1224
+ return (0, import_typescript_estree.parse)(code, {
1225
+ filePath,
1226
+ loc: true,
1227
+ range: true,
1228
+ tokens: true,
1229
+ comment: true,
1230
+ jsx: filePath.endsWith("x")
1231
+ });
1232
+ } catch (error) {
1233
+ throw new ParseError(error.message, filePath, {
1234
+ line: error.lineNumber || 1,
1235
+ column: error.column || 0
1236
+ });
1212
1237
  }
1213
- return metadata;
1214
1238
  }
1215
1239
  parse(code, filePath) {
1216
1240
  try {
1217
- const isJavaScript = filePath.match(/\.jsx?$/i);
1218
1241
  const ast = (0, import_typescript_estree.parse)(code, {
1242
+ filePath,
1219
1243
  loc: true,
1220
1244
  range: true,
1221
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1222
- filePath,
1223
- sourceType: "module",
1224
- ecmaVersion: "latest",
1225
- comment: true
1245
+ tokens: true,
1246
+ comment: true,
1247
+ jsx: filePath.endsWith("x")
1226
1248
  });
1227
1249
  const imports = this.extractImports(ast);
1228
- const exports2 = this.extractExports(ast, imports, code);
1250
+ const exports2 = this.extractExports(ast, code);
1229
1251
  return {
1230
1252
  exports: exports2,
1231
1253
  imports,
1232
- language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
1233
- warnings: []
1254
+ language: this.language
1234
1255
  };
1235
1256
  } catch (error) {
1236
- const err = error;
1237
- throw new ParseError(
1238
- `Failed to parse ${filePath}: ${err.message}`,
1239
- filePath
1240
- );
1257
+ throw new ParseError(error.message, filePath, {
1258
+ line: error.lineNumber || 1,
1259
+ column: error.column || 0
1260
+ });
1241
1261
  }
1242
1262
  }
1243
1263
  getNamingConventions() {
1244
1264
  return {
1245
- // camelCase for variables and functions
1246
1265
  variablePattern: /^[a-z][a-zA-Z0-9]*$/,
1247
1266
  functionPattern: /^[a-z][a-zA-Z0-9]*$/,
1248
- // PascalCase for classes, types and interfaces
1249
1267
  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
1268
  constantPattern: /^[A-Z][A-Z0-9_]*$/,
1254
- // Common exceptions (React hooks, etc.)
1255
- exceptions: ["__filename", "__dirname", "__esModule"]
1269
+ typePattern: /^[A-Z][a-zA-Z0-9]*$/,
1270
+ interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
1256
1271
  };
1257
1272
  }
1258
- canHandle(filePath) {
1259
- return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
1273
+ analyzeMetadata(node, code) {
1274
+ if (!code) return {};
1275
+ return {
1276
+ isPure: this.isLikelyPure(node),
1277
+ hasSideEffects: !this.isLikelyPure(node)
1278
+ };
1260
1279
  }
1261
1280
  extractImports(ast) {
1262
1281
  const imports = [];
@@ -1294,168 +1313,165 @@ var TypeScriptParser = class {
1294
1313
  }
1295
1314
  return imports;
1296
1315
  }
1297
- extractExports(ast, imports, code) {
1316
+ extractExports(ast, code) {
1298
1317
  const exports2 = [];
1299
- const importedNames = new Set(
1300
- imports.flatMap(
1301
- (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1302
- )
1303
- );
1304
1318
  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";
1319
+ if (node.type === "ExportNamedDeclaration") {
1320
+ if (node.declaration) {
1321
+ const declaration = node.declaration;
1322
+ if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
1323
+ exports2.push(
1324
+ this.createExport(
1325
+ declaration.id.name,
1326
+ "function",
1327
+ node,
1328
+ // Pass the outer ExportNamedDeclaration
1329
+ code
1330
+ )
1331
+ );
1332
+ } else if (declaration.type === "ClassDeclaration" && declaration.id) {
1333
+ exports2.push(
1334
+ this.createExport(
1335
+ declaration.id.name,
1336
+ "class",
1337
+ node,
1338
+ // Pass the outer ExportNamedDeclaration
1339
+ code
1340
+ )
1341
+ );
1342
+ } else if (declaration.type === "TSTypeAliasDeclaration") {
1343
+ exports2.push(
1344
+ this.createExport(
1345
+ declaration.id.name,
1346
+ "type",
1347
+ node,
1348
+ // Pass the outer ExportNamedDeclaration
1349
+ code
1350
+ )
1351
+ );
1352
+ } else if (declaration.type === "TSInterfaceDeclaration") {
1353
+ exports2.push(
1354
+ this.createExport(
1355
+ declaration.id.name,
1356
+ "interface",
1357
+ node,
1358
+ // Pass the outer ExportNamedDeclaration
1359
+ code
1360
+ )
1361
+ );
1362
+ } else if (declaration.type === "VariableDeclaration") {
1363
+ for (const decl of declaration.declarations) {
1364
+ if (decl.id.type === "Identifier") {
1365
+ exports2.push(
1366
+ this.createExport(
1367
+ decl.id.name,
1368
+ "const",
1369
+ node,
1370
+ // Pass the outer ExportNamedDeclaration
1371
+ code,
1372
+ decl.init
1373
+ )
1374
+ );
1375
+ }
1376
+ }
1377
+ }
1324
1378
  }
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
- });
1379
+ } else if (node.type === "ExportDefaultDeclaration") {
1380
+ exports2.push(this.createExport("default", "default", node, code));
1337
1381
  }
1338
1382
  }
1339
1383
  return exports2;
1340
1384
  }
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) => {
1385
+ createExport(name, type, node, code, initializer) {
1386
+ const documentation = this.extractDocumentation(node, code);
1387
+ let methodCount;
1388
+ let propertyCount;
1389
+ let parameters;
1390
+ let isPrimitive = false;
1391
+ if (initializer) {
1392
+ if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
1393
+ isPrimitive = true;
1394
+ }
1395
+ }
1396
+ const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1397
+ if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
1398
+ const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
1399
+ methodCount = body.filter(
1400
+ (m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
1401
+ ).length;
1402
+ propertyCount = body.filter(
1403
+ (m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
1404
+ ).length;
1405
+ if (structNode.type === "ClassDeclaration") {
1406
+ const constructor = body.find(
1407
+ (m) => m.type === "MethodDefinition" && m.kind === "constructor"
1408
+ );
1409
+ if (constructor && constructor.value && constructor.value.params) {
1410
+ parameters = constructor.value.params.map((p) => {
1411
+ if (p.type === "Identifier") return p.name;
1412
+ if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
1413
+ return p.parameter.name;
1414
+ }
1415
+ return void 0;
1416
+ }).filter(Boolean);
1417
+ }
1418
+ }
1419
+ }
1420
+ if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
1421
+ const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
1422
+ if (funcNode && funcNode.params) {
1423
+ parameters = funcNode.params.map((p) => {
1349
1424
  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
- });
1425
+ return void 0;
1426
+ }).filter(Boolean);
1427
+ }
1428
+ }
1429
+ return {
1430
+ name,
1431
+ type,
1432
+ isPrimitive,
1433
+ loc: node.loc ? {
1434
+ start: { line: node.loc.start.line, column: node.loc.start.column },
1435
+ end: { line: node.loc.end.line, column: node.loc.end.column }
1436
+ } : void 0,
1437
+ documentation,
1438
+ methodCount,
1439
+ propertyCount,
1440
+ parameters,
1441
+ isPure: this.isLikelyPure(node),
1442
+ hasSideEffects: !this.isLikelyPure(node)
1443
+ };
1444
+ }
1445
+ extractDocumentation(node, code) {
1446
+ if (node.range) {
1447
+ const start = node.range[0];
1448
+ const precedingCode = code.substring(0, start);
1449
+ const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1450
+ if (jsdocMatch) {
1451
+ return {
1452
+ content: jsdocMatch[1].trim(),
1453
+ type: "jsdoc"
1454
+ };
1455
+ }
1456
+ }
1457
+ return void 0;
1458
+ }
1459
+ isLikelyPure(node) {
1460
+ const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1461
+ if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
1462
+ return true;
1463
+ if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
1464
+ const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
1465
+ if (body && body.type === "BlockStatement") {
1466
+ const bodyContent = JSON.stringify(body);
1467
+ if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
1468
+ return false;
1417
1469
  }
1470
+ return true;
1418
1471
  }
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
- });
1472
+ return true;
1457
1473
  }
1458
- return exports2;
1474
+ return false;
1459
1475
  }
1460
1476
  };
1461
1477
 
@@ -1745,7 +1761,7 @@ var PythonParser = class extends BaseLanguageParser {
1745
1761
  * Extract import information using AST walk.
1746
1762
  *
1747
1763
  * @param rootNode - Root node of the Python AST.
1748
- * @returns Array of discovered ImportInfo objects.
1764
+ * @returns Array of discovered FileImport objects.
1749
1765
  */
1750
1766
  extractImportsAST(rootNode) {
1751
1767
  const imports = [];
@@ -2302,7 +2318,7 @@ var JavaParser = class extends BaseLanguageParser {
2302
2318
  * Extract import information using AST walk.
2303
2319
  *
2304
2320
  * @param rootNode - Root node of the Java AST.
2305
- * @returns Array of discovered ImportInfo objects.
2321
+ * @returns Array of discovered FileImport objects.
2306
2322
  */
2307
2323
  extractImportsAST(rootNode) {
2308
2324
  const imports = [];
@@ -2534,7 +2550,7 @@ var CSharpParser = class extends BaseLanguageParser {
2534
2550
  * Extract import information (usings) using AST walk.
2535
2551
  *
2536
2552
  * @param rootNode - Root node of the C# AST.
2537
- * @returns Array of discovered ImportInfo objects.
2553
+ * @returns Array of discovered FileImport objects.
2538
2554
  */
2539
2555
  extractImportsAST(rootNode) {
2540
2556
  const imports = [];
@@ -2787,7 +2803,7 @@ var GoParser = class extends BaseLanguageParser {
2787
2803
  * Extract import information using AST walk.
2788
2804
  *
2789
2805
  * @param rootNode - Root node of the Go AST.
2790
- * @returns Array of discovered ImportInfo objects.
2806
+ * @returns Array of discovered FileImport objects.
2791
2807
  */
2792
2808
  extractImportsAST(rootNode) {
2793
2809
  const imports = [];
@@ -3275,10 +3291,25 @@ async function loadConfig(rootDir) {
3275
3291
  const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
3276
3292
  config = JSON.parse(content);
3277
3293
  }
3278
- if (typeof config !== "object" || config === null) {
3279
- throw new Error("Config must be an object");
3294
+ const legacyKeys = ["toolConfigs"];
3295
+ const rootLevelTools = [
3296
+ "pattern-detect",
3297
+ "context-analyzer",
3298
+ "naming-consistency",
3299
+ "ai-signal-clarity"
3300
+ ];
3301
+ const allKeys = Object.keys(config);
3302
+ const foundLegacy = allKeys.filter(
3303
+ (k) => legacyKeys.includes(k) || rootLevelTools.includes(k)
3304
+ );
3305
+ if (foundLegacy.length > 0) {
3306
+ console.warn(
3307
+ `\u26A0\uFE0F Legacy configuration keys found: ${foundLegacy.join(
3308
+ ", "
3309
+ )}. These are deprecated and should be moved under the "tools" key.`
3310
+ );
3280
3311
  }
3281
- return config;
3312
+ return AIReadyConfigSchema.parse(config);
3282
3313
  } catch (error) {
3283
3314
  const errorMessage = error instanceof Error ? error.message : String(error);
3284
3315
  const configError = new Error(
@@ -3306,12 +3337,10 @@ function mergeConfigWithDefaults(userConfig, defaults) {
3306
3337
  if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
3307
3338
  if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
3308
3339
  }
3309
- const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
3310
- if (toolOverrides) {
3340
+ if (userConfig.tools) {
3311
3341
  if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
3312
- for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
3342
+ for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
3313
3343
  if (typeof toolConfig === "object" && toolConfig !== null) {
3314
- mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
3315
3344
  mergedConfig.toolConfigs[toolName] = {
3316
3345
  ...mergedConfig.toolConfigs[toolName],
3317
3346
  ...toolConfig
@@ -3613,12 +3642,12 @@ function getRecommendedThreshold(fileCount, modelTier = "standard") {
3613
3642
  return base + modelBonus;
3614
3643
  }
3615
3644
  function normalizeToolName(shortName) {
3616
- return TOOL_NAME_MAP[shortName.toLowerCase()] || shortName;
3645
+ return TOOL_NAME_MAP[shortName.toLowerCase()] ?? shortName;
3617
3646
  }
3618
3647
  function getToolWeight(toolName, toolConfig, cliOverride, profile = "default" /* Default */) {
3619
3648
  if (cliOverride !== void 0) return cliOverride;
3620
3649
  if (toolConfig?.scoreWeight !== void 0) return toolConfig.scoreWeight;
3621
- const profileWeights = SCORING_PROFILES[profile] || DEFAULT_TOOL_WEIGHTS;
3650
+ const profileWeights = SCORING_PROFILES[profile] ?? DEFAULT_TOOL_WEIGHTS;
3622
3651
  return profileWeights[toolName] ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 5;
3623
3652
  }
3624
3653
  function parseWeightString(weightStr) {
@@ -3655,7 +3684,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3655
3684
  const toolsUsed = [];
3656
3685
  const calculationWeights = {};
3657
3686
  for (const [toolName, output] of toolOutputs.entries()) {
3658
- const weight = weights.get(toolName) || 5;
3687
+ const weight = weights.get(toolName) ?? 5;
3659
3688
  weightedSum += output.score * weight;
3660
3689
  totalWeight += weight;
3661
3690
  toolsUsed.push(toolName);
@@ -3666,7 +3695,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3666
3695
  const rating = getRating(overall);
3667
3696
  const formulaParts = Array.from(toolOutputs.entries()).map(
3668
3697
  ([name, output]) => {
3669
- const w = weights.get(name) || 5;
3698
+ const w = weights.get(name) ?? 5;
3670
3699
  return `(${output.score} \xD7 ${w})`;
3671
3700
  }
3672
3701
  );
@@ -3759,6 +3788,13 @@ function formatToolScore(output) {
3759
3788
 
3760
3789
  // src/business/pricing-models.ts
3761
3790
  var MODEL_PRICING_PRESETS = {
3791
+ "gpt-5.4-mini": {
3792
+ name: "GPT-5.4 Mini",
3793
+ pricePer1KInputTokens: 1e-4,
3794
+ pricePer1KOutputTokens: 4e-4,
3795
+ contextTier: "extended",
3796
+ typicalQueriesPerDevPerDay: 200
3797
+ },
3762
3798
  "gpt-5.3": {
3763
3799
  name: "GPT-5.3",
3764
3800
  pricePer1KInputTokens: 2e-3,
@@ -3780,20 +3816,6 @@ var MODEL_PRICING_PRESETS = {
3780
3816
  contextTier: "frontier",
3781
3817
  typicalQueriesPerDevPerDay: 120
3782
3818
  },
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
3819
  "gemini-1-5-pro": {
3798
3820
  name: "Gemini 1.5 Pro (legacy)",
3799
3821
  pricePer1KInputTokens: 125e-5,
@@ -3817,7 +3839,7 @@ var MODEL_PRICING_PRESETS = {
3817
3839
  }
3818
3840
  };
3819
3841
  function getModelPreset(modelId) {
3820
- return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
3842
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-5.4-mini"];
3821
3843
  }
3822
3844
 
3823
3845
  // src/business/cost-metrics.ts
@@ -3838,7 +3860,7 @@ function calculateMonthlyCost(tokenWaste, config = {}) {
3838
3860
  // Added baseline chattiness
3839
3861
  }
3840
3862
  });
3841
- const preset = getModelPreset("claude-3.5-sonnet");
3863
+ const preset = getModelPreset("gpt-5.4-mini");
3842
3864
  return estimateCostFromBudget(budget, preset, config);
3843
3865
  }
3844
3866
  function calculateTokenBudget(params) {
@@ -4668,6 +4690,40 @@ function calculateAgentGrounding(params) {
4668
4690
  }
4669
4691
 
4670
4692
  // src/metrics/testability-index.ts
4693
+ function isLikelyEntryPoint(filePath) {
4694
+ const basename = filePath.split("/").pop() || "";
4695
+ const lowerBasename = basename.toLowerCase();
4696
+ const entryPointPatterns = [
4697
+ "cli",
4698
+ "main",
4699
+ "bin",
4700
+ "index",
4701
+ // often used as entry point
4702
+ "run",
4703
+ "serve",
4704
+ "start",
4705
+ "boot",
4706
+ "init"
4707
+ ];
4708
+ const nameWithoutExt = lowerBasename.replace(
4709
+ /\.(ts|js|tsx|jsx|mjs|cjs)$/,
4710
+ ""
4711
+ );
4712
+ if (entryPointPatterns.some(
4713
+ (p) => nameWithoutExt === p || nameWithoutExt.endsWith(`-${p}`) || nameWithoutExt.startsWith(`${p}-`)
4714
+ )) {
4715
+ return true;
4716
+ }
4717
+ const cliDirPatterns = ["/bin/", "/cli/", "/cmd/", "/commands/"];
4718
+ if (cliDirPatterns.some((p) => filePath.includes(p))) {
4719
+ return true;
4720
+ }
4721
+ return false;
4722
+ }
4723
+ function calculateFilePurityScore(pureFunctions, totalFunctions) {
4724
+ if (totalFunctions === 0) return 100;
4725
+ return Math.round(pureFunctions / totalFunctions * 100);
4726
+ }
4671
4727
  function calculateTestabilityIndex(params) {
4672
4728
  const {
4673
4729
  testFiles,
@@ -4679,17 +4735,18 @@ function calculateTestabilityIndex(params) {
4679
4735
  bloatedInterfaces,
4680
4736
  totalInterfaces,
4681
4737
  externalStateMutations,
4682
- hasTestFramework
4738
+ hasTestFramework,
4739
+ fileDetails
4683
4740
  } = params;
4684
4741
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
4685
4742
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
4686
4743
  const purityScore = Math.round(
4687
- (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
4744
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.7) * 100
4688
4745
  );
4689
4746
  const dependencyInjectionScore = Math.round(
4690
4747
  Math.min(
4691
4748
  100,
4692
- (totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
4749
+ (totalClasses > 0 ? injectionPatterns / totalClasses : 0.8) * 100
4693
4750
  )
4694
4751
  );
4695
4752
  const interfaceFocusScore = Math.max(
@@ -4705,7 +4762,39 @@ function calculateTestabilityIndex(params) {
4705
4762
  )
4706
4763
  );
4707
4764
  const frameworkWeight = hasTestFramework ? 1 : 0.8;
4708
- const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
4765
+ let fileMetrics;
4766
+ let libraryPureFunctions = pureFunctions;
4767
+ let libraryTotalFunctions = totalFunctions;
4768
+ let entryPointCount = 0;
4769
+ if (fileDetails && fileDetails.length > 0) {
4770
+ fileMetrics = fileDetails.map((file) => {
4771
+ const isEntryPoint = isLikelyEntryPoint(file.filePath);
4772
+ const purityScore2 = calculateFilePurityScore(
4773
+ file.pureFunctions,
4774
+ file.totalFunctions
4775
+ );
4776
+ if (isEntryPoint) {
4777
+ entryPointCount++;
4778
+ libraryPureFunctions -= file.pureFunctions;
4779
+ libraryTotalFunctions -= file.totalFunctions;
4780
+ }
4781
+ return {
4782
+ filePath: file.filePath,
4783
+ score: purityScore2,
4784
+ // Simplified - just purity for file-level
4785
+ purityScore: purityScore2,
4786
+ isEntryPoint
4787
+ };
4788
+ });
4789
+ }
4790
+ const libraryPurityScore = Math.max(
4791
+ 0,
4792
+ Math.round(
4793
+ (libraryTotalFunctions > 0 ? libraryPureFunctions / libraryTotalFunctions : 0.7) * 100
4794
+ )
4795
+ );
4796
+ const effectivePurityScore = fileDetails && fileDetails.length > 0 ? libraryPurityScore : purityScore;
4797
+ const rawScore = (testCoverageRatio * 0.3 + effectivePurityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
4709
4798
  const score = Math.max(0, Math.min(100, Math.round(rawScore)));
4710
4799
  let rating;
4711
4800
  if (score >= 85) rating = "excellent";
@@ -4730,9 +4819,9 @@ function calculateTestabilityIndex(params) {
4730
4819
  `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
4731
4820
  );
4732
4821
  }
4733
- if (purityScore < 50)
4822
+ if (effectivePurityScore < 50)
4734
4823
  recommendations.push(
4735
- "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
4824
+ entryPointCount > 0 ? `Extract pure functions from library code (${entryPointCount} entry point files excluded) \u2014 pure functions are trivially AI-testable` : "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
4736
4825
  );
4737
4826
  if (dependencyInjectionScore < 50 && totalClasses > 0)
4738
4827
  recommendations.push(
@@ -4747,13 +4836,15 @@ function calculateTestabilityIndex(params) {
4747
4836
  rating,
4748
4837
  dimensions: {
4749
4838
  testCoverageRatio,
4750
- purityScore,
4839
+ purityScore: effectivePurityScore,
4751
4840
  dependencyInjectionScore,
4752
4841
  interfaceFocusScore,
4753
4842
  observabilityScore
4754
4843
  },
4755
4844
  aiChangeSafetyRating,
4756
- recommendations
4845
+ recommendations,
4846
+ fileMetrics,
4847
+ libraryScore: Math.max(0, fileMetrics ? libraryPurityScore : purityScore)
4757
4848
  };
4758
4849
  }
4759
4850
 
@@ -4770,9 +4861,9 @@ function calculateDocDrift(params) {
4770
4861
  const outdatedRatio = totalExports > 0 ? outdatedComments / totalExports : 0;
4771
4862
  const complexityRatio = totalExports > 0 ? undocumentedComplexity / totalExports : 0;
4772
4863
  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;
4864
+ const DRIFT_THRESHOLD = 0.35;
4865
+ const OUTDATED_THRESHOLD = 0.5;
4866
+ const COMPLEXITY_THRESHOLD = 0.3;
4776
4867
  const UNCOMMENTED_THRESHOLD = 0.8;
4777
4868
  const driftRisk = Math.min(100, driftRatio / DRIFT_THRESHOLD * 100);
4778
4869
  const outdatedRisk = Math.min(
@@ -5238,6 +5329,7 @@ function emitIssuesAsAnnotations(issues) {
5238
5329
  }
5239
5330
  // Annotate the CommonJS export names for ESM import in node:
5240
5331
  0 && (module.exports = {
5332
+ AIReadyConfigSchema,
5241
5333
  AnalysisResultSchema,
5242
5334
  AnalysisStatus,
5243
5335
  AnalysisStatusSchema,
@@ -5363,6 +5455,10 @@ function emitIssuesAsAnnotations(issues) {
5363
5455
  loadMergedConfig,
5364
5456
  loadScoreHistory,
5365
5457
  mergeConfigWithDefaults,
5458
+ normalizeAnalysisResult,
5459
+ normalizeIssue,
5460
+ normalizeMetrics,
5461
+ normalizeSpokeOutput,
5366
5462
  normalizeToolName,
5367
5463
  parseFileExports,
5368
5464
  parseWeightString,