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