@aiready/cli 0.14.2 → 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.
Files changed (44) hide show
  1. package/.aiready/aiready-report-20260314-164626.json +2 -5
  2. package/.aiready/aiready-report-20260314-164741.json +2 -5
  3. package/.turbo/turbo-build.log +29 -28
  4. package/.turbo/turbo-lint.log +0 -32
  5. package/.turbo/turbo-test.log +35 -125
  6. package/aiready-report.json +30703 -0
  7. package/dist/cli.js +415 -378
  8. package/dist/cli.mjs +358 -320
  9. package/package.json +12 -12
  10. package/packages/core/src/.aiready/aiready-report-20260314-161145.json +4 -10
  11. package/packages/core/src/.aiready/aiready-report-20260314-161152.json +10 -28
  12. package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +4 -10
  13. package/src/.aiready/aiready-report-20260312-103623.json +3 -9
  14. package/src/.aiready/aiready-report-20260312-110843.json +3 -9
  15. package/src/.aiready/aiready-report-20260312-110955.json +3 -9
  16. package/src/.aiready/aiready-report-20260314-203209.json +3 -9
  17. package/src/.aiready/aiready-report-20260314-203736.json +3 -9
  18. package/src/.aiready/aiready-report-20260314-203857.json +3 -9
  19. package/src/.aiready/aiready-report-20260314-204047.json +3 -9
  20. package/src/__tests__/cli.test.ts +1 -1
  21. package/src/__tests__/config-shape.test.ts +0 -1
  22. package/src/__tests__/unified.test.ts +1 -1
  23. package/src/cli.ts +2 -1
  24. package/src/commands/__tests__/consistency.test.ts +3 -0
  25. package/src/commands/__tests__/extra-commands.test.ts +29 -38
  26. package/src/commands/__tests__/init.test.ts +56 -0
  27. package/src/commands/__tests__/scan.test.ts +4 -2
  28. package/src/commands/__tests__/upload.test.ts +0 -1
  29. package/src/commands/__tests__/visualize.test.ts +3 -7
  30. package/src/commands/ai-signal-clarity.ts +1 -56
  31. package/src/commands/bug.ts +1 -2
  32. package/src/commands/deps-health.ts +1 -65
  33. package/src/commands/doc-drift.ts +1 -62
  34. package/src/commands/init.ts +58 -2
  35. package/src/commands/patterns.ts +3 -1
  36. package/src/commands/report-formatter.ts +128 -0
  37. package/src/commands/scan.ts +29 -120
  38. package/src/commands/shared/configured-tool-action.ts +35 -0
  39. package/src/commands/shared/standard-tool-actions.ts +126 -0
  40. package/src/commands/upload.ts +15 -13
  41. package/src/commands/visualize.ts +11 -4
  42. package/src/index.ts +18 -3
  43. package/src/utils/helpers.ts +86 -37
  44. package/vitest.config.ts +5 -12
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { visualizeAction } from '../visualize';
3
3
  import * as fs from 'fs';
4
4
  import * as core from '@aiready/core';
5
- import * as helpers from '../../utils/helpers';
6
5
  import { spawn } from 'child_process';
7
6
 
