@aiready/cli 0.14.3 → 0.14.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.14.3",
3
+ "version": "0.14.4",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,17 +11,17 @@
11
11
  "dependencies": {
12
12
  "chalk": "^5.3.0",
13
13
  "commander": "^14.0.0",
14
- "@aiready/agent-grounding": "0.13.2",
15
- "@aiready/consistency": "0.20.2",
16
- "@aiready/context-analyzer": "0.21.6",
17
- "@aiready/core": "0.23.2",
18
- "@aiready/doc-drift": "0.13.2",
19
- "@aiready/change-amplification": "0.13.2",
20
- "@aiready/deps": "0.13.2",
21
- "@aiready/testability": "0.6.2",
22
- "@aiready/pattern-detect": "0.16.2",
23
- "@aiready/visualizer": "0.6.2",
24
- "@aiready/ai-signal-clarity": "0.13.2"
14
+ "@aiready/context-analyzer": "0.21.7",
15
+ "@aiready/agent-grounding": "0.13.3",
16
+ "@aiready/core": "0.23.3",
17
+ "@aiready/deps": "0.13.3",
18
+ "@aiready/ai-signal-clarity": "0.13.3",
19
+ "@aiready/doc-drift": "0.13.3",
20
+ "@aiready/testability": "0.6.3",
21
+ "@aiready/visualizer": "0.6.3",
22
+ "@aiready/change-amplification": "0.13.3",
23
+ "@aiready/consistency": "0.20.3",
24
+ "@aiready/pattern-detect": "0.16.3"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.0.0",
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { analyzeUnified } from '../index';
3
3
  import { ToolRegistry, ToolName, SpokeOutputSchema } from '@aiready/core';
4
4
 
@@ -76,7 +76,6 @@ describe('CLI Configuration Shape', () => {
76
76
  rootDir: '/test',
77
77
  tools: [ToolName.PatternDetect],
78
78
  useSmartDefaults: true,
79
- // @ts-expect-error - testing internal key stripping
80
79
  batchSize: 50,
81
80
  });
82
81
 
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { analyzeUnified, scoreUnified, generateUnifiedSummary } from '../index';
3
- import { ToolRegistry, ToolName } from '@aiready/core';
3
+ import { ToolRegistry } from '@aiready/core';
4
4
 
5
5
  vi.mock('@aiready/core', async () => {
6
6
  const actual = await vi.importActual('@aiready/core');
@@ -7,7 +7,6 @@ import { testabilityAction } from '../testability';
7
7
  import { depsHealthAction } from '../deps-health';
8
8
  import { patternsAction } from '../patterns';
9
9
  import { contextAction } from '../context';
10
- import * as core from '@aiready/core';
11
10
 
12
11
  vi.mock('@aiready/core', async () => {
13
12
  const actual = await vi.importActual('@aiready/core');
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { existsSync, readFileSync, unlinkSync, mkdirSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { initAction } from '../init';
5
+
6
+ describe('initAction', () => {
7
+ const testDir = join(process.cwd(), 'temp-test-init');
8
+ const configPath = join(testDir, 'aiready.json');
9
+
10
+ beforeEach(() => {
11
+ if (!existsSync(testDir)) {
12
+ mkdirSync(testDir, { recursive: true });
13
+ }
14
+ // Mock process.cwd to use our test directory
15
+ vi.spyOn(process, 'cwd').mockReturnValue(testDir);
16
+ // Mock console.log to avoid noise
17
+ vi.spyOn(console, 'log').mockImplementation(() => {});
18
+ vi.spyOn(console, 'error').mockImplementation(() => {});
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.restoreAllMocks();
23
+ if (existsSync(configPath)) {
24
+ unlinkSync(configPath);
25
+ }
26
+ if (existsSync(testDir)) {
27
+ rmSync(testDir, { recursive: true, force: true });
28
+ }
29
+ });
30
+
31
+ it('should generate aiready.json without output field by default', async () => {
32
+ await initAction({});
33
+
34
+ expect(existsSync(configPath)).toBe(true);
35
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
36
+ expect(config).not.toHaveProperty('output');
37
+ });
38
+
39
+ it('should generate aiready.json without output field even with --full flag', async () => {
40
+ await initAction({ full: true });
41
+
42
+ expect(existsSync(configPath)).toBe(true);
43
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
44
+ expect(config).not.toHaveProperty('output');
45
+ });
46
+
47
+ it('should include scan, tools, and scoring sections', async () => {
48
+ await initAction({ full: true });
49
+
50
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
51
+ expect(config).toHaveProperty('scan');
52
+ expect(config).toHaveProperty('tools');
53
+ expect(config).toHaveProperty('scoring');
54
+ expect(config).toHaveProperty('visualizer');
55
+ });
56
+ });
@@ -3,7 +3,7 @@ import { scanAction } from '../scan';
3
3
  import * as core from '@aiready/core';
4
4
  import * as index from '../../index';
5
5
  import * as upload from '../upload';
6
- import { writeFileSync, readFileSync, existsSync } from 'fs';
6
+ import { readFileSync } from 'fs';
7
7
  import { Severity } from '@aiready/core';
8
8
 
9
9
  vi.mock('../../index', () => ({
@@ -1,6 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { uploadAction } from '../upload';
3
- import fs from 'fs';
4
3
 
5
4
  vi.mock('fs', () => ({
6
5
  default: {
@@ -1,5 +1,4 @@
1
1
  import chalk from 'chalk';
2
- import { Command } from 'commander';
3
2
 
4
3
  import { execSync } from 'child_process';
5
4
 
@@ -38,7 +37,7 @@ Type: ${type}
38
37
  console.log(chalk.green('✅ Issue Created Successfully!'));
39
38
  console.log(chalk.cyan(output));
40
39
  return;
41
- } catch (error) {
40
+ } catch {
42
41
  console.error(chalk.red('\n❌ Failed to submit via gh CLI.'));
43
42
  console.log(
44
43
  chalk.yellow(
@@ -113,10 +113,6 @@ export async function initAction(options: {
113
113
  },
114
114
  ...(options.full
115
115
  ? {
116
- output: {
117
- format: fileExt,
118
- file: 'aiready-report.json',
119
- },
120
116
  visualizer: {
121
117
  groupingDirs: ['packages', 'src', 'lib'],
122
118
  graph: {
@@ -86,7 +86,9 @@ export async function patternsAction(
86
86
  const { analyzePatterns, generateSummary, calculatePatternScore } =
87
87
  await import('@aiready/pattern-detect');
88
88
 
89
- const { results, duplicates } = await analyzePatterns(finalOptions);
89
+ const { results, duplicates } = (await analyzePatterns(
90
+ finalOptions
91
+ )) as any;
90
92
 
91
93
  const elapsedTime = getElapsedTime(startTime);
92
94
  const summary = generateSummary(results);
@@ -0,0 +1,128 @@
1
+ import chalk from 'chalk';
2
+ import {
3
+ Severity,
4
+ ScoringResult,
5
+ formatScore,
6
+ getRating,
7
+ getRatingDisplay,
8
+ } from '@aiready/core';
9
+
10
+ /**
11
+ * Handle console output for the scan results
12
+ */
13
+ export function printScanSummary(results: any, startTime: number) {
14
+ console.log(chalk.cyan('\n=== AIReady Run Summary ==='));
15
+ console.log(
16
+ ` Total issues (all tools): ${chalk.bold(String(results.summary.totalIssues || 0))}`
17
+ );
18
+ console.log(
19
+ ` Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
20
+ );
21
+ }
22
+
23
+ /**
24
+ * Print business impact analysis
25
+ */
26
+ export function printBusinessImpact(roi: any, unifiedBudget: any) {
27
+ console.log(chalk.bold('\n💰 Business Impact Analysis (Monthly)'));
28
+ console.log(
29
+ ` Potential Savings: ${chalk.green(chalk.bold('$' + roi.monthlySavings.toLocaleString()))}`
30
+ );
31
+ console.log(
32
+ ` Productivity Gain: ${chalk.cyan(chalk.bold(roi.productivityGainHours + 'h'))} (est. dev time)`
33
+ );
34
+ console.log(
35
+ ` Context Efficiency: ${chalk.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + '%')}`
36
+ );
37
+ console.log(
38
+ ` Annual Value: ${chalk.bold('$' + roi.annualValue.toLocaleString())} (ROI Prediction)`
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Print detailed scoring breakdown
44
+ */
45
+ export function printScoring(
46
+ scoringResult: ScoringResult,
47
+ scoringProfile: string
48
+ ) {
49
+ console.log(chalk.bold('\n📊 AI Readiness Overall Score'));
50
+ console.log(` ${formatScore(scoringResult)}`);
51
+ console.log(chalk.dim(` (Scoring Profile: ${scoringProfile})`));
52
+
53
+ if (scoringResult.breakdown) {
54
+ console.log(chalk.bold('\nTool breakdown:'));
55
+ scoringResult.breakdown.forEach((tool) => {
56
+ const rating = getRating(tool.score);
57
+ const emoji = getRatingDisplay(rating).emoji;
58
+ console.log(
59
+ ` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
60
+ );
61
+ });
62
+
63
+ // Top Actionable Recommendations
64
+ const allRecs = scoringResult.breakdown
65
+ .flatMap((t) =>
66
+ (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
67
+ )
68
+ .sort((a, b) => b.estimatedImpact - a.estimatedImpact)
69
+ .slice(0, 3);
70
+
71
+ if (allRecs.length > 0) {
72
+ console.log(chalk.bold('\n🎯 Top Actionable Recommendations:'));
73
+ allRecs.forEach((rec, i) => {
74
+ const priorityIcon =
75
+ rec.priority === 'high'
76
+ ? '🔴'
77
+ : rec.priority === 'medium'
78
+ ? '🟡'
79
+ : '🔵';
80
+ console.log(` ${i + 1}. ${priorityIcon} ${chalk.bold(rec.action)}`);
81
+ console.log(
82
+ ` Impact: ${chalk.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
83
+ );
84
+ });
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Normalize report mapping (CLI logic)
91
+ */
92
+ export function mapToUnifiedReport(
93
+ res: any,
94
+ scoring: ScoringResult | undefined
95
+ ) {
96
+ const allResults: any[] = [];
97
+ const totalFilesSet = new Set<string>();
98
+ let criticalCount = 0;
99
+ let majorCount = 0;
100
+
101
+ res.summary.toolsRun.forEach((toolId: string) => {
102
+ const spokeRes = res[toolId];
103
+ if (!spokeRes || !spokeRes.results) return;
104
+
105
+ spokeRes.results.forEach((r: any) => {
106
+ totalFilesSet.add(r.fileName);
107
+ allResults.push(r);
108
+ r.issues?.forEach((i: any) => {
109
+ if (i.severity === Severity.Critical || i.severity === 'critical')
110
+ criticalCount++;
111
+ if (i.severity === Severity.Major || i.severity === 'major')
112
+ majorCount++;
113
+ });
114
+ });
115
+ });
116
+
117
+ return {
118
+ ...res,
119
+ results: allResults,
120
+ summary: {
121
+ ...res.summary,
122
+ totalFiles: totalFilesSet.size,
123
+ criticalIssues: criticalCount,
124
+ majorIssues: majorCount,
125
+ },
126
+ scoring,
127
+ };
128
+ }
@@ -9,28 +9,20 @@ import {
9
9
  loadMergedConfig,
10
10
  handleJSONOutput,
11
11
  handleCLIError,
12
- getElapsedTime,
13
12
  resolveOutputPath,
14
- formatScore,
15
- formatToolScore,
16
- calculateTokenBudget,
17
- estimateCostFromBudget,
18
- getModelPreset,
19
- getRating,
20
- getRatingDisplay,
21
13
  getRepoMetadata,
22
- Severity,
23
- IssueType,
14
+ calculateTokenBudget,
24
15
  ToolName,
25
- ToolRegistry,
26
16
  emitIssuesAsAnnotations,
27
17
  } from '@aiready/core';
28
18
  import { analyzeUnified, scoreUnified, type ScoringResult } from '../index';
19
+ import { getReportTimestamp, warnIfGraphCapExceeded } from '../utils/helpers';
29
20
  import {
30
- getReportTimestamp,
31
- warnIfGraphCapExceeded,
32
- truncateArray,
33
- } from '../utils/helpers';
21
+ printScanSummary,
22
+ printBusinessImpact,
23
+ printScoring,
24
+ mapToUnifiedReport,
25
+ } from './report-formatter';
34
26
  import { uploadAction } from './upload';
35
27
 
36
28
  interface ScanOptions {
@@ -53,6 +45,14 @@ interface ScanOptions {
53
45
  server?: string;
54
46
  }
55
47
 
48
+ /**
49
+ * CLI action handler for the "scan" command.
50
+ * Runs a comprehensive AI-readiness analysis across multiple tools,
51
+ * including pattern detection, context analysis, and naming consistency.
52
+ *
53
+ * @param directory - The directory to analyze (defaults to ".")
54
+ * @param options - CLI options from commander
55
+ */
56
56
  export async function scanAction(directory: string, options: ScanOptions) {
57
57
  console.log(chalk.blue('🚀 Starting AIReady unified analysis...\n'));
58
58
 
@@ -211,13 +211,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
211
211
  suppressToolConfig: true,
212
212
  });
213
213
 
214
- console.log(chalk.cyan('\n=== AIReady Run Summary ==='));
215
- console.log(
216
- ` Total issues (all tools): ${chalk.bold(String(results.summary.totalIssues || 0))}`
217
- );
218
- console.log(
219
- ` Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
220
- );
214
+ printScanSummary(results, startTime);
221
215
 
222
216
  let scoringResult: ScoringResult | undefined;
223
217
  if (options.score || finalOptions.scoring?.showBreakdown) {
@@ -230,9 +224,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
230
224
  },
231
225
  });
232
226
 
233
- console.log(chalk.bold('\n📊 AI Readiness Overall Score'));
234
- console.log(` ${formatScore(scoringResult)}`);
235
- console.log(chalk.dim(` (Scoring Profile: ${scoringProfile})`));
227
+ printScoring(scoringResult, scoringProfile);
236
228
 
237
229
  // Trend comparison logic
238
230
  if (options.compareTo) {
@@ -315,19 +307,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
315
307
  modelId: modelId,
316
308
  });
317
309
 
318
- console.log(chalk.bold('\n💰 Business Impact Analysis (Monthly)'));
319
- console.log(
320
- ` Potential Savings: ${chalk.green(chalk.bold('$' + roi.monthlySavings.toLocaleString()))}`
321
- );
322
- console.log(
323
- ` Productivity Gain: ${chalk.cyan(chalk.bold(roi.productivityGainHours + 'h'))} (est. dev time)`
324
- );
325
- console.log(
326
- ` Context Efficiency: ${chalk.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + '%')}`
327
- );
328
- console.log(
329
- ` Annual Value: ${chalk.bold('$' + roi.annualValue.toLocaleString())} (ROI Prediction)`
330
- );
310
+ printBusinessImpact(roi, unifiedBudget);
331
311
 
332
312
  (results.summary as any).businessImpact = {
333
313
  estimatedMonthlyWaste: roi.monthlySavings,
@@ -338,43 +318,6 @@ export async function scanAction(directory: string, options: ScanOptions) {
338
318
  (scoringResult as any).tokenBudget = unifiedBudget;
339
319
  (scoringResult as any).businessROI = roi;
340
320
  }
341
-
342
- if (scoringResult.breakdown) {
343
- console.log(chalk.bold('\nTool breakdown:'));
344
- scoringResult.breakdown.forEach((tool) => {
345
- const rating = getRating(tool.score);
346
- const emoji = getRatingDisplay(rating).emoji;
347
- console.log(
348
- ` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
349
- );
350
- });
351
-
352
- // Top Actionable Recommendations
353
- const allRecs = scoringResult.breakdown
354
- .flatMap((t) =>
355
- (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
356
- )
357
- .sort((a, b) => b.estimatedImpact - a.estimatedImpact)
358
- .slice(0, 3);
359
-
360
- if (allRecs.length > 0) {
361
- console.log(chalk.bold('\n🎯 Top Actionable Recommendations:'));
362
- allRecs.forEach((rec, i) => {
363
- const priorityIcon =
364
- rec.priority === 'high'
365
- ? '🔴'
366
- : rec.priority === 'medium'
367
- ? '🟡'
368
- : '🔵';
369
- console.log(
370
- ` ${i + 1}. ${priorityIcon} ${chalk.bold(rec.action)}`
371
- );
372
- console.log(
373
- ` Impact: ${chalk.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
374
- );
375
- });
376
- }
377
- }
378
321
  }
379
322
 
380
323
  console.log(
@@ -395,43 +338,6 @@ export async function scanAction(directory: string, options: ScanOptions) {
395
338
  )
396
339
  );
397
340
 
398
- // Normalized report mapping
399
- const mapToUnifiedReport = (
400
- res: any,
401
- scoring: ScoringResult | undefined
402
- ) => {
403
- const allResults: any[] = [];
404
- const totalFilesSet = new Set<string>();
405
- let criticalCount = 0;
406
- let majorCount = 0;
407
-
408
- res.summary.toolsRun.forEach((toolId: string) => {
409
- const spokeRes = res[toolId];
410
- if (!spokeRes || !spokeRes.results) return;
411
-
412
- spokeRes.results.forEach((r: any) => {
413
- totalFilesSet.add(r.fileName);
414
- allResults.push(r);
415
- r.issues?.forEach((i: any) => {
416
- if (i.severity === Severity.Critical) criticalCount++;
417
- if (i.severity === Severity.Major) majorCount++;
418
- });
419
- });
420
- });
421
-
422
- return {
423
- ...res,
424
- results: allResults,
425
- summary: {
426
- ...res.summary,
427
- totalFiles: totalFilesSet.size,
428
- criticalIssues: criticalCount,
429
- majorIssues: majorCount,
430
- },
431
- scoring,
432
- };
433
- };
434
-
435
341
  const outputData = {
436
342
  ...mapToUnifiedReport(results, scoringResult),
437
343
  repository: repoMetadata,
@@ -51,7 +51,7 @@ export async function uploadAction(file: string, options: UploadOptions) {
51
51
  // Note: repoId is optional if the metadata contains it, but for now we'll require it or infer from metadata
52
52
  const repoId = options.repoId || reportData.repository?.repoId;
53
53
 
54
- const res = await fetch(`${serverUrl}/api/analysis/upload`, {
54
+ const response = await fetch(`${serverUrl}/api/analysis/upload`, {
55
55
  method: 'POST',
56
56
  headers: {
57
57
  'Content-Type': 'application/json',
@@ -63,19 +63,21 @@ export async function uploadAction(file: string, options: UploadOptions) {
63
63
  }),
64
64
  });
65
65
 
66
- const contentType = res.headers.get('content-type');
67
- let result: any = {};
66
+ const contentType = response.headers.get('content-type');
67
+ let uploadResult: any = {};
68
68
 
69
69
  if (contentType?.includes('application/json')) {
70
- result = await res.json();
70
+ uploadResult = await response.json();
71
71
  } else {
72
- const text = await res.text();
73
- result = { error: text || res.statusText };
72
+ const text = await response.text();
73
+ uploadResult = { error: text || response.statusText };
74
74
  }
75
75
 
76
- if (!res.ok) {
76
+ if (!response.ok) {
77
77
  console.error(
78
- chalk.red(`❌ Upload failed: ${result.error || res.statusText}`)
78
+ chalk.red(
79
+ `❌ Upload failed: ${uploadResult.error || response.statusText}`
80
+ )
79
81
  );
80
82
 
81
83
  // Special case for redirects or HTML error pages
@@ -85,7 +87,7 @@ export async function uploadAction(file: string, options: UploadOptions) {
85
87
  ' Note: Received an HTML response. This often indicates a redirect (e.g., to a login page) or a server error.'
86
88
  )
87
89
  );
88
- if (result.error?.includes('Redirecting')) {
90
+ if (uploadResult.error?.includes('Redirecting')) {
89
91
  console.log(
90
92
  chalk.dim(
91
93
  ' Detected redirect. Check if the API endpoint requires authentication or has changed.'
@@ -94,7 +96,7 @@ export async function uploadAction(file: string, options: UploadOptions) {
94
96
  }
95
97
  }
96
98
 
97
- if (res.status === 401) {
99
+ if (response.status === 401) {
98
100
  console.log(
99
101
  chalk.dim(' Hint: Your API key may be invalid or expired.')
100
102
  );
@@ -106,9 +108,9 @@ export async function uploadAction(file: string, options: UploadOptions) {
106
108
  console.log(chalk.green(`\n✅ Upload successful! (${duration}s)`));
107
109
  console.log(chalk.cyan(` View results: ${serverUrl}/dashboard`));
108
110
 
109
- if (result.analysis) {
110
- console.log(chalk.dim(` Analysis ID: ${result.analysis.id}`));
111
- console.log(chalk.dim(` Score: ${result.analysis.aiScore}/100`));
111
+ if (uploadResult.analysis) {
112
+ console.log(chalk.dim(` Analysis ID: ${uploadResult.analysis.id}`));
113
+ console.log(chalk.dim(` Score: ${uploadResult.analysis.aiScore}/100`));
112
114
  }
113
115
  } catch (error) {
114
116
  handleCLIError(error, 'Upload');
@@ -17,6 +17,14 @@ interface VisualizeOptions {
17
17
  dev?: boolean;
18
18
  }
19
19
 
20
+ /**
21
+ * CLI action handler for the "visualize" command.
22
+ * Generates an interactive dependency graph visualization of the project
23
+ * to help understand code structure and AI context usage.
24
+ *
25
+ * @param directory - The directory to analyze and visualize
26
+ * @param options - CLI options from commander
27
+ */
20
28
  export async function visualizeAction(
21
29
  directory: string,
22
30
  options: VisualizeOptions
@@ -220,7 +228,7 @@ export async function visualizeAction(
220
228
 
221
229
  // Generate static HTML (default behavior or fallback from failed --dev)
222
230
  console.log('Generating HTML...');
223
- const html = generateHTML(graph);
231
+ const html = generateHTML(graph as any);
224
232
  const defaultOutput = 'visualization.html';
225
233
  const outPath = resolvePath(dirPath, options.output || defaultOutput);
226
234
  writeFileSync(outPath, html, 'utf8');
package/src/index.ts CHANGED
@@ -3,15 +3,12 @@ import {
3
3
  ToolName,
4
4
  calculateOverallScore,
5
5
  calculateTokenBudget,
6
- GLOBAL_SCAN_OPTIONS,
7
6
  GLOBAL_INFRA_OPTIONS,
8
7
  COMMON_FINE_TUNING_OPTIONS,
9
8
  initializeParsers,
10
9
  } from '@aiready/core';
11
10
  import type {
12
- AnalysisResult,
13
11
  ScanOptions,
14
- SpokeOutput,
15
12
  ToolScoringOutput,
16
13
  ScoringResult,
17
14
  } from '@aiready/core';
@@ -29,16 +26,30 @@ import '@aiready/change-amplification';
29
26
 
30
27
  export type { ToolScoringOutput, ScoringResult };
31
28
 
29
+ /**
30
+ * Options for running a unified AI-readiness analysis across multiple tools.
31
+ * Extends base ScanOptions with CLI-specific configurations.
32
+ */
32
33
  export interface UnifiedAnalysisOptions extends ScanOptions {
34
+ /** Root directory for analysis */
33
35
  rootDir: string;
36
+ /** List of tools to run (e.g. ['patterns', 'context']) */
34
37
  tools?: string[];
38
+ /** Overrides for specific tool configurations */
35
39
  toolConfigs?: Record<string, any>;
40
+ /** Minimum similarity threshold for pattern detection (0-1) */
36
41
  minSimilarity?: number;
42
+ /** Minimum number of lines for a pattern to be considered */
37
43
  minLines?: number;
44
+ /** Maximum number of candidates to check per code block */
38
45
  maxCandidatesPerBlock?: number;
46
+ /** Minimum number of shared tokens for a match */
39
47
  minSharedTokens?: number;
48
+ /** Whether to use optimized defaults based on project size/language */
40
49
  useSmartDefaults?: boolean;
50
+ /** Specific options for naming consistency analysis */
41
51
  consistency?: any;
52
+ /** Optional callback for tracking analysis progress */
42
53
  progressCallback?: (event: {
43
54
  tool: string;
44
55
  data?: any;
@@ -48,6 +59,10 @@ export interface UnifiedAnalysisOptions extends ScanOptions {
48
59
  }) => void;
49
60
  }
50
61
 
62
+ /**
63
+ * The consolidated result of a unified analysis across all requested tools.
64
+ * Contains tool-specific outputs, scoring, and a high-level summary.
65
+ */
51
66
  export interface UnifiedAnalysisResult {
52
67
  // Dynamic keys based on ToolName
53
68
  [key: string]: any;
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { resolve as resolvePath } from 'path';
6
- import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
6
+ import { existsSync, readFileSync } from 'fs';
7
7
  import chalk from 'chalk';
8
8
  import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
9
9
  import type { ToolScoringOutput } from '@aiready/core';