@aiready/core 0.7.9 → 0.7.10

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.d.mts CHANGED
@@ -34,9 +34,12 @@ interface AIReadyConfig {
34
34
  scan?: {
35
35
  include?: string[];
36
36
  exclude?: string[];
37
+ tools?: string[];
37
38
  };
38
39
  tools?: {
39
40
  'pattern-detect'?: {
41
+ enabled?: boolean;
42
+ scoreWeight?: number;
40
43
  minSimilarity?: number;
41
44
  minLines?: number;
42
45
  batchSize?: number;
@@ -47,6 +50,8 @@ interface AIReadyConfig {
47
50
  maxResults?: number;
48
51
  };
49
52
  'context-analyzer'?: {
53
+ enabled?: boolean;
54
+ scoreWeight?: number;
50
55
  maxDepth?: number;
51
56
  maxContextBudget?: number;
52
57
  minCohesion?: number;
@@ -59,10 +64,23 @@ interface AIReadyConfig {
59
64
  pathDomainMap?: Record<string, string>;
60
65
  };
61
66
  'consistency'?: {
67
+ enabled?: boolean;
68
+ scoreWeight?: number;
62
69
  acceptedAbbreviations?: string[];
63
70
  shortWords?: string[];
64
71
  disableChecks?: ('single-letter' | 'abbreviation' | 'convention-mix' | 'unclear' | 'poor-naming')[];
65
72
  };
73
+ [toolName: string]: {
74
+ enabled?: boolean;
75
+ scoreWeight?: number;
76
+ [key: string]: any;
77
+ } | undefined;
78
+ };
79
+ scoring?: {
80
+ threshold?: number;
81
+ showBreakdown?: boolean;
82
+ compareBaseline?: string;
83
+ saveTo?: string;
66
84
  };
67
85
  output?: {
68
86
  format?: 'console' | 'json' | 'html';
@@ -196,4 +214,114 @@ declare function handleCLIError(error: unknown, commandName: string): never;
196
214
  */
197
215
  declare function getElapsedTime(startTime: number): string;
198
216
 
199
- export { type AIReadyConfig, type ASTNode, type AnalysisResult, type CLIOptions, DEFAULT_EXCLUDE, type ExportWithImports, type FileImport, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, calculateImportSimilarity, estimateTokens, extractFunctions, extractImports, getElapsedTime, getFileExtension, handleCLIError, handleJSONOutput, isSourceFile, loadConfig, loadMergedConfig, mergeConfigWithDefaults, parseCode, parseFileExports, readFileContent, resolveOutputPath, scanFiles };
217
+ /**
218
+ * AI Readiness Scoring System
219
+ *
220
+ * Provides dynamic, composable scoring across multiple analysis tools.
221
+ * Each tool contributes a 0-100 score with configurable weights.
222
+ */
223
+ interface ToolScoringOutput {
224
+ /** Unique tool identifier (e.g., "pattern-detect") */
225
+ toolName: string;
226
+ /** Normalized 0-100 score for this tool */
227
+ score: number;
228
+ /** Raw metrics used to calculate the score */
229
+ rawMetrics: Record<string, any>;
230
+ /** Factors that influenced the score */
231
+ factors: Array<{
232
+ name: string;
233
+ impact: number;
234
+ description: string;
235
+ }>;
236
+ /** Actionable recommendations with estimated impact */
237
+ recommendations: Array<{
238
+ action: string;
239
+ estimatedImpact: number;
240
+ priority: 'high' | 'medium' | 'low';
241
+ }>;
242
+ }
243
+ interface ScoringResult {
244
+ /** Overall AI Readiness Score (0-100) */
245
+ overall: number;
246
+ /** Rating category */
247
+ rating: 'Excellent' | 'Good' | 'Fair' | 'Needs Work' | 'Critical';
248
+ /** Timestamp of score calculation */
249
+ timestamp: string;
250
+ /** Tools that contributed to this score */
251
+ toolsUsed: string[];
252
+ /** Breakdown by tool */
253
+ breakdown: ToolScoringOutput[];
254
+ /** Calculation details */
255
+ calculation: {
256
+ formula: string;
257
+ weights: Record<string, number>;
258
+ normalized: string;
259
+ };
260
+ }
261
+ interface ScoringConfig {
262
+ /** Minimum passing score (exit code 1 if below) */
263
+ threshold?: number;
264
+ /** Show detailed breakdown in output */
265
+ showBreakdown?: boolean;
266
+ /** Path to baseline JSON for comparison */
267
+ compareBaseline?: string;
268
+ /** Auto-save score to this path */
269
+ saveTo?: string;
270
+ }
271
+ /**
272
+ * Default weights for known tools.
273
+ * New tools get weight of 10 if not specified.
274
+ */
275
+ declare const DEFAULT_TOOL_WEIGHTS: Record<string, number>;
276
+ /**
277
+ * Tool name normalization map (shorthand -> full name)
278
+ */
279
+ declare const TOOL_NAME_MAP: Record<string, string>;
280
+ /**
281
+ * Normalize tool name from shorthand to full name
282
+ */
283
+ declare function normalizeToolName(shortName: string): string;
284
+ /**
285
+ * Get tool weight with fallback priority:
286
+ * 1. CLI override
287
+ * 2. Tool config scoreWeight
288
+ * 3. Default weight
289
+ * 4. 10 (for unknown tools)
290
+ */
291
+ declare function getToolWeight(toolName: string, toolConfig?: {
292
+ scoreWeight?: number;
293
+ }, cliOverride?: number): number;
294
+ /**
295
+ * Parse weight string from CLI (e.g., "patterns:50,context:30")
296
+ */
297
+ declare function parseWeightString(weightStr?: string): Map<string, number>;
298
+ /**
299
+ * Calculate overall AI Readiness Score from multiple tool scores.
300
+ *
301
+ * Formula: Σ(tool_score × tool_weight) / Σ(active_tool_weights)
302
+ *
303
+ * This allows dynamic composition - score adjusts automatically
304
+ * based on which tools actually ran.
305
+ */
306
+ declare function calculateOverallScore(toolOutputs: Map<string, ToolScoringOutput>, config?: any, cliWeights?: Map<string, number>): ScoringResult;
307
+ /**
308
+ * Convert numeric score to rating category
309
+ */
310
+ declare function getRating(score: number): ScoringResult['rating'];
311
+ /**
312
+ * Get rating emoji and color for display
313
+ */
314
+ declare function getRatingDisplay(rating: ScoringResult['rating']): {
315
+ emoji: string;
316
+ color: string;
317
+ };
318
+ /**
319
+ * Format score for display with rating
320
+ */
321
+ declare function formatScore(result: ScoringResult): string;
322
+ /**
323
+ * Format individual tool score for display
324
+ */
325
+ declare function formatToolScore(output: ToolScoringOutput): string;
326
+
327
+ export { type AIReadyConfig, type ASTNode, type AnalysisResult, type CLIOptions, DEFAULT_EXCLUDE, DEFAULT_TOOL_WEIGHTS, type ExportWithImports, type FileImport, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, type ScoringConfig, type ScoringResult, TOOL_NAME_MAP, type ToolScoringOutput, calculateImportSimilarity, calculateOverallScore, estimateTokens, extractFunctions, extractImports, formatScore, formatToolScore, getElapsedTime, getFileExtension, getRating, getRatingDisplay, getToolWeight, handleCLIError, handleJSONOutput, isSourceFile, loadConfig, loadMergedConfig, mergeConfigWithDefaults, normalizeToolName, parseCode, parseFileExports, parseWeightString, readFileContent, resolveOutputPath, scanFiles };
package/dist/index.d.ts CHANGED
@@ -34,9 +34,12 @@ interface AIReadyConfig {
34
34
  scan?: {
35
35
  include?: string[];
36
36
  exclude?: string[];
37
+ tools?: string[];
37
38
  };
38
39
  tools?: {
39
40
  'pattern-detect'?: {
41
+ enabled?: boolean;
42
+ scoreWeight?: number;
40
43
  minSimilarity?: number;
41
44
  minLines?: number;
42
45
  batchSize?: number;
@@ -47,6 +50,8 @@ interface AIReadyConfig {
47
50
  maxResults?: number;
48
51
  };
49
52
  'context-analyzer'?: {
53
+ enabled?: boolean;
54
+ scoreWeight?: number;
50
55
  maxDepth?: number;
51
56
  maxContextBudget?: number;
52
57
  minCohesion?: number;
@@ -59,10 +64,23 @@ interface AIReadyConfig {
59
64
  pathDomainMap?: Record<string, string>;
60
65
  };
61
66
  'consistency'?: {
67
+ enabled?: boolean;
68
+ scoreWeight?: number;
62
69
  acceptedAbbreviations?: string[];
63
70
  shortWords?: string[];
64
71
  disableChecks?: ('single-letter' | 'abbreviation' | 'convention-mix' | 'unclear' | 'poor-naming')[];
65
72
  };
73
+ [toolName: string]: {
74
+ enabled?: boolean;
75
+ scoreWeight?: number;
76
+ [key: string]: any;
77
+ } | undefined;
78
+ };
79
+ scoring?: {
80
+ threshold?: number;
81
+ showBreakdown?: boolean;
82
+ compareBaseline?: string;
83
+ saveTo?: string;
66
84
  };
67
85
  output?: {
68
86
  format?: 'console' | 'json' | 'html';
@@ -196,4 +214,114 @@ declare function handleCLIError(error: unknown, commandName: string): never;
196
214
  */
197
215
  declare function getElapsedTime(startTime: number): string;
198
216
 
199
- export { type AIReadyConfig, type ASTNode, type AnalysisResult, type CLIOptions, DEFAULT_EXCLUDE, type ExportWithImports, type FileImport, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, calculateImportSimilarity, estimateTokens, extractFunctions, extractImports, getElapsedTime, getFileExtension, handleCLIError, handleJSONOutput, isSourceFile, loadConfig, loadMergedConfig, mergeConfigWithDefaults, parseCode, parseFileExports, readFileContent, resolveOutputPath, scanFiles };
217
+ /**
218
+ * AI Readiness Scoring System
219
+ *
220
+ * Provides dynamic, composable scoring across multiple analysis tools.
221
+ * Each tool contributes a 0-100 score with configurable weights.
222
+ */
223
+ interface ToolScoringOutput {
224
+ /** Unique tool identifier (e.g., "pattern-detect") */
225
+ toolName: string;
226
+ /** Normalized 0-100 score for this tool */
227
+ score: number;
228
+ /** Raw metrics used to calculate the score */
229
+ rawMetrics: Record<string, any>;
230
+ /** Factors that influenced the score */
231
+ factors: Array<{
232
+ name: string;
233
+ impact: number;
234
+ description: string;
235
+ }>;
236
+ /** Actionable recommendations with estimated impact */
237
+ recommendations: Array<{
238
+ action: string;
239
+ estimatedImpact: number;
240
+ priority: 'high' | 'medium' | 'low';
241
+ }>;
242
+ }
243
+ interface ScoringResult {
244
+ /** Overall AI Readiness Score (0-100) */
245
+ overall: number;
246
+ /** Rating category */
247
+ rating: 'Excellent' | 'Good' | 'Fair' | 'Needs Work' | 'Critical';
248
+ /** Timestamp of score calculation */
249
+ timestamp: string;
250
+ /** Tools that contributed to this score */
251
+ toolsUsed: string[];
252
+ /** Breakdown by tool */
253
+ breakdown: ToolScoringOutput[];
254
+ /** Calculation details */
255
+ calculation: {
256
+ formula: string;
257
+ weights: Record<string, number>;
258
+ normalized: string;
259
+ };
260
+ }
261
+ interface ScoringConfig {
262
+ /** Minimum passing score (exit code 1 if below) */
263
+ threshold?: number;
264
+ /** Show detailed breakdown in output */
265
+ showBreakdown?: boolean;
266
+ /** Path to baseline JSON for comparison */
267
+ compareBaseline?: string;
268
+ /** Auto-save score to this path */
269
+ saveTo?: string;
270
+ }
271
+ /**
272
+ * Default weights for known tools.
273
+ * New tools get weight of 10 if not specified.
274
+ */
275
+ declare const DEFAULT_TOOL_WEIGHTS: Record<string, number>;
276
+ /**
277
+ * Tool name normalization map (shorthand -> full name)
278
+ */
279
+ declare const TOOL_NAME_MAP: Record<string, string>;
280
+ /**
281
+ * Normalize tool name from shorthand to full name
282
+ */
283
+ declare function normalizeToolName(shortName: string): string;
284
+ /**
285
+ * Get tool weight with fallback priority:
286
+ * 1. CLI override
287
+ * 2. Tool config scoreWeight
288
+ * 3. Default weight
289
+ * 4. 10 (for unknown tools)
290
+ */
291
+ declare function getToolWeight(toolName: string, toolConfig?: {
292
+ scoreWeight?: number;
293
+ }, cliOverride?: number): number;
294
+ /**
295
+ * Parse weight string from CLI (e.g., "patterns:50,context:30")
296
+ */
297
+ declare function parseWeightString(weightStr?: string): Map<string, number>;
298
+ /**
299
+ * Calculate overall AI Readiness Score from multiple tool scores.
300
+ *
301
+ * Formula: Σ(tool_score × tool_weight) / Σ(active_tool_weights)
302
+ *
303
+ * This allows dynamic composition - score adjusts automatically
304
+ * based on which tools actually ran.
305
+ */
306
+ declare function calculateOverallScore(toolOutputs: Map<string, ToolScoringOutput>, config?: any, cliWeights?: Map<string, number>): ScoringResult;
307
+ /**
308
+ * Convert numeric score to rating category
309
+ */
310
+ declare function getRating(score: number): ScoringResult['rating'];
311
+ /**
312
+ * Get rating emoji and color for display
313
+ */
314
+ declare function getRatingDisplay(rating: ScoringResult['rating']): {
315
+ emoji: string;
316
+ color: string;
317
+ };
318
+ /**
319
+ * Format score for display with rating
320
+ */
321
+ declare function formatScore(result: ScoringResult): string;
322
+ /**
323
+ * Format individual tool score for display
324
+ */
325
+ declare function formatToolScore(output: ToolScoringOutput): string;
326
+
327
+ export { type AIReadyConfig, type ASTNode, type AnalysisResult, type CLIOptions, DEFAULT_EXCLUDE, DEFAULT_TOOL_WEIGHTS, type ExportWithImports, type FileImport, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, type ScoringConfig, type ScoringResult, TOOL_NAME_MAP, type ToolScoringOutput, calculateImportSimilarity, calculateOverallScore, estimateTokens, extractFunctions, extractImports, formatScore, formatToolScore, getElapsedTime, getFileExtension, getRating, getRatingDisplay, getToolWeight, handleCLIError, handleJSONOutput, isSourceFile, loadConfig, loadMergedConfig, mergeConfigWithDefaults, normalizeToolName, parseCode, parseFileExports, parseWeightString, readFileContent, resolveOutputPath, scanFiles };
package/dist/index.js CHANGED
@@ -21,20 +21,30 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DEFAULT_EXCLUDE: () => DEFAULT_EXCLUDE,
24
+ DEFAULT_TOOL_WEIGHTS: () => DEFAULT_TOOL_WEIGHTS,
25
+ TOOL_NAME_MAP: () => TOOL_NAME_MAP,
24
26
  calculateImportSimilarity: () => calculateImportSimilarity,
27
+ calculateOverallScore: () => calculateOverallScore,
25
28
  estimateTokens: () => estimateTokens,
26
29
  extractFunctions: () => extractFunctions,
27
30
  extractImports: () => extractImports,
31
+ formatScore: () => formatScore,
32
+ formatToolScore: () => formatToolScore,
28
33
  getElapsedTime: () => getElapsedTime,
29
34
  getFileExtension: () => getFileExtension,
35
+ getRating: () => getRating,
36
+ getRatingDisplay: () => getRatingDisplay,
37
+ getToolWeight: () => getToolWeight,
30
38
  handleCLIError: () => handleCLIError,
31
39
  handleJSONOutput: () => handleJSONOutput,
32
40
  isSourceFile: () => isSourceFile,
33
41
  loadConfig: () => loadConfig,
34
42
  loadMergedConfig: () => loadMergedConfig,
35
43
  mergeConfigWithDefaults: () => mergeConfigWithDefaults,
44
+ normalizeToolName: () => normalizeToolName,
36
45
  parseCode: () => parseCode,
37
46
  parseFileExports: () => parseFileExports,
47
+ parseWeightString: () => parseWeightString,
38
48
  readFileContent: () => readFileContent,
39
49
  resolveOutputPath: () => resolveOutputPath,
40
50
  scanFiles: () => scanFiles
@@ -409,23 +419,177 @@ function handleCLIError(error, commandName) {
409
419
  function getElapsedTime(startTime) {
410
420
  return ((Date.now() - startTime) / 1e3).toFixed(2);
411
421
  }
422
+
423
+ // src/scoring.ts
424
+ var DEFAULT_TOOL_WEIGHTS = {
425
+ "pattern-detect": 40,
426
+ "context-analyzer": 35,
427
+ "consistency": 25,
428
+ "doc-drift": 20,
429
+ "deps": 15
430
+ };
431
+ var TOOL_NAME_MAP = {
432
+ "patterns": "pattern-detect",
433
+ "context": "context-analyzer",
434
+ "consistency": "consistency",
435
+ "doc-drift": "doc-drift",
436
+ "deps": "deps"
437
+ };
438
+ function normalizeToolName(shortName) {
439
+ return TOOL_NAME_MAP[shortName] || shortName;
440
+ }
441
+ function getToolWeight(toolName, toolConfig, cliOverride) {
442
+ if (cliOverride !== void 0) {
443
+ return cliOverride;
444
+ }
445
+ if (toolConfig?.scoreWeight !== void 0) {
446
+ return toolConfig.scoreWeight;
447
+ }
448
+ return DEFAULT_TOOL_WEIGHTS[toolName] || 10;
449
+ }
450
+ function parseWeightString(weightStr) {
451
+ const weights = /* @__PURE__ */ new Map();
452
+ if (!weightStr) {
453
+ return weights;
454
+ }
455
+ const pairs = weightStr.split(",");
456
+ for (const pair of pairs) {
457
+ const [toolShortName, weightStr2] = pair.split(":");
458
+ if (toolShortName && weightStr2) {
459
+ const toolName = normalizeToolName(toolShortName.trim());
460
+ const weight = parseInt(weightStr2.trim(), 10);
461
+ if (!isNaN(weight) && weight > 0) {
462
+ weights.set(toolName, weight);
463
+ }
464
+ }
465
+ }
466
+ return weights;
467
+ }
468
+ function calculateOverallScore(toolOutputs, config, cliWeights) {
469
+ if (toolOutputs.size === 0) {
470
+ throw new Error("No tool outputs provided for scoring");
471
+ }
472
+ const weights = /* @__PURE__ */ new Map();
473
+ for (const [toolName] of toolOutputs.entries()) {
474
+ const cliWeight = cliWeights?.get(toolName);
475
+ const configWeight = config?.tools?.[toolName]?.scoreWeight;
476
+ const weight = cliWeight ?? configWeight ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 10;
477
+ weights.set(toolName, weight);
478
+ }
479
+ let weightedSum = 0;
480
+ let totalWeight = 0;
481
+ const breakdown = [];
482
+ const toolsUsed = [];
483
+ const calculationWeights = {};
484
+ for (const [toolName, output] of toolOutputs.entries()) {
485
+ const weight = weights.get(toolName) || 10;
486
+ const weightedScore = output.score * weight;
487
+ weightedSum += weightedScore;
488
+ totalWeight += weight;
489
+ toolsUsed.push(toolName);
490
+ calculationWeights[toolName] = weight;
491
+ breakdown.push(output);
492
+ }
493
+ const overall = Math.round(weightedSum / totalWeight);
494
+ const rating = getRating(overall);
495
+ const formulaParts = Array.from(toolOutputs.entries()).map(([name, output]) => {
496
+ const w = weights.get(name) || 10;
497
+ return `(${output.score} \xD7 ${w})`;
498
+ });
499
+ const formulaStr = `[${formulaParts.join(" + ")}] / ${totalWeight} = ${overall}`;
500
+ return {
501
+ overall,
502
+ rating,
503
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
504
+ toolsUsed,
505
+ breakdown,
506
+ calculation: {
507
+ formula: formulaStr,
508
+ weights: calculationWeights,
509
+ normalized: formulaStr
510
+ }
511
+ };
512
+ }
513
+ function getRating(score) {
514
+ if (score >= 90) return "Excellent";
515
+ if (score >= 75) return "Good";
516
+ if (score >= 60) return "Fair";
517
+ if (score >= 40) return "Needs Work";
518
+ return "Critical";
519
+ }
520
+ function getRatingDisplay(rating) {
521
+ switch (rating) {
522
+ case "Excellent":
523
+ return { emoji: "\u2705", color: "green" };
524
+ case "Good":
525
+ return { emoji: "\u{1F44D}", color: "blue" };
526
+ case "Fair":
527
+ return { emoji: "\u26A0\uFE0F", color: "yellow" };
528
+ case "Needs Work":
529
+ return { emoji: "\u{1F528}", color: "orange" };
530
+ case "Critical":
531
+ return { emoji: "\u274C", color: "red" };
532
+ }
533
+ }
534
+ function formatScore(result) {
535
+ const { emoji, color } = getRatingDisplay(result.rating);
536
+ return `${result.overall}/100 (${result.rating}) ${emoji}`;
537
+ }
538
+ function formatToolScore(output) {
539
+ let result = ` Score: ${output.score}/100
540
+
541
+ `;
542
+ if (output.factors && output.factors.length > 0) {
543
+ result += ` Factors:
544
+ `;
545
+ output.factors.forEach((factor) => {
546
+ const impactSign = factor.impact > 0 ? "+" : "";
547
+ result += ` \u2022 ${factor.name}: ${impactSign}${factor.impact} - ${factor.description}
548
+ `;
549
+ });
550
+ result += "\n";
551
+ }
552
+ if (output.recommendations && output.recommendations.length > 0) {
553
+ result += ` Recommendations:
554
+ `;
555
+ output.recommendations.forEach((rec, i) => {
556
+ const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
557
+ result += ` ${i + 1}. ${priorityIcon} ${rec.action}
558
+ `;
559
+ result += ` Impact: +${rec.estimatedImpact} points
560
+
561
+ `;
562
+ });
563
+ }
564
+ return result;
565
+ }
412
566
  // Annotate the CommonJS export names for ESM import in node:
413
567
  0 && (module.exports = {
414
568
  DEFAULT_EXCLUDE,
569
+ DEFAULT_TOOL_WEIGHTS,
570
+ TOOL_NAME_MAP,
415
571
  calculateImportSimilarity,
572
+ calculateOverallScore,
416
573
  estimateTokens,
417
574
  extractFunctions,
418
575
  extractImports,
576
+ formatScore,
577
+ formatToolScore,
419
578
  getElapsedTime,
420
579
  getFileExtension,
580
+ getRating,
581
+ getRatingDisplay,
582
+ getToolWeight,
421
583
  handleCLIError,
422
584
  handleJSONOutput,
423
585
  isSourceFile,
424
586
  loadConfig,
425
587
  loadMergedConfig,
426
588
  mergeConfigWithDefaults,
589
+ normalizeToolName,
427
590
  parseCode,
428
591
  parseFileExports,
592
+ parseWeightString,
429
593
  readFileContent,
430
594
  resolveOutputPath,
431
595
  scanFiles
package/dist/index.mjs CHANGED
@@ -366,22 +366,176 @@ function handleCLIError(error, commandName) {
366
366
  function getElapsedTime(startTime) {
367
367
  return ((Date.now() - startTime) / 1e3).toFixed(2);
368
368
  }
369
+
370
+ // src/scoring.ts
371
+ var DEFAULT_TOOL_WEIGHTS = {
372
+ "pattern-detect": 40,
373
+ "context-analyzer": 35,
374
+ "consistency": 25,
375
+ "doc-drift": 20,
376
+ "deps": 15
377
+ };
378
+ var TOOL_NAME_MAP = {
379
+ "patterns": "pattern-detect",
380
+ "context": "context-analyzer",
381
+ "consistency": "consistency",
382
+ "doc-drift": "doc-drift",
383
+ "deps": "deps"
384
+ };
385
+ function normalizeToolName(shortName) {
386
+ return TOOL_NAME_MAP[shortName] || shortName;
387
+ }
388
+ function getToolWeight(toolName, toolConfig, cliOverride) {
389
+ if (cliOverride !== void 0) {
390
+ return cliOverride;
391
+ }
392
+ if (toolConfig?.scoreWeight !== void 0) {
393
+ return toolConfig.scoreWeight;
394
+ }
395
+ return DEFAULT_TOOL_WEIGHTS[toolName] || 10;
396
+ }
397
+ function parseWeightString(weightStr) {
398
+ const weights = /* @__PURE__ */ new Map();
399
+ if (!weightStr) {
400
+ return weights;
401
+ }
402
+ const pairs = weightStr.split(",");
403
+ for (const pair of pairs) {
404
+ const [toolShortName, weightStr2] = pair.split(":");
405
+ if (toolShortName && weightStr2) {
406
+ const toolName = normalizeToolName(toolShortName.trim());
407
+ const weight = parseInt(weightStr2.trim(), 10);
408
+ if (!isNaN(weight) && weight > 0) {
409
+ weights.set(toolName, weight);
410
+ }
411
+ }
412
+ }
413
+ return weights;
414
+ }
415
+ function calculateOverallScore(toolOutputs, config, cliWeights) {
416
+ if (toolOutputs.size === 0) {
417
+ throw new Error("No tool outputs provided for scoring");
418
+ }
419
+ const weights = /* @__PURE__ */ new Map();
420
+ for (const [toolName] of toolOutputs.entries()) {
421
+ const cliWeight = cliWeights?.get(toolName);
422
+ const configWeight = config?.tools?.[toolName]?.scoreWeight;
423
+ const weight = cliWeight ?? configWeight ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 10;
424
+ weights.set(toolName, weight);
425
+ }
426
+ let weightedSum = 0;
427
+ let totalWeight = 0;
428
+ const breakdown = [];
429
+ const toolsUsed = [];
430
+ const calculationWeights = {};
431
+ for (const [toolName, output] of toolOutputs.entries()) {
432
+ const weight = weights.get(toolName) || 10;
433
+ const weightedScore = output.score * weight;
434
+ weightedSum += weightedScore;
435
+ totalWeight += weight;
436
+ toolsUsed.push(toolName);
437
+ calculationWeights[toolName] = weight;
438
+ breakdown.push(output);
439
+ }
440
+ const overall = Math.round(weightedSum / totalWeight);
441
+ const rating = getRating(overall);
442
+ const formulaParts = Array.from(toolOutputs.entries()).map(([name, output]) => {
443
+ const w = weights.get(name) || 10;
444
+ return `(${output.score} \xD7 ${w})`;
445
+ });
446
+ const formulaStr = `[${formulaParts.join(" + ")}] / ${totalWeight} = ${overall}`;
447
+ return {
448
+ overall,
449
+ rating,
450
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
451
+ toolsUsed,
452
+ breakdown,
453
+ calculation: {
454
+ formula: formulaStr,
455
+ weights: calculationWeights,
456
+ normalized: formulaStr
457
+ }
458
+ };
459
+ }
460
+ function getRating(score) {
461
+ if (score >= 90) return "Excellent";
462
+ if (score >= 75) return "Good";
463
+ if (score >= 60) return "Fair";
464
+ if (score >= 40) return "Needs Work";
465
+ return "Critical";
466
+ }
467
+ function getRatingDisplay(rating) {
468
+ switch (rating) {
469
+ case "Excellent":
470
+ return { emoji: "\u2705", color: "green" };
471
+ case "Good":
472
+ return { emoji: "\u{1F44D}", color: "blue" };
473
+ case "Fair":
474
+ return { emoji: "\u26A0\uFE0F", color: "yellow" };
475
+ case "Needs Work":
476
+ return { emoji: "\u{1F528}", color: "orange" };
477
+ case "Critical":
478
+ return { emoji: "\u274C", color: "red" };
479
+ }
480
+ }
481
+ function formatScore(result) {
482
+ const { emoji, color } = getRatingDisplay(result.rating);
483
+ return `${result.overall}/100 (${result.rating}) ${emoji}`;
484
+ }
485
+ function formatToolScore(output) {
486
+ let result = ` Score: ${output.score}/100
487
+
488
+ `;
489
+ if (output.factors && output.factors.length > 0) {
490
+ result += ` Factors:
491
+ `;
492
+ output.factors.forEach((factor) => {
493
+ const impactSign = factor.impact > 0 ? "+" : "";
494
+ result += ` \u2022 ${factor.name}: ${impactSign}${factor.impact} - ${factor.description}
495
+ `;
496
+ });
497
+ result += "\n";
498
+ }
499
+ if (output.recommendations && output.recommendations.length > 0) {
500
+ result += ` Recommendations:
501
+ `;
502
+ output.recommendations.forEach((rec, i) => {
503
+ const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
504
+ result += ` ${i + 1}. ${priorityIcon} ${rec.action}
505
+ `;
506
+ result += ` Impact: +${rec.estimatedImpact} points
507
+
508
+ `;
509
+ });
510
+ }
511
+ return result;
512
+ }
369
513
  export {
370
514
  DEFAULT_EXCLUDE,
515
+ DEFAULT_TOOL_WEIGHTS,
516
+ TOOL_NAME_MAP,
371
517
  calculateImportSimilarity,
518
+ calculateOverallScore,
372
519
  estimateTokens,
373
520
  extractFunctions,
374
521
  extractImports,
522
+ formatScore,
523
+ formatToolScore,
375
524
  getElapsedTime,
376
525
  getFileExtension,
526
+ getRating,
527
+ getRatingDisplay,
528
+ getToolWeight,
377
529
  handleCLIError,
378
530
  handleJSONOutput,
379
531
  isSourceFile,
380
532
  loadConfig,
381
533
  loadMergedConfig,
382
534
  mergeConfigWithDefaults,
535
+ normalizeToolName,
383
536
  parseCode,
384
537
  parseFileExports,
538
+ parseWeightString,
385
539
  readFileContent,
386
540
  resolveOutputPath,
387
541
  scanFiles
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.7.9",
3
+ "version": "0.7.10",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",