@aiready/core 0.7.7 → 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';
@@ -85,6 +103,16 @@ interface Report {
85
103
  }
86
104
 
87
105
  declare const DEFAULT_EXCLUDE: string[];
106
+ /**
107
+ * Scan files in a directory using glob patterns
108
+ *
109
+ * Note: This scanner includes multiple language file types by default (.ts, .tsx, .js, .jsx, .py, .java)
110
+ * to support potential multi-language analysis in the future. Individual tools (like @aiready/consistency)
111
+ * should filter to their supported languages before processing.
112
+ *
113
+ * @param options - Scan configuration
114
+ * @returns Array of absolute file paths matching the patterns
115
+ */
88
116
  declare function scanFiles(options: ScanOptions): Promise<string[]>;
89
117
  declare function readFileContent(filePath: string): Promise<string>;
90
118
  declare function getFileExtension(filePath: string): string;
@@ -186,4 +214,114 @@ declare function handleCLIError(error: unknown, commandName: string): never;
186
214
  */
187
215
  declare function getElapsedTime(startTime: number): string;
188
216
 
189
- 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';
@@ -85,6 +103,16 @@ interface Report {
85
103
  }
86
104
 
87
105
  declare const DEFAULT_EXCLUDE: string[];
106
+ /**
107
+ * Scan files in a directory using glob patterns
108
+ *
109
+ * Note: This scanner includes multiple language file types by default (.ts, .tsx, .js, .jsx, .py, .java)
110
+ * to support potential multi-language analysis in the future. Individual tools (like @aiready/consistency)
111
+ * should filter to their supported languages before processing.
112
+ *
113
+ * @param options - Scan configuration
114
+ * @returns Array of absolute file paths matching the patterns
115
+ */
88
116
  declare function scanFiles(options: ScanOptions): Promise<string[]>;
89
117
  declare function readFileContent(filePath: string): Promise<string>;
90
118
  declare function getFileExtension(filePath: string): string;
@@ -186,4 +214,114 @@ declare function handleCLIError(error: unknown, commandName: string): never;
186
214
  */
187
215
  declare function getElapsedTime(startTime: number): string;
188
216
 
189
- 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
@@ -93,6 +103,7 @@ async function scanFiles(options) {
93
103
  const {
94
104
  rootDir,
95
105
  include = ["**/*.{ts,tsx,js,jsx,py,java}"],
106
+ // Broad default - tools should filter further
96
107
  exclude
97
108
  } = options;
98
109
  const finalExclude = exclude ? [.../* @__PURE__ */ new Set([...DEFAULT_EXCLUDE, ...exclude])] : DEFAULT_EXCLUDE;
@@ -408,23 +419,177 @@ function handleCLIError(error, commandName) {
408
419
  function getElapsedTime(startTime) {
409
420
  return ((Date.now() - startTime) / 1e3).toFixed(2);
410
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
+ }
411
566
  // Annotate the CommonJS export names for ESM import in node:
412
567
  0 && (module.exports = {
413
568
  DEFAULT_EXCLUDE,
569
+ DEFAULT_TOOL_WEIGHTS,
570
+ TOOL_NAME_MAP,
414
571
  calculateImportSimilarity,
572
+ calculateOverallScore,
415
573
  estimateTokens,
416
574
  extractFunctions,
417
575
  extractImports,
576
+ formatScore,
577
+ formatToolScore,
418
578
  getElapsedTime,
419
579
  getFileExtension,
580
+ getRating,
581
+ getRatingDisplay,
582
+ getToolWeight,
420
583
  handleCLIError,
421
584
  handleJSONOutput,
422
585
  isSourceFile,
423
586
  loadConfig,
424
587
  loadMergedConfig,
425
588
  mergeConfigWithDefaults,
589
+ normalizeToolName,
426
590
  parseCode,
427
591
  parseFileExports,
592
+ parseWeightString,
428
593
  readFileContent,
429
594
  resolveOutputPath,
430
595
  scanFiles
package/dist/index.mjs CHANGED
@@ -50,6 +50,7 @@ async function scanFiles(options) {
50
50
  const {
51
51
  rootDir,
52
52
  include = ["**/*.{ts,tsx,js,jsx,py,java}"],
53
+ // Broad default - tools should filter further
53
54
  exclude
54
55
  } = options;
55
56
  const finalExclude = exclude ? [.../* @__PURE__ */ new Set([...DEFAULT_EXCLUDE, ...exclude])] : DEFAULT_EXCLUDE;
@@ -365,22 +366,176 @@ function handleCLIError(error, commandName) {
365
366
  function getElapsedTime(startTime) {
366
367
  return ((Date.now() - startTime) / 1e3).toFixed(2);
367
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
+ }
368
513
  export {
369
514
  DEFAULT_EXCLUDE,
515
+ DEFAULT_TOOL_WEIGHTS,
516
+ TOOL_NAME_MAP,
370
517
  calculateImportSimilarity,
518
+ calculateOverallScore,
371
519
  estimateTokens,
372
520
  extractFunctions,
373
521
  extractImports,
522
+ formatScore,
523
+ formatToolScore,
374
524
  getElapsedTime,
375
525
  getFileExtension,
526
+ getRating,
527
+ getRatingDisplay,
528
+ getToolWeight,
376
529
  handleCLIError,
377
530
  handleJSONOutput,
378
531
  isSourceFile,
379
532
  loadConfig,
380
533
  loadMergedConfig,
381
534
  mergeConfigWithDefaults,
535
+ normalizeToolName,
382
536
  parseCode,
383
537
  parseFileExports,
538
+ parseWeightString,
384
539
  readFileContent,
385
540
  resolveOutputPath,
386
541
  scanFiles
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.7.7",
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",