@aiready/cli 0.14.9 → 0.14.11
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-20260319-201106.json +5566 -0
- package/.aiready/aiready-report-20260319-201511.json +5566 -0
- package/.aiready/aiready-report-20260319-202017.json +5708 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +19 -19
- package/dist/chunk-HBPSESJV.mjs +289 -0
- package/dist/chunk-JRRBBFYB.mjs +307 -0
- package/dist/chunk-YP2EVXQN.mjs +303 -0
- package/dist/chunk-ZQOTGPK4.mjs +289 -0
- package/dist/cli.js +219 -102
- package/dist/cli.mjs +196 -83
- package/dist/index.js +25 -21
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
- package/src/commands/__tests__/init.test.ts +6 -11
- package/src/commands/bug.ts +1 -1
- package/src/commands/consistency.ts +5 -5
- package/src/commands/context.ts +4 -4
- package/src/commands/init.ts +76 -42
- package/src/commands/patterns.ts +3 -3
- package/src/commands/report-formatter.ts +138 -12
- package/src/commands/scan.ts +18 -18
- package/src/commands/upload.ts +6 -6
- package/src/commands/visualize.ts +4 -4
- package/src/index.ts +35 -25
|
@@ -28,29 +28,24 @@ describe('initAction', () => {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('should generate aiready.json
|
|
31
|
+
it('should generate aiready.json with output field by default', async () => {
|
|
32
32
|
await initAction({});
|
|
33
33
|
|
|
34
34
|
expect(existsSync(configPath)).toBe(true);
|
|
35
35
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
36
|
-
expect(config).
|
|
36
|
+
expect(config).toHaveProperty('output');
|
|
37
|
+
expect(config.output.format).toBe('console');
|
|
37
38
|
});
|
|
38
39
|
|
|
39
|
-
it('should
|
|
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 () => {
|
|
40
|
+
it('should include scan, tools, scoring, and visualizer sections', async () => {
|
|
48
41
|
await initAction({ full: true });
|
|
49
42
|
|
|
50
43
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
51
44
|
expect(config).toHaveProperty('scan');
|
|
52
45
|
expect(config).toHaveProperty('tools');
|
|
53
46
|
expect(config).toHaveProperty('scoring');
|
|
47
|
+
expect(config).toHaveProperty('output');
|
|
54
48
|
expect(config).toHaveProperty('visualizer');
|
|
49
|
+
expect(config).toHaveProperty('threshold');
|
|
55
50
|
});
|
|
56
51
|
});
|
package/src/commands/bug.ts
CHANGED
|
@@ -11,7 +11,7 @@ export async function bugAction(message: string | undefined, options: any) {
|
|
|
11
11
|
|
|
12
12
|
if (message) {
|
|
13
13
|
// Agent-assisted pre-filled issue
|
|
14
|
-
const type = options.type
|
|
14
|
+
const type = options.type ?? 'bug';
|
|
15
15
|
const title = `[${type.toUpperCase()}] ${message}`;
|
|
16
16
|
const label =
|
|
17
17
|
type === 'bug' ? 'bug' : type === 'feature' ? 'enhancement' : 'metric';
|
|
@@ -34,7 +34,7 @@ export async function consistencyAction(
|
|
|
34
34
|
console.log(chalk.blue('🔍 Analyzing consistency...\n'));
|
|
35
35
|
|
|
36
36
|
const startTime = Date.now();
|
|
37
|
-
const resolvedDir = resolvePath(process.cwd(), directory
|
|
37
|
+
const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
|
|
38
38
|
|
|
39
39
|
try {
|
|
40
40
|
// Define defaults
|
|
@@ -69,7 +69,7 @@ export async function consistencyAction(
|
|
|
69
69
|
// Calculate score if requested
|
|
70
70
|
let consistencyScore: ToolScoringOutput | undefined;
|
|
71
71
|
if (options.score) {
|
|
72
|
-
const issues = report.results?.flatMap((r: any) => r.issues)
|
|
72
|
+
const issues = report.results?.flatMap((r: any) => r.issues) ?? [];
|
|
73
73
|
consistencyScore = calculateConsistencyScore(
|
|
74
74
|
issues,
|
|
75
75
|
report.summary.filesAnalyzed
|
|
@@ -77,8 +77,8 @@ export async function consistencyAction(
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const outputFormat =
|
|
80
|
-
options.output
|
|
81
|
-
const userOutputFile = options.outputFile
|
|
80
|
+
options.output ?? finalOptions.output?.format ?? 'console';
|
|
81
|
+
const userOutputFile = options.outputFile ?? finalOptions.output?.file;
|
|
82
82
|
|
|
83
83
|
if (outputFormat === 'json') {
|
|
84
84
|
const outputData = {
|
|
@@ -121,7 +121,7 @@ export async function consistencyAction(
|
|
|
121
121
|
console.log(` Naming: ${chalk.yellow(report.summary.namingIssues)}`);
|
|
122
122
|
console.log(` Patterns: ${chalk.yellow(report.summary.patternIssues)}`);
|
|
123
123
|
console.log(
|
|
124
|
-
` Architecture: ${chalk.yellow(report.summary.architectureIssues
|
|
124
|
+
` Architecture: ${chalk.yellow(report.summary.architectureIssues ?? 0)}`
|
|
125
125
|
);
|
|
126
126
|
console.log(`Analysis Time: ${chalk.gray(elapsedTime + 's')}\n`);
|
|
127
127
|
|
package/src/commands/context.ts
CHANGED
|
@@ -32,7 +32,7 @@ export async function contextAction(
|
|
|
32
32
|
console.log(chalk.blue('🧠 Analyzing context costs...\n'));
|
|
33
33
|
|
|
34
34
|
const startTime = Date.now();
|
|
35
|
-
const resolvedDir = resolvePath(process.cwd(), directory
|
|
35
|
+
const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
38
|
// Define defaults
|
|
@@ -94,8 +94,8 @@ export async function contextAction(
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const outputFormat =
|
|
97
|
-
options.output
|
|
98
|
-
const userOutputFile = options.outputFile
|
|
97
|
+
options.output ?? finalOptions.output?.format ?? 'console';
|
|
98
|
+
const userOutputFile = options.outputFile ?? finalOptions.output?.file;
|
|
99
99
|
|
|
100
100
|
if (outputFormat === 'json') {
|
|
101
101
|
const outputData = {
|
|
@@ -117,7 +117,7 @@ export async function contextAction(
|
|
|
117
117
|
);
|
|
118
118
|
} else {
|
|
119
119
|
// Console output - format the results nicely
|
|
120
|
-
const terminalWidth = process.stdout.columns
|
|
120
|
+
const terminalWidth = process.stdout.columns ?? 80;
|
|
121
121
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
122
122
|
const divider = '━'.repeat(dividerWidth);
|
|
123
123
|
|
package/src/commands/init.ts
CHANGED
|
@@ -20,6 +20,10 @@ export async function initAction(options: {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const baseConfig = {
|
|
23
|
+
// Target quality score threshold (0-100)
|
|
24
|
+
threshold: 75,
|
|
25
|
+
|
|
26
|
+
// Global scan settings
|
|
23
27
|
scan: {
|
|
24
28
|
include: [
|
|
25
29
|
'src/**/*.ts',
|
|
@@ -46,6 +50,20 @@ export async function initAction(options: {
|
|
|
46
50
|
ToolName.ChangeAmplification,
|
|
47
51
|
],
|
|
48
52
|
},
|
|
53
|
+
|
|
54
|
+
// Output preferences
|
|
55
|
+
output: {
|
|
56
|
+
format: 'console',
|
|
57
|
+
showBreakdown: true,
|
|
58
|
+
saveTo: 'aiready-report.json',
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Scoring profile and weights
|
|
62
|
+
scoring: {
|
|
63
|
+
profile: 'balanced',
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Tool-specific configurations
|
|
49
67
|
tools: {
|
|
50
68
|
[ToolName.PatternDetect]: {
|
|
51
69
|
minSimilarity: 0.8,
|
|
@@ -73,9 +91,33 @@ export async function initAction(options: {
|
|
|
73
91
|
},
|
|
74
92
|
[ToolName.NamingConsistency]: {
|
|
75
93
|
shortWords: ['id', 'db', 'ui', 'ai'],
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
acceptedAbbreviations: [
|
|
95
|
+
'API',
|
|
96
|
+
'JSON',
|
|
97
|
+
'CSV',
|
|
98
|
+
'HTML',
|
|
99
|
+
'CSS',
|
|
100
|
+
'HTTP',
|
|
101
|
+
'URL',
|
|
102
|
+
'SDK',
|
|
103
|
+
'CLI',
|
|
104
|
+
'AI',
|
|
105
|
+
'ML',
|
|
106
|
+
'ID',
|
|
107
|
+
'DB',
|
|
108
|
+
'UI',
|
|
109
|
+
'UX',
|
|
110
|
+
'DOM',
|
|
111
|
+
'UUID',
|
|
112
|
+
'GUID',
|
|
113
|
+
'DEFAULT',
|
|
114
|
+
'MAX',
|
|
115
|
+
'MIN',
|
|
116
|
+
'config',
|
|
117
|
+
'INIT',
|
|
118
|
+
'SKILL',
|
|
119
|
+
],
|
|
120
|
+
...(options.full ? { disableChecks: [] } : {}),
|
|
79
121
|
},
|
|
80
122
|
[ToolName.AiSignalClarity]: {
|
|
81
123
|
checkMagicLiterals: true,
|
|
@@ -86,53 +128,45 @@ export async function initAction(options: {
|
|
|
86
128
|
? { checkImplicitSideEffects: false, checkDeepCallbacks: false }
|
|
87
129
|
: {}),
|
|
88
130
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
: {}),
|
|
131
|
+
[ToolName.AgentGrounding]: {
|
|
132
|
+
maxRecommendedDepth: 5,
|
|
133
|
+
readmeStaleDays: 30,
|
|
134
|
+
},
|
|
135
|
+
[ToolName.TestabilityIndex]: {
|
|
136
|
+
minCoverageRatio: 0.7,
|
|
137
|
+
testPatterns: ['**/*.test.ts', '**/__tests__/**'],
|
|
138
|
+
},
|
|
139
|
+
[ToolName.DocDrift]: {
|
|
140
|
+
maxCommits: 50,
|
|
141
|
+
staleMonths: 3,
|
|
142
|
+
},
|
|
143
|
+
[ToolName.DependencyHealth]: {
|
|
144
|
+
trainingCutoffYear: 2023,
|
|
145
|
+
},
|
|
146
|
+
[ToolName.ChangeAmplification]: {
|
|
147
|
+
// No specific options yet, uses global scan settings
|
|
148
|
+
},
|
|
108
149
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
|
|
151
|
+
// Visualizer settings (interactive graph)
|
|
152
|
+
visualizer: {
|
|
153
|
+
groupingDirs: ['packages', 'src', 'lib'],
|
|
154
|
+
graph: {
|
|
155
|
+
maxNodes: 5000,
|
|
156
|
+
maxEdges: 10000,
|
|
157
|
+
},
|
|
113
158
|
},
|
|
114
|
-
...(options.full
|
|
115
|
-
? {
|
|
116
|
-
visualizer: {
|
|
117
|
-
groupingDirs: ['packages', 'src', 'lib'],
|
|
118
|
-
graph: {
|
|
119
|
-
maxNodes: 5000,
|
|
120
|
-
maxEdges: 10000,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
}
|
|
124
|
-
: {}),
|
|
125
159
|
};
|
|
126
160
|
|
|
127
161
|
const defaultConfig = baseConfig;
|
|
128
162
|
|
|
129
163
|
let content: string;
|
|
130
164
|
if (fileExt === 'js') {
|
|
131
|
-
content = `/**
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
content = `/**
|
|
166
|
+
* AIReady Configuration
|
|
167
|
+
* @type {import('@aiready/core').AIReadyConfig}
|
|
168
|
+
*/
|
|
169
|
+
module.exports = ${JSON.stringify(defaultConfig, null, 2)};\n`;
|
|
136
170
|
} else {
|
|
137
171
|
content = JSON.stringify(defaultConfig, null, 2);
|
|
138
172
|
}
|
package/src/commands/patterns.ts
CHANGED
|
@@ -35,7 +35,7 @@ export async function patternsAction(
|
|
|
35
35
|
console.log(chalk.blue('🔍 Analyzing patterns...\n'));
|
|
36
36
|
|
|
37
37
|
const startTime = Date.now();
|
|
38
|
-
const resolvedDir = resolvePath(process.cwd(), directory
|
|
38
|
+
const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
|
|
39
39
|
|
|
40
40
|
try {
|
|
41
41
|
// Determine if smart defaults should be used
|
|
@@ -100,8 +100,8 @@ export async function patternsAction(
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const outputFormat =
|
|
103
|
-
options.output
|
|
104
|
-
const userOutputFile = options.outputFile
|
|
103
|
+
options.output ?? finalOptions.output?.format ?? 'console';
|
|
104
|
+
const userOutputFile = options.outputFile ?? finalOptions.output?.file;
|
|
105
105
|
|
|
106
106
|
if (outputFormat === 'json') {
|
|
107
107
|
const outputData = {
|
|
@@ -7,6 +7,95 @@ import {
|
|
|
7
7
|
getRatingDisplay,
|
|
8
8
|
} from '@aiready/core';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Generate a visual progress bar for a score.
|
|
12
|
+
*
|
|
13
|
+
* @param score - The score value (0-100)
|
|
14
|
+
* @param width - The width of the progress bar
|
|
15
|
+
* @returns A colored progress bar string
|
|
16
|
+
*/
|
|
17
|
+
function generateProgressBar(score: number, width: number = 20): string {
|
|
18
|
+
const filled = Math.round((score / 100) * width);
|
|
19
|
+
const empty = width - filled;
|
|
20
|
+
|
|
21
|
+
let color = chalk.red;
|
|
22
|
+
if (score >= 90) color = chalk.green;
|
|
23
|
+
else if (score >= 75) color = chalk.cyan;
|
|
24
|
+
else if (score >= 60) color = chalk.yellow;
|
|
25
|
+
|
|
26
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
27
|
+
return color(bar);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Count issues by severity level from all tool results.
|
|
32
|
+
*
|
|
33
|
+
* @param results - The unified results object
|
|
34
|
+
* @returns Object with counts for each severity level
|
|
35
|
+
*/
|
|
36
|
+
function countIssuesBySeverity(results: any): {
|
|
37
|
+
critical: number;
|
|
38
|
+
major: number;
|
|
39
|
+
minor: number;
|
|
40
|
+
} {
|
|
41
|
+
let critical = 0;
|
|
42
|
+
let major = 0;
|
|
43
|
+
let minor = 0;
|
|
44
|
+
|
|
45
|
+
if (results.summary?.toolsRun) {
|
|
46
|
+
for (const toolId of results.summary.toolsRun) {
|
|
47
|
+
const toolRes = results[toolId];
|
|
48
|
+
if (toolRes?.results) {
|
|
49
|
+
for (const fileRes of toolRes.results) {
|
|
50
|
+
if (fileRes.issues) {
|
|
51
|
+
for (const issue of fileRes.issues) {
|
|
52
|
+
const sev = issue.severity?.toLowerCase();
|
|
53
|
+
if (sev === 'critical') critical++;
|
|
54
|
+
else if (sev === 'major') major++;
|
|
55
|
+
else minor++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { critical, major, minor };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get top files with most issues.
|
|
68
|
+
*
|
|
69
|
+
* @param results - The unified results object
|
|
70
|
+
* @param limit - Maximum number of files to return
|
|
71
|
+
* @returns Array of files with issue counts
|
|
72
|
+
*/
|
|
73
|
+
function getTopFilesWithIssues(
|
|
74
|
+
results: any,
|
|
75
|
+
limit: number = 5
|
|
76
|
+
): Array<{ file: string; count: number }> {
|
|
77
|
+
const fileCounts = new Map<string, number>();
|
|
78
|
+
|
|
79
|
+
if (results.summary?.toolsRun) {
|
|
80
|
+
for (const toolId of results.summary.toolsRun) {
|
|
81
|
+
const toolRes = results[toolId];
|
|
82
|
+
if (toolRes?.results) {
|
|
83
|
+
for (const fileRes of toolRes.results) {
|
|
84
|
+
if (fileRes.issues?.length > 0) {
|
|
85
|
+
const current = fileCounts.get(fileRes.fileName) || 0;
|
|
86
|
+
fileCounts.set(fileRes.fileName, current + fileRes.issues.length);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return Array.from(fileCounts.entries())
|
|
94
|
+
.map(([file, count]) => ({ file, count }))
|
|
95
|
+
.sort((a, b) => b.count - a.count)
|
|
96
|
+
.slice(0, limit);
|
|
97
|
+
}
|
|
98
|
+
|
|
10
99
|
/**
|
|
11
100
|
* Handle console output for the scan results.
|
|
12
101
|
*
|
|
@@ -14,12 +103,48 @@ import {
|
|
|
14
103
|
* @param startTime - The timestamp when the scan started.
|
|
15
104
|
*/
|
|
16
105
|
export function printScanSummary(results: any, startTime: number) {
|
|
106
|
+
// Count issues by severity
|
|
107
|
+
const severity = countIssuesBySeverity(results);
|
|
108
|
+
const totalIssues = severity.critical + severity.major + severity.minor;
|
|
109
|
+
|
|
110
|
+
// Get top files with issues
|
|
111
|
+
const topFiles = getTopFilesWithIssues(results);
|
|
112
|
+
|
|
17
113
|
console.log(chalk.cyan('\n=== AIReady Run Summary ==='));
|
|
114
|
+
console.log(` Total issues: ${chalk.bold(String(totalIssues))}`);
|
|
115
|
+
|
|
116
|
+
// Severity breakdown
|
|
117
|
+
if (totalIssues > 0) {
|
|
118
|
+
console.log(chalk.dim(' Severity breakdown:'));
|
|
119
|
+
if (severity.critical > 0) {
|
|
120
|
+
console.log(
|
|
121
|
+
` ${chalk.red('●')} Critical: ${chalk.bold(severity.critical)}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (severity.major > 0) {
|
|
125
|
+
console.log(
|
|
126
|
+
` ${chalk.yellow('●')} Major: ${chalk.bold(severity.major)}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (severity.minor > 0) {
|
|
130
|
+
console.log(
|
|
131
|
+
` ${chalk.blue('●')} Minor: ${chalk.bold(severity.minor)}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Top files with issues
|
|
137
|
+
if (topFiles.length > 0) {
|
|
138
|
+
console.log(chalk.dim('\n Top files with issues:'));
|
|
139
|
+
topFiles.forEach((item) => {
|
|
140
|
+
console.log(
|
|
141
|
+
` ${chalk.yellow('→')} ${item.file}: ${chalk.bold(item.count)} issues`
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
18
146
|
console.log(
|
|
19
|
-
|
|
20
|
-
);
|
|
21
|
-
console.log(
|
|
22
|
-
` Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
|
|
147
|
+
`\n Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
|
|
23
148
|
);
|
|
24
149
|
}
|
|
25
150
|
|
|
@@ -61,25 +186,26 @@ export function printScoring(
|
|
|
61
186
|
|
|
62
187
|
if (scoringResult.breakdown) {
|
|
63
188
|
console.log(chalk.bold('\nTool breakdown:'));
|
|
64
|
-
scoringResult.breakdown.forEach((tool) => {
|
|
189
|
+
scoringResult.breakdown.forEach((tool: any) => {
|
|
65
190
|
const rating = getRating(tool.score);
|
|
66
191
|
const emoji = getRatingDisplay(rating).emoji;
|
|
192
|
+
const progressBar = generateProgressBar(tool.score, 15);
|
|
67
193
|
console.log(
|
|
68
|
-
`
|
|
194
|
+
` ${progressBar} ${tool.score}/100 (${rating}) ${emoji} ${tool.toolName}`
|
|
69
195
|
);
|
|
70
196
|
});
|
|
71
197
|
|
|
72
|
-
// Top Actionable Recommendations
|
|
198
|
+
// Top Actionable Recommendations - increased from 3 to 5
|
|
73
199
|
const allRecs = scoringResult.breakdown
|
|
74
|
-
.flatMap((t) =>
|
|
75
|
-
(t.recommendations
|
|
200
|
+
.flatMap((t: any) =>
|
|
201
|
+
(t.recommendations ?? []).map((r: any) => ({ ...r, tool: t.toolName }))
|
|
76
202
|
)
|
|
77
|
-
.sort((a, b) => b.estimatedImpact - a.estimatedImpact)
|
|
78
|
-
.slice(0,
|
|
203
|
+
.sort((a: any, b: any) => b.estimatedImpact - a.estimatedImpact)
|
|
204
|
+
.slice(0, 5); // Increased from 3 to 5
|
|
79
205
|
|
|
80
206
|
if (allRecs.length > 0) {
|
|
81
207
|
console.log(chalk.bold('\n🎯 Top Actionable Recommendations:'));
|
|
82
|
-
allRecs.forEach((rec, i) => {
|
|
208
|
+
allRecs.forEach((rec: any, i: number) => {
|
|
83
209
|
const priorityIcon =
|
|
84
210
|
rec.priority === 'high'
|
|
85
211
|
? '🔴'
|
package/src/commands/scan.ts
CHANGED
|
@@ -57,7 +57,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
57
57
|
console.log(chalk.blue('🚀 Starting AIReady unified analysis...\n'));
|
|
58
58
|
|
|
59
59
|
const startTime = Date.now();
|
|
60
|
-
const resolvedDir = resolvePath(process.cwd(), directory
|
|
60
|
+
const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
|
|
61
61
|
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
62
62
|
|
|
63
63
|
try {
|
|
@@ -159,7 +159,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
159
159
|
const { getSmartDefaults } = await import('@aiready/pattern-detect');
|
|
160
160
|
const patternSmartDefaults = await getSmartDefaults(
|
|
161
161
|
resolvedDir,
|
|
162
|
-
finalOptions.toolConfigs?.[ToolName.PatternDetect]
|
|
162
|
+
finalOptions.toolConfigs?.[ToolName.PatternDetect] ?? {}
|
|
163
163
|
);
|
|
164
164
|
|
|
165
165
|
// Merge smart defaults into toolConfigs instead of root level
|
|
@@ -173,7 +173,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
173
173
|
console.log(chalk.cyan('\n=== AIReady Run Preview ==='));
|
|
174
174
|
console.log(
|
|
175
175
|
chalk.white('Tools to run:'),
|
|
176
|
-
(finalOptions.tools
|
|
176
|
+
(finalOptions.tools ?? []).join(', ')
|
|
177
177
|
);
|
|
178
178
|
|
|
179
179
|
// Dynamic progress callback
|
|
@@ -202,7 +202,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
202
202
|
|
|
203
203
|
// Determine scoring profile for project-type-aware weighting
|
|
204
204
|
const scoringProfile =
|
|
205
|
-
options.profile
|
|
205
|
+
options.profile ?? baseOptions.scoring?.profile ?? 'default';
|
|
206
206
|
|
|
207
207
|
const results = await analyzeUnified({
|
|
208
208
|
...finalOptions,
|
|
@@ -233,7 +233,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
233
233
|
readFileSync(resolvePath(process.cwd(), options.compareTo), 'utf8')
|
|
234
234
|
);
|
|
235
235
|
const prevScore =
|
|
236
|
-
prevReport.scoring?.overall
|
|
236
|
+
prevReport.scoring?.overall ?? prevReport.scoring?.score;
|
|
237
237
|
if (typeof prevScore === 'number') {
|
|
238
238
|
const diff = scoringResult.overall - prevScore;
|
|
239
239
|
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
@@ -262,19 +262,19 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
// Token Budget & Cost Logic
|
|
265
|
-
const totalWastedDuplication = (scoringResult.breakdown
|
|
266
|
-
(sum, s) =>
|
|
267
|
-
sum + (s.tokenBudget?.wastedTokens
|
|
265
|
+
const totalWastedDuplication = (scoringResult.breakdown ?? []).reduce(
|
|
266
|
+
(sum: number, s: any) =>
|
|
267
|
+
sum + (s.tokenBudget?.wastedTokens?.bySource?.duplication ?? 0),
|
|
268
268
|
0
|
|
269
269
|
);
|
|
270
|
-
const totalWastedFragmentation = (scoringResult.breakdown
|
|
271
|
-
(sum, s) =>
|
|
272
|
-
sum + (s.tokenBudget?.wastedTokens
|
|
270
|
+
const totalWastedFragmentation = (scoringResult.breakdown ?? []).reduce(
|
|
271
|
+
(sum: number, s: any) =>
|
|
272
|
+
sum + (s.tokenBudget?.wastedTokens?.bySource?.fragmentation ?? 0),
|
|
273
273
|
0
|
|
274
274
|
);
|
|
275
275
|
const totalContext = Math.max(
|
|
276
|
-
...(scoringResult.breakdown
|
|
277
|
-
(s) => s.tokenBudget?.totalContextTokens
|
|
276
|
+
...(scoringResult.breakdown ?? []).map(
|
|
277
|
+
(s: any) => s.tokenBudget?.totalContextTokens ?? 0
|
|
278
278
|
),
|
|
279
279
|
0
|
|
280
280
|
);
|
|
@@ -300,7 +300,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
const modelId = options.model
|
|
303
|
+
const modelId = options.model ?? 'gpt-5.4-mini';
|
|
304
304
|
const roi = (await import('@aiready/core')).calculateBusinessROI({
|
|
305
305
|
tokenWaste: unifiedBudget.wastedTokens.total,
|
|
306
306
|
issues: allIssues,
|
|
@@ -345,9 +345,9 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
345
345
|
|
|
346
346
|
// Output persistence
|
|
347
347
|
const outputFormat =
|
|
348
|
-
options.output
|
|
348
|
+
options.output ?? finalOptions.output?.format ?? 'console';
|
|
349
349
|
const outputPath = resolveOutputPath(
|
|
350
|
-
options.outputFile
|
|
350
|
+
options.outputFile ?? finalOptions.output?.file,
|
|
351
351
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
352
352
|
resolvedDir
|
|
353
353
|
);
|
|
@@ -380,8 +380,8 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
380
380
|
const threshold = options.threshold
|
|
381
381
|
? parseInt(options.threshold)
|
|
382
382
|
: undefined;
|
|
383
|
-
const failOnLevel = options.failOn
|
|
384
|
-
const isCI = options.ci
|
|
383
|
+
const failOnLevel = options.failOn ?? 'critical';
|
|
384
|
+
const isCI = options.ci ?? process.env.CI === 'true';
|
|
385
385
|
|
|
386
386
|
let shouldFail = false;
|
|
387
387
|
let failReason = '';
|
package/src/commands/upload.ts
CHANGED
|
@@ -13,10 +13,10 @@ export async function uploadAction(file: string, options: UploadOptions) {
|
|
|
13
13
|
const startTime = Date.now();
|
|
14
14
|
const filePath = resolvePath(process.cwd(), file);
|
|
15
15
|
const serverUrl =
|
|
16
|
-
options.server
|
|
17
|
-
process.env.AIREADY_SERVER
|
|
16
|
+
options.server ??
|
|
17
|
+
process.env.AIREADY_SERVER ??
|
|
18
18
|
'https://dev.platform.getaiready.dev';
|
|
19
|
-
const apiKey = options.apiKey
|
|
19
|
+
const apiKey = options.apiKey ?? process.env.AIREADY_API_KEY;
|
|
20
20
|
|
|
21
21
|
if (!apiKey) {
|
|
22
22
|
console.error(chalk.red('❌ API Key is required for upload.'));
|
|
@@ -49,7 +49,7 @@ export async function uploadAction(file: string, options: UploadOptions) {
|
|
|
49
49
|
|
|
50
50
|
// Prepare upload payload
|
|
51
51
|
// Note: repoId is optional if the metadata contains it, but for now we'll require it or infer from metadata
|
|
52
|
-
const repoId = options.repoId
|
|
52
|
+
const repoId = options.repoId ?? reportData.repository?.repoId;
|
|
53
53
|
|
|
54
54
|
const response = await fetch(`${serverUrl}/api/analysis/upload`, {
|
|
55
55
|
method: 'POST',
|
|
@@ -70,13 +70,13 @@ export async function uploadAction(file: string, options: UploadOptions) {
|
|
|
70
70
|
uploadResult = await response.json();
|
|
71
71
|
} else {
|
|
72
72
|
const text = await response.text();
|
|
73
|
-
uploadResult = { error: text
|
|
73
|
+
uploadResult = { error: text ?? response.statusText };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
if (!response.ok) {
|
|
77
77
|
console.error(
|
|
78
78
|
chalk.red(
|
|
79
|
-
`❌ Upload failed: ${uploadResult.error
|
|
79
|
+
`❌ Upload failed: ${uploadResult.error ?? response.statusText}`
|
|
80
80
|
)
|
|
81
81
|
);
|
|
82
82
|
|
|
@@ -30,7 +30,7 @@ export async function visualizeAction(
|
|
|
30
30
|
options: VisualizeOptions
|
|
31
31
|
) {
|
|
32
32
|
try {
|
|
33
|
-
const dirPath = resolvePath(process.cwd(), directory
|
|
33
|
+
const dirPath = resolvePath(process.cwd(), directory ?? '.');
|
|
34
34
|
let reportPath = options.report
|
|
35
35
|
? resolvePath(dirPath, options.report)
|
|
36
36
|
: null;
|
|
@@ -87,7 +87,7 @@ export async function visualizeAction(
|
|
|
87
87
|
const graph = GraphBuilder.buildFromReport(report, dirPath);
|
|
88
88
|
|
|
89
89
|
// Check if --dev mode is requested and available
|
|
90
|
-
let useDevMode = options.dev
|
|
90
|
+
let useDevMode = options.dev ?? false;
|
|
91
91
|
let devServerStarted = false;
|
|
92
92
|
|
|
93
93
|
if (useDevMode) {
|
|
@@ -230,7 +230,7 @@ export async function visualizeAction(
|
|
|
230
230
|
console.log('Generating HTML...');
|
|
231
231
|
const html = generateHTML(graph as any);
|
|
232
232
|
const defaultOutput = 'visualization.html';
|
|
233
|
-
const outPath = resolvePath(dirPath, options.output
|
|
233
|
+
const outPath = resolvePath(dirPath, options.output ?? defaultOutput);
|
|
234
234
|
writeFileSync(outPath, html, 'utf8');
|
|
235
235
|
console.log(chalk.green(`✅ Visualization written to: ${outPath}`));
|
|
236
236
|
|
|
@@ -250,7 +250,7 @@ export async function visualizeAction(
|
|
|
250
250
|
|
|
251
251
|
const server = http.createServer(async (req, res) => {
|
|
252
252
|
try {
|
|
253
|
-
const urlPath = req.url
|
|
253
|
+
const urlPath = req.url ?? '/';
|
|
254
254
|
if (urlPath === '/' || urlPath === '/index.html') {
|
|
255
255
|
const content = await fsp.readFile(outPath, 'utf8');
|
|
256
256
|
res.writeHead(200, {
|