@aiready/cli 0.14.1 → 0.14.3

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 (33) 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 +7 -7
  4. package/.turbo/turbo-test.log +19 -110
  5. package/dist/cli.js +149 -125
  6. package/dist/cli.mjs +69 -45
  7. package/package.json +12 -12
  8. package/packages/core/src/.aiready/aiready-report-20260314-161145.json +4 -10
  9. package/packages/core/src/.aiready/aiready-report-20260314-161152.json +10 -28
  10. package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +4 -10
  11. package/src/.aiready/aiready-report-20260312-103623.json +3 -9
  12. package/src/.aiready/aiready-report-20260312-110843.json +3 -9
  13. package/src/.aiready/aiready-report-20260312-110955.json +3 -9
  14. package/src/.aiready/aiready-report-20260314-203209.json +30713 -0
  15. package/src/.aiready/aiready-report-20260314-203736.json +30713 -0
  16. package/src/.aiready/aiready-report-20260314-203857.json +30713 -0
  17. package/src/.aiready/aiready-report-20260314-204047.json +30713 -0
  18. package/src/__tests__/config-shape.test.ts +1 -1
  19. package/src/cli.ts +2 -1
  20. package/src/commands/__tests__/consistency.test.ts +3 -0
  21. package/src/commands/__tests__/extra-commands.test.ts +29 -37
  22. package/src/commands/__tests__/scan.test.ts +3 -1
  23. package/src/commands/__tests__/visualize.test.ts +3 -7
  24. package/src/commands/ai-signal-clarity.ts +1 -56
  25. package/src/commands/deps-health.ts +1 -65
  26. package/src/commands/doc-drift.ts +1 -62
  27. package/src/commands/init.ts +62 -2
  28. package/src/commands/scan.ts +11 -8
  29. package/src/commands/shared/configured-tool-action.ts +35 -0
  30. package/src/commands/shared/standard-tool-actions.ts +126 -0
  31. package/src/commands/visualize.ts +2 -3
  32. package/src/utils/helpers.ts +85 -36
  33. package/vitest.config.ts +5 -12
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Shared implementations for standard config-driven CLI actions.
3
+ */
4
+
5
+ import chalk from 'chalk';
6
+ import type { ToolScoringOutput } from '@aiready/core';
7
+ import { buildToolScoringOutput } from '../../utils/helpers';
8
+ import { runConfiguredToolAction } from './configured-tool-action';
9
+
10
+ export async function docDriftAction(
11
+ directory: string,
12
+ options: any
13
+ ): Promise<ToolScoringOutput | undefined> {
14
+ const { analyzeDocDrift } = await import('@aiready/doc-drift');
15
+
16
+ return runConfiguredToolAction(directory, options, {
17
+ defaults: { staleMonths: 6 },
18
+ analyze: analyzeDocDrift as any,
19
+ getExtras: (cmdOptions, merged) => ({
20
+ staleMonths: cmdOptions.staleMonths ?? merged.staleMonths ?? 6,
21
+ }),
22
+ score: (toolReport): ToolScoringOutput =>
23
+ buildToolScoringOutput('doc-drift', toolReport as any),
24
+ render: (report: any, scoring) => {
25
+ const { summary } = report;
26
+ const ratingColors: Record<string, (s: string) => string> = {
27
+ minimal: chalk.green,
28
+ low: chalk.cyan,
29
+ moderate: chalk.yellow,
30
+ high: chalk.red,
31
+ severe: chalk.bgRed.white,
32
+ };
33
+ const color = ratingColors[summary.rating] ?? chalk.white;
34
+ console.log(
35
+ ` 📝 Documentation Drift: ${chalk.bold(100 - scoring.score + '/100 health')} (${color(summary.rating)} risk)`
36
+ );
37
+ if (report.issues.length > 0) {
38
+ console.log(
39
+ chalk.dim(` Found ${report.issues.length} drift issues.`)
40
+ );
41
+ } else {
42
+ console.log(chalk.dim(` No documentation drift detected.`));
43
+ }
44
+ },
45
+ });
46
+ }
47
+
48
+ export async function depsHealthAction(
49
+ directory: string,
50
+ options: any
51
+ ): Promise<ToolScoringOutput | undefined> {
52
+ const { analyzeDeps } = await import('@aiready/deps');
53
+
54
+ return runConfiguredToolAction(directory, options, {
55
+ defaults: { trainingCutoffYear: 2023 },
56
+ analyze: analyzeDeps as any,
57
+ getExtras: (cmdOptions, merged) => ({
58
+ trainingCutoffYear:
59
+ cmdOptions.trainingCutoffYear ?? merged.trainingCutoffYear ?? 2023,
60
+ }),
61
+ score: (toolReport): ToolScoringOutput =>
62
+ buildToolScoringOutput('dependency-health', toolReport as any),
63
+ render: (report: any, scoring) => {
64
+ const { summary } = report;
65
+ const ratingColors: Record<string, (s: string) => string> = {
66
+ excellent: chalk.green,
67
+ good: chalk.blueBright,
68
+ moderate: chalk.yellow,
69
+ poor: chalk.red,
70
+ hazardous: chalk.bgRed.white,
71
+ };
72
+ const color = ratingColors[summary.rating] ?? chalk.white;
73
+ console.log(
74
+ ` 📦 Dependency Health: ${chalk.bold(scoring.score + '/100 health')} (${color(summary.rating)})`
75
+ );
76
+ if (report.issues.length > 0) {
77
+ console.log(
78
+ chalk.dim(` Found ${report.issues.length} dependency issues.`)
79
+ );
80
+ } else {
81
+ console.log(
82
+ chalk.dim(` Dependencies look healthy for AI assistance.`)
83
+ );
84
+ }
85
+ },
86
+ });
87
+ }
88
+
89
+ export async function aiSignalClarityAction(
90
+ directory: string,
91
+ options: any
92
+ ): Promise<ToolScoringOutput | undefined> {
93
+ const { analyzeAiSignalClarity, calculateAiSignalClarityScore } =
94
+ await import('@aiready/ai-signal-clarity');
95
+
96
+ return runConfiguredToolAction(directory, options, {
97
+ defaults: { minSeverity: 'info' },
98
+ analyze: analyzeAiSignalClarity as any,
99
+ getExtras: (cmdOptions, merged) => ({
100
+ minSeverity: cmdOptions.minSeverity ?? merged.minSeverity ?? 'info',
101
+ }),
102
+ score: (toolReport) => calculateAiSignalClarityScore(toolReport as any),
103
+ render: (report: any, scoring) => {
104
+ const { summary } = report;
105
+ const ratingColors: Record<string, (s: string) => string> = {
106
+ minimal: chalk.green,
107
+ low: chalk.cyan,
108
+ moderate: chalk.yellow,
109
+ high: chalk.red,
110
+ severe: chalk.bgRed.white,
111
+ };
112
+ const color = ratingColors[summary.rating] ?? chalk.white;
113
+ console.log(
114
+ ` 🧠 AI Signal Clarity: ${chalk.bold(scoring.score + '/100')} (${color(summary.rating)})`
115
+ );
116
+ console.log(` Top Risk: ${chalk.italic(summary.topRisk)}`);
117
+ if (summary.totalSignals > 0) {
118
+ console.log(
119
+ chalk.dim(
120
+ ` ${summary.criticalSignals} critical ${summary.majorSignals} major ${summary.minorSignals} minor signals`
121
+ )
122
+ );
123
+ }
124
+ },
125
+ });
126
+ }
@@ -7,8 +7,7 @@ import { writeFileSync, readFileSync, existsSync, copyFileSync } from 'fs';
7
7
  import { resolve as resolvePath } from 'path';
8
8
  import { spawn } from 'child_process';
9
9
  import { handleCLIError } from '@aiready/core';
10
- import { generateHTML } from '@aiready/core';
11
- import { findLatestScanReport } from '../utils/helpers';
10
+ import { generateHTML, findLatestReport } from '@aiready/core';
12
11
 
13
12
  interface VisualizeOptions {
14
13
  report?: string;
@@ -30,7 +29,7 @@ export async function visualizeAction(
30
29
 
31
30
  // If report not provided or not found, try to find latest scan report
32
31
  if (!reportPath || !existsSync(reportPath)) {
33
- const latestScan = findLatestScanReport(dirPath);
32
+ const latestScan = findLatestReport(dirPath);
34
33
  if (latestScan) {
35
34
  reportPath = latestScan;
36
35
  console.log(
@@ -5,6 +5,11 @@
5
5
  import { resolve as resolvePath } from 'path';
6
6
  import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
7
7
  import chalk from 'chalk';
8
+ import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
9
+ import type { ToolScoringOutput } from '@aiready/core';
10
+
11
+ // Re-export findLatestReport from core for deduplication with visualizer
12
+ export { findLatestReport } from '@aiready/core';
8
13
 
9
14
  /**
10
15
  * Generate timestamp for report filenames (YYYYMMDD-HHMMSS)
@@ -16,42 +21,6 @@ export function getReportTimestamp(): string {
16
21
  return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
17
22
  }
18
23
 
19
- /**
20
- * Find the latest aiready report in the .aiready directory
21
- * Searches for both new format (aiready-report-*) and legacy format (aiready-scan-*)
22
- */
23
- export function findLatestScanReport(dirPath: string): string | null {
24
- const aireadyDir = resolvePath(dirPath, '.aiready');
25
- if (!existsSync(aireadyDir)) {
26
- return null;
27
- }
28
-
29
- // Search for new format first, then legacy format
30
- let files = readdirSync(aireadyDir).filter(
31
- (f) => f.startsWith('aiready-report-') && f.endsWith('.json')
32
- );
33
- if (files.length === 0) {
34
- files = readdirSync(aireadyDir).filter(
35
- (f) => f.startsWith('aiready-scan-') && f.endsWith('.json')
36
- );
37
- }
38
-
39
- if (files.length === 0) {
40
- return null;
41
- }
42
-
43
- // Sort by modification time, most recent first
44
- const sortedFiles = files
45
- .map((f) => ({
46
- name: f,
47
- path: resolvePath(aireadyDir, f),
48
- mtime: statSync(resolvePath(aireadyDir, f)).mtime,
49
- }))
50
- .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
51
-
52
- return sortedFiles[0].path;
53
- }
54
-
55
24
  /**
56
25
  * Warn if graph caps may be exceeded
57
26
  */
@@ -156,3 +125,83 @@ export function truncateArray(arr: any[] | undefined, cap = 8): string {
156
125
  const more = arr.length - shown.length;
157
126
  return shown.join(', ') + (more > 0 ? `, ... (+${more} more)` : '');
158
127
  }
128
+
129
+ /**
130
+ * Build a common ToolScoringOutput payload from a tool report.
131
+ */
132
+ export function buildToolScoringOutput(
133
+ toolName: string,
134
+ report: {
135
+ summary: { score: number };
136
+ rawData?: Record<string, unknown>;
137
+ recommendations?: string[];
138
+ }
139
+ ): ToolScoringOutput {
140
+ return {
141
+ toolName,
142
+ score: report.summary.score,
143
+ rawMetrics: report.rawData ?? {},
144
+ factors: [],
145
+ recommendations: (report.recommendations ?? []).map((action: string) => ({
146
+ action,
147
+ estimatedImpact: 5,
148
+ priority: 'medium',
149
+ })),
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Load config and apply tool-level defaults.
155
+ */
156
+ export async function loadMergedToolConfig<T extends Record<string, unknown>>(
157
+ directory: string,
158
+ defaults: T
159
+ ): Promise<T & Record<string, unknown>> {
160
+ const config = await loadConfig(directory);
161
+ return mergeConfigWithDefaults(config, defaults) as T &
162
+ Record<string, unknown>;
163
+ }
164
+
165
+ /**
166
+ * Shared base scan options used by CLI tool commands.
167
+ */
168
+ export function buildCommonScanOptions(
169
+ directory: string,
170
+ options: any,
171
+ extras: Record<string, unknown> = {}
172
+ ): Record<string, unknown> {
173
+ return {
174
+ rootDir: directory,
175
+ include: options.include,
176
+ exclude: options.exclude,
177
+ ...extras,
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Execute a config-driven tool command with shared CLI plumbing.
183
+ */
184
+ export async function runConfiguredToolCommand<TReport, TScoring>(params: {
185
+ directory: string;
186
+ options: any;
187
+ defaults: Record<string, unknown>;
188
+ analyze: (scanOptions: any) => Promise<TReport>;
189
+ getExtras: (
190
+ options: any,
191
+ merged: Record<string, unknown>
192
+ ) => Record<string, unknown>;
193
+ score: (report: TReport) => TScoring;
194
+ }): Promise<{ report: TReport; scoring: TScoring }> {
195
+ const merged = await loadMergedToolConfig(params.directory, params.defaults);
196
+ const report = await params.analyze(
197
+ buildCommonScanOptions(
198
+ params.directory,
199
+ params.options,
200
+ params.getExtras(params.options, merged)
201
+ )
202
+ );
203
+ return {
204
+ report,
205
+ scoring: params.score(report),
206
+ };
207
+ }
package/vitest.config.ts CHANGED
@@ -1,20 +1,13 @@
1
1
  import { defineConfig } from 'vitest/config';
2
- import { resolve } from 'path';
2
+ import { createAireadyVitestAliases } from '../../vitest-aliases';
3
3
 
4
4
  export default defineConfig({
5
5
  test: {
6
6
  globals: true,
7
7
  environment: 'node',
8
- alias: {
9
- '@aiready/core': resolve(__dirname, '../core/src/index.ts'),
10
- '@aiready/pattern-detect': resolve(
11
- __dirname,
12
- '../pattern-detect/src/index.ts'
13
- ),
14
- '@aiready/context-analyzer': resolve(
15
- __dirname,
16
- '../context-analyzer/src/index.ts'
17
- ),
18
- },
8
+ alias: createAireadyVitestAliases(__dirname, {
9
+ packagesRootRelative: '..',
10
+ useIndexEntrypoints: true,
11
+ }),
19
12
  },
20
13
  });