@aiready/cli 0.14.14 โ†’ 0.14.16

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 (98) hide show
  1. package/CONTRIBUTING.md +86 -1
  2. package/LICENSE +21 -0
  3. package/README.md +152 -52
  4. package/dist/cli.js +73 -12
  5. package/dist/cli.mjs +73 -12
  6. package/package.json +44 -14
  7. package/.aiready/aiready-report-20260227-133806.json +0 -7805
  8. package/.aiready/aiready-report-20260227-133938.json +0 -7951
  9. package/.aiready/aiready-report-20260228-003433.json +0 -7939
  10. package/.aiready/aiready-report-20260228-003613.json +0 -771
  11. package/.aiready/aiready-report-20260314-164626.json +0 -59
  12. package/.aiready/aiready-report-20260314-164741.json +0 -59
  13. package/.aiready/aiready-report-20260319-201106.json +0 -5566
  14. package/.aiready/aiready-report-20260319-201511.json +0 -5566
  15. package/.aiready/aiready-report-20260319-202017.json +0 -5708
  16. package/.github/FUNDING.yml +0 -5
  17. package/.turbo/turbo-build.log +0 -29
  18. package/.turbo/turbo-lint.log +0 -0
  19. package/.turbo/turbo-test.log +0 -76
  20. package/aiready-report.json +0 -30703
  21. package/coverage/base.css +0 -224
  22. package/coverage/block-navigation.js +0 -87
  23. package/coverage/clover.xml +0 -865
  24. package/coverage/coverage-final.json +0 -15
  25. package/coverage/favicon.png +0 -0
  26. package/coverage/index.html +0 -146
  27. package/coverage/prettify.css +0 -1
  28. package/coverage/prettify.js +0 -2
  29. package/coverage/sort-arrow-sprite.png +0 -0
  30. package/coverage/sorter.js +0 -210
  31. package/coverage/src/commands/agent-grounding.ts.html +0 -271
  32. package/coverage/src/commands/ai-signal-clarity.ts.html +0 -253
  33. package/coverage/src/commands/change-amplification.ts.html +0 -94
  34. package/coverage/src/commands/consistency.ts.html +0 -781
  35. package/coverage/src/commands/context.ts.html +0 -871
  36. package/coverage/src/commands/deps-health.ts.html +0 -280
  37. package/coverage/src/commands/doc-drift.ts.html +0 -271
  38. package/coverage/src/commands/index.html +0 -281
  39. package/coverage/src/commands/patterns.ts.html +0 -745
  40. package/coverage/src/commands/scan.ts.html +0 -1393
  41. package/coverage/src/commands/testability.ts.html +0 -304
  42. package/coverage/src/commands/upload.ts.html +0 -466
  43. package/coverage/src/commands/visualize.ts.html +0 -1027
  44. package/coverage/src/index.html +0 -116
  45. package/coverage/src/index.ts.html +0 -1372
  46. package/coverage/src/utils/helpers.ts.html +0 -559
  47. package/coverage/src/utils/index.html +0 -116
  48. package/docs/SPOKE_GUIDE.md +0 -184
  49. package/packages/core/src/.aiready/aiready-report-20260314-161145.json +0 -224
  50. package/packages/core/src/.aiready/aiready-report-20260314-161152.json +0 -235
  51. package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +0 -224
  52. package/src/.aiready/aiready-report-20260312-103623.json +0 -32574
  53. package/src/.aiready/aiready-report-20260312-110843.json +0 -28740
  54. package/src/.aiready/aiready-report-20260312-110955.json +0 -28740
  55. package/src/.aiready/aiready-report-20260314-203209.json +0 -30713
  56. package/src/.aiready/aiready-report-20260314-203736.json +0 -30713
  57. package/src/.aiready/aiready-report-20260314-203857.json +0 -30713
  58. package/src/.aiready/aiready-report-20260314-204047.json +0 -30713
  59. package/src/.aiready/aiready-report-20260318-002110.json +0 -28782
  60. package/src/__tests__/cli.test.ts +0 -85
  61. package/src/__tests__/config-shape.test.ts +0 -105
  62. package/src/__tests__/unified.test.ts +0 -95
  63. package/src/cli.ts +0 -333
  64. package/src/commands/__tests__/agent-grounding.test.ts +0 -24
  65. package/src/commands/__tests__/ai-signal-clarity.test.ts +0 -32
  66. package/src/commands/__tests__/consistency.test.ts +0 -100
  67. package/src/commands/__tests__/deps-health.test.ts +0 -26
  68. package/src/commands/__tests__/doc-drift.test.ts +0 -26
  69. package/src/commands/__tests__/extra-commands.test.ts +0 -168
  70. package/src/commands/__tests__/init.test.ts +0 -51
  71. package/src/commands/__tests__/scan.test.ts +0 -153
  72. package/src/commands/__tests__/testability.test.ts +0 -36
  73. package/src/commands/__tests__/upload.test.ts +0 -50
  74. package/src/commands/__tests__/visualize.test.ts +0 -78
  75. package/src/commands/agent-grounding.ts +0 -62
  76. package/src/commands/ai-signal-clarity.ts +0 -1
  77. package/src/commands/bug.ts +0 -99
  78. package/src/commands/change-amplification.ts +0 -3
  79. package/src/commands/consistency.ts +0 -232
  80. package/src/commands/context.ts +0 -262
  81. package/src/commands/deps-health.ts +0 -1
  82. package/src/commands/doc-drift.ts +0 -1
  83. package/src/commands/index.ts +0 -20
  84. package/src/commands/init.ts +0 -199
  85. package/src/commands/patterns.ts +0 -222
  86. package/src/commands/report-formatter.ts +0 -267
  87. package/src/commands/scan.ts +0 -432
  88. package/src/commands/shared/configured-tool-action.ts +0 -35
  89. package/src/commands/shared/standard-tool-actions.ts +0 -126
  90. package/src/commands/testability.ts +0 -73
  91. package/src/commands/upload.ts +0 -129
  92. package/src/commands/visualize.ts +0 -321
  93. package/src/index.ts +0 -465
  94. package/src/utils/__tests__/helpers.test.ts +0 -35
  95. package/src/utils/helpers.ts +0 -234
  96. package/tsconfig.json +0 -11
  97. package/tsconfig.tsbuildinfo +0 -1
  98. package/vitest.config.ts +0 -13
@@ -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
- }