8
7
  vi.mock('fs', async () => {
@@ -29,10 +28,7 @@ vi.mock('@aiready/visualizer/graph', () => ({
29
28
  vi.mock('@aiready/core', () => ({
30
29
  handleCLIError: vi.fn(),
31
30
  generateHTML: vi.fn().mockReturnValue('<html></html>'),
32
- }));
33
-
34
- vi.mock('../../utils/helpers', () => ({
35
- findLatestScanReport: vi.fn(),
31
+ findLatestReport: vi.fn(),
36
32
  }));
37
33
 
38
34
  describe('Visualize CLI Action', () => {
@@ -54,7 +50,7 @@ describe('Visualize CLI Action', () => {
54
50
  });
55
51
 
56
52
  it('should find latest report if none specified', async () => {
57
- vi.spyOn(helpers, 'findLatestScanReport').mockReturnValue('latest.json');
53
+ vi.mocked(core.findLatestReport).mockReturnValue('latest.json');
58
54
  await visualizeAction('.', {});
59
55
  expect(fs.readFileSync).toHaveBeenCalledWith(
60
56
  expect.stringContaining('latest.json'),
@@ -64,7 +60,7 @@ describe('Visualize CLI Action', () => {
64
60
 
65
61
  it('should handle missing reports', async () => {
66
62
  vi.spyOn(fs, 'existsSync').mockReturnValue(false);
67
- vi.spyOn(helpers, 'findLatestScanReport').mockReturnValue(null);
63
+ vi.mocked(core.findLatestReport).mockReturnValue(null);
68
64
  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
69
65
 
70
66
  await visualizeAction('.', { report: 'missing.json' });
@@ -1,56 +1 @@
1
- /**
2
- * AI signal clarity command for unified CLI
3
- */
4
-
5
- import chalk from 'chalk';
6
- import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
7
- import type { ToolScoringOutput } from '@aiready/core';
8
-
9
- export async function aiSignalClarityAction(
10
- directory: string,
11
- options: any
12
- ): Promise<ToolScoringOutput | undefined> {
13
- const { analyzeAiSignalClarity, calculateAiSignalClarityScore } =
14
- await import('@aiready/ai-signal-clarity');
15
-
16
- const config = await loadConfig(directory);
17
- const merged = mergeConfigWithDefaults(config, {
18
- minSeverity: 'info',
19
- });
20
-
21
- const report = await analyzeAiSignalClarity({
22
- rootDir: directory,
23
- minSeverity: options.minSeverity ?? merged.minSeverity ?? 'info',
24
- include: options.include,
25
- exclude: options.exclude,
26
- });
27
-
28
- const scoring = calculateAiSignalClarityScore(report);
29
-
30
- if (options.output === 'json') {
31
- return scoring;
32
- }
33
-
34
- const { summary } = report;
35
- const ratingColors: Record<string, (s: string) => string> = {
36
- minimal: chalk.green,
37
- low: chalk.cyan,
38
- moderate: chalk.yellow,
39
- high: chalk.red,
40
- severe: chalk.bgRed.white,
41
- };
42
- const color = ratingColors[summary.rating] ?? chalk.white;
43
- console.log(
44
- ` 🧠 AI Signal Clarity: ${chalk.bold(scoring.score + '/100')} (${color(summary.rating)})`
45
- );
46
- console.log(` Top Risk: ${chalk.italic(summary.topRisk)}`);
47
- if (summary.totalSignals > 0) {
48
- console.log(
49
- chalk.dim(
50
- ` ${summary.criticalSignals} critical ${summary.majorSignals} major ${summary.minorSignals} minor signals`
51
- )
52
- );
53
- }
54
-
55
- return scoring;
56
- }
1
+ export { aiSignalClarityAction } from './shared/standard-tool-actions';
@@ -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(
@@ -1,65 +1 @@
1
- /**
2
- * Dependency health command for unified CLI
3
- */
4
-
5
- import chalk from 'chalk';
6
- import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
7
- import type { ToolScoringOutput } from '@aiready/core';
8
-
9
- export async function depsHealthAction(
10
- directory: string,
11
- options: any
12
- ): Promise<ToolScoringOutput | undefined> {
13
- const { analyzeDeps } = await import('@aiready/deps');
14
-
15
- const config = await loadConfig(directory);
16
- const merged = mergeConfigWithDefaults(config, {
17
- trainingCutoffYear: 2023,
18
- });
19
-
20
- const report = await analyzeDeps({
21
- rootDir: directory,
22
- include: options.include,
23
- exclude: options.exclude,
24
- trainingCutoffYear:
25
- options.trainingCutoffYear ?? merged.trainingCutoffYear ?? 2023,
26
- });
27
-
28
- const scoring: ToolScoringOutput = {
29
- toolName: 'dependency-health',
30
- score: report.summary.score,
31
- rawMetrics: report.rawData,
32
- factors: [],
33
- recommendations: report.recommendations.map((action: string) => ({
34
- action,
35
- estimatedImpact: 5,
36
- priority: 'medium',
37
- })),
38
- };
39
-
40
- if (options.output === 'json') {
41
- return scoring;
42
- }
43
-
44
- const { summary } = report;
45
- const ratingColors: Record<string, (s: string) => string> = {
46
- excellent: chalk.green,
47
- good: chalk.blueBright,
48
- moderate: chalk.yellow,
49
- poor: chalk.red,
50
- hazardous: chalk.bgRed.white,
51
- };
52
- const color = ratingColors[summary.rating] ?? chalk.white;
53
- console.log(
54
- ` šŸ“¦ Dependency Health: ${chalk.bold(scoring.score + '/100 health')} (${color(summary.rating)})`
55
- );
56
- if (report.issues.length > 0) {
57
- console.log(
58
- chalk.dim(` Found ${report.issues.length} dependency issues.`)
59
- );
60
- } else {
61
- console.log(chalk.dim(` Dependencies look healthy for AI assistance.`));
62
- }
63
-
64
- return scoring;
65
- }
1
+ export { depsHealthAction } from './shared/standard-tool-actions';
@@ -1,62 +1 @@
1
- /**
2
- * Doc drift risk command for unified CLI
3
- */
4
-
5
- import chalk from 'chalk';
6
- import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
7
- import type { ToolScoringOutput } from '@aiready/core';
8
-
9
- export async function docDriftAction(
10
- directory: string,
11
- options: any
12
- ): Promise<ToolScoringOutput | undefined> {
13
- const { analyzeDocDrift } = await import('@aiready/doc-drift');
14
-
15
- const config = await loadConfig(directory);
16
- const merged = mergeConfigWithDefaults(config, {
17
- staleMonths: 6,
18
- });
19
-
20
- const report = await analyzeDocDrift({
21
- rootDir: directory,
22
- include: options.include,
23
- exclude: options.exclude,
24
- staleMonths: options.staleMonths ?? merged.staleMonths ?? 6,
25
- });
26
-
27
- const scoring: ToolScoringOutput = {
28
- toolName: 'doc-drift',
29
- score: report.summary.score,
30
- rawMetrics: report.rawData,
31
- factors: [],
32
- recommendations: report.recommendations.map((action: string) => ({
33
- action,
34
- estimatedImpact: 5,
35
- priority: 'medium',
36
- })),
37
- };
38
-
39
- if (options.output === 'json') {
40
- return scoring;
41
- }
42
-
43
- const { summary } = report;
44
- const ratingColors: Record<string, (s: string) => string> = {
45
- minimal: chalk.green,
46
- low: chalk.cyan,
47
- moderate: chalk.yellow,
48
- high: chalk.red,
49
- severe: chalk.bgRed.white,
50
- };
51
- const color = ratingColors[summary.rating] ?? chalk.white;
52
- console.log(
53
- ` šŸ“ Documentation Drift: ${chalk.bold(100 - scoring.score + '/100 health')} (${color(summary.rating)} risk)`
54
- );
55
- if (report.issues.length > 0) {
56
- console.log(chalk.dim(` Found ${report.issues.length} drift issues.`));
57
- } else {
58
- console.log(chalk.dim(` No documentation drift detected.`));
59
- }
60
-
61
- return scoring;
62
- }
1
+ export { docDriftAction } from './shared/standard-tool-actions';
@@ -6,6 +6,7 @@ import { ToolName } from '@aiready/core';
6
6
  export async function initAction(options: {
7
7
  force?: boolean;
8
8
  format?: 'json' | 'js';
9
+ full?: boolean;
9
10
  }) {
10
11
  const fileExt = options.format === 'js' ? 'js' : 'json';
11
12
  const fileName = fileExt === 'js' ? 'aiready.config.js' : 'aiready.json';
@@ -18,7 +19,7 @@ export async function initAction(options: {
18
19
  process.exit(1);
19
20
  }
20
21
 
21
- const defaultConfig = {
22
+ const baseConfig = {
22
23
  scan: {
23
24
  include: [
24
25
  'src/**/*.ts',
@@ -49,28 +50,83 @@ export async function initAction(options: {
49
50
  [ToolName.PatternDetect]: {
50
51
  minSimilarity: 0.8,
51
52
  minLines: 5,
53
+ ...(options.full
54
+ ? {
55
+ batchSize: 50,
56
+ approx: true,
57
+ minSharedTokens: 10,
58
+ maxCandidatesPerBlock: 100,
59
+ }
60
+ : {}),
52
61
  },
53
62
  [ToolName.ContextAnalyzer]: {
54
63
  maxContextBudget: 128000,
55
64
  minCohesion: 0.6,
65
+ ...(options.full
66
+ ? {
67
+ maxDepth: 7,
68
+ maxFragmentation: 0.4,
69
+ focus: 'all',
70
+ includeNodeModules: false,
71
+ }
72
+ : {}),
56
73
  },
57
74
  [ToolName.NamingConsistency]: {
58
75
  shortWords: ['id', 'db', 'ui', 'ai'],
76
+ ...(options.full
77
+ ? { acceptedAbbreviations: [], disableChecks: [] }
78
+ : {}),
59
79
  },
60
80
  [ToolName.AiSignalClarity]: {
61
81
  checkMagicLiterals: true,
62
82
  checkBooleanTraps: true,
63
83
  checkAmbiguousNames: true,
64
84
  checkUndocumentedExports: true,
85
+ ...(options.full
86
+ ? { checkImplicitSideEffects: false, checkDeepCallbacks: false }
87
+ : {}),
65
88
  },
89
+ ...(options.full
90
+ ? {
91
+ [ToolName.AgentGrounding]: {
92
+ maxRecommendedDepth: 5,
93
+ readmeStaleDays: 30,
94
+ },
95
+ [ToolName.TestabilityIndex]: {
96
+ minCoverageRatio: 0.7,
97
+ testPatterns: ['**/*.test.ts', '**/__tests__/**'],
98
+ },
99
+ [ToolName.DocDrift]: {
100
+ maxCommits: 50,
101
+ staleMonths: 3,
102
+ },
103
+ [ToolName.DependencyHealth]: {
104
+ trainingCutoffYear: 2023,
105
+ },
106
+ }
107
+ : {}),
66
108
  },
67
109
  scoring: {
68
110
  threshold: 70,
69
111
  showBreakdown: true,
112
+ ...(options.full ? { profile: 'default' } : {}),
70
113
  },
114
+ ...(options.full
115
+ ? {
116
+ visualizer: {
117
+ groupingDirs: ['packages', 'src', 'lib'],
118
+ graph: {
119
+ maxNodes: 5000,
120
+ maxEdges: 10000,
121
+ },
122
+ },
123
+ }
124
+ : {}),
71
125
  };
72
126
 
73
- let content = '';
127
+ const defaultConfig = baseConfig;
128
+
129
+ let content: string;
74
130
  if (fileExt === 'js') {
75
131
  content = `/** @type {import('@aiready/core').AIReadyConfig} */\nmodule.exports = ${JSON.stringify(
76
132
  defaultConfig,
@@ -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
+ }