@aiready/cli 0.9.39 → 0.9.41

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
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  __require,
4
4
  analyzeUnified
5
- } from "./chunk-PDOONNSK.mjs";
5
+ } from "./chunk-HLBKROD3.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
@@ -12,7 +12,7 @@ import { fileURLToPath } from "url";
12
12
 
13
13
  // src/commands/scan.ts
14
14
  import chalk2 from "chalk";
15
- import { readFileSync as readFileSync2 } from "fs";
15
+ import { writeFileSync, readFileSync as readFileSync2 } from "fs";
16
16
  import { resolve as resolvePath2 } from "path";
17
17
  import {
18
18
  loadMergedConfig,
@@ -42,19 +42,28 @@ function findLatestScanReport(dirPath) {
42
42
  if (!existsSync(aireadyDir)) {
43
43
  return null;
44
44
  }
45
- let files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json"));
45
+ let files = readdirSync(aireadyDir).filter(
46
+ (f) => f.startsWith("aiready-report-") && f.endsWith(".json")
47
+ );
46
48
  if (files.length === 0) {
47
- files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-scan-") && f.endsWith(".json"));
49
+ files = readdirSync(aireadyDir).filter(
50
+ (f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
51
+ );
48
52
  }
49
53
  if (files.length === 0) {
50
54
  return null;
51
55
  }
52
- const sortedFiles = files.map((f) => ({ name: f, path: resolvePath(aireadyDir, f), mtime: statSync(resolvePath(aireadyDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
56
+ const sortedFiles = files.map((f) => ({
57
+ name: f,
58
+ path: resolvePath(aireadyDir, f),
59
+ mtime: statSync(resolvePath(aireadyDir, f)).mtime
60
+ })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
53
61
  return sortedFiles[0].path;
54
62
  }
55
- function warnIfGraphCapExceeded(report, dirPath) {
63
+ async function warnIfGraphCapExceeded(report, dirPath) {
56
64
  try {
57
- const { loadConfig: loadConfig4 } = __require("@aiready/core");
65
+ const { loadConfig: loadConfig4 } = await import("@aiready/core");
66
+ void loadConfig4;
58
67
  let graphConfig = { maxNodes: 400, maxEdges: 600 };
59
68
  const configPath = resolvePath(dirPath, "aiready.json");
60
69
  if (existsSync(configPath)) {
@@ -66,7 +75,8 @@ function warnIfGraphCapExceeded(report, dirPath) {
66
75
  maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
67
76
  };
68
77
  }
69
- } catch (e) {
78
+ } catch (err) {
79
+ void err;
70
80
  }
71
81
  }
72
82
  const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
@@ -77,21 +87,30 @@ function warnIfGraphCapExceeded(report, dirPath) {
77
87
  }, 0) || 0;
78
88
  if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
79
89
  console.log("");
80
- console.log(chalk.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
90
+ console.log(
91
+ chalk.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`)
92
+ );
81
93
  if (nodeCount > graphConfig.maxNodes) {
82
- console.log(chalk.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
94
+ console.log(
95
+ chalk.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`)
96
+ );
83
97
  }
84
98
  if (edgeCount > graphConfig.maxEdges) {
85
- console.log(chalk.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
99
+ console.log(
100
+ chalk.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`)
101
+ );
86
102
  }
87
103
  console.log(chalk.dim(` To increase limits, add to aiready.json:`));
88
104
  console.log(chalk.dim(` {`));
89
105
  console.log(chalk.dim(` "visualizer": {`));
90
- console.log(chalk.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
106
+ console.log(
107
+ chalk.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`)
108
+ );
91
109
  console.log(chalk.dim(` }`));
92
110
  console.log(chalk.dim(` }`));
93
111
  }
94
- } catch (e) {
112
+ } catch (err) {
113
+ void err;
95
114
  }
96
115
  }
97
116
  function generateMarkdownReport(report, elapsedTime) {
@@ -140,17 +159,28 @@ async function scanAction(directory, options) {
140
159
  const resolvedDir = resolvePath2(process.cwd(), directory || ".");
141
160
  try {
142
161
  const defaults = {
143
- tools: ["patterns", "context", "consistency", "aiSignalClarity", "grounding", "testability", "doc-drift", "deps-health"],
162
+ tools: [
163
+ "patterns",
164
+ "context",
165
+ "consistency",
166
+ "aiSignalClarity",
167
+ "grounding",
168
+ "testability",
169
+ "doc-drift",
170
+ "deps-health",
171
+ "changeAmplification"
172
+ ],
144
173
  include: void 0,
145
174
  exclude: void 0,
146
175
  output: {
147
- format: "json",
176
+ format: "console",
148
177
  file: void 0
149
178
  }
150
179
  };
151
180
  let profileTools = options.tools ? options.tools.split(",").map((t) => {
152
181
  const tool = t.trim();
153
- if (tool === "hallucination" || tool === "hallucination-risk") return "aiSignalClarity";
182
+ if (tool === "hallucination" || tool === "hallucination-risk")
183
+ return "aiSignalClarity";
154
184
  return tool;
155
185
  }) : void 0;
156
186
  if (options.profile) {
@@ -168,28 +198,56 @@ async function scanAction(directory, options) {
168
198
  profileTools = ["context", "consistency", "grounding"];
169
199
  break;
170
200
  default:
171
- console.log(chalk2.yellow(`
172
- \u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`));
201
+ console.log(
202
+ chalk2.yellow(
203
+ `
204
+ \u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`
205
+ )
206
+ );
173
207
  }
174
208
  }
175
- const baseOptions = await loadMergedConfig(resolvedDir, defaults, {
176
- tools: profileTools,
209
+ const cliOverrides = {
177
210
  include: options.include?.split(","),
178
211
  exclude: options.exclude?.split(",")
179
- });
212
+ };
213
+ if (profileTools) {
214
+ cliOverrides.tools = profileTools;
215
+ }
216
+ const baseOptions = await loadMergedConfig(
217
+ resolvedDir,
218
+ defaults,
219
+ cliOverrides
220
+ );
180
221
  let finalOptions = { ...baseOptions };
181
222
  if (baseOptions.tools.includes("patterns")) {
182
223
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
183
- const patternSmartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
184
- finalOptions = { ...patternSmartDefaults, ...finalOptions, ...baseOptions };
224
+ const patternSmartDefaults = await getSmartDefaults(
225
+ resolvedDir,
226
+ baseOptions
227
+ );
228
+ finalOptions = {
229
+ ...patternSmartDefaults,
230
+ ...finalOptions,
231
+ ...baseOptions
232
+ };
185
233
  }
186
234
  console.log(chalk2.cyan("\n=== AIReady Run Preview ==="));
187
- console.log(chalk2.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
235
+ console.log(
236
+ chalk2.white("Tools to run:"),
237
+ (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
238
+ );
188
239
  console.log(chalk2.white("Will use settings from config and defaults."));
189
240
  console.log(chalk2.white("\nGeneral settings:"));
190
- if (finalOptions.rootDir) console.log(` rootDir: ${chalk2.bold(String(finalOptions.rootDir))}`);
191
- if (finalOptions.include) console.log(` include: ${chalk2.bold(truncateArray(finalOptions.include, 6))}`);
192
- if (finalOptions.exclude) console.log(` exclude: ${chalk2.bold(truncateArray(finalOptions.exclude, 6))}`);
241
+ if (finalOptions.rootDir)
242
+ console.log(` rootDir: ${chalk2.bold(String(finalOptions.rootDir))}`);
243
+ if (finalOptions.include)
244
+ console.log(
245
+ ` include: ${chalk2.bold(truncateArray(finalOptions.include, 6))}`
246
+ );
247
+ if (finalOptions.exclude)
248
+ console.log(
249
+ ` exclude: ${chalk2.bold(truncateArray(finalOptions.exclude, 6))}`
250
+ );
193
251
  if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
194
252
  const patternDetectConfig = finalOptions["pattern-detect"] || {
195
253
  minSimilarity: finalOptions.minSimilarity,
@@ -203,15 +261,40 @@ async function scanAction(directory, options) {
203
261
  includeTests: finalOptions.includeTests
204
262
  };
205
263
  console.log(chalk2.white("\nPattern-detect settings:"));
206
- console.log(` minSimilarity: ${chalk2.bold(patternDetectConfig.minSimilarity ?? "default")}`);
207
- console.log(` minLines: ${chalk2.bold(patternDetectConfig.minLines ?? "default")}`);
208
- if (patternDetectConfig.approx !== void 0) console.log(` approx: ${chalk2.bold(String(patternDetectConfig.approx))}`);
209
- if (patternDetectConfig.minSharedTokens !== void 0) console.log(` minSharedTokens: ${chalk2.bold(String(patternDetectConfig.minSharedTokens))}`);
210
- if (patternDetectConfig.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${chalk2.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`);
211
- if (patternDetectConfig.batchSize !== void 0) console.log(` batchSize: ${chalk2.bold(String(patternDetectConfig.batchSize))}`);
212
- if (patternDetectConfig.streamResults !== void 0) console.log(` streamResults: ${chalk2.bold(String(patternDetectConfig.streamResults))}`);
213
- if (patternDetectConfig.severity !== void 0) console.log(` severity: ${chalk2.bold(String(patternDetectConfig.severity))}`);
214
- if (patternDetectConfig.includeTests !== void 0) console.log(` includeTests: ${chalk2.bold(String(patternDetectConfig.includeTests))}`);
264
+ console.log(
265
+ ` minSimilarity: ${chalk2.bold(patternDetectConfig.minSimilarity ?? "default")}`
266
+ );
267
+ console.log(
268
+ ` minLines: ${chalk2.bold(patternDetectConfig.minLines ?? "default")}`
269
+ );
270
+ if (patternDetectConfig.approx !== void 0)
271
+ console.log(
272
+ ` approx: ${chalk2.bold(String(patternDetectConfig.approx))}`
273
+ );
274
+ if (patternDetectConfig.minSharedTokens !== void 0)
275
+ console.log(
276
+ ` minSharedTokens: ${chalk2.bold(String(patternDetectConfig.minSharedTokens))}`
277
+ );
278
+ if (patternDetectConfig.maxCandidatesPerBlock !== void 0)
279
+ console.log(
280
+ ` maxCandidatesPerBlock: ${chalk2.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
281
+ );
282
+ if (patternDetectConfig.batchSize !== void 0)
283
+ console.log(
284
+ ` batchSize: ${chalk2.bold(String(patternDetectConfig.batchSize))}`
285
+ );
286
+ if (patternDetectConfig.streamResults !== void 0)
287
+ console.log(
288
+ ` streamResults: ${chalk2.bold(String(patternDetectConfig.streamResults))}`
289
+ );
290
+ if (patternDetectConfig.severity !== void 0)
291
+ console.log(
292
+ ` severity: ${chalk2.bold(String(patternDetectConfig.severity))}`
293
+ );
294
+ if (patternDetectConfig.includeTests !== void 0)
295
+ console.log(
296
+ ` includeTests: ${chalk2.bold(String(patternDetectConfig.includeTests))}`
297
+ );
215
298
  }
216
299
  if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
217
300
  const ca = finalOptions["context-analyzer"] || {
@@ -223,20 +306,42 @@ async function scanAction(directory, options) {
223
306
  };
224
307
  console.log(chalk2.white("\nContext-analyzer settings:"));
225
308
  console.log(` maxDepth: ${chalk2.bold(ca.maxDepth ?? "default")}`);
226
- console.log(` maxContextBudget: ${chalk2.bold(ca.maxContextBudget ?? "default")}`);
227
- if (ca.minCohesion !== void 0) console.log(` minCohesion: ${chalk2.bold(String(ca.minCohesion))}`);
228
- if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${chalk2.bold(String(ca.maxFragmentation))}`);
229
- if (ca.includeNodeModules !== void 0) console.log(` includeNodeModules: ${chalk2.bold(String(ca.includeNodeModules))}`);
309
+ console.log(
310
+ ` maxContextBudget: ${chalk2.bold(ca.maxContextBudget ?? "default")}`
311
+ );
312
+ if (ca.minCohesion !== void 0)
313
+ console.log(` minCohesion: ${chalk2.bold(String(ca.minCohesion))}`);
314
+ if (ca.maxFragmentation !== void 0)
315
+ console.log(
316
+ ` maxFragmentation: ${chalk2.bold(String(ca.maxFragmentation))}`
317
+ );
318
+ if (ca.includeNodeModules !== void 0)
319
+ console.log(
320
+ ` includeNodeModules: ${chalk2.bold(String(ca.includeNodeModules))}`
321
+ );
230
322
  }
231
323
  if (finalOptions.consistency) {
232
324
  const c = finalOptions.consistency;
233
325
  console.log(chalk2.white("\nConsistency settings:"));
234
- console.log(` checkNaming: ${chalk2.bold(String(c.checkNaming ?? true))}`);
235
- console.log(` checkPatterns: ${chalk2.bold(String(c.checkPatterns ?? true))}`);
236
- console.log(` checkArchitecture: ${chalk2.bold(String(c.checkArchitecture ?? false))}`);
237
- if (c.minSeverity) console.log(` minSeverity: ${chalk2.bold(c.minSeverity)}`);
238
- if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${chalk2.bold(truncateArray(c.acceptedAbbreviations, 8))}`);
239
- if (c.shortWords) console.log(` shortWords: ${chalk2.bold(truncateArray(c.shortWords, 8))}`);
326
+ console.log(
327
+ ` checkNaming: ${chalk2.bold(String(c.checkNaming ?? true))}`
328
+ );
329
+ console.log(
330
+ ` checkPatterns: ${chalk2.bold(String(c.checkPatterns ?? true))}`
331
+ );
332
+ console.log(
333
+ ` checkArchitecture: ${chalk2.bold(String(c.checkArchitecture ?? false))}`
334
+ );
335
+ if (c.minSeverity)
336
+ console.log(` minSeverity: ${chalk2.bold(c.minSeverity)}`);
337
+ if (c.acceptedAbbreviations)
338
+ console.log(
339
+ ` acceptedAbbreviations: ${chalk2.bold(truncateArray(c.acceptedAbbreviations, 8))}`
340
+ );
341
+ if (c.shortWords)
342
+ console.log(
343
+ ` shortWords: ${chalk2.bold(truncateArray(c.shortWords, 8))}`
344
+ );
240
345
  }
241
346
  console.log(chalk2.white("\nStarting analysis..."));
242
347
  const progressCallback = (event) => {
@@ -245,40 +350,62 @@ async function scanAction(directory, options) {
245
350
  try {
246
351
  if (event.tool === "patterns") {
247
352
  const pr = event.data;
248
- console.log(` Duplicate patterns: ${chalk2.bold(String(pr.duplicates?.length || 0))}`);
249
- console.log(` Files with pattern issues: ${chalk2.bold(String(pr.results?.length || 0))}`);
353
+ console.log(
354
+ ` Duplicate patterns: ${chalk2.bold(String(pr.duplicates?.length || 0))}`
355
+ );
356
+ console.log(
357
+ ` Files with pattern issues: ${chalk2.bold(String(pr.results?.length || 0))}`
358
+ );
250
359
  if (pr.duplicates && pr.duplicates.length > 0) {
251
360
  pr.duplicates.slice(0, 5).forEach((d, i) => {
252
- console.log(` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`);
361
+ console.log(
362
+ ` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`
363
+ );
253
364
  });
254
365
  }
255
366
  if (pr.results && pr.results.length > 0) {
256
367
  console.log(` Top files with pattern issues:`);
257
- const sortedByIssues = [...pr.results].sort((a, b) => (b.issues?.length || 0) - (a.issues?.length || 0));
368
+ const sortedByIssues = [...pr.results].sort(
369
+ (a, b) => (b.issues?.length || 0) - (a.issues?.length || 0)
370
+ );
258
371
  sortedByIssues.slice(0, 5).forEach((r, i) => {
259
- console.log(` ${i + 1}. ${r.fileName.split("/").pop()} - ${r.issues.length} issue(s)`);
372
+ console.log(
373
+ ` ${i + 1}. ${r.fileName.split("/").pop()} - ${r.issues.length} issue(s)`
374
+ );
260
375
  });
261
376
  }
262
377
  if (pr.groups && pr.groups.length >= 0) {
263
- console.log(` \u2705 Grouped ${chalk2.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk2.bold(String(pr.groups.length))} file pairs`);
378
+ console.log(
379
+ ` \u2705 Grouped ${chalk2.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk2.bold(String(pr.groups.length))} file pairs`
380
+ );
264
381
  }
265
382
  if (pr.clusters && pr.clusters.length >= 0) {
266
- console.log(` \u2705 Created ${chalk2.bold(String(pr.clusters.length))} refactor clusters`);
383
+ console.log(
384
+ ` \u2705 Created ${chalk2.bold(String(pr.clusters.length))} refactor clusters`
385
+ );
267
386
  pr.clusters.slice(0, 3).forEach((cl, idx) => {
268
387
  const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
269
- console.log(` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`);
388
+ console.log(
389
+ ` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`
390
+ );
270
391
  });
271
392
  }
272
393
  } else if (event.tool === "context") {
273
394
  const cr = event.data;
274
- console.log(` Context issues found: ${chalk2.bold(String(cr.length || 0))}`);
395
+ console.log(
396
+ ` Context issues found: ${chalk2.bold(String(cr.length || 0))}`
397
+ );
275
398
  cr.slice(0, 5).forEach((c, i) => {
276
399
  const msg = c.message ? ` - ${c.message}` : "";
277
- console.log(` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`);
400
+ console.log(
401
+ ` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`
402
+ );
278
403
  });
279
404
  } else if (event.tool === "consistency") {
280
405
  const rep = event.data;
281
- console.log(` Consistency totalIssues: ${chalk2.bold(String(rep.summary?.totalIssues || 0))}`);
406
+ console.log(
407
+ ` Consistency totalIssues: ${chalk2.bold(String(rep.summary?.totalIssues || 0))}`
408
+ );
282
409
  if (rep.results && rep.results.length > 0) {
283
410
  const fileMap = /* @__PURE__ */ new Map();
284
411
  rep.results.forEach((r) => {
@@ -288,61 +415,126 @@ async function scanAction(directory, options) {
288
415
  fileMap.get(file).push(issue);
289
416
  });
290
417
  });
291
- const files = Array.from(fileMap.entries()).sort((a, b) => b[1].length - a[1].length);
418
+ const files = Array.from(fileMap.entries()).sort(
419
+ (a, b) => b[1].length - a[1].length
420
+ );
292
421
  const topFiles = files.slice(0, 10);
293
422
  topFiles.forEach(([file, issues], idx) => {
294
- const counts = issues.reduce((acc, it) => {
295
- const s = (it.severity || "info").toLowerCase();
296
- acc[s] = (acc[s] || 0) + 1;
297
- return acc;
298
- }, {});
299
- const sample = issues.find((it) => it.severity === "critical" || it.severity === "major") || issues[0];
423
+ const counts = issues.reduce(
424
+ (acc, it) => {
425
+ const s = (it.severity || "info").toLowerCase();
426
+ acc[s] = (acc[s] || 0) + 1;
427
+ return acc;
428
+ },
429
+ {}
430
+ );
431
+ const sample = issues.find(
432
+ (it) => it.severity === "critical" || it.severity === "major"
433
+ ) || issues[0];
300
434
  const sampleMsg = sample ? ` \u2014 ${sample.message}` : "";
301
- console.log(` ${idx + 1}. ${file} \u2014 ${issues.length} issue(s) (critical:${counts.critical || 0} major:${counts.major || 0} minor:${counts.minor || 0} info:${counts.info || 0})${sampleMsg}`);
435
+ console.log(
436
+ ` ${idx + 1}. ${file} \u2014 ${issues.length} issue(s) (critical:${counts.critical || 0} major:${counts.major || 0} minor:${counts.minor || 0} info:${counts.info || 0})${sampleMsg}`
437
+ );
302
438
  });
303
439
  const remaining = files.length - topFiles.length;
304
440
  if (remaining > 0) {
305
- console.log(chalk2.dim(` ... and ${remaining} more files with issues (use --output json for full details)`));
441
+ console.log(
442
+ chalk2.dim(
443
+ ` ... and ${remaining} more files with issues (use --output json for full details)`
444
+ )
445
+ );
306
446
  }
307
447
  }
308
448
  } else if (event.tool === "doc-drift") {
309
449
  const dr = event.data;
310
- console.log(` Issues found: ${chalk2.bold(String(dr.issues?.length || 0))}`);
450
+ console.log(
451
+ ` Issues found: ${chalk2.bold(String(dr.issues?.length || 0))}`
452
+ );
311
453
  if (dr.rawData) {
312
- console.log(` Signature Mismatches: ${chalk2.bold(dr.rawData.outdatedComments || 0)}`);
313
- console.log(` Undocumented Complexity: ${chalk2.bold(dr.rawData.undocumentedComplexity || 0)}`);
454
+ console.log(
455
+ ` Signature Mismatches: ${chalk2.bold(dr.rawData.outdatedComments || 0)}`
456
+ );
457
+ console.log(
458
+ ` Undocumented Complexity: ${chalk2.bold(dr.rawData.undocumentedComplexity || 0)}`
459
+ );
314
460
  }
315
461
  } else if (event.tool === "deps-health") {
316
462
  const dr = event.data;
317
- console.log(` Packages Analyzed: ${chalk2.bold(String(dr.summary?.packagesAnalyzed || 0))}`);
463
+ console.log(
464
+ ` Packages Analyzed: ${chalk2.bold(String(dr.summary?.packagesAnalyzed || 0))}`
465
+ );
318
466
  if (dr.rawData) {
319
- console.log(` Deprecated Packages: ${chalk2.bold(dr.rawData.deprecatedPackages || 0)}`);
320
- console.log(` AI Cutoff Skew Score: ${chalk2.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`);
467
+ console.log(
468
+ ` Deprecated Packages: ${chalk2.bold(dr.rawData.deprecatedPackages || 0)}`
469
+ );
470
+ console.log(
471
+ ` AI Cutoff Skew Score: ${chalk2.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`
472
+ );
473
+ }
474
+ } else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
475
+ const dr = event.data;
476
+ console.log(
477
+ ` Coupling issues: ${chalk2.bold(String(dr.issues?.length || 0))}`
478
+ );
479
+ if (dr.summary) {
480
+ console.log(
481
+ ` Complexity Score: ${chalk2.bold(dr.summary.score || 0)}/100`
482
+ );
321
483
  }
322
484
  }
323
485
  } catch (err) {
486
+ void err;
324
487
  }
325
488
  };
326
- const results = await analyzeUnified({ ...finalOptions, progressCallback, suppressToolConfig: true });
489
+ const results = await analyzeUnified({
490
+ ...finalOptions,
491
+ progressCallback,
492
+ suppressToolConfig: true
493
+ });
327
494
  console.log(chalk2.cyan("\n=== AIReady Run Summary ==="));
328
- console.log(chalk2.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
495
+ console.log(
496
+ chalk2.white("Tools run:"),
497
+ (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
498
+ );
329
499
  console.log(chalk2.cyan("\nResults summary:"));
330
- console.log(` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`);
331
- if (results.duplicates) console.log(` Duplicate patterns found: ${chalk2.bold(String(results.duplicates.length || 0))}`);
332
- if (results.patterns) console.log(` Pattern files with issues: ${chalk2.bold(String(results.patterns.length || 0))}`);
333
- if (results.context) console.log(` Context issues: ${chalk2.bold(String(results.context.length || 0))}`);
334
- if (results.consistency) console.log(` Consistency issues: ${chalk2.bold(String(results.consistency.summary.totalIssues || 0))}`);
500
+ console.log(
501
+ ` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
502
+ );
503
+ if (results.duplicates)
504
+ console.log(
505
+ ` Duplicate patterns found: ${chalk2.bold(String(results.duplicates.length || 0))}`
506
+ );
507
+ if (results.patterns)
508
+ console.log(
509
+ ` Pattern files with issues: ${chalk2.bold(String(results.patterns.length || 0))}`
510
+ );
511
+ if (results.context)
512
+ console.log(
513
+ ` Context issues: ${chalk2.bold(String(results.context.length || 0))}`
514
+ );
515
+ console.log(
516
+ ` Consistency issues: ${chalk2.bold(String(results.consistency?.summary?.totalIssues || 0))}`
517
+ );
518
+ if (results.changeAmplification)
519
+ console.log(
520
+ ` Change amplification: ${chalk2.bold(String(results.changeAmplification.summary?.score || 0))}/100`
521
+ );
335
522
  console.log(chalk2.cyan("===========================\n"));
336
523
  const elapsedTime = getElapsedTime(startTime);
524
+ void elapsedTime;
337
525
  let scoringResult;
338
526
  if (options.score || finalOptions.scoring?.showBreakdown) {
339
527
  const toolScores = /* @__PURE__ */ new Map();
340
528
  if (results.duplicates) {
341
529
  const { calculatePatternScore } = await import("@aiready/pattern-detect");
342
530
  try {
343
- const patternScore = calculatePatternScore(results.duplicates, results.patterns?.length || 0);
531
+ const patternScore = calculatePatternScore(
532
+ results.duplicates,
533
+ results.patterns?.length || 0
534
+ );
344
535
  toolScores.set("pattern-detect", patternScore);
345
536
  } catch (err) {
537
+ void err;
346
538
  }
347
539
  }
348
540
  if (results.context) {
@@ -352,6 +544,7 @@ async function scanAction(directory, options) {
352
544
  const contextScore = calculateContextScore(ctxSummary);
353
545
  toolScores.set("context-analyzer", contextScore);
354
546
  } catch (err) {
547
+ void err;
355
548
  }
356
549
  }
357
550
  if (results.consistency) {
@@ -359,17 +552,24 @@ async function scanAction(directory, options) {
359
552
  try {
360
553
  const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
361
554
  const totalFiles = results.consistency.summary?.filesAnalyzed || 0;
362
- const consistencyScore = calculateConsistencyScore(issues, totalFiles);
555
+ const consistencyScore = calculateConsistencyScore(
556
+ issues,
557
+ totalFiles
558
+ );
363
559
  toolScores.set("consistency", consistencyScore);
364
560
  } catch (err) {
561
+ void err;
365
562
  }
366
563
  }
367
564
  if (results.aiSignalClarity) {
368
- const { calculateHallucinationScore } = await import("@aiready/ai-signal-clarity");
565
+ const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
369
566
  try {
370
- const hrScore = calculateHallucinationScore(results.aiSignalClarity);
567
+ const hrScore = calculateAiSignalClarityScore(
568
+ results.aiSignalClarity
569
+ );
371
570
  toolScores.set("ai-signal-clarity", hrScore);
372
571
  } catch (err) {
572
+ void err;
373
573
  }
374
574
  }
375
575
  if (results.grounding) {
@@ -378,6 +578,7 @@ async function scanAction(directory, options) {
378
578
  const agScore = calculateGroundingScore(results.grounding);
379
579
  toolScores.set("agent-grounding", agScore);
380
580
  } catch (err) {
581
+ void err;
381
582
  }
382
583
  }
383
584
  if (results.testability) {
@@ -386,6 +587,7 @@ async function scanAction(directory, options) {
386
587
  const tbScore = calculateTestabilityScore(results.testability);
387
588
  toolScores.set("testability", tbScore);
388
589
  } catch (err) {
590
+ void err;
389
591
  }
390
592
  }
391
593
  if (results.docDrift) {
@@ -394,7 +596,13 @@ async function scanAction(directory, options) {
394
596
  score: results.docDrift.summary.score,
395
597
  rawMetrics: results.docDrift.rawData,
396
598
  factors: [],
397
- recommendations: results.docDrift.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
599
+ recommendations: (results.docDrift.recommendations || []).map(
600
+ (action) => ({
601
+ action,
602
+ estimatedImpact: 5,
603
+ priority: "medium"
604
+ })
605
+ )
398
606
  });
399
607
  }
400
608
  if (results.deps) {
@@ -403,17 +611,43 @@ async function scanAction(directory, options) {
403
611
  score: results.deps.summary.score,
404
612
  rawMetrics: results.deps.rawData,
405
613
  factors: [],
406
- recommendations: results.deps.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
614
+ recommendations: (results.deps.recommendations || []).map(
615
+ (action) => ({
616
+ action,
617
+ estimatedImpact: 5,
618
+ priority: "medium"
619
+ })
620
+ )
621
+ });
622
+ }
623
+ if (results.changeAmplification) {
624
+ toolScores.set("change-amplification", {
625
+ toolName: "change-amplification",
626
+ score: results.changeAmplification.summary.score,
627
+ rawMetrics: results.changeAmplification.rawData,
628
+ factors: [],
629
+ recommendations: (results.changeAmplification.recommendations || []).map((action) => ({
630
+ action,
631
+ estimatedImpact: 5,
632
+ priority: "medium"
633
+ }))
407
634
  });
408
635
  }
409
636
  const cliWeights = parseWeightString(options.weights);
410
637
  if (toolScores.size > 0) {
411
- scoringResult = calculateOverallScore(toolScores, finalOptions, cliWeights.size ? cliWeights : void 0);
638
+ scoringResult = calculateOverallScore(
639
+ toolScores,
640
+ finalOptions,
641
+ cliWeights.size ? cliWeights : void 0
642
+ );
412
643
  console.log(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
413
644
  console.log(` ${formatScore(scoringResult)}`);
414
645
  if (options.compareTo) {
415
646
  try {
416
- const prevReportStr = readFileSync2(resolvePath2(process.cwd(), options.compareTo), "utf8");
647
+ const prevReportStr = readFileSync2(
648
+ resolvePath2(process.cwd(), options.compareTo),
649
+ "utf8"
650
+ );
417
651
  const prevReport = JSON.parse(prevReportStr);
418
652
  const prevScore = prevReport.scoring?.score || prevReport.scoring?.overallScore;
419
653
  if (typeof prevScore === "number") {
@@ -421,23 +655,44 @@ async function scanAction(directory, options) {
421
655
  const diffStr = diff > 0 ? `+${diff}` : String(diff);
422
656
  console.log();
423
657
  if (diff > 0) {
424
- console.log(chalk2.green(` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`));
658
+ console.log(
659
+ chalk2.green(
660
+ ` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
661
+ )
662
+ );
425
663
  } else if (diff < 0) {
426
- console.log(chalk2.red(` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`));
664
+ console.log(
665
+ chalk2.red(
666
+ ` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
667
+ )
668
+ );
427
669
  } else {
428
- console.log(chalk2.blue(` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`));
670
+ console.log(
671
+ chalk2.blue(
672
+ ` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
673
+ )
674
+ );
429
675
  }
430
676
  scoringResult.trend = {
431
677
  previousScore: prevScore,
432
678
  difference: diff
433
679
  };
434
680
  } else {
435
- console.log(chalk2.yellow(`
436
- \u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`));
681
+ console.log(
682
+ chalk2.yellow(
683
+ `
684
+ \u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
685
+ )
686
+ );
437
687
  }
438
688
  } catch (e) {
439
- console.log(chalk2.yellow(`
440
- \u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`));
689
+ void e;
690
+ console.log(
691
+ chalk2.yellow(
692
+ `
693
+ \u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
694
+ )
695
+ );
441
696
  }
442
697
  }
443
698
  if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
@@ -445,7 +700,9 @@ async function scanAction(directory, options) {
445
700
  scoringResult.breakdown.forEach((tool) => {
446
701
  const rating = getRating(tool.score);
447
702
  const rd = getRatingDisplay(rating);
448
- console.log(` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`);
703
+ console.log(
704
+ ` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`
705
+ );
449
706
  });
450
707
  console.log();
451
708
  if (finalOptions.scoring?.showBreakdown) {
@@ -463,10 +720,34 @@ async function scanAction(directory, options) {
463
720
  if (outputFormat === "json") {
464
721
  const timestamp = getReportTimestamp();
465
722
  const defaultFilename = `aiready-report-${timestamp}.json`;
466
- const outputPath = resolveOutputPath(userOutputFile, defaultFilename, resolvedDir);
723
+ const outputPath = resolveOutputPath(
724
+ userOutputFile,
725
+ defaultFilename,
726
+ resolvedDir
727
+ );
467
728
  const outputData = { ...results, scoring: scoringResult };
468
- handleJSONOutput(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
469
- warnIfGraphCapExceeded(outputData, resolvedDir);
729
+ handleJSONOutput(
730
+ outputData,
731
+ outputPath,
732
+ `\u2705 Report saved to ${outputPath}`
733
+ );
734
+ await warnIfGraphCapExceeded(outputData, resolvedDir);
735
+ } else {
736
+ const timestamp = getReportTimestamp();
737
+ const defaultFilename = `aiready-report-${timestamp}.json`;
738
+ const outputPath = resolveOutputPath(
739
+ userOutputFile,
740
+ defaultFilename,
741
+ resolvedDir
742
+ );
743
+ const outputData = { ...results, scoring: scoringResult };
744
+ try {
745
+ writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
746
+ console.log(chalk2.dim(`\u2705 Report auto-persisted to ${outputPath}`));
747
+ await warnIfGraphCapExceeded(outputData, resolvedDir);
748
+ } catch (err) {
749
+ void err;
750
+ }
470
751
  }
471
752
  const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
472
753
  if (isCI && scoringResult) {
@@ -483,16 +764,22 @@ async function scanAction(directory, options) {
483
764
  }
484
765
  console.log("::endgroup::");
485
766
  if (threshold && scoringResult.overall < threshold) {
486
- console.log(`::error::AI Readiness Score ${scoringResult.overall} is below threshold ${threshold}`);
767
+ console.log(
768
+ `::error::AI Readiness Score ${scoringResult.overall} is below threshold ${threshold}`
769
+ );
487
770
  } else if (threshold) {
488
- console.log(`::notice::AI Readiness Score: ${scoringResult.overall}/100 (threshold: ${threshold})`);
771
+ console.log(
772
+ `::notice::AI Readiness Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
773
+ );
489
774
  }
490
775
  if (results.patterns) {
491
776
  const criticalPatterns = results.patterns.flatMap(
492
777
  (p) => p.issues.filter((i) => i.severity === "critical")
493
778
  );
494
779
  criticalPatterns.slice(0, 10).forEach((issue) => {
495
- console.log(`::warning file=${issue.location?.file || "unknown"},line=${issue.location?.line || 1}::${issue.message}`);
780
+ console.log(
781
+ `::warning file=${issue.location?.file || "unknown"},line=${issue.location?.line || 1}::${issue.message}`
782
+ );
496
783
  });
497
784
  }
498
785
  }
@@ -541,16 +828,30 @@ async function scanAction(directory, options) {
541
828
  console.log(chalk2.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
542
829
  console.log(chalk2.red(` Reason: ${failReason}`));
543
830
  console.log(chalk2.dim("\n Remediation steps:"));
544
- console.log(chalk2.dim(" 1. Run `aiready scan` locally to see detailed issues"));
831
+ console.log(
832
+ chalk2.dim(" 1. Run `aiready scan` locally to see detailed issues")
833
+ );
545
834
  console.log(chalk2.dim(" 2. Fix the critical issues before merging"));
546
- console.log(chalk2.dim(" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"));
835
+ console.log(
836
+ chalk2.dim(
837
+ " 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
838
+ )
839
+ );
547
840
  process.exit(1);
548
841
  } else {
549
842
  console.log(chalk2.green("\n\u2705 PR PASSED: AI Readiness Check"));
550
843
  if (threshold) {
551
- console.log(chalk2.green(` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`));
844
+ console.log(
845
+ chalk2.green(
846
+ ` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
847
+ )
848
+ );
552
849
  }
553
- console.log(chalk2.dim("\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"));
850
+ console.log(
851
+ chalk2.dim(
852
+ "\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
853
+ )
854
+ );
554
855
  }
555
856
  }
556
857
  } catch (error) {
@@ -629,7 +930,11 @@ async function patternsAction(directory, options) {
629
930
  if (options.minSharedTokens) {
630
931
  cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
631
932
  }
632
- const finalOptions = await loadMergedConfig2(resolvedDir, defaults, cliOptions);
933
+ const finalOptions = await loadMergedConfig2(
934
+ resolvedDir,
935
+ defaults,
936
+ cliOptions
937
+ );
633
938
  const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
634
939
  const { results, duplicates } = await analyzePatterns(finalOptions);
635
940
  const elapsedTime = getElapsedTime2(startTime);
@@ -651,7 +956,11 @@ async function patternsAction(directory, options) {
651
956
  `aiready-report-${getReportTimestamp()}.json`,
652
957
  resolvedDir
653
958
  );
654
- handleJSONOutput2(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
959
+ handleJSONOutput2(
960
+ outputData,
961
+ outputPath,
962
+ `\u2705 Results saved to ${outputPath}`
963
+ );
655
964
  } else {
656
965
  const terminalWidth = process.stdout.columns || 80;
657
966
  const dividerWidth = Math.min(60, terminalWidth - 2);
@@ -659,10 +968,22 @@ async function patternsAction(directory, options) {
659
968
  console.log(chalk3.cyan(divider));
660
969
  console.log(chalk3.bold.white(" PATTERN ANALYSIS SUMMARY"));
661
970
  console.log(chalk3.cyan(divider) + "\n");
662
- console.log(chalk3.white(`\u{1F4C1} Files analyzed: ${chalk3.bold(results.length)}`));
663
- console.log(chalk3.yellow(`\u26A0 Duplicate patterns found: ${chalk3.bold(summary.totalPatterns)}`));
664
- console.log(chalk3.red(`\u{1F4B0} Token cost (wasted): ${chalk3.bold(summary.totalTokenCost.toLocaleString())}`));
665
- console.log(chalk3.gray(`\u23F1 Analysis time: ${chalk3.bold(elapsedTime + "s")}`));
971
+ console.log(
972
+ chalk3.white(`\u{1F4C1} Files analyzed: ${chalk3.bold(results.length)}`)
973
+ );
974
+ console.log(
975
+ chalk3.yellow(
976
+ `\u26A0 Duplicate patterns found: ${chalk3.bold(summary.totalPatterns)}`
977
+ )
978
+ );
979
+ console.log(
980
+ chalk3.red(
981
+ `\u{1F4B0} Token cost (wasted): ${chalk3.bold(summary.totalTokenCost.toLocaleString())}`
982
+ )
983
+ );
984
+ console.log(
985
+ chalk3.gray(`\u23F1 Analysis time: ${chalk3.bold(elapsedTime + "s")}`)
986
+ );
666
987
  const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
667
988
  if (sortedTypes.length > 0) {
668
989
  console.log(chalk3.cyan("\n" + divider));
@@ -682,13 +1003,21 @@ async function patternsAction(directory, options) {
682
1003
  const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
683
1004
  const file1Name = dup.file1.split("/").pop() || dup.file1;
684
1005
  const file2Name = dup.file2.split("/").pop() || dup.file2;
685
- console.log(`${severityIcon} ${severity}: ${chalk3.bold(file1Name)} \u2194 ${chalk3.bold(file2Name)}`);
686
- console.log(` Similarity: ${chalk3.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk3.bold(dup.tokenCost.toLocaleString())} tokens each`);
687
- console.log(` Lines: ${chalk3.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk3.cyan(dup.line2 + "-" + dup.endLine2)}
688
- `);
1006
+ console.log(
1007
+ `${severityIcon} ${severity}: ${chalk3.bold(file1Name)} \u2194 ${chalk3.bold(file2Name)}`
1008
+ );
1009
+ console.log(
1010
+ ` Similarity: ${chalk3.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk3.bold(dup.tokenCost.toLocaleString())} tokens each`
1011
+ );
1012
+ console.log(
1013
+ ` Lines: ${chalk3.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk3.cyan(dup.line2 + "-" + dup.endLine2)}
1014
+ `
1015
+ );
689
1016
  });
690
1017
  } else {
691
- console.log(chalk3.green("\n\u2728 Great! No duplicate patterns detected.\n"));
1018
+ console.log(
1019
+ chalk3.green("\n\u2728 Great! No duplicate patterns detected.\n")
1020
+ );
692
1021
  }
693
1022
  if (patternScore) {
694
1023
  console.log(chalk3.cyan(divider));
@@ -735,7 +1064,7 @@ async function contextAction(directory, options) {
735
1064
  file: void 0
736
1065
  }
737
1066
  };
738
- let baseOptions = await loadMergedConfig3(resolvedDir, defaults, {
1067
+ const baseOptions = await loadMergedConfig3(resolvedDir, defaults, {
739
1068
  maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
740
1069
  maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
741
1070
  include: options.include?.split(","),
@@ -743,13 +1072,20 @@ async function contextAction(directory, options) {
743
1072
  });
744
1073
  let finalOptions = { ...baseOptions };
745
1074
  const { getSmartDefaults } = await import("@aiready/context-analyzer");
746
- const contextSmartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
1075
+ const contextSmartDefaults = await getSmartDefaults(
1076
+ resolvedDir,
1077
+ baseOptions
1078
+ );
747
1079
  finalOptions = { ...contextSmartDefaults, ...finalOptions };
748
1080
  console.log("\u{1F4CB} Configuration:");
749
1081
  console.log(` Max depth: ${finalOptions.maxDepth}`);
750
1082
  console.log(` Max context budget: ${finalOptions.maxContextBudget}`);
751
- console.log(` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`);
752
- console.log(` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`);
1083
+ console.log(
1084
+ ` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`
1085
+ );
1086
+ console.log(
1087
+ ` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`
1088
+ );
753
1089
  console.log(` Analysis focus: ${finalOptions.focus}`);
754
1090
  console.log("");
755
1091
  const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
@@ -773,7 +1109,11 @@ async function contextAction(directory, options) {
773
1109
  `aiready-report-${getReportTimestamp()}.json`,
774
1110
  resolvedDir
775
1111
  );
776
- handleJSONOutput3(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
1112
+ handleJSONOutput3(
1113
+ outputData,
1114
+ outputPath,
1115
+ `\u2705 Results saved to ${outputPath}`
1116
+ );
777
1117
  } else {
778
1118
  const terminalWidth = process.stdout.columns || 80;
779
1119
  const dividerWidth = Math.min(60, terminalWidth - 2);
@@ -781,59 +1121,103 @@ async function contextAction(directory, options) {
781
1121
  console.log(chalk4.cyan(divider));
782
1122
  console.log(chalk4.bold.white(" CONTEXT ANALYSIS SUMMARY"));
783
1123
  console.log(chalk4.cyan(divider) + "\n");
784
- console.log(chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(summary.totalFiles)}`));
785
- console.log(chalk4.white(`\u{1F4CA} Total tokens: ${chalk4.bold(summary.totalTokens.toLocaleString())}`));
786
- console.log(chalk4.yellow(`\u{1F4B0} Avg context budget: ${chalk4.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
787
- console.log(chalk4.white(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}
788
- `));
1124
+ console.log(
1125
+ chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(summary.totalFiles)}`)
1126
+ );
1127
+ console.log(
1128
+ chalk4.white(
1129
+ `\u{1F4CA} Total tokens: ${chalk4.bold(summary.totalTokens.toLocaleString())}`
1130
+ )
1131
+ );
1132
+ console.log(
1133
+ chalk4.yellow(
1134
+ `\u{1F4B0} Avg context budget: ${chalk4.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
1135
+ )
1136
+ );
1137
+ console.log(
1138
+ chalk4.white(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}
1139
+ `)
1140
+ );
789
1141
  const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
790
1142
  if (totalIssues > 0) {
791
1143
  console.log(chalk4.bold("\u26A0\uFE0F Issues Found:\n"));
792
1144
  if (summary.criticalIssues > 0) {
793
- console.log(chalk4.red(` \u{1F534} Critical: ${chalk4.bold(summary.criticalIssues)}`));
1145
+ console.log(
1146
+ chalk4.red(` \u{1F534} Critical: ${chalk4.bold(summary.criticalIssues)}`)
1147
+ );
794
1148
  }
795
1149
  if (summary.majorIssues > 0) {
796
- console.log(chalk4.yellow(` \u{1F7E1} Major: ${chalk4.bold(summary.majorIssues)}`));
1150
+ console.log(
1151
+ chalk4.yellow(` \u{1F7E1} Major: ${chalk4.bold(summary.majorIssues)}`)
1152
+ );
797
1153
  }
798
1154
  if (summary.minorIssues > 0) {
799
- console.log(chalk4.blue(` \u{1F535} Minor: ${chalk4.bold(summary.minorIssues)}`));
1155
+ console.log(
1156
+ chalk4.blue(` \u{1F535} Minor: ${chalk4.bold(summary.minorIssues)}`)
1157
+ );
800
1158
  }
801
- console.log(chalk4.green(`
1159
+ console.log(
1160
+ chalk4.green(
1161
+ `
802
1162
  \u{1F4A1} Potential savings: ${chalk4.bold(summary.totalPotentialSavings.toLocaleString())} tokens
803
- `));
1163
+ `
1164
+ )
1165
+ );
804
1166
  } else {
805
1167
  console.log(chalk4.green("\u2705 No significant issues found!\n"));
806
1168
  }
807
1169
  if (summary.deepFiles.length > 0) {
808
1170
  console.log(chalk4.bold("\u{1F4CF} Deep Import Chains:\n"));
809
- console.log(chalk4.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
810
- console.log(chalk4.gray(` Maximum depth: ${summary.maxImportDepth}
811
- `));
1171
+ console.log(
1172
+ chalk4.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1173
+ );
1174
+ console.log(
1175
+ chalk4.gray(` Maximum depth: ${summary.maxImportDepth}
1176
+ `)
1177
+ );
812
1178
  summary.deepFiles.slice(0, 10).forEach((item) => {
813
1179
  const fileName = item.file.split("/").slice(-2).join("/");
814
- console.log(` ${chalk4.cyan("\u2192")} ${chalk4.white(fileName)} ${chalk4.dim(`(depth: ${item.depth})`)}`);
1180
+ console.log(
1181
+ ` ${chalk4.cyan("\u2192")} ${chalk4.white(fileName)} ${chalk4.dim(`(depth: ${item.depth})`)}`
1182
+ );
815
1183
  });
816
1184
  console.log();
817
1185
  }
818
1186
  if (summary.fragmentedModules.length > 0) {
819
1187
  console.log(chalk4.bold("\u{1F9E9} Fragmented Modules:\n"));
820
- console.log(chalk4.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
821
- `));
1188
+ console.log(
1189
+ chalk4.gray(
1190
+ ` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
1191
+ `
1192
+ )
1193
+ );
822
1194
  summary.fragmentedModules.slice(0, 10).forEach((module) => {
823
- console.log(` ${chalk4.yellow("\u25CF")} ${chalk4.white(module.domain)} - ${chalk4.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
824
- console.log(chalk4.dim(` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`));
1195
+ console.log(
1196
+ ` ${chalk4.yellow("\u25CF")} ${chalk4.white(module.domain)} - ${chalk4.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
1197
+ );
1198
+ console.log(
1199
+ chalk4.dim(
1200
+ ` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
1201
+ )
1202
+ );
825
1203
  });
826
1204
  console.log();
827
1205
  }
828
1206
  if (summary.lowCohesionFiles.length > 0) {
829
1207
  console.log(chalk4.bold("\u{1F500} Low Cohesion Files:\n"));
830
- console.log(chalk4.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
831
- `));
1208
+ console.log(
1209
+ chalk4.gray(
1210
+ ` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
1211
+ `
1212
+ )
1213
+ );
832
1214
  summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
833
1215
  const fileName = item.file.split("/").slice(-2).join("/");
834
1216
  const scorePercent = (item.score * 100).toFixed(0);
835
1217
  const color = item.score < 0.4 ? chalk4.red : chalk4.yellow;
836
- console.log(` ${color("\u25CB")} ${chalk4.white(fileName)} ${chalk4.dim(`(${scorePercent}% cohesion)`)}`);
1218
+ console.log(
1219
+ ` ${color("\u25CB")} ${chalk4.white(fileName)} ${chalk4.dim(`(${scorePercent}% cohesion)`)}`
1220
+ );
837
1221
  });
838
1222
  console.log();
839
1223
  }
@@ -842,7 +1226,9 @@ async function contextAction(directory, options) {
842
1226
  summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
843
1227
  const fileName = item.file.split("/").slice(-2).join("/");
844
1228
  const severityColor = item.severity === "critical" ? chalk4.red : item.severity === "major" ? chalk4.yellow : chalk4.blue;
845
- console.log(` ${severityColor("\u25CF")} ${chalk4.white(fileName)} ${chalk4.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
1229
+ console.log(
1230
+ ` ${severityColor("\u25CF")} ${chalk4.white(fileName)} ${chalk4.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1231
+ );
846
1232
  });
847
1233
  console.log();
848
1234
  }
@@ -900,7 +1286,10 @@ async function consistencyAction(directory, options) {
900
1286
  let consistencyScore;
901
1287
  if (options.score) {
902
1288
  const issues = report.results?.flatMap((r) => r.issues) || [];
903
- consistencyScore = calculateConsistencyScore(issues, report.summary.filesAnalyzed);
1289
+ consistencyScore = calculateConsistencyScore(
1290
+ issues,
1291
+ report.summary.filesAnalyzed
1292
+ );
904
1293
  }
905
1294
  const outputFormat = options.output || finalOptions.output?.format || "console";
906
1295
  const userOutputFile = options.outputFile || finalOptions.output?.file;
@@ -918,7 +1307,11 @@ async function consistencyAction(directory, options) {
918
1307
  `aiready-report-${getReportTimestamp()}.json`,
919
1308
  resolvedDir
920
1309
  );
921
- handleJSONOutput4(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
1310
+ handleJSONOutput4(
1311
+ outputData,
1312
+ outputPath,
1313
+ `\u2705 Results saved to ${outputPath}`
1314
+ );
922
1315
  } else if (outputFormat === "markdown") {
923
1316
  const markdown = generateMarkdownReport(report, elapsedTime);
924
1317
  const outputPath = resolveOutputPath4(
@@ -930,15 +1323,23 @@ async function consistencyAction(directory, options) {
930
1323
  console.log(chalk5.green(`\u2705 Report saved to ${outputPath}`));
931
1324
  } else {
932
1325
  console.log(chalk5.bold("\n\u{1F4CA} Summary\n"));
933
- console.log(`Files Analyzed: ${chalk5.cyan(report.summary.filesAnalyzed)}`);
1326
+ console.log(
1327
+ `Files Analyzed: ${chalk5.cyan(report.summary.filesAnalyzed)}`
1328
+ );
934
1329
  console.log(`Total Issues: ${chalk5.yellow(report.summary.totalIssues)}`);
935
1330
  console.log(` Naming: ${chalk5.yellow(report.summary.namingIssues)}`);
936
1331
  console.log(` Patterns: ${chalk5.yellow(report.summary.patternIssues)}`);
937
- console.log(` Architecture: ${chalk5.yellow(report.summary.architectureIssues || 0)}`);
1332
+ console.log(
1333
+ ` Architecture: ${chalk5.yellow(report.summary.architectureIssues || 0)}`
1334
+ );
938
1335
  console.log(`Analysis Time: ${chalk5.gray(elapsedTime + "s")}
939
1336
  `);
940
1337
  if (report.summary.totalIssues === 0) {
941
- console.log(chalk5.green("\u2728 No consistency issues found! Your codebase is well-maintained.\n"));
1338
+ console.log(
1339
+ chalk5.green(
1340
+ "\u2728 No consistency issues found! Your codebase is well-maintained.\n"
1341
+ )
1342
+ );
942
1343
  } else {
943
1344
  const namingResults = report.results.filter(
944
1345
  (r) => r.issues.some((i) => i.category === "naming")
@@ -954,10 +1355,14 @@ async function consistencyAction(directory, options) {
954
1355
  for (const issue of result.issues) {
955
1356
  if (shown >= 5) break;
956
1357
  const severityColor = issue.severity === "critical" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
957
- console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`);
1358
+ console.log(
1359
+ `${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
1360
+ );
958
1361
  console.log(` ${issue.message}`);
959
1362
  if (issue.suggestion) {
960
- console.log(` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`);
1363
+ console.log(
1364
+ ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
1365
+ );
961
1366
  }
962
1367
  console.log();
963
1368
  shown++;
@@ -977,10 +1382,14 @@ async function consistencyAction(directory, options) {
977
1382
  for (const issue of result.issues) {
978
1383
  if (shown >= 5) break;
979
1384
  const severityColor = issue.severity === "critical" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
980
- console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`);
1385
+ console.log(
1386
+ `${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
1387
+ );
981
1388
  console.log(` ${issue.message}`);
982
1389
  if (issue.suggestion) {
983
- console.log(` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`);
1390
+ console.log(
1391
+ ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
1392
+ );
984
1393
  }
985
1394
  console.log();
986
1395
  shown++;
@@ -1013,7 +1422,7 @@ async function consistencyAction(directory, options) {
1013
1422
 
1014
1423
  // src/commands/visualize.ts
1015
1424
  import chalk6 from "chalk";
1016
- import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync as copyFileSync2 } from "fs";
1425
+ import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
1017
1426
  import { resolve as resolvePath6 } from "path";
1018
1427
  import { spawn } from "child_process";
1019
1428
  import { handleCLIError as handleCLIError5 } from "@aiready/core";
@@ -1026,15 +1435,21 @@ async function visualizeAction(directory, options) {
1026
1435
  const latestScan = findLatestScanReport(dirPath);
1027
1436
  if (latestScan) {
1028
1437
  reportPath = latestScan;
1029
- console.log(chalk6.dim(`Found latest report: ${latestScan.split("/").pop()}`));
1438
+ console.log(
1439
+ chalk6.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1440
+ );
1030
1441
  } else {
1031
1442
  console.error(chalk6.red("\u274C No AI readiness report found"));
1032
- console.log(chalk6.dim(`
1443
+ console.log(
1444
+ chalk6.dim(
1445
+ `
1033
1446
  Generate a report with:
1034
1447
  aiready scan --output json
1035
1448
 
1036
1449
  Or specify a custom report:
1037
- aiready visualise --report <path-to-report.json>`));
1450
+ aiready visualise --report <path-to-report.json>`
1451
+ )
1452
+ );
1038
1453
  return;
1039
1454
  }
1040
1455
  }
@@ -1052,6 +1467,7 @@ Or specify a custom report:
1052
1467
  };
1053
1468
  }
1054
1469
  } catch (e) {
1470
+ void e;
1055
1471
  }
1056
1472
  }
1057
1473
  const envVisualizerConfig = JSON.stringify(graphConfig);
@@ -1072,11 +1488,18 @@ Or specify a custom report:
1072
1488
  } else {
1073
1489
  const nodemodulesLocations = [
1074
1490
  resolvePath6(dirPath, "node_modules", "@aiready", "visualizer"),
1075
- resolvePath6(process.cwd(), "node_modules", "@aiready", "visualizer")
1491
+ resolvePath6(
1492
+ process.cwd(),
1493
+ "node_modules",
1494
+ "@aiready",
1495
+ "visualizer"
1496
+ )
1076
1497
  ];
1077
1498
  let currentDir = dirPath;
1078
1499
  while (currentDir !== "/" && currentDir !== ".") {
1079
- nodemodulesLocations.push(resolvePath6(currentDir, "node_modules", "@aiready", "visualizer"));
1500
+ nodemodulesLocations.push(
1501
+ resolvePath6(currentDir, "node_modules", "@aiready", "visualizer")
1502
+ );
1080
1503
  const parent = resolvePath6(currentDir, "..");
1081
1504
  if (parent === currentDir) break;
1082
1505
  currentDir = parent;
@@ -1093,7 +1516,8 @@ Or specify a custom report:
1093
1516
  const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
1094
1517
  webDir = resolvePath6(vizPkgPath, "..");
1095
1518
  visualizerAvailable = true;
1096
- } catch (e) {
1519
+ } catch (err) {
1520
+ void err;
1097
1521
  }
1098
1522
  }
1099
1523
  }
@@ -1104,7 +1528,7 @@ Or specify a custom report:
1104
1528
  const copyReportToViz = () => {
1105
1529
  try {
1106
1530
  const destPath = resolvePath6(spawnCwd, "web", "report-data.json");
1107
- copyFileSync2(reportPath, destPath);
1531
+ copyFileSync(reportPath, destPath);
1108
1532
  console.log(`\u{1F4CB} Report synced to ${destPath}`);
1109
1533
  } catch (e) {
1110
1534
  console.error("Failed to sync report:", e);
@@ -1121,30 +1545,46 @@ Or specify a custom report:
1121
1545
  AIREADY_REPORT_PATH: reportPath,
1122
1546
  AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1123
1547
  };
1124
- const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1548
+ const vite = spawn("pnpm", ["run", "dev:web"], {
1549
+ cwd: spawnCwd,
1550
+ stdio: "inherit",
1551
+ shell: true,
1552
+ env: envForSpawn
1553
+ });
1125
1554
  const onExit = () => {
1126
1555
  try {
1127
1556
  reportWatcher.close();
1128
- } catch (e) {
1557
+ } catch (err) {
1558
+ void err;
1129
1559
  }
1130
1560
  try {
1131
1561
  vite.kill();
1132
- } catch (e) {
1562
+ } catch (err) {
1563
+ void err;
1133
1564
  }
1134
1565
  process.exit(0);
1135
1566
  };
1136
1567
  process.on("SIGINT", onExit);
1137
1568
  process.on("SIGTERM", onExit);
1138
1569
  devServerStarted = true;
1570
+ void devServerStarted;
1139
1571
  return;
1140
1572
  } else {
1141
- console.log(chalk6.yellow("\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."));
1142
- console.log(chalk6.cyan(" Falling back to static HTML generation...\n"));
1573
+ console.log(
1574
+ chalk6.yellow(
1575
+ "\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
1576
+ )
1577
+ );
1578
+ console.log(
1579
+ chalk6.cyan(" Falling back to static HTML generation...\n")
1580
+ );
1143
1581
  useDevMode = false;
1144
1582
  }
1145
1583
  } catch (err) {
1146
1584
  console.error("Failed to start dev server:", err);
1147
- console.log(chalk6.cyan(" Falling back to static HTML generation...\n"));
1585
+ console.log(
1586
+ chalk6.cyan(" Falling back to static HTML generation...\n")
1587
+ );
1148
1588
  useDevMode = false;
1149
1589
  }
1150
1590
  }
@@ -1166,20 +1606,25 @@ Or specify a custom report:
1166
1606
  const urlPath = req.url || "/";
1167
1607
  if (urlPath === "/" || urlPath === "/index.html") {
1168
1608
  const content = await fsp.readFile(outPath, "utf8");
1169
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1609
+ res.writeHead(200, {
1610
+ "Content-Type": "text/html; charset=utf-8"
1611
+ });
1170
1612
  res.end(content);
1171
1613
  return;
1172
1614
  }
1173
1615
  res.writeHead(404, { "Content-Type": "text/plain" });
1174
1616
  res.end("Not found");
1175
1617
  } catch (e) {
1618
+ void e;
1176
1619
  res.writeHead(500, { "Content-Type": "text/plain" });
1177
1620
  res.end("Server error");
1178
1621
  }
1179
1622
  });
1180
1623
  server.listen(port, () => {
1181
1624
  const addr = `http://localhost:${port}/`;
1182
- console.log(chalk6.cyan(`\u{1F310} Local visualization server running at ${addr}`));
1625
+ console.log(
1626
+ chalk6.cyan(`\u{1F310} Local visualization server running at ${addr}`)
1627
+ );
1183
1628
  spawn(opener, [`"${addr}"`], { shell: true });
1184
1629
  });
1185
1630
  process.on("SIGINT", () => {
@@ -1244,19 +1689,23 @@ var getDirname = () => {
1244
1689
  if (typeof __dirname !== "undefined") return __dirname;
1245
1690
  return dirname(fileURLToPath(import.meta.url));
1246
1691
  };
1247
- var packageJson = JSON.parse(readFileSync4(join(getDirname(), "../package.json"), "utf8"));
1692
+ var packageJson = JSON.parse(
1693
+ readFileSync4(join(getDirname(), "../package.json"), "utf8")
1694
+ );
1248
1695
  var program = new Command();
1249
- program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText("after", `
1696
+ program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
1697
+ "after",
1698
+ `
1250
1699
  AI READINESS SCORING:
1251
1700
  Get a 0-100 score indicating how AI-ready your codebase is.
1252
1701
  Use --score flag with any analysis command for detailed breakdown.
1253
1702
 
1254
1703
  EXAMPLES:
1255
- $ aiready scan # Quick analysis of current directory
1704
+ $ aiready scan # Comprehensive analysis of current directory
1256
1705
  $ aiready scan --score # Get AI Readiness Score (0-100)
1257
1706
  $ aiready scan --tools patterns # Run only pattern detection
1258
- $ aiready patterns --similarity 0.6 # Custom similarity threshold
1259
- $ aiready scan --output json --output-file results.json
1707
+ $ npx @aiready/cli scan # Industry standard way to run standard scan
1708
+ $ aiready scan --output json # Output raw JSON for piping
1260
1709
 
1261
1710
  GETTING STARTED:
1262
1711
  1. Run 'aiready scan' to analyze your codebase
@@ -1281,23 +1730,105 @@ CONFIGURATION:
1281
1730
  VERSION: ${packageJson.version}
1282
1731
  DOCUMENTATION: https://aiready.dev/docs/cli
1283
1732
  GITHUB: https://github.com/caopengau/aiready-cli
1284
- LANDING: https://github.com/caopengau/aiready-landing`);
1285
- program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)").option("--profile <type>", "Scan profile to use (agentic, cost, security, onboarding)").option("--compare-to <path>", "Compare results against a previous AIReady report JSON").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "json").option("--output-file <path>", "Output file path (for json)").option("--no-score", "Disable calculating AI Readiness Score (enabled by default)").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option("--ci", "CI mode: GitHub Actions annotations, no colors, fail on threshold").option("--fail-on <level>", "Fail on issues: critical, major, any", "critical").addHelpText("after", scanHelpText).action(async (directory, options) => {
1733
+ LANDING: https://github.com/caopengau/aiready-landing`
1734
+ );
1735
+ program.command("scan").description(
1736
+ "Run comprehensive AI-readiness analysis (patterns + context + consistency)"
1737
+ ).argument("[directory]", "Directory to analyze", ".").option(
1738
+ "-t, --tools <tools>",
1739
+ "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
1740
+ ).option(
1741
+ "--profile <type>",
1742
+ "Scan profile to use (agentic, cost, security, onboarding)"
1743
+ ).option(
1744
+ "--compare-to <path>",
1745
+ "Compare results against a previous AIReady report JSON"
1746
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option(
1747
+ "--no-score",
1748
+ "Disable calculating AI Readiness Score (enabled by default)"
1749
+ ).option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option(
1750
+ "--ci",
1751
+ "CI mode: GitHub Actions annotations, no colors, fail on threshold"
1752
+ ).option(
1753
+ "--fail-on <level>",
1754
+ "Fail on issues: critical, major, any",
1755
+ "critical"
1756
+ ).addHelpText("after", scanHelpText).action(async (directory, options) => {
1286
1757
  await scanAction(directory, options);
1287
1758
  });
1288
- program.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for patterns (0-100)").addHelpText("after", patternsHelpText).action(async (directory, options) => {
1759
+ program.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
1760
+ "--max-candidates <number>",
1761
+ "Maximum candidates per block (performance tuning)"
1762
+ ).option(
1763
+ "--min-shared-tokens <number>",
1764
+ "Minimum shared tokens for candidates (performance tuning)"
1765
+ ).option(
1766
+ "--full-scan",
1767
+ "Disable smart defaults for comprehensive analysis (slower)"
1768
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option(
1769
+ "--score",
1770
+ "Calculate and display AI Readiness Score for patterns (0-100)"
1771
+ ).addHelpText("after", patternsHelpText).action(async (directory, options) => {
1289
1772
  await patternsAction(directory, options);
1290
1773
  });
1291
- program.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for context (0-100)").action(async (directory, options) => {
1774
+ program.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option(
1775
+ "--max-context <number>",
1776
+ "Maximum acceptable context budget (tokens)",
1777
+ "10000"
1778
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option(
1779
+ "--score",
1780
+ "Calculate and display AI Readiness Score for context (0-100)"
1781
+ ).action(async (directory, options) => {
1292
1782
  await contextAction(directory, options);
1293
1783
  });
1294
- program.command("consistency").description("Check naming conventions and architectural consistency").argument("[directory]", "Directory to analyze", ".").option("--naming", "Check naming conventions (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code patterns (default: true)").option("--no-patterns", "Skip pattern analysis").option("--min-severity <level>", "Minimum severity: info|minor|major|critical", "info").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json, markdown", "console").option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score for consistency (0-100)").action(async (directory, options) => {
1784
+ program.command("consistency").description("Check naming conventions and architectural consistency").argument("[directory]", "Directory to analyze", ".").option("--naming", "Check naming conventions (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code patterns (default: true)").option("--no-patterns", "Skip pattern analysis").option(
1785
+ "--min-severity <level>",
1786
+ "Minimum severity: info|minor|major|critical",
1787
+ "info"
1788
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
1789
+ "-o, --output <format>",
1790
+ "Output format: console, json, markdown",
1791
+ "console"
1792
+ ).option("--output-file <path>", "Output file path (for json/markdown)").option(
1793
+ "--score",
1794
+ "Calculate and display AI Readiness Score for consistency (0-100)"
1795
+ ).action(async (directory, options) => {
1295
1796
  await consistencyAction(directory, options);
1296
1797
  });
1297
- program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option("--report <path>", "Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)").option("-o, --output <path>", "Output HTML path (relative to directory)", "packages/visualizer/visualization.html").option("--open", "Open generated HTML in default browser").option("--serve [port]", "Start a local static server to serve the visualization (optional port number)", false).option("--dev", "Start Vite dev server (live reload) for interactive development", true).addHelpText("after", visualiseHelpText).action(async (directory, options) => {
1798
+ program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
1799
+ "--report <path>",
1800
+ "Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
1801
+ ).option(
1802
+ "-o, --output <path>",
1803
+ "Output HTML path (relative to directory)",
1804
+ "packages/visualizer/visualization.html"
1805
+ ).option("--open", "Open generated HTML in default browser").option(
1806
+ "--serve [port]",
1807
+ "Start a local static server to serve the visualization (optional port number)",
1808
+ false
1809
+ ).option(
1810
+ "--dev",
1811
+ "Start Vite dev server (live reload) for interactive development",
1812
+ true
1813
+ ).addHelpText("after", visualiseHelpText).action(async (directory, options) => {
1298
1814
  await visualizeAction(directory, options);
1299
1815
  });
1300
- program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option("--report <path>", "Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)").option("-o, --output <path>", "Output HTML path (relative to directory)", "packages/visualizer/visualization.html").option("--open", "Open generated HTML in default browser").option("--serve [port]", "Start a local static server to serve the visualization (optional port number)", false).option("--dev", "Start Vite dev server (live reload) for interactive development", false).addHelpText("after", visualizeHelpText).action(async (directory, options) => {
1816
+ program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option(
1817
+ "--report <path>",
1818
+ "Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
1819
+ ).option(
1820
+ "-o, --output <path>",
1821
+ "Output HTML path (relative to directory)",
1822
+ "packages/visualizer/visualization.html"
1823
+ ).option("--open", "Open generated HTML in default browser").option(
1824
+ "--serve [port]",
1825
+ "Start a local static server to serve the visualization (optional port number)",
1826
+ false
1827
+ ).option(
1828
+ "--dev",
1829
+ "Start Vite dev server (live reload) for interactive development",
1830
+ false
1831
+ ).addHelpText("after", visualizeHelpText).action(async (directory, options) => {
1301
1832
  await visualizeAction(directory, options);
1302
1833
  });
1303
1834
  program.command("change-amplification").description("Analyze graph metrics for change amplification").argument("[directory]", "Directory to analyze", ".").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {