@aiready/cli 0.14.9 → 0.14.11

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/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  __require,
4
4
  analyzeUnified,
5
5
  scoreUnified
6
- } from "./chunk-VOKP7FGM.mjs";
6
+ } from "./chunk-JRRBBFYB.mjs";
7
7
 
8
8
  // src/cli.ts
9
9
  import { Command } from "commander";
@@ -131,13 +131,91 @@ import {
131
131
  getRating,
132
132
  getRatingDisplay
133
133
  } from "@aiready/core";
134
+ function generateProgressBar(score, width = 20) {
135
+ const filled = Math.round(score / 100 * width);
136
+ const empty = width - filled;
137
+ let color = chalk2.red;
138
+ if (score >= 90) color = chalk2.green;
139
+ else if (score >= 75) color = chalk2.cyan;
140
+ else if (score >= 60) color = chalk2.yellow;
141
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
142
+ return color(bar);
143
+ }
144
+ function countIssuesBySeverity(results) {
145
+ let critical = 0;
146
+ let major = 0;
147
+ let minor = 0;
148
+ if (results.summary?.toolsRun) {
149
+ for (const toolId of results.summary.toolsRun) {
150
+ const toolRes = results[toolId];
151
+ if (toolRes?.results) {
152
+ for (const fileRes of toolRes.results) {
153
+ if (fileRes.issues) {
154
+ for (const issue of fileRes.issues) {
155
+ const sev = issue.severity?.toLowerCase();
156
+ if (sev === "critical") critical++;
157
+ else if (sev === "major") major++;
158
+ else minor++;
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return { critical, major, minor };
166
+ }
167
+ function getTopFilesWithIssues(results, limit = 5) {
168
+ const fileCounts = /* @__PURE__ */ new Map();
169
+ if (results.summary?.toolsRun) {
170
+ for (const toolId of results.summary.toolsRun) {
171
+ const toolRes = results[toolId];
172
+ if (toolRes?.results) {
173
+ for (const fileRes of toolRes.results) {
174
+ if (fileRes.issues?.length > 0) {
175
+ const current = fileCounts.get(fileRes.fileName) || 0;
176
+ fileCounts.set(fileRes.fileName, current + fileRes.issues.length);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ return Array.from(fileCounts.entries()).map(([file, count]) => ({ file, count })).sort((a, b) => b.count - a.count).slice(0, limit);
183
+ }
134
184
  function printScanSummary(results, startTime) {
185
+ const severity = countIssuesBySeverity(results);
186
+ const totalIssues = severity.critical + severity.major + severity.minor;
187
+ const topFiles = getTopFilesWithIssues(results);
135
188
  console.log(chalk2.cyan("\n=== AIReady Run Summary ==="));
189
+ console.log(` Total issues: ${chalk2.bold(String(totalIssues))}`);
190
+ if (totalIssues > 0) {
191
+ console.log(chalk2.dim(" Severity breakdown:"));
192
+ if (severity.critical > 0) {
193
+ console.log(
194
+ ` ${chalk2.red("\u25CF")} Critical: ${chalk2.bold(severity.critical)}`
195
+ );
196
+ }
197
+ if (severity.major > 0) {
198
+ console.log(
199
+ ` ${chalk2.yellow("\u25CF")} Major: ${chalk2.bold(severity.major)}`
200
+ );
201
+ }
202
+ if (severity.minor > 0) {
203
+ console.log(
204
+ ` ${chalk2.blue("\u25CF")} Minor: ${chalk2.bold(severity.minor)}`
205
+ );
206
+ }
207
+ }
208
+ if (topFiles.length > 0) {
209
+ console.log(chalk2.dim("\n Top files with issues:"));
210
+ topFiles.forEach((item) => {
211
+ console.log(
212
+ ` ${chalk2.yellow("\u2192")} ${item.file}: ${chalk2.bold(item.count)} issues`
213
+ );
214
+ });
215
+ }
136
216
  console.log(
137
- ` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
138
- );
139
- console.log(
140
- ` Execution time: ${chalk2.bold(((Date.now() - startTime) / 1e3).toFixed(2) + "s")}`
217
+ `
218
+ Execution time: ${chalk2.bold(((Date.now() - startTime) / 1e3).toFixed(2) + "s")}`
141
219
  );
142
220
  }
143
221
  function printBusinessImpact(roi, unifiedBudget) {
@@ -164,13 +242,14 @@ function printScoring(scoringResult, scoringProfile) {
164
242
  scoringResult.breakdown.forEach((tool) => {
165
243
  const rating = getRating(tool.score);
166
244
  const emoji = getRatingDisplay(rating).emoji;
245
+ const progressBar = generateProgressBar(tool.score, 15);
167
246
  console.log(
168
- ` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
247
+ ` ${progressBar} ${tool.score}/100 (${rating}) ${emoji} ${tool.toolName}`
169
248
  );
170
249
  });
171
250
  const allRecs = scoringResult.breakdown.flatMap(
172
- (t) => (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
173
- ).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 3);
251
+ (t) => (t.recommendations ?? []).map((r) => ({ ...r, tool: t.toolName }))
252
+ ).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
174
253
  if (allRecs.length > 0) {
175
254
  console.log(chalk2.bold("\n\u{1F3AF} Top Actionable Recommendations:"));
176
255
  allRecs.forEach((rec, i) => {
@@ -223,8 +302,8 @@ import { handleCLIError } from "@aiready/core";
223
302
  async function uploadAction(file, options) {
224
303
  const startTime = Date.now();
225
304
  const filePath = resolvePath2(process.cwd(), file);
226
- const serverUrl = options.server || process.env.AIREADY_SERVER || "https://dev.platform.getaiready.dev";
227
- const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
305
+ const serverUrl = options.server ?? process.env.AIREADY_SERVER ?? "https://dev.platform.getaiready.dev";
306
+ const apiKey = options.apiKey ?? process.env.AIREADY_API_KEY;
228
307
  if (!apiKey) {
229
308
  console.error(chalk3.red("\u274C API Key is required for upload."));
230
309
  console.log(
@@ -249,7 +328,7 @@ async function uploadAction(file, options) {
249
328
  const reportContent = fs.readFileSync(filePath, "utf-8");
250
329
  const reportData = JSON.parse(reportContent);
251
330
  console.log(chalk3.dim(` Successfully parsed report JSON.`));
252
- const repoId = options.repoId || reportData.repository?.repoId;
331
+ const repoId = options.repoId ?? reportData.repository?.repoId;
253
332
  const response = await fetch(`${serverUrl}/api/analysis/upload`, {
254
333
  method: "POST",
255
334
  headers: {
@@ -268,12 +347,12 @@ async function uploadAction(file, options) {
268
347
  uploadResult = await response.json();
269
348
  } else {
270
349
  const text = await response.text();
271
- uploadResult = { error: text || response.statusText };
350
+ uploadResult = { error: text ?? response.statusText };
272
351
  }
273
352
  if (!response.ok) {
274
353
  console.error(
275
354
  chalk3.red(
276
- `\u274C Upload failed: ${uploadResult.error || response.statusText}`
355
+ `\u274C Upload failed: ${uploadResult.error ?? response.statusText}`
277
356
  )
278
357
  );
279
358
  if (contentType?.includes("text/html")) {
@@ -324,7 +403,7 @@ ENVIRONMENT VARIABLES:
324
403
  async function scanAction(directory, options) {
325
404
  console.log(chalk4.blue("\u{1F680} Starting AIReady unified analysis...\n"));
326
405
  const startTime = Date.now();
327
- const resolvedDir = resolvePath3(process.cwd(), directory || ".");
406
+ const resolvedDir = resolvePath3(process.cwd(), directory ?? ".");
328
407
  const repoMetadata = getRepoMetadata(resolvedDir);
329
408
  try {
330
409
  const defaults = {
@@ -414,7 +493,7 @@ async function scanAction(directory, options) {
414
493
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
415
494
  const patternSmartDefaults = await getSmartDefaults(
416
495
  resolvedDir,
417
- finalOptions.toolConfigs?.[ToolName.PatternDetect] || {}
496
+ finalOptions.toolConfigs?.[ToolName.PatternDetect] ?? {}
418
497
  );
419
498
  if (!finalOptions.toolConfigs) finalOptions.toolConfigs = {};
420
499
  finalOptions.toolConfigs[ToolName.PatternDetect] = {
@@ -425,7 +504,7 @@ async function scanAction(directory, options) {
425
504
  console.log(chalk4.cyan("\n=== AIReady Run Preview ==="));
426
505
  console.log(
427
506
  chalk4.white("Tools to run:"),
428
- (finalOptions.tools || []).join(", ")
507
+ (finalOptions.tools ?? []).join(", ")
429
508
  );
430
509
  const progressCallback = (event) => {
431
510
  if (event.message) {
@@ -446,7 +525,7 @@ async function scanAction(directory, options) {
446
525
  );
447
526
  }
448
527
  };
449
- const scoringProfile = options.profile || baseOptions.scoring?.profile || "default";
528
+ const scoringProfile = options.profile ?? baseOptions.scoring?.profile ?? "default";
450
529
  const results = await analyzeUnified({
451
530
  ...finalOptions,
452
531
  progressCallback,
@@ -470,7 +549,7 @@ async function scanAction(directory, options) {
470
549
  const prevReport = JSON.parse(
471
550
  readFileSync2(resolvePath3(process.cwd(), options.compareTo), "utf8")
472
551
  );
473
- const prevScore = prevReport.scoring?.overall || prevReport.scoring?.score;
552
+ const prevScore = prevReport.scoring?.overall ?? prevReport.scoring?.score;
474
553
  if (typeof prevScore === "number") {
475
554
  const diff = scoringResult.overall - prevScore;
476
555
  const diffStr = diff > 0 ? `+${diff}` : String(diff);
@@ -497,17 +576,17 @@ async function scanAction(directory, options) {
497
576
  void e;
498
577
  }
499
578
  }
500
- const totalWastedDuplication = (scoringResult.breakdown || []).reduce(
501
- (sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.duplication || 0),
579
+ const totalWastedDuplication = (scoringResult.breakdown ?? []).reduce(
580
+ (sum, s) => sum + (s.tokenBudget?.wastedTokens?.bySource?.duplication ?? 0),
502
581
  0
503
582
  );
504
- const totalWastedFragmentation = (scoringResult.breakdown || []).reduce(
505
- (sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.fragmentation || 0),
583
+ const totalWastedFragmentation = (scoringResult.breakdown ?? []).reduce(
584
+ (sum, s) => sum + (s.tokenBudget?.wastedTokens?.bySource?.fragmentation ?? 0),
506
585
  0
507
586
  );
508
587
  const totalContext = Math.max(
509
- ...(scoringResult.breakdown || []).map(
510
- (s) => s.tokenBudget?.totalContextTokens || 0
588
+ ...(scoringResult.breakdown ?? []).map(
589
+ (s) => s.tokenBudget?.totalContextTokens ?? 0
511
590
  ),
512
591
  0
513
592
  );
@@ -531,7 +610,7 @@ async function scanAction(directory, options) {
531
610
  });
532
611
  }
533
612
  }
534
- const modelId = options.model || "claude-3-5-sonnet";
613
+ const modelId = options.model ?? "gpt-5.4-mini";
535
614
  const roi = (await import("@aiready/core")).calculateBusinessROI({
536
615
  tokenWaste: unifiedBudget.wastedTokens.total,
537
616
  issues: allIssues,
@@ -568,9 +647,9 @@ async function scanAction(directory, options) {
568
647
  ...mapToUnifiedReport(results, scoringResult),
569
648
  repository: repoMetadata
570
649
  };
571
- const outputFormat = options.output || finalOptions.output?.format || "console";
650
+ const outputFormat = options.output ?? finalOptions.output?.format ?? "console";
572
651
  const outputPath = resolveOutputPath(
573
- options.outputFile || finalOptions.output?.file,
652
+ options.outputFile ?? finalOptions.output?.file,
574
653
  `aiready-report-${getReportTimestamp()}.json`,
575
654
  resolvedDir
576
655
  );
@@ -597,8 +676,8 @@ async function scanAction(directory, options) {
597
676
  await warnIfGraphCapExceeded(outputData, resolvedDir);
598
677
  if (scoringResult) {
599
678
  const threshold = options.threshold ? parseInt(options.threshold) : void 0;
600
- const failOnLevel = options.failOn || "critical";
601
- const isCI = options.ci || process.env.CI === "true";
679
+ const failOnLevel = options.failOn ?? "critical";
680
+ const isCI = options.ci ?? process.env.CI === "true";
602
681
  let shouldFail = false;
603
682
  let failReason = "";
604
683
  const report = mapToUnifiedReport(results, scoringResult);
@@ -654,6 +733,9 @@ async function initAction(options) {
654
733
  process.exit(1);
655
734
  }
656
735
  const baseConfig = {
736
+ // Target quality score threshold (0-100)
737
+ threshold: 75,
738
+ // Global scan settings
657
739
  scan: {
658
740
  include: [
659
741
  "src/**/*.ts",
@@ -680,6 +762,17 @@ async function initAction(options) {
680
762
  ToolName2.ChangeAmplification
681
763
  ]
682
764
  },
765
+ // Output preferences
766
+ output: {
767
+ format: "console",
768
+ showBreakdown: true,
769
+ saveTo: "aiready-report.json"
770
+ },
771
+ // Scoring profile and weights
772
+ scoring: {
773
+ profile: "balanced"
774
+ },
775
+ // Tool-specific configurations
683
776
  tools: {
684
777
  [ToolName2.PatternDetect]: {
685
778
  minSimilarity: 0.8,
@@ -703,7 +796,33 @@ async function initAction(options) {
703
796
  },
704
797
  [ToolName2.NamingConsistency]: {
705
798
  shortWords: ["id", "db", "ui", "ai"],
706
- ...options.full ? { acceptedAbbreviations: [], disableChecks: [] } : {}
799
+ acceptedAbbreviations: [
800
+ "API",
801
+ "JSON",
802
+ "CSV",
803
+ "HTML",
804
+ "CSS",
805
+ "HTTP",
806
+ "URL",
807
+ "SDK",
808
+ "CLI",
809
+ "AI",
810
+ "ML",
811
+ "ID",
812
+ "DB",
813
+ "UI",
814
+ "UX",
815
+ "DOM",
816
+ "UUID",
817
+ "GUID",
818
+ "DEFAULT",
819
+ "MAX",
820
+ "MIN",
821
+ "config",
822
+ "INIT",
823
+ "SKILL"
824
+ ],
825
+ ...options.full ? { disableChecks: [] } : {}
707
826
  },
708
827
  [ToolName2.AiSignalClarity]: {
709
828
  checkMagicLiterals: true,
@@ -712,48 +831,42 @@ async function initAction(options) {
712
831
  checkUndocumentedExports: true,
713
832
  ...options.full ? { checkImplicitSideEffects: false, checkDeepCallbacks: false } : {}
714
833
  },
715
- ...options.full ? {
716
- [ToolName2.AgentGrounding]: {
717
- maxRecommendedDepth: 5,
718
- readmeStaleDays: 30
719
- },
720
- [ToolName2.TestabilityIndex]: {
721
- minCoverageRatio: 0.7,
722
- testPatterns: ["**/*.test.ts", "**/__tests__/**"]
723
- },
724
- [ToolName2.DocDrift]: {
725
- maxCommits: 50,
726
- staleMonths: 3
727
- },
728
- [ToolName2.DependencyHealth]: {
729
- trainingCutoffYear: 2023
730
- }
731
- } : {}
732
- },
733
- scoring: {
734
- threshold: 70,
735
- showBreakdown: true,
736
- ...options.full ? { profile: "default" } : {}
834
+ [ToolName2.AgentGrounding]: {
835
+ maxRecommendedDepth: 5,
836
+ readmeStaleDays: 30
837
+ },
838
+ [ToolName2.TestabilityIndex]: {
839
+ minCoverageRatio: 0.7,
840
+ testPatterns: ["**/*.test.ts", "**/__tests__/**"]
841
+ },
842
+ [ToolName2.DocDrift]: {
843
+ maxCommits: 50,
844
+ staleMonths: 3
845
+ },
846
+ [ToolName2.DependencyHealth]: {
847
+ trainingCutoffYear: 2023
848
+ },
849
+ [ToolName2.ChangeAmplification]: {
850
+ // No specific options yet, uses global scan settings
851
+ }
737
852
  },
738
- ...options.full ? {
739
- visualizer: {
740
- groupingDirs: ["packages", "src", "lib"],
741
- graph: {
742
- maxNodes: 5e3,
743
- maxEdges: 1e4
744
- }
853
+ // Visualizer settings (interactive graph)
854
+ visualizer: {
855
+ groupingDirs: ["packages", "src", "lib"],
856
+ graph: {
857
+ maxNodes: 5e3,
858
+ maxEdges: 1e4
745
859
  }
746
- } : {}
860
+ }
747
861
  };
748
862
  const defaultConfig = baseConfig;
749
863
  let content;
750
864
  if (fileExt === "js") {
751
- content = `/** @type {import('@aiready/core').AIReadyConfig} */
752
- module.exports = ${JSON.stringify(
753
- defaultConfig,
754
- null,
755
- 2
756
- )};
865
+ content = `/**
866
+ * AIReady Configuration
867
+ * @type {import('@aiready/core').AIReadyConfig}
868
+ */
869
+ module.exports = ${JSON.stringify(defaultConfig, null, 2)};
757
870
  `;
758
871
  } else {
759
872
  content = JSON.stringify(defaultConfig, null, 2);
@@ -789,7 +902,7 @@ import {
789
902
  async function patternsAction(directory, options) {
790
903
  console.log(chalk6.blue("\u{1F50D} Analyzing patterns...\n"));
791
904
  const startTime = Date.now();
792
- const resolvedDir = resolvePath4(process.cwd(), directory || ".");
905
+ const resolvedDir = resolvePath4(process.cwd(), directory ?? ".");
793
906
  try {
794
907
  const useSmartDefaults = !options.fullScan;
795
908
  const defaults = {
@@ -833,8 +946,8 @@ async function patternsAction(directory, options) {
833
946
  if (options.score) {
834
947
  patternScore = calculatePatternScore(duplicates, results.length);
835
948
  }
836
- const outputFormat = options.output || finalOptions.output?.format || "console";
837
- const userOutputFile = options.outputFile || finalOptions.output?.file;
949
+ const outputFormat = options.output ?? finalOptions.output?.format ?? "console";
950
+ const userOutputFile = options.outputFile ?? finalOptions.output?.file;
838
951
  if (outputFormat === "json") {
839
952
  const outputData = {
840
953
  results,
@@ -942,7 +1055,7 @@ import {
942
1055
  async function contextAction(directory, options) {
943
1056
  console.log(chalk7.blue("\u{1F9E0} Analyzing context costs...\n"));
944
1057
  const startTime = Date.now();
945
- const resolvedDir = resolvePath5(process.cwd(), directory || ".");
1058
+ const resolvedDir = resolvePath5(process.cwd(), directory ?? ".");
946
1059
  try {
947
1060
  const defaults = {
948
1061
  maxDepth: 5,
@@ -986,8 +1099,8 @@ async function contextAction(directory, options) {
986
1099
  if (options.score) {
987
1100
  contextScore = calculateContextScore(summary);
988
1101
  }
989
- const outputFormat = options.output || finalOptions.output?.format || "console";
990
- const userOutputFile = options.outputFile || finalOptions.output?.file;
1102
+ const outputFormat = options.output ?? finalOptions.output?.format ?? "console";
1103
+ const userOutputFile = options.outputFile ?? finalOptions.output?.file;
991
1104
  if (outputFormat === "json") {
992
1105
  const outputData = {
993
1106
  results,
@@ -1005,7 +1118,7 @@ async function contextAction(directory, options) {
1005
1118
  `\u2705 Results saved to ${outputPath}`
1006
1119
  );
1007
1120
  } else {
1008
- const terminalWidth = process.stdout.columns || 80;
1121
+ const terminalWidth = process.stdout.columns ?? 80;
1009
1122
  const dividerWidth = Math.min(60, terminalWidth - 2);
1010
1123
  const divider = "\u2501".repeat(dividerWidth);
1011
1124
  console.log(chalk7.cyan(divider));
@@ -1150,7 +1263,7 @@ import {
1150
1263
  async function consistencyAction(directory, options) {
1151
1264
  console.log(chalk8.blue("\u{1F50D} Analyzing consistency...\n"));
1152
1265
  const startTime = Date.now();
1153
- const resolvedDir = resolvePath6(process.cwd(), directory || ".");
1266
+ const resolvedDir = resolvePath6(process.cwd(), directory ?? ".");
1154
1267
  try {
1155
1268
  const defaults = {
1156
1269
  checkNaming: true,
@@ -1175,14 +1288,14 @@ async function consistencyAction(directory, options) {
1175
1288
  const elapsedTime = getElapsedTime3(startTime);
1176
1289
  let consistencyScore;
1177
1290
  if (options.score) {
1178
- const issues = report.results?.flatMap((r) => r.issues) || [];
1291
+ const issues = report.results?.flatMap((r) => r.issues) ?? [];
1179
1292
  consistencyScore = calculateConsistencyScore(
1180
1293
  issues,
1181
1294
  report.summary.filesAnalyzed
1182
1295
  );
1183
1296
  }
1184
- const outputFormat = options.output || finalOptions.output?.format || "console";
1185
- const userOutputFile = options.outputFile || finalOptions.output?.file;
1297
+ const outputFormat = options.output ?? finalOptions.output?.format ?? "console";
1298
+ const userOutputFile = options.outputFile ?? finalOptions.output?.file;
1186
1299
  if (outputFormat === "json") {
1187
1300
  const outputData = {
1188
1301
  ...report,
@@ -1220,7 +1333,7 @@ async function consistencyAction(directory, options) {
1220
1333
  console.log(` Naming: ${chalk8.yellow(report.summary.namingIssues)}`);
1221
1334
  console.log(` Patterns: ${chalk8.yellow(report.summary.patternIssues)}`);
1222
1335
  console.log(
1223
- ` Architecture: ${chalk8.yellow(report.summary.architectureIssues || 0)}`
1336
+ ` Architecture: ${chalk8.yellow(report.summary.architectureIssues ?? 0)}`
1224
1337
  );
1225
1338
  console.log(`Analysis Time: ${chalk8.gray(elapsedTime + "s")}
1226
1339
  `);
@@ -1319,7 +1432,7 @@ import { handleCLIError as handleCLIError6 } from "@aiready/core";
1319
1432
  import { generateHTML, findLatestReport as findLatestReport2 } from "@aiready/core";
1320
1433
  async function visualizeAction(directory, options) {
1321
1434
  try {
1322
- const dirPath = resolvePath7(process.cwd(), directory || ".");
1435
+ const dirPath = resolvePath7(process.cwd(), directory ?? ".");
1323
1436
  let reportPath = options.report ? resolvePath7(dirPath, options.report) : null;
1324
1437
  if (!reportPath || !existsSync3(reportPath)) {
1325
1438
  const latestScan = findLatestReport2(dirPath);
@@ -1365,7 +1478,7 @@ Or specify a custom report:
1365
1478
  console.log("Building graph from report...");
1366
1479
  const { GraphBuilder } = await import("@aiready/visualizer/graph");
1367
1480
  const graph = GraphBuilder.buildFromReport(report, dirPath);
1368
- let useDevMode = options.dev || false;
1481
+ let useDevMode = options.dev ?? false;
1369
1482
  let devServerStarted = false;
1370
1483
  if (useDevMode) {
1371
1484
  try {
@@ -1481,7 +1594,7 @@ Or specify a custom report:
1481
1594
  console.log("Generating HTML...");
1482
1595
  const html = generateHTML(graph);
1483
1596
  const defaultOutput = "visualization.html";
1484
- const outPath = resolvePath7(dirPath, options.output || defaultOutput);
1597
+ const outPath = resolvePath7(dirPath, options.output ?? defaultOutput);
1485
1598
  writeFileSync4(outPath, html, "utf8");
1486
1599
  console.log(chalk9.green(`\u2705 Visualization written to: ${outPath}`));
1487
1600
  if (options.open || options.serve) {
@@ -1493,7 +1606,7 @@ Or specify a custom report:
1493
1606
  const fsp = await import("fs/promises");
1494
1607
  const server = http.createServer(async (req, res) => {
1495
1608
  try {
1496
- const urlPath = req.url || "/";
1609
+ const urlPath = req.url ?? "/";
1497
1610
  if (urlPath === "/" || urlPath === "/index.html") {
1498
1611
  const content = await fsp.readFile(outPath, "utf8");
1499
1612
  res.writeHead(200, {
@@ -1631,7 +1744,7 @@ async function bugAction(message, options) {
1631
1744
  const repoUrl = "https://github.com/caopengau/aiready-cli";
1632
1745
  const repoSlug = "caopengau/aiready-cli";
1633
1746
  if (message) {
1634
- const type = options.type || "bug";
1747
+ const type = options.type ?? "bug";
1635
1748
  const title = `[${type.toUpperCase()}] ${message}`;
1636
1749
  const label = type === "bug" ? "bug" : type === "feature" ? "enhancement" : "metric";
1637
1750
  const body = `
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ function sanitizeToolConfig(config) {
84
84
  async function analyzeUnified(options) {
85
85
  await (0, import_core.initializeParsers)();
86
86
  const startTime = Date.now();
87
- const requestedTools = options.tools || [
87
+ const requestedTools = options.tools ?? [
88
88
  "patterns",
89
89
  "context",
90
90
  "consistency"
@@ -106,7 +106,7 @@ async function analyzeUnified(options) {
106
106
  for (const toolName of requestedTools) {
107
107
  let provider = import_core.ToolRegistry.find(toolName);
108
108
  if (!provider) {
109
- const packageName = TOOL_PACKAGE_MAP[toolName] || (toolName.startsWith("@aiready/") ? toolName : `@aiready/${toolName}`);
109
+ const packageName = TOOL_PACKAGE_MAP[toolName] ?? (toolName.startsWith("@aiready/") ? toolName : `@aiready/${toolName}`);
110
110
  try {
111
111
  await import(packageName);
112
112
  provider = import_core.ToolRegistry.find(toolName);
@@ -184,29 +184,15 @@ async function analyzeUnified(options) {
184
184
  } else {
185
185
  result.summary.toolConfigs[provider.id] = sanitizeToolConfig(toolOptions);
186
186
  }
187
- const toolFiles = output.summary?.totalFiles || output.summary?.filesAnalyzed || 0;
187
+ const toolFiles = output.summary?.totalFiles ?? output.summary?.filesAnalyzed ?? 0;
188
188
  if (toolFiles > result.summary.totalFiles) {
189
189
  result.summary.totalFiles = toolFiles;
190
190
  }
191
191
  const issueCount = output.results.reduce(
192
- (sum, file) => sum + (file.issues?.length || 0),
192
+ (sum, file) => sum + (file.issues?.length ?? 0),
193
193
  0
194
194
  );
195
195
  result.summary.totalIssues += issueCount;
196
- if (provider.alias && Array.isArray(provider.alias)) {
197
- for (const alias of provider.alias) {
198
- if (!result[alias]) {
199
- result[alias] = output;
200
- }
201
- }
202
- }
203
- const camelCaseId = provider.id.replace(
204
- /-([a-z])/g,
205
- (g) => g[1].toUpperCase()
206
- );
207
- if (camelCaseId !== provider.id && !result[camelCaseId]) {
208
- result[camelCaseId] = output;
209
- }
210
196
  } catch (err) {
211
197
  console.error(`\u274C Error running tool '${provider.id}':`, err);
212
198
  }
@@ -221,6 +207,24 @@ async function analyzeUnified(options) {
221
207
  tools: result.summary.toolConfigs
222
208
  });
223
209
  result.summary.executionTime = Date.now() - startTime;
210
+ const keyMappings = {
211
+ "pattern-detect": ["patternDetect", "patterns"],
212
+ "context-analyzer": ["contextAnalyzer", "context"],
213
+ "naming-consistency": ["namingConsistency", "consistency"],
214
+ "ai-signal-clarity": ["aiSignalClarity"],
215
+ "agent-grounding": ["agentGrounding"],
216
+ "testability-index": ["testabilityIndex", "testability"],
217
+ "doc-drift": ["docDrift"],
218
+ "dependency-health": ["dependencyHealth", "deps"],
219
+ "change-amplification": ["changeAmplification"]
220
+ };
221
+ for (const [kebabKey, aliases] of Object.entries(keyMappings)) {
222
+ if (result[kebabKey]) {
223
+ for (const alias of aliases) {
224
+ result[alias] = result[kebabKey];
225
+ }
226
+ }
227
+ }
224
228
  return result;
225
229
  }
226
230
  async function scoreUnified(results, options) {
@@ -235,7 +239,7 @@ async function scoreUnified(results, options) {
235
239
  if (!toolScore.tokenBudget) {
236
240
  if (toolId === import_core.ToolName.PatternDetect && output.duplicates) {
237
241
  const wastedTokens = output.duplicates.reduce(
238
- (sum, d) => sum + (d.tokenCost || 0),
242
+ (sum, d) => sum + (d.tokenCost ?? 0),
239
243
  0
240
244
  );
241
245
  toolScore.tokenBudget = (0, import_core.calculateTokenBudget)({
@@ -251,7 +255,7 @@ async function scoreUnified(results, options) {
251
255
  totalContextTokens: output.summary.totalTokens,
252
256
  wastedTokens: {
253
257
  duplication: 0,
254
- fragmentation: output.summary.totalPotentialSavings || 0,
258
+ fragmentation: output.summary.totalPotentialSavings ?? 0,
255
259
  chattiness: 0
256
260
  }
257
261
  });
@@ -296,7 +300,7 @@ function generateUnifiedSummary(result) {
296
300
  const toolResult = result[provider.id];
297
301
  if (toolResult) {
298
302
  const issueCount = toolResult.results.reduce(
299
- (sum, r) => sum + (r.issues?.length || 0),
303
+ (sum, r) => sum + (r.issues?.length ?? 0),
300
304
  0
301
305
  );
302
306
  output += `\u2022 ${provider.id}: ${issueCount} issues
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  analyzeUnified,
3
3
  generateUnifiedSummary,
4
4
  scoreUnified
5
- } from "./chunk-VOKP7FGM.mjs";
5
+ } from "./chunk-JRRBBFYB.mjs";
6
6
  export {
7
7
  analyzeUnified,
8
8
  generateUnifiedSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.14.9",
3
+ "version": "0.14.11",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,17 +11,17 @@
11
11
  "dependencies": {
12
12
  "chalk": "^5.3.0",
13
13
  "commander": "^14.0.0",
14
- "@aiready/agent-grounding": "0.13.6",
15
- "@aiready/consistency": "0.20.6",
16
- "@aiready/context-analyzer": "0.21.10",
17
- "@aiready/core": "0.23.7",
18
- "@aiready/deps": "0.13.6",
19
- "@aiready/change-amplification": "0.13.6",
20
- "@aiready/visualizer": "0.6.6",
21
- "@aiready/ai-signal-clarity": "0.13.7",
22
- "@aiready/pattern-detect": "0.16.6",
23
- "@aiready/testability": "0.6.6",
24
- "@aiready/doc-drift": "0.13.6"
14
+ "@aiready/context-analyzer": "0.21.12",
15
+ "@aiready/consistency": "0.20.8",
16
+ "@aiready/core": "0.23.9",
17
+ "@aiready/agent-grounding": "0.13.8",
18
+ "@aiready/pattern-detect": "0.16.8",
19
+ "@aiready/deps": "0.13.9",
20
+ "@aiready/visualizer": "0.6.8",
21
+ "@aiready/change-amplification": "0.13.8",
22
+ "@aiready/doc-drift": "0.13.8",
23
+ "@aiready/testability": "0.6.8",
24
+ "@aiready/ai-signal-clarity": "0.13.9"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.0.0",