@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.
- package/.aiready/aiready-report-20260314-164626.json +2 -5
- package/.aiready/aiready-report-20260314-164741.json +2 -5
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-test.log +19 -110
- package/dist/cli.js +149 -125
- package/dist/cli.mjs +69 -45
- package/package.json +12 -12
- package/packages/core/src/.aiready/aiready-report-20260314-161145.json +4 -10
- package/packages/core/src/.aiready/aiready-report-20260314-161152.json +10 -28
- package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +4 -10
- package/src/.aiready/aiready-report-20260312-103623.json +3 -9
- package/src/.aiready/aiready-report-20260312-110843.json +3 -9
- package/src/.aiready/aiready-report-20260312-110955.json +3 -9
- package/src/.aiready/aiready-report-20260314-203209.json +30713 -0
- package/src/.aiready/aiready-report-20260314-203736.json +30713 -0
- package/src/.aiready/aiready-report-20260314-203857.json +30713 -0
- package/src/.aiready/aiready-report-20260314-204047.json +30713 -0
- package/src/__tests__/config-shape.test.ts +1 -1
- package/src/cli.ts +2 -1
- package/src/commands/__tests__/consistency.test.ts +3 -0
- package/src/commands/__tests__/extra-commands.test.ts +29 -37
- package/src/commands/__tests__/scan.test.ts +3 -1
- package/src/commands/__tests__/visualize.test.ts +3 -7
- package/src/commands/ai-signal-clarity.ts +1 -56
- package/src/commands/deps-health.ts +1 -65
- package/src/commands/doc-drift.ts +1 -62
- package/src/commands/init.ts +62 -2
- package/src/commands/scan.ts +11 -8
- package/src/commands/shared/configured-tool-action.ts +35 -0
- package/src/commands/shared/standard-tool-actions.ts +126 -0
- package/src/commands/visualize.ts +2 -3
- package/src/utils/helpers.ts +85 -36
- 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 =
|
|
32
|
+
const latestScan = findLatestReport(dirPath);
|
|
34
33
|
if (latestScan) {
|
|
35
34
|
reportPath = latestScan;
|
|
36
35
|
console.log(
|
package/src/utils/helpers.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
});
|