@aiready/cli 0.14.13 โ 0.14.15
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/CONTRIBUTING.md +86 -1
- package/LICENSE +21 -0
- package/package.json +33 -12
- package/.aiready/aiready-report-20260227-133806.json +0 -7805
- package/.aiready/aiready-report-20260227-133938.json +0 -7951
- package/.aiready/aiready-report-20260228-003433.json +0 -7939
- package/.aiready/aiready-report-20260228-003613.json +0 -771
- package/.aiready/aiready-report-20260314-164626.json +0 -59
- package/.aiready/aiready-report-20260314-164741.json +0 -59
- package/.aiready/aiready-report-20260319-201106.json +0 -5566
- package/.aiready/aiready-report-20260319-201511.json +0 -5566
- package/.aiready/aiready-report-20260319-202017.json +0 -5708
- package/.github/FUNDING.yml +0 -5
- package/.turbo/turbo-build.log +0 -29
- package/.turbo/turbo-lint.log +0 -0
- package/.turbo/turbo-test.log +0 -76
- package/aiready-report.json +0 -30703
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -865
- package/coverage/coverage-final.json +0 -15
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -146
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/commands/agent-grounding.ts.html +0 -271
- package/coverage/src/commands/ai-signal-clarity.ts.html +0 -253
- package/coverage/src/commands/change-amplification.ts.html +0 -94
- package/coverage/src/commands/consistency.ts.html +0 -781
- package/coverage/src/commands/context.ts.html +0 -871
- package/coverage/src/commands/deps-health.ts.html +0 -280
- package/coverage/src/commands/doc-drift.ts.html +0 -271
- package/coverage/src/commands/index.html +0 -281
- package/coverage/src/commands/patterns.ts.html +0 -745
- package/coverage/src/commands/scan.ts.html +0 -1393
- package/coverage/src/commands/testability.ts.html +0 -304
- package/coverage/src/commands/upload.ts.html +0 -466
- package/coverage/src/commands/visualize.ts.html +0 -1027
- package/coverage/src/index.html +0 -116
- package/coverage/src/index.ts.html +0 -1372
- package/coverage/src/utils/helpers.ts.html +0 -559
- package/coverage/src/utils/index.html +0 -116
- package/docs/SPOKE_GUIDE.md +0 -184
- package/packages/core/src/.aiready/aiready-report-20260314-161145.json +0 -224
- package/packages/core/src/.aiready/aiready-report-20260314-161152.json +0 -235
- package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +0 -224
- package/src/.aiready/aiready-report-20260312-103623.json +0 -32574
- package/src/.aiready/aiready-report-20260312-110843.json +0 -28740
- package/src/.aiready/aiready-report-20260312-110955.json +0 -28740
- package/src/.aiready/aiready-report-20260314-203209.json +0 -30713
- package/src/.aiready/aiready-report-20260314-203736.json +0 -30713
- package/src/.aiready/aiready-report-20260314-203857.json +0 -30713
- package/src/.aiready/aiready-report-20260314-204047.json +0 -30713
- package/src/.aiready/aiready-report-20260318-002110.json +0 -28782
- package/src/__tests__/cli.test.ts +0 -85
- package/src/__tests__/config-shape.test.ts +0 -105
- package/src/__tests__/unified.test.ts +0 -95
- package/src/cli.ts +0 -333
- package/src/commands/__tests__/agent-grounding.test.ts +0 -24
- package/src/commands/__tests__/ai-signal-clarity.test.ts +0 -32
- package/src/commands/__tests__/consistency.test.ts +0 -100
- package/src/commands/__tests__/deps-health.test.ts +0 -26
- package/src/commands/__tests__/doc-drift.test.ts +0 -26
- package/src/commands/__tests__/extra-commands.test.ts +0 -168
- package/src/commands/__tests__/init.test.ts +0 -51
- package/src/commands/__tests__/scan.test.ts +0 -153
- package/src/commands/__tests__/testability.test.ts +0 -36
- package/src/commands/__tests__/upload.test.ts +0 -50
- package/src/commands/__tests__/visualize.test.ts +0 -78
- package/src/commands/agent-grounding.ts +0 -62
- package/src/commands/ai-signal-clarity.ts +0 -1
- package/src/commands/bug.ts +0 -99
- package/src/commands/change-amplification.ts +0 -3
- package/src/commands/consistency.ts +0 -232
- package/src/commands/context.ts +0 -262
- package/src/commands/deps-health.ts +0 -1
- package/src/commands/doc-drift.ts +0 -1
- package/src/commands/index.ts +0 -20
- package/src/commands/init.ts +0 -199
- package/src/commands/patterns.ts +0 -222
- package/src/commands/report-formatter.ts +0 -267
- package/src/commands/scan.ts +0 -432
- package/src/commands/shared/configured-tool-action.ts +0 -35
- package/src/commands/shared/standard-tool-actions.ts +0 -126
- package/src/commands/testability.ts +0 -73
- package/src/commands/upload.ts +0 -129
- package/src/commands/visualize.ts +0 -321
- package/src/index.ts +0 -465
- package/src/utils/__tests__/helpers.test.ts +0 -35
- package/src/utils/helpers.ts +0 -234
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -13
package/src/commands/scan.ts
DELETED
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scan command - Run comprehensive AI-readiness analysis using the tool registry
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { writeFileSync, readFileSync } from 'fs';
|
|
7
|
-
import { resolve as resolvePath } from 'path';
|
|
8
|
-
import {
|
|
9
|
-
loadMergedConfig,
|
|
10
|
-
handleJSONOutput,
|
|
11
|
-
handleCLIError,
|
|
12
|
-
resolveOutputPath,
|
|
13
|
-
getRepoMetadata,
|
|
14
|
-
calculateTokenBudget,
|
|
15
|
-
ToolName,
|
|
16
|
-
emitIssuesAsAnnotations,
|
|
17
|
-
} from '@aiready/core';
|
|
18
|
-
import { analyzeUnified, scoreUnified, type ScoringResult } from '../index';
|
|
19
|
-
import { getReportTimestamp, warnIfGraphCapExceeded } from '../utils/helpers';
|
|
20
|
-
import {
|
|
21
|
-
printScanSummary,
|
|
22
|
-
printBusinessImpact,
|
|
23
|
-
printScoring,
|
|
24
|
-
mapToUnifiedReport,
|
|
25
|
-
} from './report-formatter';
|
|
26
|
-
import { uploadAction } from './upload';
|
|
27
|
-
|
|
28
|
-
interface ScanOptions {
|
|
29
|
-
tools?: string;
|
|
30
|
-
profile?: string;
|
|
31
|
-
compareTo?: string;
|
|
32
|
-
include?: string;
|
|
33
|
-
exclude?: string;
|
|
34
|
-
output?: string;
|
|
35
|
-
outputFile?: string;
|
|
36
|
-
score?: boolean;
|
|
37
|
-
noScore?: boolean;
|
|
38
|
-
weights?: string;
|
|
39
|
-
threshold?: string;
|
|
40
|
-
ci?: boolean;
|
|
41
|
-
failOn?: string;
|
|
42
|
-
model?: string;
|
|
43
|
-
apiKey?: string;
|
|
44
|
-
upload?: boolean;
|
|
45
|
-
server?: string;
|
|
46
|
-
}
|
|
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
|
-
export async function scanAction(directory: string, options: ScanOptions) {
|
|
57
|
-
console.log(chalk.blue('๐ Starting AIReady unified analysis...\n'));
|
|
58
|
-
|
|
59
|
-
const startTime = Date.now();
|
|
60
|
-
const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
|
|
61
|
-
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// Define defaults using canonical IDs
|
|
65
|
-
const defaults = {
|
|
66
|
-
tools: [
|
|
67
|
-
'pattern-detect',
|
|
68
|
-
'context-analyzer',
|
|
69
|
-
'naming-consistency',
|
|
70
|
-
'ai-signal-clarity',
|
|
71
|
-
'agent-grounding',
|
|
72
|
-
'testability-index',
|
|
73
|
-
'doc-drift',
|
|
74
|
-
'dependency-health',
|
|
75
|
-
'change-amplification',
|
|
76
|
-
],
|
|
77
|
-
include: undefined,
|
|
78
|
-
exclude: undefined,
|
|
79
|
-
output: {
|
|
80
|
-
format: 'console',
|
|
81
|
-
file: undefined,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Map profile to tool IDs
|
|
86
|
-
let profileTools: string[] | undefined = options.tools
|
|
87
|
-
? options.tools.split(',').map((t) => t.trim())
|
|
88
|
-
: undefined;
|
|
89
|
-
if (options.profile) {
|
|
90
|
-
switch (options.profile.toLowerCase()) {
|
|
91
|
-
case 'agentic':
|
|
92
|
-
profileTools = [
|
|
93
|
-
ToolName.AiSignalClarity,
|
|
94
|
-
ToolName.AgentGrounding,
|
|
95
|
-
ToolName.TestabilityIndex,
|
|
96
|
-
];
|
|
97
|
-
break;
|
|
98
|
-
case 'cost':
|
|
99
|
-
profileTools = [ToolName.PatternDetect, ToolName.ContextAnalyzer];
|
|
100
|
-
break;
|
|
101
|
-
case 'logic':
|
|
102
|
-
profileTools = [
|
|
103
|
-
ToolName.TestabilityIndex,
|
|
104
|
-
ToolName.NamingConsistency,
|
|
105
|
-
ToolName.ContextAnalyzer,
|
|
106
|
-
ToolName.PatternDetect,
|
|
107
|
-
ToolName.ChangeAmplification,
|
|
108
|
-
];
|
|
109
|
-
break;
|
|
110
|
-
case 'ui':
|
|
111
|
-
profileTools = [
|
|
112
|
-
ToolName.NamingConsistency,
|
|
113
|
-
ToolName.ContextAnalyzer,
|
|
114
|
-
ToolName.PatternDetect,
|
|
115
|
-
ToolName.DocDrift,
|
|
116
|
-
ToolName.AiSignalClarity,
|
|
117
|
-
];
|
|
118
|
-
break;
|
|
119
|
-
case 'security':
|
|
120
|
-
profileTools = [
|
|
121
|
-
ToolName.NamingConsistency,
|
|
122
|
-
ToolName.TestabilityIndex,
|
|
123
|
-
];
|
|
124
|
-
break;
|
|
125
|
-
case 'onboarding':
|
|
126
|
-
profileTools = [
|
|
127
|
-
ToolName.ContextAnalyzer,
|
|
128
|
-
ToolName.NamingConsistency,
|
|
129
|
-
ToolName.AgentGrounding,
|
|
130
|
-
];
|
|
131
|
-
break;
|
|
132
|
-
default:
|
|
133
|
-
console.log(
|
|
134
|
-
chalk.yellow(
|
|
135
|
-
`\nโ ๏ธ Unknown profile '${options.profile}'. Using defaults.`
|
|
136
|
-
)
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const cliOverrides: any = {
|
|
142
|
-
include: options.include?.split(','),
|
|
143
|
-
exclude: options.exclude?.split(','),
|
|
144
|
-
};
|
|
145
|
-
if (profileTools) cliOverrides.tools = profileTools;
|
|
146
|
-
|
|
147
|
-
const baseOptions = (await loadMergedConfig(
|
|
148
|
-
resolvedDir,
|
|
149
|
-
defaults,
|
|
150
|
-
cliOverrides
|
|
151
|
-
)) as any;
|
|
152
|
-
|
|
153
|
-
// Apply smart defaults for pattern detection if requested
|
|
154
|
-
const finalOptions = { ...baseOptions };
|
|
155
|
-
if (
|
|
156
|
-
baseOptions.tools.includes(ToolName.PatternDetect) ||
|
|
157
|
-
baseOptions.tools.includes('patterns')
|
|
158
|
-
) {
|
|
159
|
-
const { getSmartDefaults } = await import('@aiready/pattern-detect');
|
|
160
|
-
const patternSmartDefaults = await getSmartDefaults(
|
|
161
|
-
resolvedDir,
|
|
162
|
-
finalOptions.toolConfigs?.[ToolName.PatternDetect] ?? {}
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
// Merge smart defaults into toolConfigs instead of root level
|
|
166
|
-
if (!finalOptions.toolConfigs) finalOptions.toolConfigs = {};
|
|
167
|
-
finalOptions.toolConfigs[ToolName.PatternDetect] = {
|
|
168
|
-
...patternSmartDefaults,
|
|
169
|
-
...finalOptions.toolConfigs[ToolName.PatternDetect],
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
console.log(chalk.cyan('\n=== AIReady Run Preview ==='));
|
|
174
|
-
console.log(
|
|
175
|
-
chalk.white('Tools to run:'),
|
|
176
|
-
(finalOptions.tools ?? []).join(', ')
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Dynamic progress callback
|
|
180
|
-
const progressCallback = (event: any) => {
|
|
181
|
-
// Handle progress messages
|
|
182
|
-
if (event.message) {
|
|
183
|
-
process.stdout.write(`\r\x1b[K [${event.tool}] ${event.message}`);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Handle tool completion
|
|
188
|
-
process.stdout.write('\r\x1b[K'); // Clear the progress line
|
|
189
|
-
console.log(chalk.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
190
|
-
const res = event.data;
|
|
191
|
-
if (res && res.summary) {
|
|
192
|
-
if (res.summary.totalIssues !== undefined)
|
|
193
|
-
console.log(` Issues found: ${chalk.bold(res.summary.totalIssues)}`);
|
|
194
|
-
if (res.summary.score !== undefined)
|
|
195
|
-
console.log(` Tool Score: ${chalk.bold(res.summary.score)}/100`);
|
|
196
|
-
if (res.summary.totalFiles !== undefined)
|
|
197
|
-
console.log(
|
|
198
|
-
` Files analyzed: ${chalk.bold(res.summary.totalFiles)}`
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Determine scoring profile for project-type-aware weighting
|
|
204
|
-
const scoringProfile =
|
|
205
|
-
options.profile ?? baseOptions.scoring?.profile ?? 'default';
|
|
206
|
-
|
|
207
|
-
const results = await analyzeUnified({
|
|
208
|
-
...finalOptions,
|
|
209
|
-
progressCallback,
|
|
210
|
-
onProgress: () => {},
|
|
211
|
-
suppressToolConfig: true,
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
printScanSummary(results, startTime);
|
|
215
|
-
|
|
216
|
-
let scoringResult: ScoringResult | undefined;
|
|
217
|
-
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
218
|
-
// Pass the profile to scoreUnified
|
|
219
|
-
scoringResult = await scoreUnified(results, {
|
|
220
|
-
...finalOptions,
|
|
221
|
-
scoring: {
|
|
222
|
-
...finalOptions.scoring,
|
|
223
|
-
profile: scoringProfile,
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
printScoring(scoringResult, scoringProfile);
|
|
228
|
-
|
|
229
|
-
// Trend comparison logic
|
|
230
|
-
if (options.compareTo) {
|
|
231
|
-
try {
|
|
232
|
-
const prevReport = JSON.parse(
|
|
233
|
-
readFileSync(resolvePath(process.cwd(), options.compareTo), 'utf8')
|
|
234
|
-
);
|
|
235
|
-
const prevScore =
|
|
236
|
-
prevReport.scoring?.overall ?? prevReport.scoring?.score;
|
|
237
|
-
if (typeof prevScore === 'number') {
|
|
238
|
-
const diff = scoringResult.overall - prevScore;
|
|
239
|
-
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
240
|
-
if (diff > 0)
|
|
241
|
-
console.log(
|
|
242
|
-
chalk.green(
|
|
243
|
-
` ๐ Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} โ ${scoringResult.overall})`
|
|
244
|
-
)
|
|
245
|
-
);
|
|
246
|
-
else if (diff < 0)
|
|
247
|
-
console.log(
|
|
248
|
-
chalk.red(
|
|
249
|
-
` ๐ Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} โ ${scoringResult.overall})`
|
|
250
|
-
)
|
|
251
|
-
);
|
|
252
|
-
else
|
|
253
|
-
console.log(
|
|
254
|
-
chalk.blue(
|
|
255
|
-
` โ Trend: No change (${prevScore} โ ${scoringResult.overall})`
|
|
256
|
-
)
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
} catch (e) {
|
|
260
|
-
void e;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Token Budget & Cost Logic
|
|
265
|
-
const totalWastedDuplication = (scoringResult.breakdown ?? []).reduce(
|
|
266
|
-
(sum: number, s: any) =>
|
|
267
|
-
sum + (s.tokenBudget?.wastedTokens?.bySource?.duplication ?? 0),
|
|
268
|
-
0
|
|
269
|
-
);
|
|
270
|
-
const totalWastedFragmentation = (scoringResult.breakdown ?? []).reduce(
|
|
271
|
-
(sum: number, s: any) =>
|
|
272
|
-
sum + (s.tokenBudget?.wastedTokens?.bySource?.fragmentation ?? 0),
|
|
273
|
-
0
|
|
274
|
-
);
|
|
275
|
-
const totalContext = Math.max(
|
|
276
|
-
...(scoringResult.breakdown ?? []).map(
|
|
277
|
-
(s: any) => s.tokenBudget?.totalContextTokens ?? 0
|
|
278
|
-
),
|
|
279
|
-
0
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
if (totalContext > 0) {
|
|
283
|
-
const unifiedBudget = calculateTokenBudget({
|
|
284
|
-
totalContextTokens: totalContext,
|
|
285
|
-
wastedTokens: {
|
|
286
|
-
duplication: totalWastedDuplication,
|
|
287
|
-
fragmentation: totalWastedFragmentation,
|
|
288
|
-
chattiness: totalContext * 0.1, // Default chattiness
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const allIssues: any[] = [];
|
|
293
|
-
for (const toolId of results.summary.toolsRun) {
|
|
294
|
-
if (results[toolId]?.results) {
|
|
295
|
-
results[toolId].results.forEach((fileRes: any) => {
|
|
296
|
-
if (fileRes.issues) {
|
|
297
|
-
allIssues.push(...fileRes.issues);
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const modelId = options.model ?? 'gpt-5.4-mini';
|
|
304
|
-
const roi = (await import('@aiready/core')).calculateBusinessROI({
|
|
305
|
-
tokenWaste: unifiedBudget.wastedTokens.total,
|
|
306
|
-
issues: allIssues,
|
|
307
|
-
modelId: modelId,
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
printBusinessImpact(roi, unifiedBudget);
|
|
311
|
-
|
|
312
|
-
(results.summary as any).businessImpact = {
|
|
313
|
-
estimatedMonthlyWaste: roi.monthlySavings,
|
|
314
|
-
potentialSavings: roi.monthlySavings,
|
|
315
|
-
productivityHours: roi.productivityGainHours,
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
(scoringResult as any).tokenBudget = unifiedBudget;
|
|
319
|
-
(scoringResult as any).businessROI = roi;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
console.log(
|
|
324
|
-
chalk.dim(
|
|
325
|
-
'\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'
|
|
326
|
-
)
|
|
327
|
-
);
|
|
328
|
-
console.log(chalk.dim('๐ฌ Found a bug or have a metric idea?'));
|
|
329
|
-
console.log(chalk.dim('๐ Copy/paste this to your AI agent:'));
|
|
330
|
-
console.log(
|
|
331
|
-
chalk.cyan(
|
|
332
|
-
` "Any feedback for the tools? Please use 'aiready bug' to report โค๏ธ"`
|
|
333
|
-
)
|
|
334
|
-
);
|
|
335
|
-
console.log(
|
|
336
|
-
chalk.dim(
|
|
337
|
-
'โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'
|
|
338
|
-
)
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
const outputData = {
|
|
342
|
-
...mapToUnifiedReport(results, scoringResult),
|
|
343
|
-
repository: repoMetadata,
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
// Output persistence
|
|
347
|
-
const outputFormat =
|
|
348
|
-
options.output ?? finalOptions.output?.format ?? 'console';
|
|
349
|
-
const outputPath = resolveOutputPath(
|
|
350
|
-
options.outputFile ?? finalOptions.output?.file,
|
|
351
|
-
`aiready-report-${getReportTimestamp()}.json`,
|
|
352
|
-
resolvedDir
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
if (outputFormat === 'json') {
|
|
356
|
-
handleJSONOutput(
|
|
357
|
-
outputData,
|
|
358
|
-
outputPath,
|
|
359
|
-
`โ
Report saved to ${outputPath}`
|
|
360
|
-
);
|
|
361
|
-
} else {
|
|
362
|
-
try {
|
|
363
|
-
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
364
|
-
console.log(chalk.dim(`โ
Report auto-persisted to ${outputPath}`));
|
|
365
|
-
} catch (err) {
|
|
366
|
-
void err;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (options.upload) {
|
|
371
|
-
await uploadAction(outputPath, {
|
|
372
|
-
apiKey: options.apiKey,
|
|
373
|
-
server: options.server,
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
377
|
-
|
|
378
|
-
// Score Check & Gatekeeper logic
|
|
379
|
-
if (scoringResult) {
|
|
380
|
-
const threshold = options.threshold
|
|
381
|
-
? parseInt(options.threshold)
|
|
382
|
-
: undefined;
|
|
383
|
-
const failOnLevel = options.failOn ?? 'critical';
|
|
384
|
-
const isCI = options.ci ?? process.env.CI === 'true';
|
|
385
|
-
|
|
386
|
-
let shouldFail = false;
|
|
387
|
-
let failReason = '';
|
|
388
|
-
|
|
389
|
-
// Emit annotations only in CI
|
|
390
|
-
const report = mapToUnifiedReport(results, scoringResult);
|
|
391
|
-
if (isCI && report.results && report.results.length > 0) {
|
|
392
|
-
console.log(
|
|
393
|
-
chalk.cyan(
|
|
394
|
-
`\n๐ Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
395
|
-
)
|
|
396
|
-
);
|
|
397
|
-
emitIssuesAsAnnotations(report.results);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (threshold && scoringResult.overall < threshold) {
|
|
401
|
-
shouldFail = true;
|
|
402
|
-
failReason = `Score ${scoringResult.overall} < threshold ${threshold}`;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// If failOnLevel is set (default 'critical'), check for issues
|
|
406
|
-
// But only fail if not 'none'
|
|
407
|
-
if (failOnLevel !== 'none') {
|
|
408
|
-
if (failOnLevel === 'critical' && report.summary.criticalIssues > 0) {
|
|
409
|
-
shouldFail = true;
|
|
410
|
-
failReason = `Found ${report.summary.criticalIssues} critical issues`;
|
|
411
|
-
} else if (
|
|
412
|
-
failOnLevel === 'major' &&
|
|
413
|
-
report.summary.criticalIssues + report.summary.majorIssues > 0
|
|
414
|
-
) {
|
|
415
|
-
shouldFail = true;
|
|
416
|
-
failReason = `Found ${report.summary.criticalIssues} critical and ${report.summary.majorIssues} major issues`;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (shouldFail) {
|
|
421
|
-
console.log(chalk.red(`\n๐ซ SCAN FAILED: ${failReason}`));
|
|
422
|
-
process.exit(1);
|
|
423
|
-
} else {
|
|
424
|
-
console.log(chalk.green('\nโ
SCAN PASSED'));
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
} catch (error) {
|
|
428
|
-
handleCLIError(error, 'Analysis');
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
export const scanHelpText = `...`;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ToolScoringOutput } from '@aiready/core';
|
|
2
|
-
import { runConfiguredToolCommand } from '../../utils/helpers';
|
|
3
|
-
|
|
4
|
-
export interface ConfiguredToolActionConfig<TReport> {
|
|
5
|
-
defaults: Record<string, unknown>;
|
|
6
|
-
analyze: (scanOptions: any) => Promise<TReport>;
|
|
7
|
-
getExtras: (
|
|
8
|
-
options: any,
|
|
9
|
-
merged: Record<string, unknown>
|
|
10
|
-
) => Record<string, unknown>;
|
|
11
|
-
score: (report: TReport) => ToolScoringOutput;
|
|
12
|
-
render: (report: TReport, scoring: ToolScoringOutput) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function runConfiguredToolAction<TReport>(
|
|
16
|
-
directory: string,
|
|
17
|
-
options: any,
|
|
18
|
-
config: ConfiguredToolActionConfig<TReport>
|
|
19
|
-
): Promise<ToolScoringOutput | undefined> {
|
|
20
|
-
const { report, scoring } = await runConfiguredToolCommand({
|
|
21
|
-
directory,
|
|
22
|
-
options,
|
|
23
|
-
defaults: config.defaults,
|
|
24
|
-
analyze: config.analyze,
|
|
25
|
-
getExtras: config.getExtras,
|
|
26
|
-
score: config.score,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (options.output === 'json') {
|
|
30
|
-
return scoring;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
config.render(report, scoring);
|
|
34
|
-
return scoring;
|
|
35
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Testability 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 testabilityAction(
|
|
10
|
-
directory: string,
|
|
11
|
-
options: any
|
|
12
|
-
): Promise<ToolScoringOutput | undefined> {
|
|
13
|
-
const { analyzeTestability, calculateTestabilityScore } =
|
|
14
|
-
await import('@aiready/testability');
|
|
15
|
-
|
|
16
|
-
const config = await loadConfig(directory);
|
|
17
|
-
const merged = mergeConfigWithDefaults(config, {
|
|
18
|
-
minCoverageRatio: 0.3,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const report = await analyzeTestability({
|
|
22
|
-
rootDir: directory,
|
|
23
|
-
minCoverageRatio: options.minCoverageRatio ?? merged.minCoverageRatio,
|
|
24
|
-
include: options.include,
|
|
25
|
-
exclude: options.exclude,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const scoring = calculateTestabilityScore(report);
|
|
29
|
-
|
|
30
|
-
if (options.output === 'json') {
|
|
31
|
-
return scoring;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const safetyIcons: Record<string, string> = {
|
|
35
|
-
safe: 'โ
',
|
|
36
|
-
'moderate-risk': 'โ ๏ธ ',
|
|
37
|
-
'high-risk': '๐ด',
|
|
38
|
-
'blind-risk': '๐',
|
|
39
|
-
};
|
|
40
|
-
const safetyColors: Record<string, (s: string) => string> = {
|
|
41
|
-
safe: chalk.green,
|
|
42
|
-
'moderate-risk': chalk.yellow,
|
|
43
|
-
'high-risk': chalk.red,
|
|
44
|
-
'blind-risk': chalk.bgRed.white,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const safety = report.summary.aiChangeSafetyRating;
|
|
48
|
-
const icon = safetyIcons[safety] ?? 'โ';
|
|
49
|
-
const color = safetyColors[safety] ?? chalk.white;
|
|
50
|
-
|
|
51
|
-
console.log(
|
|
52
|
-
` ๐งช Testability: ${chalk.bold(scoring.score + '/100')} (${report.summary.rating})`
|
|
53
|
-
);
|
|
54
|
-
console.log(
|
|
55
|
-
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
56
|
-
);
|
|
57
|
-
console.log(
|
|
58
|
-
chalk.dim(
|
|
59
|
-
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
60
|
-
)
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Critical blind-risk banner in the unified output
|
|
64
|
-
if (safety === 'blind-risk') {
|
|
65
|
-
console.log(
|
|
66
|
-
chalk.red.bold(
|
|
67
|
-
'\n โ ๏ธ NO TESTS โ AI changes to this codebase are completely unverifiable!\n'
|
|
68
|
-
)
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return scoring;
|
|
73
|
-
}
|