@aiready/cli 0.14.9 → 0.14.10

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.
@@ -57,7 +57,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
57
57
  console.log(chalk.blue('🚀 Starting AIReady unified analysis...\n'));
58
58
 
59
59
  const startTime = Date.now();
60
- const resolvedDir = resolvePath(process.cwd(), directory || '.');
60
+ const resolvedDir = resolvePath(process.cwd(), directory ?? '.');
61
61
  const repoMetadata = getRepoMetadata(resolvedDir);
62
62
 
63
63
  try {
@@ -159,7 +159,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
159
159
  const { getSmartDefaults } = await import('@aiready/pattern-detect');
160
160
  const patternSmartDefaults = await getSmartDefaults(
161
161
  resolvedDir,
162
- finalOptions.toolConfigs?.[ToolName.PatternDetect] || {}
162
+ finalOptions.toolConfigs?.[ToolName.PatternDetect] ?? {}
163
163
  );
164
164
 
165
165
  // Merge smart defaults into toolConfigs instead of root level
@@ -173,7 +173,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
173
173
  console.log(chalk.cyan('\n=== AIReady Run Preview ==='));
174
174
  console.log(
175
175
  chalk.white('Tools to run:'),
176
- (finalOptions.tools || []).join(', ')
176
+ (finalOptions.tools ?? []).join(', ')
177
177
  );
178
178
 
179
179
  // Dynamic progress callback
@@ -202,7 +202,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
202
202
 
203
203
  // Determine scoring profile for project-type-aware weighting
204
204
  const scoringProfile =
205
- options.profile || baseOptions.scoring?.profile || 'default';
205
+ options.profile ?? baseOptions.scoring?.profile ?? 'default';
206
206
 
207
207
  const results = await analyzeUnified({
208
208
  ...finalOptions,
@@ -233,7 +233,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
233
233
  readFileSync(resolvePath(process.cwd(), options.compareTo), 'utf8')
234
234
  );
235
235
  const prevScore =
236
- prevReport.scoring?.overall || prevReport.scoring?.score;
236
+ prevReport.scoring?.overall ?? prevReport.scoring?.score;
237
237
  if (typeof prevScore === 'number') {
238
238
  const diff = scoringResult.overall - prevScore;
239
239
  const diffStr = diff > 0 ? `+${diff}` : String(diff);
@@ -262,19 +262,19 @@ export async function scanAction(directory: string, options: ScanOptions) {
262
262
  }
263
263
 
264
264
  // Token Budget & Cost Logic
265
- const totalWastedDuplication = (scoringResult.breakdown || []).reduce(
266
- (sum, s) =>
267
- sum + (s.tokenBudget?.wastedTokens.bySource.duplication || 0),
265
+ const totalWastedDuplication = (scoringResult.breakdown ?? []).reduce(
266
+ (sum: number, s: any) =>
267
+ sum + (s.tokenBudget?.wastedTokens?.bySource?.duplication ?? 0),
268
268
  0
269
269
  );
270
- const totalWastedFragmentation = (scoringResult.breakdown || []).reduce(
271
- (sum, s) =>
272
- sum + (s.tokenBudget?.wastedTokens.bySource.fragmentation || 0),
270
+ const totalWastedFragmentation = (scoringResult.breakdown ?? []).reduce(
271
+ (sum: number, s: any) =>
272
+ sum + (s.tokenBudget?.wastedTokens?.bySource?.fragmentation ?? 0),
273
273
  0
274
274
  );
275
275
  const totalContext = Math.max(
276
- ...(scoringResult.breakdown || []).map(
277
- (s) => s.tokenBudget?.totalContextTokens || 0
276
+ ...(scoringResult.breakdown ?? []).map(
277
+ (s: any) => s.tokenBudget?.totalContextTokens ?? 0
278
278
  ),
279
279
  0
280
280
  );
@@ -300,7 +300,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
300
300
  }
301
301
  }
302
302
 
303
- const modelId = options.model || 'claude-3-5-sonnet';
303
+ const modelId = options.model ?? 'gpt-5.4-mini';
304
304
  const roi = (await import('@aiready/core')).calculateBusinessROI({
305
305
  tokenWaste: unifiedBudget.wastedTokens.total,
306
306
  issues: allIssues,
@@ -345,9 +345,9 @@ export async function scanAction(directory: string, options: ScanOptions) {
345
345
 
346
346
  // Output persistence
347
347
  const outputFormat =
348
- options.output || finalOptions.output?.format || 'console';
348
+ options.output ?? finalOptions.output?.format ?? 'console';
349
349
  const outputPath = resolveOutputPath(
350
- options.outputFile || finalOptions.output?.file,
350
+ options.outputFile ?? finalOptions.output?.file,
351
351
  `aiready-report-${getReportTimestamp()}.json`,
352
352
  resolvedDir
353
353
  );
@@ -380,8 +380,8 @@ export async function scanAction(directory: string, options: ScanOptions) {
380
380
  const threshold = options.threshold
381
381
  ? parseInt(options.threshold)
382
382
  : undefined;
383
- const failOnLevel = options.failOn || 'critical';
384
- const isCI = options.ci || process.env.CI === 'true';
383
+ const failOnLevel = options.failOn ?? 'critical';
384
+ const isCI = options.ci ?? process.env.CI === 'true';
385
385
 
386
386
  let shouldFail = false;
387
387
  let failReason = '';
@@ -13,10 +13,10 @@ export async function uploadAction(file: string, options: UploadOptions) {
13
13
  const startTime = Date.now();
14
14
  const filePath = resolvePath(process.cwd(), file);
15
15
  const serverUrl =
16
- options.server ||
17
- process.env.AIREADY_SERVER ||
16
+ options.server ??
17
+ process.env.AIREADY_SERVER ??
18
18
  'https://dev.platform.getaiready.dev';
19
- const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
19
+ const apiKey = options.apiKey ?? process.env.AIREADY_API_KEY;
20
20
 
21
21
  if (!apiKey) {
22
22
  console.error(chalk.red('❌ API Key is required for upload.'));
@@ -49,7 +49,7 @@ export async function uploadAction(file: string, options: UploadOptions) {
49
49
 
50
50
  // Prepare upload payload
51
51
  // Note: repoId is optional if the metadata contains it, but for now we'll require it or infer from metadata
52
- const repoId = options.repoId || reportData.repository?.repoId;
52
+ const repoId = options.repoId ?? reportData.repository?.repoId;
53
53
 
54
54
  const response = await fetch(`${serverUrl}/api/analysis/upload`, {
55
55
  method: 'POST',
@@ -70,13 +70,13 @@ export async function uploadAction(file: string, options: UploadOptions) {
70
70
  uploadResult = await response.json();
71
71
  } else {
72
72
  const text = await response.text();
73
- uploadResult = { error: text || response.statusText };
73
+ uploadResult = { error: text ?? response.statusText };
74
74
  }
75
75
 
76
76
  if (!response.ok) {
77
77
  console.error(
78
78
  chalk.red(
79
- `❌ Upload failed: ${uploadResult.error || response.statusText}`
79
+ `❌ Upload failed: ${uploadResult.error ?? response.statusText}`
80
80
  )
81
81
  );
82
82
 
@@ -30,7 +30,7 @@ export async function visualizeAction(
30
30
  options: VisualizeOptions
31
31
  ) {
32
32
  try {
33
- const dirPath = resolvePath(process.cwd(), directory || '.');
33
+ const dirPath = resolvePath(process.cwd(), directory ?? '.');
34
34
  let reportPath = options.report
35
35
  ? resolvePath(dirPath, options.report)
36
36
  : null;
@@ -87,7 +87,7 @@ export async function visualizeAction(
87
87
  const graph = GraphBuilder.buildFromReport(report, dirPath);
88
88
 
89
89
  // Check if --dev mode is requested and available
90
- let useDevMode = options.dev || false;
90
+ let useDevMode = options.dev ?? false;
91
91
  let devServerStarted = false;
92
92
 
93
93
  if (useDevMode) {
@@ -230,7 +230,7 @@ export async function visualizeAction(
230
230
  console.log('Generating HTML...');
231
231
  const html = generateHTML(graph as any);
232
232
  const defaultOutput = 'visualization.html';
233
- const outPath = resolvePath(dirPath, options.output || defaultOutput);
233
+ const outPath = resolvePath(dirPath, options.output ?? defaultOutput);
234
234
  writeFileSync(outPath, html, 'utf8');
235
235
  console.log(chalk.green(`✅ Visualization written to: ${outPath}`));
236
236
 
@@ -250,7 +250,7 @@ export async function visualizeAction(
250
250
 
251
251
  const server = http.createServer(async (req, res) => {
252
252
  try {
253
- const urlPath = req.url || '/';
253
+ const urlPath = req.url ?? '/';
254
254
  if (urlPath === '/' || urlPath === '/index.html') {
255
255
  const content = await fsp.readFile(outPath, 'utf8');
256
256
  res.writeHead(200, {
package/src/index.ts CHANGED
@@ -57,6 +57,12 @@ export interface UnifiedAnalysisOptions extends ScanOptions {
57
57
  total?: number;
58
58
  message?: string;
59
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;
60
66
  }
61
67
 
62
68
  /**
@@ -161,7 +167,7 @@ export async function analyzeUnified(
161
167
  await initializeParsers();
162
168
 
163
169
  const startTime = Date.now();
164
- const requestedTools = options.tools || [
170
+ const requestedTools = options.tools ?? [
165
171
  'patterns',
166
172
  'context',
167
173
  'consistency',
@@ -186,7 +192,7 @@ export async function analyzeUnified(
186
192
  // Dynamic Loading: If provider not found, attempt to import the package
187
193
  if (!provider) {
188
194
  const packageName =
189
- TOOL_PACKAGE_MAP[toolName] ||
195
+ TOOL_PACKAGE_MAP[toolName] ??
190
196
  (toolName.startsWith('@aiready/') ? toolName : `@aiready/${toolName}`);
191
197
  try {
192
198
  await import(packageName);
@@ -301,34 +307,16 @@ export async function analyzeUnified(
301
307
 
302
308
  // Track total files analyzed across all tools
303
309
  const toolFiles =
304
- output.summary?.totalFiles || output.summary?.filesAnalyzed || 0;
310
+ output.summary?.totalFiles ?? output.summary?.filesAnalyzed ?? 0;
305
311
  if (toolFiles > result.summary.totalFiles) {
306
312
  result.summary.totalFiles = toolFiles;
307
313
  }
308
314
 
309
315
  const issueCount = output.results.reduce(
310
- (sum: number, file: any) => sum + (file.issues?.length || 0),
316
+ (sum: number, file: any) => sum + (file.issues?.length ?? 0),
311
317
  0
312
318
  );
313
319
  result.summary.totalIssues += issueCount;
314
-
315
- // Robust backward compatibility fallbacks
316
- // 1. Add all aliases as keys (e.g., 'patterns', 'context', 'consistency')
317
- if (provider.alias && Array.isArray(provider.alias)) {
318
- for (const alias of provider.alias) {
319
- if (!result[alias]) {
320
- (result as any)[alias] = output;
321
- }
322
- }
323
- }
324
-
325
- // 2. Add camelCase version of canonical ID (e.g., 'patternDetect', 'contextAnalyzer')
326
- const camelCaseId = provider.id.replace(/-([a-z])/g, (g) =>
327
- g[1].toUpperCase()
328
- );
329
- if (camelCaseId !== provider.id && !result[camelCaseId]) {
330
- (result as any)[camelCaseId] = output;
331
- }
332
320
  } catch (err) {
333
321
  console.error(`❌ Error running tool '${provider.id}':`, err);
334
322
  }
@@ -347,6 +335,28 @@ export async function analyzeUnified(
347
335
  });
348
336
 
349
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
+
350
360
  return result;
351
361
  }
352
362
 
@@ -378,7 +388,7 @@ export async function scoreUnified(
378
388
  if (!toolScore.tokenBudget) {
379
389
  if (toolId === ToolName.PatternDetect && (output as any).duplicates) {
380
390
  const wastedTokens = (output as any).duplicates.reduce(
381
- (sum: number, d: any) => sum + (d.tokenCost || 0),
391
+ (sum: number, d: any) => sum + (d.tokenCost ?? 0),
382
392
  0
383
393
  );
384
394
  toolScore.tokenBudget = calculateTokenBudget({
@@ -394,7 +404,7 @@ export async function scoreUnified(
394
404
  totalContextTokens: output.summary.totalTokens,
395
405
  wastedTokens: {
396
406
  duplication: 0,
397
- fragmentation: output.summary.totalPotentialSavings || 0,
407
+ fragmentation: output.summary.totalPotentialSavings ?? 0,
398
408
  chattiness: 0,
399
409
  },
400
410
  });
@@ -444,7 +454,7 @@ export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
444
454
  const toolResult = result[provider.id];
445
455
  if (toolResult) {
446
456
  const issueCount = toolResult.results.reduce(
447
- (sum: number, r: any) => sum + (r.issues?.length || 0),
457
+ (sum: number, r: any) => sum + (r.issues?.length ?? 0),
448
458
  0
449
459
  );
450
460
  output += `• ${provider.id}: ${issueCount} issues\n`;