@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
package/src/index.ts DELETED
@@ -1,465 +0,0 @@
1
- import {
2
- ToolRegistry,
3
- ToolName,
4
- calculateOverallScore,
5
- calculateTokenBudget,
6
- GLOBAL_INFRA_OPTIONS,
7
- COMMON_FINE_TUNING_OPTIONS,
8
- initializeParsers,
9
- } from '@aiready/core';
10
- import type {
11
- ScanOptions,
12
- ToolScoringOutput,
13
- ScoringResult,
14
- } from '@aiready/core';
15
-
16
- // Pre-import all tool providers to ensure they are registered by default
17
- import '@aiready/pattern-detect';
18
- import '@aiready/context-analyzer';
19
- import '@aiready/consistency';
20
- import '@aiready/ai-signal-clarity';
21
- import '@aiready/agent-grounding';
22
- import '@aiready/testability';
23
- import '@aiready/doc-drift';
24
- import '@aiready/deps';
25
- import '@aiready/change-amplification';
26
-
27
- export type { ToolScoringOutput, ScoringResult };
28
-
29
- /**
30
- * Options for running a unified AI-readiness analysis across multiple tools.
31
- * Extends base ScanOptions with CLI-specific configurations.
32
- */
33
- export interface UnifiedAnalysisOptions extends ScanOptions {
34
- /** Root directory for analysis */
35
- rootDir: string;
36
- /** List of tools to run (e.g. ['patterns', 'context']) */
37
- tools?: string[];
38
- /** Overrides for specific tool configurations */
39
- toolConfigs?: Record<string, any>;
40
- /** Minimum similarity threshold for pattern detection (0-1) */
41
- minSimilarity?: number;
42
- /** Minimum number of lines for a pattern to be considered */
43
- minLines?: number;
44
- /** Maximum number of candidates to check per code block */
45
- maxCandidatesPerBlock?: number;
46
- /** Minimum number of shared tokens for a match */
47
- minSharedTokens?: number;
48
- /** Whether to use optimized defaults based on project size/language */
49
- useSmartDefaults?: boolean;
50
- /** Specific options for naming consistency analysis */
51
- consistency?: any;
52
- /** Optional callback for tracking analysis progress */
53
- progressCallback?: (event: {
54
- tool: string;
55
- data?: any;
56
- processed?: number;
57
- total?: number;
58
- message?: string;
59
- }) => void;
60
- /** Files or directories to include in scan */
61
- include?: string[];
62
- /** Files or directories to exclude from scan */
63
- exclude?: string[];
64
- /** Batch size for comparisons */
65
- batchSize?: number;
66
- }
67
-
68
- /**
69
- * The consolidated result of a unified analysis across all requested tools.
70
- * Contains tool-specific outputs, scoring, and a high-level summary.
71
- */
72
- export interface UnifiedAnalysisResult {
73
- // Dynamic keys based on ToolName
74
- [key: string]: any;
75
-
76
- summary: {
77
- totalFiles: number;
78
- totalIssues: number;
79
- criticalIssues: number;
80
- majorIssues: number;
81
- toolsRun: string[];
82
- executionTime: number;
83
- config?: any;
84
- toolConfigs?: Record<string, any>;
85
- };
86
- scoring?: ScoringResult;
87
- }
88
-
89
- /**
90
- * Mapping between ToolName and @aiready/ package names.
91
- * Used for dynamic registration on-demand.
92
- */
93
- const TOOL_PACKAGE_MAP: Record<string, string> = {
94
- [ToolName.PatternDetect]: '@aiready/pattern-detect',
95
- [ToolName.ContextAnalyzer]: '@aiready/context-analyzer',
96
- [ToolName.NamingConsistency]: '@aiready/consistency',
97
- [ToolName.AiSignalClarity]: '@aiready/ai-signal-clarity',
98
- [ToolName.AgentGrounding]: '@aiready/agent-grounding',
99
- [ToolName.TestabilityIndex]: '@aiready/testability',
100
- [ToolName.DocDrift]: '@aiready/doc-drift',
101
- [ToolName.DependencyHealth]: '@aiready/deps',
102
- [ToolName.ChangeAmplification]: '@aiready/change-amplification',
103
- // Aliases handled by registry
104
- patterns: '@aiready/pattern-detect',
105
- duplicates: '@aiready/pattern-detect',
106
- context: '@aiready/context-analyzer',
107
- fragmentation: '@aiready/context-analyzer',
108
- consistency: '@aiready/consistency',
109
- 'ai-signal': '@aiready/ai-signal-clarity',
110
- grounding: '@aiready/agent-grounding',
111
- testability: '@aiready/testability',
112
- 'deps-health': '@aiready/deps',
113
- 'change-amp': '@aiready/change-amplification',
114
- };
115
-
116
- /**
117
- * Deeply sanitizes a configuration object by removing infrastructure keys like rootDir.
118
- * Works recursively to clean up nested tool configurations to ensure compatibility
119
- * with the AIReadyConfig schema for portable configuration files.
120
- */
121
- function sanitizeConfigRecursive(obj: any): any {
122
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj;
123
-
124
- const sanitized: any = {};
125
- const infraToStrip = [
126
- 'rootDir',
127
- 'onProgress',
128
- 'progressCallback',
129
- 'streamResults',
130
- 'batchSize',
131
- 'useSmartDefaults',
132
- ];
133
-
134
- for (const [key, value] of Object.entries(obj)) {
135
- // Skip infrastructure keys that shouldn't be in a portable config file
136
- if (infraToStrip.includes(key)) continue;
137
-
138
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
139
- sanitized[key] = sanitizeConfigRecursive(value);
140
- } else {
141
- sanitized[key] = value;
142
- }
143
- }
144
-
145
- return sanitized;
146
- }
147
-
148
- /**
149
- * Sanitize tool configuration by removing global options to match AIReadyConfig schema
150
- */
151
- function sanitizeToolConfig(config: any): any {
152
- return sanitizeConfigRecursive(config);
153
- }
154
-
155
- /**
156
- * AIReady Unified Analysis.
157
- * Orchestrates all registered tools via the ToolRegistry.
158
- *
159
- * @param options - Unified analysis configuration including tools and rootDir.
160
- * @returns Promise resolving to the consolidated analysis result.
161
- * @lastUpdated 2026-03-18
162
- */
163
- export async function analyzeUnified(
164
- options: UnifiedAnalysisOptions
165
- ): Promise<UnifiedAnalysisResult> {
166
- // Initialize language parsers
167
- await initializeParsers();
168
-
169
- const startTime = Date.now();
170
- const requestedTools = options.tools ?? [
171
- 'patterns',
172
- 'context',
173
- 'consistency',
174
- ];
175
-
176
- const result: UnifiedAnalysisResult = {
177
- summary: {
178
- totalIssues: 0,
179
- criticalIssues: 0, // Added as per instruction
180
- majorIssues: 0, // Added as per instruction
181
- totalFiles: 0,
182
- toolsRun: [],
183
- executionTime: 0,
184
- config: options,
185
- toolConfigs: {},
186
- },
187
- };
188
-
189
- for (const toolName of requestedTools) {
190
- let provider = ToolRegistry.find(toolName);
191
-
192
- // Dynamic Loading: If provider not found, attempt to import the package
193
- if (!provider) {
194
- const packageName =
195
- TOOL_PACKAGE_MAP[toolName] ??
196
- (toolName.startsWith('@aiready/') ? toolName : `@aiready/${toolName}`);
197
- try {
198
- await import(packageName);
199
- provider = ToolRegistry.find(toolName);
200
- if (provider) {
201
- console.log(
202
- `✅ Successfully loaded tool provider: ${toolName} from ${packageName}`
203
- );
204
- } else {
205
- console.log(
206
- `⚠️ Loaded ${packageName} but provider ${toolName} still not found in registry.`
207
- );
208
- }
209
- } catch (err: any) {
210
- console.log(
211
- `❌ Failed to dynamically load tool ${toolName} (${packageName}):`,
212
- err.message
213
- );
214
- }
215
- }
216
-
217
- if (!provider) {
218
- console.warn(
219
- `⚠️ Warning: Tool provider for '${toolName}' not found. Skipping.`
220
- );
221
- continue;
222
- }
223
-
224
- try {
225
- // Sanitize options for metadata tracking (remove functions/internal keys)
226
- const sanitizedOptions = { ...options };
227
- delete (sanitizedOptions as any).onProgress;
228
- delete (sanitizedOptions as any).progressCallback;
229
-
230
- // 1. Start with sanitized global subset
231
- const toolOptions: any = {
232
- rootDir: options.rootDir, // Always include rootDir
233
- };
234
-
235
- // 1. Pass through all known infra and fine-tuning options from root level
236
- [...GLOBAL_INFRA_OPTIONS, ...COMMON_FINE_TUNING_OPTIONS].forEach(
237
- (key) => {
238
- if (key in options && key !== 'toolConfigs' && key !== 'tools') {
239
- toolOptions[key] = (options as any)[key];
240
- }
241
- }
242
- );
243
-
244
- // 3. Add tool-specific overrides
245
- // Check priority:
246
- // a) options.toolConfigs[toolId]
247
- // b) options.tools[toolId] (if tools is an object, not the array of tool names)
248
- // c) options[toolId] (legacy)
249
- if (options.toolConfigs?.[provider.id]) {
250
- Object.assign(toolOptions, options.toolConfigs[provider.id]);
251
- } else if (
252
- options.tools &&
253
- !Array.isArray(options.tools) &&
254
- typeof options.tools === 'object' &&
255
- (options.tools as any)[provider.id]
256
- ) {
257
- Object.assign(toolOptions, (options.tools as any)[provider.id]);
258
- } else if ((options as any)[provider.id]) {
259
- // Fallback for legacy tool-specific keys
260
- Object.assign(toolOptions, (options as any)[provider.id]);
261
- }
262
-
263
- // 4. Attach progress callback
264
- toolOptions.onProgress = (
265
- processed: number,
266
- total: number,
267
- message: string
268
- ) => {
269
- if (options.progressCallback) {
270
- options.progressCallback({
271
- tool: provider!.id,
272
- processed,
273
- total,
274
- message,
275
- });
276
- }
277
- };
278
-
279
- const output = await provider.analyze(toolOptions);
280
-
281
- // Inject sanitized configuration into metadata for audit
282
- if (output.metadata) {
283
- output.metadata.config = sanitizeToolConfig(toolOptions);
284
- }
285
-
286
- if (options.progressCallback) {
287
- options.progressCallback({ tool: provider.id, data: output });
288
- }
289
-
290
- result[provider.id] = output;
291
- result.summary.toolsRun.push(provider.id);
292
-
293
- // Collect tool-specific configuration for the audit log
294
- if (output.summary?.config) {
295
- result.summary.toolConfigs![provider.id] = sanitizeToolConfig(
296
- output.summary.config
297
- );
298
- } else if (output.metadata?.config) {
299
- result.summary.toolConfigs![provider.id] = sanitizeToolConfig(
300
- output.metadata.config
301
- );
302
- } else {
303
- // Fallback to our sanitized input options if spoke didn't return config
304
- result.summary.toolConfigs![provider.id] =
305
- sanitizeToolConfig(toolOptions);
306
- }
307
-
308
- // Track total files analyzed across all tools
309
- const toolFiles =
310
- output.summary?.totalFiles ?? output.summary?.filesAnalyzed ?? 0;
311
- if (toolFiles > result.summary.totalFiles) {
312
- result.summary.totalFiles = toolFiles;
313
- }
314
-
315
- const issueCount = output.results.reduce(
316
- (sum: number, file: any) => sum + (file.issues?.length ?? 0),
317
- 0
318
- );
319
- result.summary.totalIssues += issueCount;
320
- } catch (err) {
321
- console.error(`❌ Error running tool '${provider.id}':`, err);
322
- }
323
- }
324
-
325
- // Finalize configuration for metadata to match AIReadyConfig schema
326
- // We use sanitizeConfigRecursive to ensure no internal keys (like rootDir) remain
327
- result.summary.config = sanitizeConfigRecursive({
328
- scan: {
329
- tools: requestedTools,
330
- include: options.include,
331
- exclude: options.exclude,
332
- },
333
- // Use 'tools' for tool-specific configurations to match AIReadyConfig
334
- tools: result.summary.toolConfigs,
335
- });
336
-
337
- result.summary.executionTime = Date.now() - startTime;
338
-
339
- // Add backward compatibility key mappings (kebab-case -> camelCase and aliases)
340
- const keyMappings: Record<string, string[]> = {
341
- 'pattern-detect': ['patternDetect', 'patterns'],
342
- 'context-analyzer': ['contextAnalyzer', 'context'],
343
- 'naming-consistency': ['namingConsistency', 'consistency'],
344
- 'ai-signal-clarity': ['aiSignalClarity'],
345
- 'agent-grounding': ['agentGrounding'],
346
- 'testability-index': ['testabilityIndex', 'testability'],
347
- 'doc-drift': ['docDrift'],
348
- 'dependency-health': ['dependencyHealth', 'deps'],
349
- 'change-amplification': ['changeAmplification'],
350
- };
351
-
352
- for (const [kebabKey, aliases] of Object.entries(keyMappings)) {
353
- if (result[kebabKey]) {
354
- for (const alias of aliases) {
355
- result[alias] = result[kebabKey];
356
- }
357
- }
358
- }
359
-
360
- return result;
361
- }
362
-
363
- /**
364
- * AIReady Unified Scoring.
365
- * Calculates scores for all analyzed tools.
366
- *
367
- * @param results - The consolidated results from a unified analysis.
368
- * @param options - Analysis options for weighting and budget calculation.
369
- * @returns Promise resolving to the final scoring result.
370
- */
371
- export async function scoreUnified(
372
- results: UnifiedAnalysisResult,
373
- options: UnifiedAnalysisOptions
374
- ): Promise<ScoringResult> {
375
- const toolScores: Map<string, ToolScoringOutput> = new Map();
376
-
377
- for (const toolId of results.summary.toolsRun) {
378
- const provider = ToolRegistry.get(toolId as ToolName);
379
- if (!provider) continue;
380
-
381
- const output = results[toolId];
382
- if (!output) continue;
383
-
384
- try {
385
- const toolScore = provider.score(output, options);
386
-
387
- // Special handling for token budget calculation if not provided by tool
388
- if (!toolScore.tokenBudget) {
389
- if (toolId === ToolName.PatternDetect && (output as any).duplicates) {
390
- const wastedTokens = (output as any).duplicates.reduce(
391
- (sum: number, d: any) => sum + (d.tokenCost ?? 0),
392
- 0
393
- );
394
- toolScore.tokenBudget = calculateTokenBudget({
395
- totalContextTokens: wastedTokens * 2,
396
- wastedTokens: {
397
- duplication: wastedTokens,
398
- fragmentation: 0,
399
- chattiness: 0,
400
- },
401
- });
402
- } else if (toolId === ToolName.ContextAnalyzer && output.summary) {
403
- toolScore.tokenBudget = calculateTokenBudget({
404
- totalContextTokens: output.summary.totalTokens,
405
- wastedTokens: {
406
- duplication: 0,
407
- fragmentation: output.summary.totalPotentialSavings ?? 0,
408
- chattiness: 0,
409
- },
410
- });
411
- }
412
- }
413
-
414
- toolScores.set(toolId, toolScore);
415
- } catch (err) {
416
- console.error(`❌ Error scoring tool '${toolId}':`, err);
417
- }
418
- }
419
-
420
- // Handle case where toolScores is empty
421
- if (toolScores.size === 0) {
422
- return {
423
- overall: 0,
424
- rating: 'Critical',
425
- timestamp: new Date().toISOString(),
426
- toolsUsed: [],
427
- breakdown: [],
428
- calculation: {
429
- formula: '0 / 0 = 0',
430
- weights: {},
431
- normalized: '0 / 0 = 0',
432
- },
433
- } as ScoringResult;
434
- }
435
-
436
- return calculateOverallScore(toolScores, options, undefined);
437
- }
438
-
439
- /**
440
- * Generate human-readable summary of unified results.
441
- *
442
- * @param result - The consolidated analysis result object.
443
- * @returns Formatted summary string.
444
- */
445
- export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
446
- const { summary } = result;
447
- let output = `🚀 AIReady Analysis Complete\n\n`;
448
- output += `📊 Summary:\n`;
449
- output += ` Tools run: ${summary.toolsRun.join(', ')}\n`;
450
- output += ` Total issues found: ${summary.totalIssues}\n`;
451
- output += ` Execution time: ${(summary.executionTime / 1000).toFixed(2)}s\n\n`;
452
-
453
- for (const provider of ToolRegistry.getAll()) {
454
- const toolResult = result[provider.id];
455
- if (toolResult) {
456
- const issueCount = toolResult.results.reduce(
457
- (sum: number, r: any) => sum + (r.issues?.length ?? 0),
458
- 0
459
- );
460
- output += `• ${provider.id}: ${issueCount} issues\n`;
461
- }
462
- }
463
-
464
- return output;
465
- }
@@ -1,35 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- getReportTimestamp,
4
- truncateArray,
5
- generateMarkdownReport,
6
- } from '../helpers';
7
-
8
- describe('CLI Helpers', () => {
9
- it('should generate a valid timestamp', () => {
10
- const ts = getReportTimestamp();
11
- expect(ts).toMatch(/^\d{8}-\d{6}$/);
12
- });
13
-
14
- it('should truncate arrays correctly', () => {
15
- const arr = [1, 2, 3, 4, 5];
16
- expect(truncateArray(arr, 3)).toContain('+2 more');
17
- expect(truncateArray(arr, 10)).toBe('1, 2, 3, 4, 5');
18
- });
19
-
20
- it('should generate markdown report', () => {
21
- const report = {
22
- summary: {
23
- filesAnalyzed: 10,
24
- totalIssues: 5,
25
- namingIssues: 2,
26
- patternIssues: 3,
27
- },
28
- recommendations: ['Fix naming'],
29
- };
30
- const md = generateMarkdownReport(report, '1.5');
31
- expect(md).toContain('# Consistency Analysis Report');
32
- expect(md).toContain('**Files Analyzed:** 10');
33
- expect(md).toContain('Fix naming');
34
- });
35
- });