@aiready/cli 0.9.26 → 0.9.28

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.js CHANGED
@@ -25,6 +25,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli.ts
27
27
  var import_commander = require("commander");
28
+ var import_fs4 = require("fs");
29
+ var import_path7 = require("path");
30
+
31
+ // src/commands/scan.ts
32
+ var import_chalk2 = __toESM(require("chalk"));
33
+ var import_path2 = require("path");
34
+ var import_core = require("@aiready/core");
28
35
 
29
36
  // src/index.ts
30
37
  var import_pattern_detect = require("@aiready/pattern-detect");
@@ -112,59 +119,114 @@ async function analyzeUnified(options) {
112
119
  return result;
113
120
  }
114
121
 
115
- // src/cli.ts
116
- var import_chalk = __toESM(require("chalk"));
117
- var import_fs = require("fs");
122
+ // src/utils/helpers.ts
118
123
  var import_path = require("path");
119
- var import_core = require("@aiready/core");
120
- var import_fs2 = require("fs");
121
- var import_path2 = require("path");
122
- var packageJson = JSON.parse((0, import_fs2.readFileSync)((0, import_path.join)(__dirname, "../package.json"), "utf8"));
123
- var program = new import_commander.Command();
124
- program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText("after", `
125
- AI READINESS SCORING:
126
- Get a 0-100 score indicating how AI-ready your codebase is.
127
- Use --score flag with any analysis command for detailed breakdown.
124
+ var import_fs = require("fs");
125
+ var import_chalk = __toESM(require("chalk"));
126
+ function getReportTimestamp() {
127
+ const now = /* @__PURE__ */ new Date();
128
+ const pad = (n) => String(n).padStart(2, "0");
129
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
130
+ }
131
+ function findLatestScanReport(dirPath) {
132
+ const aireadyDir = (0, import_path.resolve)(dirPath, ".aiready");
133
+ if (!(0, import_fs.existsSync)(aireadyDir)) {
134
+ return null;
135
+ }
136
+ let files = (0, import_fs.readdirSync)(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json"));
137
+ if (files.length === 0) {
138
+ files = (0, import_fs.readdirSync)(aireadyDir).filter((f) => f.startsWith("aiready-scan-") && f.endsWith(".json"));
139
+ }
140
+ if (files.length === 0) {
141
+ return null;
142
+ }
143
+ const sortedFiles = files.map((f) => ({ name: f, path: (0, import_path.resolve)(aireadyDir, f), mtime: (0, import_fs.statSync)((0, import_path.resolve)(aireadyDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
144
+ return sortedFiles[0].path;
145
+ }
146
+ function warnIfGraphCapExceeded(report, dirPath) {
147
+ try {
148
+ const { loadConfig } = require("@aiready/core");
149
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
150
+ const configPath = (0, import_path.resolve)(dirPath, "aiready.json");
151
+ if ((0, import_fs.existsSync)(configPath)) {
152
+ try {
153
+ const rawConfig = JSON.parse((0, import_fs.readFileSync)(configPath, "utf8"));
154
+ if (rawConfig.visualizer?.graph) {
155
+ graphConfig = {
156
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
157
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
158
+ };
159
+ }
160
+ } catch (e) {
161
+ }
162
+ }
163
+ const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
164
+ const edgeCount = report.context?.reduce((sum, ctx) => {
165
+ const relCount = ctx.relatedFiles?.length || 0;
166
+ const depCount = ctx.dependencies?.length || 0;
167
+ return sum + relCount + depCount;
168
+ }, 0) || 0;
169
+ if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
170
+ console.log("");
171
+ console.log(import_chalk.default.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
172
+ if (nodeCount > graphConfig.maxNodes) {
173
+ console.log(import_chalk.default.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
174
+ }
175
+ if (edgeCount > graphConfig.maxEdges) {
176
+ console.log(import_chalk.default.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
177
+ }
178
+ console.log(import_chalk.default.dim(` To increase limits, add to aiready.json:`));
179
+ console.log(import_chalk.default.dim(` {`));
180
+ console.log(import_chalk.default.dim(` "visualizer": {`));
181
+ console.log(import_chalk.default.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
182
+ console.log(import_chalk.default.dim(` }`));
183
+ console.log(import_chalk.default.dim(` }`));
184
+ }
185
+ } catch (e) {
186
+ }
187
+ }
188
+ function generateMarkdownReport(report, elapsedTime) {
189
+ let markdown = `# Consistency Analysis Report
128
190
 
129
- EXAMPLES:
130
- $ aiready scan # Quick analysis of current directory
131
- $ aiready scan --score # Get AI Readiness Score (0-100)
132
- $ aiready scan --tools patterns # Run only pattern detection
133
- $ aiready patterns --similarity 0.6 # Custom similarity threshold
134
- $ aiready scan --output json --output-file results.json
191
+ `;
192
+ markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
193
+ `;
194
+ markdown += `**Analysis Time:** ${elapsedTime}s
135
195
 
136
- GETTING STARTED:
137
- 1. Run 'aiready scan' to analyze your codebase
138
- 2. Use 'aiready scan --score' for AI readiness assessment
139
- 3. Create aiready.json for persistent configuration
140
- 4. Set up CI/CD with '--threshold' for quality gates
196
+ `;
197
+ markdown += `## Summary
141
198
 
142
- CONFIGURATION:
143
- Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
144
- CLI options override config file settings
199
+ `;
200
+ markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
201
+ `;
202
+ markdown += `- **Total Issues:** ${report.summary.totalIssues}
203
+ `;
204
+ markdown += ` - Naming: ${report.summary.namingIssues}
205
+ `;
206
+ markdown += ` - Patterns: ${report.summary.patternIssues}
145
207
 
146
- Example aiready.json:
147
- {
148
- "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
149
- "tools": {
150
- "pattern-detect": { "minSimilarity": 0.5 },
151
- "context-analyzer": { "maxContextBudget": 15000 }
152
- },
153
- "output": { "format": "json", "directory": ".aiready" }
208
+ `;
209
+ if (report.recommendations.length > 0) {
210
+ markdown += `## Recommendations
211
+
212
+ `;
213
+ report.recommendations.forEach((rec, i) => {
214
+ markdown += `${i + 1}. ${rec}
215
+ `;
216
+ });
154
217
  }
218
+ return markdown;
219
+ }
220
+ function truncateArray(arr, cap = 8) {
221
+ if (!Array.isArray(arr)) return "";
222
+ const shown = arr.slice(0, cap).map((v) => String(v));
223
+ const more = arr.length - shown.length;
224
+ return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
225
+ }
155
226
 
156
- VERSION: ${packageJson.version}
157
- DOCUMENTATION: https://aiready.dev/docs/cli
158
- GITHUB: https://github.com/caopengau/aiready-cli
159
- LANDING: https://github.com/caopengau/aiready-landing`);
160
- 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)", "patterns,context,consistency").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 (patterns:40,context:35,consistency:25)").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").addHelpText("after", `
161
- EXAMPLES:
162
- $ aiready scan # Analyze all tools
163
- $ aiready scan --tools patterns,context # Skip consistency
164
- $ aiready scan --score --threshold 75 # CI/CD with threshold
165
- $ aiready scan --output json --output-file report.json
166
- `).action(async (directory, options) => {
167
- console.log(import_chalk.default.blue("\u{1F680} Starting AIReady unified analysis...\n"));
227
+ // src/commands/scan.ts
228
+ async function scanAction(directory, options) {
229
+ console.log(import_chalk2.default.blue("\u{1F680} Starting AIReady unified analysis...\n"));
168
230
  const startTime = Date.now();
169
231
  const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
170
232
  try {
@@ -188,19 +250,13 @@ EXAMPLES:
188
250
  const patternSmartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
189
251
  finalOptions = { ...patternSmartDefaults, ...finalOptions, ...baseOptions };
190
252
  }
191
- console.log(import_chalk.default.cyan("\n=== AIReady Run Preview ==="));
192
- console.log(import_chalk.default.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
193
- console.log(import_chalk.default.white("Will use settings from config and defaults."));
194
- const truncate = (arr, cap = 8) => {
195
- if (!Array.isArray(arr)) return "";
196
- const shown = arr.slice(0, cap).map((v) => String(v));
197
- const more = arr.length - shown.length;
198
- return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
199
- };
200
- console.log(import_chalk.default.white("\nGeneral settings:"));
201
- if (finalOptions.rootDir) console.log(` rootDir: ${import_chalk.default.bold(String(finalOptions.rootDir))}`);
202
- if (finalOptions.include) console.log(` include: ${import_chalk.default.bold(truncate(finalOptions.include, 6))}`);
203
- if (finalOptions.exclude) console.log(` exclude: ${import_chalk.default.bold(truncate(finalOptions.exclude, 6))}`);
253
+ console.log(import_chalk2.default.cyan("\n=== AIReady Run Preview ==="));
254
+ console.log(import_chalk2.default.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
255
+ console.log(import_chalk2.default.white("Will use settings from config and defaults."));
256
+ console.log(import_chalk2.default.white("\nGeneral settings:"));
257
+ if (finalOptions.rootDir) console.log(` rootDir: ${import_chalk2.default.bold(String(finalOptions.rootDir))}`);
258
+ if (finalOptions.include) console.log(` include: ${import_chalk2.default.bold(truncateArray(finalOptions.include, 6))}`);
259
+ if (finalOptions.exclude) console.log(` exclude: ${import_chalk2.default.bold(truncateArray(finalOptions.exclude, 6))}`);
204
260
  if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
205
261
  const patternDetectConfig = finalOptions["pattern-detect"] || {
206
262
  minSimilarity: finalOptions.minSimilarity,
@@ -213,16 +269,16 @@ EXAMPLES:
213
269
  severity: finalOptions.severity,
214
270
  includeTests: finalOptions.includeTests
215
271
  };
216
- console.log(import_chalk.default.white("\nPattern-detect settings:"));
217
- console.log(` minSimilarity: ${import_chalk.default.bold(patternDetectConfig.minSimilarity ?? "default")}`);
218
- console.log(` minLines: ${import_chalk.default.bold(patternDetectConfig.minLines ?? "default")}`);
219
- if (patternDetectConfig.approx !== void 0) console.log(` approx: ${import_chalk.default.bold(String(patternDetectConfig.approx))}`);
220
- if (patternDetectConfig.minSharedTokens !== void 0) console.log(` minSharedTokens: ${import_chalk.default.bold(String(patternDetectConfig.minSharedTokens))}`);
221
- if (patternDetectConfig.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${import_chalk.default.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`);
222
- if (patternDetectConfig.batchSize !== void 0) console.log(` batchSize: ${import_chalk.default.bold(String(patternDetectConfig.batchSize))}`);
223
- if (patternDetectConfig.streamResults !== void 0) console.log(` streamResults: ${import_chalk.default.bold(String(patternDetectConfig.streamResults))}`);
224
- if (patternDetectConfig.severity !== void 0) console.log(` severity: ${import_chalk.default.bold(String(patternDetectConfig.severity))}`);
225
- if (patternDetectConfig.includeTests !== void 0) console.log(` includeTests: ${import_chalk.default.bold(String(patternDetectConfig.includeTests))}`);
272
+ console.log(import_chalk2.default.white("\nPattern-detect settings:"));
273
+ console.log(` minSimilarity: ${import_chalk2.default.bold(patternDetectConfig.minSimilarity ?? "default")}`);
274
+ console.log(` minLines: ${import_chalk2.default.bold(patternDetectConfig.minLines ?? "default")}`);
275
+ if (patternDetectConfig.approx !== void 0) console.log(` approx: ${import_chalk2.default.bold(String(patternDetectConfig.approx))}`);
276
+ if (patternDetectConfig.minSharedTokens !== void 0) console.log(` minSharedTokens: ${import_chalk2.default.bold(String(patternDetectConfig.minSharedTokens))}`);
277
+ if (patternDetectConfig.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${import_chalk2.default.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`);
278
+ if (patternDetectConfig.batchSize !== void 0) console.log(` batchSize: ${import_chalk2.default.bold(String(patternDetectConfig.batchSize))}`);
279
+ if (patternDetectConfig.streamResults !== void 0) console.log(` streamResults: ${import_chalk2.default.bold(String(patternDetectConfig.streamResults))}`);
280
+ if (patternDetectConfig.severity !== void 0) console.log(` severity: ${import_chalk2.default.bold(String(patternDetectConfig.severity))}`);
281
+ if (patternDetectConfig.includeTests !== void 0) console.log(` includeTests: ${import_chalk2.default.bold(String(patternDetectConfig.includeTests))}`);
226
282
  }
227
283
  if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
228
284
  const ca = finalOptions["context-analyzer"] || {
@@ -232,32 +288,32 @@ EXAMPLES:
232
288
  maxFragmentation: finalOptions.maxFragmentation,
233
289
  includeNodeModules: finalOptions.includeNodeModules
234
290
  };
235
- console.log(import_chalk.default.white("\nContext-analyzer settings:"));
236
- console.log(` maxDepth: ${import_chalk.default.bold(ca.maxDepth ?? "default")}`);
237
- console.log(` maxContextBudget: ${import_chalk.default.bold(ca.maxContextBudget ?? "default")}`);
238
- if (ca.minCohesion !== void 0) console.log(` minCohesion: ${import_chalk.default.bold(String(ca.minCohesion))}`);
239
- if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${import_chalk.default.bold(String(ca.maxFragmentation))}`);
240
- if (ca.includeNodeModules !== void 0) console.log(` includeNodeModules: ${import_chalk.default.bold(String(ca.includeNodeModules))}`);
291
+ console.log(import_chalk2.default.white("\nContext-analyzer settings:"));
292
+ console.log(` maxDepth: ${import_chalk2.default.bold(ca.maxDepth ?? "default")}`);
293
+ console.log(` maxContextBudget: ${import_chalk2.default.bold(ca.maxContextBudget ?? "default")}`);
294
+ if (ca.minCohesion !== void 0) console.log(` minCohesion: ${import_chalk2.default.bold(String(ca.minCohesion))}`);
295
+ if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${import_chalk2.default.bold(String(ca.maxFragmentation))}`);
296
+ if (ca.includeNodeModules !== void 0) console.log(` includeNodeModules: ${import_chalk2.default.bold(String(ca.includeNodeModules))}`);
241
297
  }
242
298
  if (finalOptions.consistency) {
243
299
  const c = finalOptions.consistency;
244
- console.log(import_chalk.default.white("\nConsistency settings:"));
245
- console.log(` checkNaming: ${import_chalk.default.bold(String(c.checkNaming ?? true))}`);
246
- console.log(` checkPatterns: ${import_chalk.default.bold(String(c.checkPatterns ?? true))}`);
247
- console.log(` checkArchitecture: ${import_chalk.default.bold(String(c.checkArchitecture ?? false))}`);
248
- if (c.minSeverity) console.log(` minSeverity: ${import_chalk.default.bold(c.minSeverity)}`);
249
- if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${import_chalk.default.bold(truncate(c.acceptedAbbreviations, 8))}`);
250
- if (c.shortWords) console.log(` shortWords: ${import_chalk.default.bold(truncate(c.shortWords, 8))}`);
300
+ console.log(import_chalk2.default.white("\nConsistency settings:"));
301
+ console.log(` checkNaming: ${import_chalk2.default.bold(String(c.checkNaming ?? true))}`);
302
+ console.log(` checkPatterns: ${import_chalk2.default.bold(String(c.checkPatterns ?? true))}`);
303
+ console.log(` checkArchitecture: ${import_chalk2.default.bold(String(c.checkArchitecture ?? false))}`);
304
+ if (c.minSeverity) console.log(` minSeverity: ${import_chalk2.default.bold(c.minSeverity)}`);
305
+ if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${import_chalk2.default.bold(truncateArray(c.acceptedAbbreviations, 8))}`);
306
+ if (c.shortWords) console.log(` shortWords: ${import_chalk2.default.bold(truncateArray(c.shortWords, 8))}`);
251
307
  }
252
- console.log(import_chalk.default.white("\nStarting analysis..."));
308
+ console.log(import_chalk2.default.white("\nStarting analysis..."));
253
309
  const progressCallback = (event) => {
254
- console.log(import_chalk.default.cyan(`
310
+ console.log(import_chalk2.default.cyan(`
255
311
  --- ${event.tool.toUpperCase()} RESULTS ---`));
256
312
  try {
257
313
  if (event.tool === "patterns") {
258
314
  const pr = event.data;
259
- console.log(` Duplicate patterns: ${import_chalk.default.bold(String(pr.duplicates?.length || 0))}`);
260
- console.log(` Files with pattern issues: ${import_chalk.default.bold(String(pr.results?.length || 0))}`);
315
+ console.log(` Duplicate patterns: ${import_chalk2.default.bold(String(pr.duplicates?.length || 0))}`);
316
+ console.log(` Files with pattern issues: ${import_chalk2.default.bold(String(pr.results?.length || 0))}`);
261
317
  if (pr.duplicates && pr.duplicates.length > 0) {
262
318
  pr.duplicates.slice(0, 5).forEach((d, i) => {
263
319
  console.log(` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`);
@@ -271,10 +327,10 @@ EXAMPLES:
271
327
  });
272
328
  }
273
329
  if (pr.groups && pr.groups.length >= 0) {
274
- console.log(` \u2705 Grouped ${import_chalk.default.bold(String(pr.duplicates?.length || 0))} duplicates into ${import_chalk.default.bold(String(pr.groups.length))} file pairs`);
330
+ console.log(` \u2705 Grouped ${import_chalk2.default.bold(String(pr.duplicates?.length || 0))} duplicates into ${import_chalk2.default.bold(String(pr.groups.length))} file pairs`);
275
331
  }
276
332
  if (pr.clusters && pr.clusters.length >= 0) {
277
- console.log(` \u2705 Created ${import_chalk.default.bold(String(pr.clusters.length))} refactor clusters`);
333
+ console.log(` \u2705 Created ${import_chalk2.default.bold(String(pr.clusters.length))} refactor clusters`);
278
334
  pr.clusters.slice(0, 3).forEach((cl, idx) => {
279
335
  const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
280
336
  console.log(` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`);
@@ -282,14 +338,14 @@ EXAMPLES:
282
338
  }
283
339
  } else if (event.tool === "context") {
284
340
  const cr = event.data;
285
- console.log(` Context issues found: ${import_chalk.default.bold(String(cr.length || 0))}`);
341
+ console.log(` Context issues found: ${import_chalk2.default.bold(String(cr.length || 0))}`);
286
342
  cr.slice(0, 5).forEach((c, i) => {
287
343
  const msg = c.message ? ` - ${c.message}` : "";
288
344
  console.log(` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`);
289
345
  });
290
346
  } else if (event.tool === "consistency") {
291
347
  const rep = event.data;
292
- console.log(` Consistency totalIssues: ${import_chalk.default.bold(String(rep.summary?.totalIssues || 0))}`);
348
+ console.log(` Consistency totalIssues: ${import_chalk2.default.bold(String(rep.summary?.totalIssues || 0))}`);
293
349
  if (rep.results && rep.results.length > 0) {
294
350
  const fileMap = /* @__PURE__ */ new Map();
295
351
  rep.results.forEach((r) => {
@@ -313,7 +369,7 @@ EXAMPLES:
313
369
  });
314
370
  const remaining = files.length - topFiles.length;
315
371
  if (remaining > 0) {
316
- console.log(import_chalk.default.dim(` ... and ${remaining} more files with issues (use --output json for full details)`));
372
+ console.log(import_chalk2.default.dim(` ... and ${remaining} more files with issues (use --output json for full details)`));
317
373
  }
318
374
  }
319
375
  }
@@ -321,15 +377,15 @@ EXAMPLES:
321
377
  }
322
378
  };
323
379
  const results = await analyzeUnified({ ...finalOptions, progressCallback, suppressToolConfig: true });
324
- console.log(import_chalk.default.cyan("\n=== AIReady Run Summary ==="));
325
- console.log(import_chalk.default.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
326
- console.log(import_chalk.default.cyan("\nResults summary:"));
327
- console.log(` Total issues (all tools): ${import_chalk.default.bold(String(results.summary.totalIssues || 0))}`);
328
- if (results.duplicates) console.log(` Duplicate patterns found: ${import_chalk.default.bold(String(results.duplicates.length || 0))}`);
329
- if (results.patterns) console.log(` Pattern files with issues: ${import_chalk.default.bold(String(results.patterns.length || 0))}`);
330
- if (results.context) console.log(` Context issues: ${import_chalk.default.bold(String(results.context.length || 0))}`);
331
- if (results.consistency) console.log(` Consistency issues: ${import_chalk.default.bold(String(results.consistency.summary.totalIssues || 0))}`);
332
- console.log(import_chalk.default.cyan("===========================\n"));
380
+ console.log(import_chalk2.default.cyan("\n=== AIReady Run Summary ==="));
381
+ console.log(import_chalk2.default.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
382
+ console.log(import_chalk2.default.cyan("\nResults summary:"));
383
+ console.log(` Total issues (all tools): ${import_chalk2.default.bold(String(results.summary.totalIssues || 0))}`);
384
+ if (results.duplicates) console.log(` Duplicate patterns found: ${import_chalk2.default.bold(String(results.duplicates.length || 0))}`);
385
+ if (results.patterns) console.log(` Pattern files with issues: ${import_chalk2.default.bold(String(results.patterns.length || 0))}`);
386
+ if (results.context) console.log(` Context issues: ${import_chalk2.default.bold(String(results.context.length || 0))}`);
387
+ if (results.consistency) console.log(` Consistency issues: ${import_chalk2.default.bold(String(results.consistency.summary.totalIssues || 0))}`);
388
+ console.log(import_chalk2.default.cyan("===========================\n"));
333
389
  const elapsedTime = (0, import_core.getElapsedTime)(startTime);
334
390
  let scoringResult;
335
391
  if (options.score || finalOptions.scoring?.showBreakdown) {
@@ -364,10 +420,10 @@ EXAMPLES:
364
420
  const cliWeights = (0, import_core.parseWeightString)(options.weights);
365
421
  if (toolScores.size > 0) {
366
422
  scoringResult = (0, import_core.calculateOverallScore)(toolScores, finalOptions, cliWeights.size ? cliWeights : void 0);
367
- console.log(import_chalk.default.bold("\n\u{1F4CA} AI Readiness Overall Score"));
423
+ console.log(import_chalk2.default.bold("\n\u{1F4CA} AI Readiness Overall Score"));
368
424
  console.log(` ${(0, import_core.formatScore)(scoringResult)}`);
369
425
  if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
370
- console.log(import_chalk.default.bold("\nTool breakdown:"));
426
+ console.log(import_chalk2.default.bold("\nTool breakdown:"));
371
427
  scoringResult.breakdown.forEach((tool) => {
372
428
  const rating = (0, import_core.getRating)(tool.score);
373
429
  const rd = (0, import_core.getRatingDisplay)(rating);
@@ -375,7 +431,7 @@ EXAMPLES:
375
431
  });
376
432
  console.log();
377
433
  if (finalOptions.scoring?.showBreakdown) {
378
- console.log(import_chalk.default.bold("Detailed tool breakdown:"));
434
+ console.log(import_chalk2.default.bold("Detailed tool breakdown:"));
379
435
  scoringResult.breakdown.forEach((tool) => {
380
436
  console.log((0, import_core.formatToolScore)(tool));
381
437
  });
@@ -394,19 +450,123 @@ EXAMPLES:
394
450
  (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
395
451
  warnIfGraphCapExceeded(outputData, resolvedDir);
396
452
  }
453
+ const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
454
+ if (isCI && scoringResult) {
455
+ const threshold = options.threshold ? parseInt(options.threshold) : void 0;
456
+ const failOnLevel = options.failOn || "critical";
457
+ if (process.env.GITHUB_ACTIONS === "true") {
458
+ console.log(`
459
+ ::group::AI Readiness Score`);
460
+ console.log(`score=${scoringResult.overallScore}`);
461
+ if (scoringResult.breakdown) {
462
+ scoringResult.breakdown.forEach((tool) => {
463
+ console.log(`${tool.toolName}=${tool.score}`);
464
+ });
465
+ }
466
+ console.log("::endgroup::");
467
+ if (threshold && scoringResult.overallScore < threshold) {
468
+ console.log(`::error::AI Readiness Score ${scoringResult.overallScore} is below threshold ${threshold}`);
469
+ } else if (threshold) {
470
+ console.log(`::notice::AI Readiness Score: ${scoringResult.overallScore}/100 (threshold: ${threshold})`);
471
+ }
472
+ if (results.patterns) {
473
+ const criticalPatterns = results.patterns.flatMap(
474
+ (p) => p.issues.filter((i) => i.severity === "critical")
475
+ );
476
+ criticalPatterns.slice(0, 10).forEach((issue) => {
477
+ console.log(`::warning file=${issue.location?.file || "unknown"},line=${issue.location?.line || 1}::${issue.message}`);
478
+ });
479
+ }
480
+ }
481
+ let shouldFail = false;
482
+ let failReason = "";
483
+ if (threshold && scoringResult.overallScore < threshold) {
484
+ shouldFail = true;
485
+ failReason = `AI Readiness Score ${scoringResult.overallScore} is below threshold ${threshold}`;
486
+ }
487
+ if (failOnLevel !== "none") {
488
+ const severityLevels = { critical: 4, major: 3, minor: 2, any: 1 };
489
+ const minSeverity = severityLevels[failOnLevel] || 4;
490
+ let criticalCount = 0;
491
+ let majorCount = 0;
492
+ if (results.patterns) {
493
+ results.patterns.forEach((p) => {
494
+ p.issues.forEach((i) => {
495
+ if (i.severity === "critical") criticalCount++;
496
+ if (i.severity === "major") majorCount++;
497
+ });
498
+ });
499
+ }
500
+ if (results.context) {
501
+ results.context.forEach((c) => {
502
+ if (c.severity === "critical") criticalCount++;
503
+ if (c.severity === "major") majorCount++;
504
+ });
505
+ }
506
+ if (results.consistency?.results) {
507
+ results.consistency.results.forEach((r) => {
508
+ r.issues?.forEach((i) => {
509
+ if (i.severity === "critical") criticalCount++;
510
+ if (i.severity === "major") majorCount++;
511
+ });
512
+ });
513
+ }
514
+ if (minSeverity >= 4 && criticalCount > 0) {
515
+ shouldFail = true;
516
+ failReason = `Found ${criticalCount} critical issues`;
517
+ } else if (minSeverity >= 3 && criticalCount + majorCount > 0) {
518
+ shouldFail = true;
519
+ failReason = `Found ${criticalCount} critical and ${majorCount} major issues`;
520
+ }
521
+ }
522
+ if (shouldFail) {
523
+ console.log(import_chalk2.default.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
524
+ console.log(import_chalk2.default.red(` Reason: ${failReason}`));
525
+ console.log(import_chalk2.default.dim("\n Remediation steps:"));
526
+ console.log(import_chalk2.default.dim(" 1. Run `aiready scan` locally to see detailed issues"));
527
+ console.log(import_chalk2.default.dim(" 2. Fix the critical issues before merging"));
528
+ console.log(import_chalk2.default.dim(" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"));
529
+ process.exit(1);
530
+ } else {
531
+ console.log(import_chalk2.default.green("\n\u2705 PR PASSED: AI Readiness Check"));
532
+ if (threshold) {
533
+ console.log(import_chalk2.default.green(` Score: ${scoringResult.overallScore}/100 (threshold: ${threshold})`));
534
+ }
535
+ console.log(import_chalk2.default.dim("\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"));
536
+ }
537
+ }
397
538
  } catch (error) {
398
539
  (0, import_core.handleCLIError)(error, "Analysis");
399
540
  }
400
- });
401
- 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", `
541
+ }
542
+ var scanHelpText = `
402
543
  EXAMPLES:
403
- $ aiready patterns # Default analysis
404
- $ aiready patterns --similarity 0.6 # Stricter matching
405
- $ aiready patterns --min-lines 10 # Larger patterns only
406
- `).action(async (directory, options) => {
407
- console.log(import_chalk.default.blue("\u{1F50D} Analyzing patterns...\n"));
544
+ $ aiready scan # Analyze all tools
545
+ $ aiready scan --tools patterns,context # Skip consistency
546
+ $ aiready scan --score --threshold 75 # CI/CD with threshold
547
+ $ aiready scan --ci --threshold 70 # GitHub Actions gatekeeper
548
+ $ aiready scan --ci --fail-on major # Fail on major+ issues
549
+ $ aiready scan --output json --output-file report.json
550
+
551
+ CI/CD INTEGRATION (Gatekeeper Mode):
552
+ Use --ci for GitHub Actions integration:
553
+ - Outputs GitHub Actions annotations for PR checks
554
+ - Fails with exit code 1 if threshold not met
555
+ - Shows clear "blocked" message with remediation steps
556
+
557
+ Example GitHub Actions workflow:
558
+ - name: AI Readiness Check
559
+ run: aiready scan --ci --threshold 70
560
+ `;
561
+
562
+ // src/commands/patterns.ts
563
+ var import_chalk3 = __toESM(require("chalk"));
564
+ var import_path3 = require("path");
565
+ var import_core2 = require("@aiready/core");
566
+ async function patternsAction(directory, options) {
567
+ console.log(import_chalk3.default.blue("\u{1F50D} Analyzing patterns...\n"));
408
568
  const startTime = Date.now();
409
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
569
+ const resolvedDir = (0, import_path3.resolve)(process.cwd(), directory || ".");
410
570
  try {
411
571
  const useSmartDefaults = !options.fullScan;
412
572
  const defaults = {
@@ -435,10 +595,10 @@ EXAMPLES:
435
595
  if (options.minSharedTokens) {
436
596
  cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
437
597
  }
438
- const finalOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, cliOptions);
598
+ const finalOptions = await (0, import_core2.loadMergedConfig)(resolvedDir, defaults, cliOptions);
439
599
  const { analyzePatterns: analyzePatterns2, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
440
600
  const { results, duplicates } = await analyzePatterns2(finalOptions);
441
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
601
+ const elapsedTime = (0, import_core2.getElapsedTime)(startTime);
442
602
  const summary = generateSummary(results);
443
603
  let patternScore;
444
604
  if (options.score) {
@@ -452,66 +612,77 @@ EXAMPLES:
452
612
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
453
613
  ...patternScore && { scoring: patternScore }
454
614
  };
455
- const outputPath = (0, import_core.resolveOutputPath)(
615
+ const outputPath = (0, import_core2.resolveOutputPath)(
456
616
  userOutputFile,
457
617
  `aiready-report-${getReportTimestamp()}.json`,
458
618
  resolvedDir
459
619
  );
460
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
620
+ (0, import_core2.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
461
621
  } else {
462
622
  const terminalWidth = process.stdout.columns || 80;
463
623
  const dividerWidth = Math.min(60, terminalWidth - 2);
464
624
  const divider = "\u2501".repeat(dividerWidth);
465
- console.log(import_chalk.default.cyan(divider));
466
- console.log(import_chalk.default.bold.white(" PATTERN ANALYSIS SUMMARY"));
467
- console.log(import_chalk.default.cyan(divider) + "\n");
468
- console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(results.length)}`));
469
- console.log(import_chalk.default.yellow(`\u26A0 Duplicate patterns found: ${import_chalk.default.bold(summary.totalPatterns)}`));
470
- console.log(import_chalk.default.red(`\u{1F4B0} Token cost (wasted): ${import_chalk.default.bold(summary.totalTokenCost.toLocaleString())}`));
471
- console.log(import_chalk.default.gray(`\u23F1 Analysis time: ${import_chalk.default.bold(elapsedTime + "s")}`));
625
+ console.log(import_chalk3.default.cyan(divider));
626
+ console.log(import_chalk3.default.bold.white(" PATTERN ANALYSIS SUMMARY"));
627
+ console.log(import_chalk3.default.cyan(divider) + "\n");
628
+ console.log(import_chalk3.default.white(`\u{1F4C1} Files analyzed: ${import_chalk3.default.bold(results.length)}`));
629
+ console.log(import_chalk3.default.yellow(`\u26A0 Duplicate patterns found: ${import_chalk3.default.bold(summary.totalPatterns)}`));
630
+ console.log(import_chalk3.default.red(`\u{1F4B0} Token cost (wasted): ${import_chalk3.default.bold(summary.totalTokenCost.toLocaleString())}`));
631
+ console.log(import_chalk3.default.gray(`\u23F1 Analysis time: ${import_chalk3.default.bold(elapsedTime + "s")}`));
472
632
  const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
473
633
  if (sortedTypes.length > 0) {
474
- console.log(import_chalk.default.cyan("\n" + divider));
475
- console.log(import_chalk.default.bold.white(" PATTERNS BY TYPE"));
476
- console.log(import_chalk.default.cyan(divider) + "\n");
634
+ console.log(import_chalk3.default.cyan("\n" + divider));
635
+ console.log(import_chalk3.default.bold.white(" PATTERNS BY TYPE"));
636
+ console.log(import_chalk3.default.cyan(divider) + "\n");
477
637
  sortedTypes.forEach(([type, count]) => {
478
- console.log(` ${import_chalk.default.white(type.padEnd(15))} ${import_chalk.default.bold(count)}`);
638
+ console.log(` ${import_chalk3.default.white(type.padEnd(15))} ${import_chalk3.default.bold(count)}`);
479
639
  });
480
640
  }
481
641
  if (summary.totalPatterns > 0 && duplicates.length > 0) {
482
- console.log(import_chalk.default.cyan("\n" + divider));
483
- console.log(import_chalk.default.bold.white(" TOP DUPLICATE PATTERNS"));
484
- console.log(import_chalk.default.cyan(divider) + "\n");
642
+ console.log(import_chalk3.default.cyan("\n" + divider));
643
+ console.log(import_chalk3.default.bold.white(" TOP DUPLICATE PATTERNS"));
644
+ console.log(import_chalk3.default.cyan(divider) + "\n");
485
645
  const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
486
646
  topDuplicates.forEach((dup) => {
487
647
  const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
488
648
  const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
489
649
  const file1Name = dup.file1.split("/").pop() || dup.file1;
490
650
  const file2Name = dup.file2.split("/").pop() || dup.file2;
491
- console.log(`${severityIcon} ${severity}: ${import_chalk.default.bold(file1Name)} \u2194 ${import_chalk.default.bold(file2Name)}`);
492
- console.log(` Similarity: ${import_chalk.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk.default.bold(dup.tokenCost.toLocaleString())} tokens each`);
493
- console.log(` Lines: ${import_chalk.default.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${import_chalk.default.cyan(dup.line2 + "-" + dup.endLine2)}
651
+ console.log(`${severityIcon} ${severity}: ${import_chalk3.default.bold(file1Name)} \u2194 ${import_chalk3.default.bold(file2Name)}`);
652
+ console.log(` Similarity: ${import_chalk3.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk3.default.bold(dup.tokenCost.toLocaleString())} tokens each`);
653
+ console.log(` Lines: ${import_chalk3.default.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${import_chalk3.default.cyan(dup.line2 + "-" + dup.endLine2)}
494
654
  `);
495
655
  });
496
656
  } else {
497
- console.log(import_chalk.default.green("\n\u2728 Great! No duplicate patterns detected.\n"));
657
+ console.log(import_chalk3.default.green("\n\u2728 Great! No duplicate patterns detected.\n"));
498
658
  }
499
659
  if (patternScore) {
500
- console.log(import_chalk.default.cyan(divider));
501
- console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Patterns)"));
502
- console.log(import_chalk.default.cyan(divider) + "\n");
503
- console.log((0, import_core.formatToolScore)(patternScore));
660
+ console.log(import_chalk3.default.cyan(divider));
661
+ console.log(import_chalk3.default.bold.white(" AI READINESS SCORE (Patterns)"));
662
+ console.log(import_chalk3.default.cyan(divider) + "\n");
663
+ console.log((0, import_core2.formatToolScore)(patternScore));
504
664
  console.log();
505
665
  }
506
666
  }
507
667
  } catch (error) {
508
- (0, import_core.handleCLIError)(error, "Pattern analysis");
668
+ (0, import_core2.handleCLIError)(error, "Pattern analysis");
509
669
  }
510
- });
511
- 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) => {
512
- console.log(import_chalk.default.blue("\u{1F9E0} Analyzing context costs...\n"));
670
+ }
671
+ var patternsHelpText = `
672
+ EXAMPLES:
673
+ $ aiready patterns # Default analysis
674
+ $ aiready patterns --similarity 0.6 # Stricter matching
675
+ $ aiready patterns --min-lines 10 # Larger patterns only
676
+ `;
677
+
678
+ // src/commands/context.ts
679
+ var import_chalk4 = __toESM(require("chalk"));
680
+ var import_path4 = require("path");
681
+ var import_core3 = require("@aiready/core");
682
+ async function contextAction(directory, options) {
683
+ console.log(import_chalk4.default.blue("\u{1F9E0} Analyzing context costs...\n"));
513
684
  const startTime = Date.now();
514
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
685
+ const resolvedDir = (0, import_path4.resolve)(process.cwd(), directory || ".");
515
686
  try {
516
687
  const defaults = {
517
688
  maxDepth: 5,
@@ -523,7 +694,7 @@ program.command("context").description("Analyze context window costs and depende
523
694
  file: void 0
524
695
  }
525
696
  };
526
- let baseOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, {
697
+ let baseOptions = await (0, import_core3.loadMergedConfig)(resolvedDir, defaults, {
527
698
  maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
528
699
  maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
529
700
  include: options.include?.split(","),
@@ -542,7 +713,7 @@ program.command("context").description("Analyze context window costs and depende
542
713
  console.log("");
543
714
  const { analyzeContext: analyzeContext2, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
544
715
  const results = await analyzeContext2(finalOptions);
545
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
716
+ const elapsedTime = (0, import_core3.getElapsedTime)(startTime);
546
717
  const summary = generateSummary(results);
547
718
  let contextScore;
548
719
  if (options.score) {
@@ -556,100 +727,106 @@ program.command("context").description("Analyze context window costs and depende
556
727
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
557
728
  ...contextScore && { scoring: contextScore }
558
729
  };
559
- const outputPath = (0, import_core.resolveOutputPath)(
730
+ const outputPath = (0, import_core3.resolveOutputPath)(
560
731
  userOutputFile,
561
732
  `aiready-report-${getReportTimestamp()}.json`,
562
733
  resolvedDir
563
734
  );
564
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
735
+ (0, import_core3.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
565
736
  } else {
566
737
  const terminalWidth = process.stdout.columns || 80;
567
738
  const dividerWidth = Math.min(60, terminalWidth - 2);
568
739
  const divider = "\u2501".repeat(dividerWidth);
569
- console.log(import_chalk.default.cyan(divider));
570
- console.log(import_chalk.default.bold.white(" CONTEXT ANALYSIS SUMMARY"));
571
- console.log(import_chalk.default.cyan(divider) + "\n");
572
- console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(summary.totalFiles)}`));
573
- console.log(import_chalk.default.white(`\u{1F4CA} Total tokens: ${import_chalk.default.bold(summary.totalTokens.toLocaleString())}`));
574
- console.log(import_chalk.default.yellow(`\u{1F4B0} Avg context budget: ${import_chalk.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
575
- console.log(import_chalk.default.white(`\u23F1 Analysis time: ${import_chalk.default.bold(elapsedTime + "s")}
740
+ console.log(import_chalk4.default.cyan(divider));
741
+ console.log(import_chalk4.default.bold.white(" CONTEXT ANALYSIS SUMMARY"));
742
+ console.log(import_chalk4.default.cyan(divider) + "\n");
743
+ console.log(import_chalk4.default.white(`\u{1F4C1} Files analyzed: ${import_chalk4.default.bold(summary.totalFiles)}`));
744
+ console.log(import_chalk4.default.white(`\u{1F4CA} Total tokens: ${import_chalk4.default.bold(summary.totalTokens.toLocaleString())}`));
745
+ console.log(import_chalk4.default.yellow(`\u{1F4B0} Avg context budget: ${import_chalk4.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
746
+ console.log(import_chalk4.default.white(`\u23F1 Analysis time: ${import_chalk4.default.bold(elapsedTime + "s")}
576
747
  `));
577
748
  const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
578
749
  if (totalIssues > 0) {
579
- console.log(import_chalk.default.bold("\u26A0\uFE0F Issues Found:\n"));
750
+ console.log(import_chalk4.default.bold("\u26A0\uFE0F Issues Found:\n"));
580
751
  if (summary.criticalIssues > 0) {
581
- console.log(import_chalk.default.red(` \u{1F534} Critical: ${import_chalk.default.bold(summary.criticalIssues)}`));
752
+ console.log(import_chalk4.default.red(` \u{1F534} Critical: ${import_chalk4.default.bold(summary.criticalIssues)}`));
582
753
  }
583
754
  if (summary.majorIssues > 0) {
584
- console.log(import_chalk.default.yellow(` \u{1F7E1} Major: ${import_chalk.default.bold(summary.majorIssues)}`));
755
+ console.log(import_chalk4.default.yellow(` \u{1F7E1} Major: ${import_chalk4.default.bold(summary.majorIssues)}`));
585
756
  }
586
757
  if (summary.minorIssues > 0) {
587
- console.log(import_chalk.default.blue(` \u{1F535} Minor: ${import_chalk.default.bold(summary.minorIssues)}`));
758
+ console.log(import_chalk4.default.blue(` \u{1F535} Minor: ${import_chalk4.default.bold(summary.minorIssues)}`));
588
759
  }
589
- console.log(import_chalk.default.green(`
590
- \u{1F4A1} Potential savings: ${import_chalk.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
760
+ console.log(import_chalk4.default.green(`
761
+ \u{1F4A1} Potential savings: ${import_chalk4.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
591
762
  `));
592
763
  } else {
593
- console.log(import_chalk.default.green("\u2705 No significant issues found!\n"));
764
+ console.log(import_chalk4.default.green("\u2705 No significant issues found!\n"));
594
765
  }
595
766
  if (summary.deepFiles.length > 0) {
596
- console.log(import_chalk.default.bold("\u{1F4CF} Deep Import Chains:\n"));
597
- console.log(import_chalk.default.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
598
- console.log(import_chalk.default.gray(` Maximum depth: ${summary.maxImportDepth}
767
+ console.log(import_chalk4.default.bold("\u{1F4CF} Deep Import Chains:\n"));
768
+ console.log(import_chalk4.default.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
769
+ console.log(import_chalk4.default.gray(` Maximum depth: ${summary.maxImportDepth}
599
770
  `));
600
771
  summary.deepFiles.slice(0, 10).forEach((item) => {
601
772
  const fileName = item.file.split("/").slice(-2).join("/");
602
- console.log(` ${import_chalk.default.cyan("\u2192")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(depth: ${item.depth})`)}`);
773
+ console.log(` ${import_chalk4.default.cyan("\u2192")} ${import_chalk4.default.white(fileName)} ${import_chalk4.default.dim(`(depth: ${item.depth})`)}`);
603
774
  });
604
775
  console.log();
605
776
  }
606
777
  if (summary.fragmentedModules.length > 0) {
607
- console.log(import_chalk.default.bold("\u{1F9E9} Fragmented Modules:\n"));
608
- console.log(import_chalk.default.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
778
+ console.log(import_chalk4.default.bold("\u{1F9E9} Fragmented Modules:\n"));
779
+ console.log(import_chalk4.default.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
609
780
  `));
610
781
  summary.fragmentedModules.slice(0, 10).forEach((module2) => {
611
- console.log(` ${import_chalk.default.yellow("\u25CF")} ${import_chalk.default.white(module2.domain)} - ${import_chalk.default.dim(`${module2.files.length} files, ${(module2.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
612
- console.log(import_chalk.default.dim(` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`));
782
+ console.log(` ${import_chalk4.default.yellow("\u25CF")} ${import_chalk4.default.white(module2.domain)} - ${import_chalk4.default.dim(`${module2.files.length} files, ${(module2.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
783
+ console.log(import_chalk4.default.dim(` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`));
613
784
  });
614
785
  console.log();
615
786
  }
616
787
  if (summary.lowCohesionFiles.length > 0) {
617
- console.log(import_chalk.default.bold("\u{1F500} Low Cohesion Files:\n"));
618
- console.log(import_chalk.default.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
788
+ console.log(import_chalk4.default.bold("\u{1F500} Low Cohesion Files:\n"));
789
+ console.log(import_chalk4.default.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
619
790
  `));
620
791
  summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
621
792
  const fileName = item.file.split("/").slice(-2).join("/");
622
793
  const scorePercent = (item.score * 100).toFixed(0);
623
- const color = item.score < 0.4 ? import_chalk.default.red : import_chalk.default.yellow;
624
- console.log(` ${color("\u25CB")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(${scorePercent}% cohesion)`)}`);
794
+ const color = item.score < 0.4 ? import_chalk4.default.red : import_chalk4.default.yellow;
795
+ console.log(` ${color("\u25CB")} ${import_chalk4.default.white(fileName)} ${import_chalk4.default.dim(`(${scorePercent}% cohesion)`)}`);
625
796
  });
626
797
  console.log();
627
798
  }
628
799
  if (summary.topExpensiveFiles.length > 0) {
629
- console.log(import_chalk.default.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
800
+ console.log(import_chalk4.default.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
630
801
  summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
631
802
  const fileName = item.file.split("/").slice(-2).join("/");
632
- const severityColor = item.severity === "critical" ? import_chalk.default.red : item.severity === "major" ? import_chalk.default.yellow : import_chalk.default.blue;
633
- console.log(` ${severityColor("\u25CF")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
803
+ const severityColor = item.severity === "critical" ? import_chalk4.default.red : item.severity === "major" ? import_chalk4.default.yellow : import_chalk4.default.blue;
804
+ console.log(` ${severityColor("\u25CF")} ${import_chalk4.default.white(fileName)} ${import_chalk4.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
634
805
  });
635
806
  console.log();
636
807
  }
637
808
  if (contextScore) {
638
- console.log(import_chalk.default.cyan(divider));
639
- console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Context)"));
640
- console.log(import_chalk.default.cyan(divider) + "\n");
641
- console.log((0, import_core.formatToolScore)(contextScore));
809
+ console.log(import_chalk4.default.cyan(divider));
810
+ console.log(import_chalk4.default.bold.white(" AI READINESS SCORE (Context)"));
811
+ console.log(import_chalk4.default.cyan(divider) + "\n");
812
+ console.log((0, import_core3.formatToolScore)(contextScore));
642
813
  console.log();
643
814
  }
644
815
  }
645
816
  } catch (error) {
646
- (0, import_core.handleCLIError)(error, "Context analysis");
817
+ (0, import_core3.handleCLIError)(error, "Context analysis");
647
818
  }
648
- });
649
- 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) => {
650
- console.log(import_chalk.default.blue("\u{1F50D} Analyzing consistency...\n"));
819
+ }
820
+
821
+ // src/commands/consistency.ts
822
+ var import_chalk5 = __toESM(require("chalk"));
823
+ var import_fs2 = require("fs");
824
+ var import_path5 = require("path");
825
+ var import_core4 = require("@aiready/core");
826
+ async function consistencyAction(directory, options) {
827
+ console.log(import_chalk5.default.blue("\u{1F50D} Analyzing consistency...\n"));
651
828
  const startTime = Date.now();
652
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
829
+ const resolvedDir = (0, import_path5.resolve)(process.cwd(), directory || ".");
653
830
  try {
654
831
  const defaults = {
655
832
  checkNaming: true,
@@ -662,7 +839,7 @@ program.command("consistency").description("Check naming conventions and archite
662
839
  file: void 0
663
840
  }
664
841
  };
665
- const finalOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, {
842
+ const finalOptions = await (0, import_core4.loadMergedConfig)(resolvedDir, defaults, {
666
843
  checkNaming: options.naming !== false,
667
844
  checkPatterns: options.patterns !== false,
668
845
  minSeverity: options.minSeverity,
@@ -671,7 +848,7 @@ program.command("consistency").description("Check naming conventions and archite
671
848
  });
672
849
  const { analyzeConsistency: analyzeConsistency2, calculateConsistencyScore } = await import("@aiready/consistency");
673
850
  const report = await analyzeConsistency2(finalOptions);
674
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
851
+ const elapsedTime = (0, import_core4.getElapsedTime)(startTime);
675
852
  let consistencyScore;
676
853
  if (options.score) {
677
854
  const issues = report.results?.flatMap((r) => r.issues) || [];
@@ -688,32 +865,32 @@ program.command("consistency").description("Check naming conventions and archite
688
865
  },
689
866
  ...consistencyScore && { scoring: consistencyScore }
690
867
  };
691
- const outputPath = (0, import_core.resolveOutputPath)(
868
+ const outputPath = (0, import_core4.resolveOutputPath)(
692
869
  userOutputFile,
693
870
  `aiready-report-${getReportTimestamp()}.json`,
694
871
  resolvedDir
695
872
  );
696
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
873
+ (0, import_core4.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
697
874
  } else if (outputFormat === "markdown") {
698
875
  const markdown = generateMarkdownReport(report, elapsedTime);
699
- const outputPath = (0, import_core.resolveOutputPath)(
876
+ const outputPath = (0, import_core4.resolveOutputPath)(
700
877
  userOutputFile,
701
878
  `aiready-report-${getReportTimestamp()}.md`,
702
879
  resolvedDir
703
880
  );
704
- (0, import_fs.writeFileSync)(outputPath, markdown);
705
- console.log(import_chalk.default.green(`\u2705 Report saved to ${outputPath}`));
881
+ (0, import_fs2.writeFileSync)(outputPath, markdown);
882
+ console.log(import_chalk5.default.green(`\u2705 Report saved to ${outputPath}`));
706
883
  } else {
707
- console.log(import_chalk.default.bold("\n\u{1F4CA} Summary\n"));
708
- console.log(`Files Analyzed: ${import_chalk.default.cyan(report.summary.filesAnalyzed)}`);
709
- console.log(`Total Issues: ${import_chalk.default.yellow(report.summary.totalIssues)}`);
710
- console.log(` Naming: ${import_chalk.default.yellow(report.summary.namingIssues)}`);
711
- console.log(` Patterns: ${import_chalk.default.yellow(report.summary.patternIssues)}`);
712
- console.log(` Architecture: ${import_chalk.default.yellow(report.summary.architectureIssues || 0)}`);
713
- console.log(`Analysis Time: ${import_chalk.default.gray(elapsedTime + "s")}
884
+ console.log(import_chalk5.default.bold("\n\u{1F4CA} Summary\n"));
885
+ console.log(`Files Analyzed: ${import_chalk5.default.cyan(report.summary.filesAnalyzed)}`);
886
+ console.log(`Total Issues: ${import_chalk5.default.yellow(report.summary.totalIssues)}`);
887
+ console.log(` Naming: ${import_chalk5.default.yellow(report.summary.namingIssues)}`);
888
+ console.log(` Patterns: ${import_chalk5.default.yellow(report.summary.patternIssues)}`);
889
+ console.log(` Architecture: ${import_chalk5.default.yellow(report.summary.architectureIssues || 0)}`);
890
+ console.log(`Analysis Time: ${import_chalk5.default.gray(elapsedTime + "s")}
714
891
  `);
715
892
  if (report.summary.totalIssues === 0) {
716
- console.log(import_chalk.default.green("\u2728 No consistency issues found! Your codebase is well-maintained.\n"));
893
+ console.log(import_chalk5.default.green("\u2728 No consistency issues found! Your codebase is well-maintained.\n"));
717
894
  } else {
718
895
  const namingResults = report.results.filter(
719
896
  (r) => r.issues.some((i) => i.category === "naming")
@@ -722,17 +899,17 @@ program.command("consistency").description("Check naming conventions and archite
722
899
  (r) => r.issues.some((i) => i.category === "patterns")
723
900
  );
724
901
  if (namingResults.length > 0) {
725
- console.log(import_chalk.default.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
902
+ console.log(import_chalk5.default.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
726
903
  let shown = 0;
727
904
  for (const result of namingResults) {
728
905
  if (shown >= 5) break;
729
906
  for (const issue of result.issues) {
730
907
  if (shown >= 5) break;
731
- const severityColor = issue.severity === "critical" ? import_chalk.default.red : issue.severity === "major" ? import_chalk.default.yellow : issue.severity === "minor" ? import_chalk.default.blue : import_chalk.default.gray;
732
- console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
908
+ const severityColor = issue.severity === "critical" ? import_chalk5.default.red : issue.severity === "major" ? import_chalk5.default.yellow : issue.severity === "minor" ? import_chalk5.default.blue : import_chalk5.default.gray;
909
+ console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk5.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
733
910
  console.log(` ${issue.message}`);
734
911
  if (issue.suggestion) {
735
- console.log(` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`);
912
+ console.log(` ${import_chalk5.default.dim("\u2192")} ${import_chalk5.default.italic(issue.suggestion)}`);
736
913
  }
737
914
  console.log();
738
915
  shown++;
@@ -740,22 +917,22 @@ program.command("consistency").description("Check naming conventions and archite
740
917
  }
741
918
  const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
742
919
  if (remaining > 0) {
743
- console.log(import_chalk.default.dim(` ... and ${remaining} more issues
920
+ console.log(import_chalk5.default.dim(` ... and ${remaining} more issues
744
921
  `));
745
922
  }
746
923
  }
747
924
  if (patternResults.length > 0) {
748
- console.log(import_chalk.default.bold("\u{1F504} Pattern Issues\n"));
925
+ console.log(import_chalk5.default.bold("\u{1F504} Pattern Issues\n"));
749
926
  let shown = 0;
750
927
  for (const result of patternResults) {
751
928
  if (shown >= 5) break;
752
929
  for (const issue of result.issues) {
753
930
  if (shown >= 5) break;
754
- const severityColor = issue.severity === "critical" ? import_chalk.default.red : issue.severity === "major" ? import_chalk.default.yellow : issue.severity === "minor" ? import_chalk.default.blue : import_chalk.default.gray;
755
- console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
931
+ const severityColor = issue.severity === "critical" ? import_chalk5.default.red : issue.severity === "major" ? import_chalk5.default.yellow : issue.severity === "minor" ? import_chalk5.default.blue : import_chalk5.default.gray;
932
+ console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk5.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
756
933
  console.log(` ${issue.message}`);
757
934
  if (issue.suggestion) {
758
- console.log(` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`);
935
+ console.log(` ${import_chalk5.default.dim("\u2192")} ${import_chalk5.default.italic(issue.suggestion)}`);
759
936
  }
760
937
  console.log();
761
938
  shown++;
@@ -763,12 +940,12 @@ program.command("consistency").description("Check naming conventions and archite
763
940
  }
764
941
  const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
765
942
  if (remaining > 0) {
766
- console.log(import_chalk.default.dim(` ... and ${remaining} more issues
943
+ console.log(import_chalk5.default.dim(` ... and ${remaining} more issues
767
944
  `));
768
945
  }
769
946
  }
770
947
  if (report.recommendations.length > 0) {
771
- console.log(import_chalk.default.bold("\u{1F4A1} Recommendations\n"));
948
+ console.log(import_chalk5.default.bold("\u{1F4A1} Recommendations\n"));
772
949
  report.recommendations.forEach((rec, i) => {
773
950
  console.log(`${i + 1}. ${rec}`);
774
951
  });
@@ -776,288 +953,35 @@ program.command("consistency").description("Check naming conventions and archite
776
953
  }
777
954
  }
778
955
  if (consistencyScore) {
779
- console.log(import_chalk.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
780
- console.log((0, import_core.formatToolScore)(consistencyScore));
956
+ console.log(import_chalk5.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
957
+ console.log((0, import_core4.formatToolScore)(consistencyScore));
781
958
  console.log();
782
959
  }
783
960
  }
784
961
  } catch (error) {
785
- (0, import_core.handleCLIError)(error, "Consistency analysis");
962
+ (0, import_core4.handleCLIError)(error, "Consistency analysis");
786
963
  }
787
- });
788
- function generateMarkdownReport(report, elapsedTime) {
789
- let markdown = `# Consistency Analysis Report
790
-
791
- `;
792
- markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
793
- `;
794
- markdown += `**Analysis Time:** ${elapsedTime}s
795
-
796
- `;
797
- markdown += `## Summary
798
-
799
- `;
800
- markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
801
- `;
802
- markdown += `- **Total Issues:** ${report.summary.totalIssues}
803
- `;
804
- markdown += ` - Naming: ${report.summary.namingIssues}
805
- `;
806
- markdown += ` - Patterns: ${report.summary.patternIssues}
807
-
808
- `;
809
- if (report.recommendations.length > 0) {
810
- markdown += `## Recommendations
811
-
812
- `;
813
- report.recommendations.forEach((rec, i) => {
814
- markdown += `${i + 1}. ${rec}
815
- `;
816
- });
817
- }
818
- return markdown;
819
964
  }
820
- function generateHTML(graph) {
821
- const payload = JSON.stringify(graph, null, 2);
822
- return `<!doctype html>
823
- <html>
824
- <head>
825
- <meta charset="utf-8" />
826
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
827
- <title>AIReady Visualization</title>
828
- <style>
829
- html,body { height: 100%; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0 }
830
- #container { display:flex; height:100vh }
831
- #panel { width: 320px; padding: 16px; background: #071130; box-shadow: -2px 0 8px rgba(0,0,0,0.3); overflow:auto }
832
- #canvasWrap { flex:1; display:flex; align-items:center; justify-content:center }
833
- canvas { background: #0b1220; border-radius:8px }
834
- .stat { margin-bottom:12px }
835
- </style>
836
- </head>
837
- <body>
838
- <div id="container">
839
- <div id="canvasWrap"><canvas id="canvas" width="1200" height="800"></canvas></div>
840
- <div id="panel">
841
- <h2>AIReady Visualization</h2>
842
- <div class="stat"><strong>Files:</strong> <span id="stat-files"></span></div>
843
- <div class="stat"><strong>Dependencies:</strong> <span id="stat-deps"></span></div>
844
- <div class="stat"><strong>Legend</strong></div>
845
- <div style="font-size:13px;line-height:1.3;color:#cbd5e1;margin-top:8px">
846
- <div style="margin-bottom:8px"><span style="display:inline-block;width:12px;height:12px;background:#ff4d4f;margin-right:8px;border:1px solid rgba(255,255,255,0.06)"></span><strong>Critical</strong>: highest severity issues.</div>
847
- <div style="margin-bottom:8px"><span style="display:inline-block;width:12px;height:12px;background:#ff9900;margin-right:8px;border:1px solid rgba(255,255,255,0.06)"></span><strong>Major</strong>: important issues.</div>
848
- <div style="margin-bottom:8px"><span style="display:inline-block;width:12px;height:12px;background:#ffd666;margin-right:8px;border:1px solid rgba(255,255,255,0.06)"></span><strong>Minor</strong>: low priority issues.</div>
849
- <div style="margin-bottom:8px"><span style="display:inline-block;width:12px;height:12px;background:#91d5ff;margin-right:8px;border:1px solid rgba(255,255,255,0.06)"></span><strong>Info</strong>: informational notes.</div>
850
- <div style="margin-top:10px;color:#94a3b8"><strong>Node size</strong>: larger = higher token cost, more issues or dependency weight.</div>
851
- <div style="margin-top:6px;color:#94a3b8"><strong>Proximity</strong>: nodes that are spatially close are more contextually related; relatedness is represented by distance and size rather than explicit edges.</div>
852
- <div style="margin-top:6px;color:#94a3b8"><strong>Edge colors</strong>: <span style="color:#fb7e81">Similarity</span>, <span style="color:#84c1ff">Dependency</span>, <span style="color:#ffa500">Reference</span>, default <span style="color:#334155">Other</span>.</div>
853
- </div>
854
- </div>
855
- </div>
856
-
857
- <script>
858
- const graphData = ${payload};
859
- document.getElementById('stat-files').textContent = graphData.metadata.totalFiles;
860
- document.getElementById('stat-deps').textContent = graphData.metadata.totalDependencies;
861
965
 
862
- const canvas = document.getElementById('canvas');
863
- const ctx = canvas.getContext('2d');
864
-
865
- const nodes = graphData.nodes.map((n, i) => ({
866
- ...n,
867
- x: canvas.width / 2 + Math.cos(i / graphData.nodes.length * Math.PI * 2) * (Math.min(canvas.width, canvas.height) / 3),
868
- y: canvas.height / 2 + Math.sin(i / graphData.nodes.length * Math.PI * 2) * (Math.min(canvas.width, canvas.height) / 3),
869
- }));
870
-
871
- function draw() {
872
- ctx.clearRect(0, 0, canvas.width, canvas.height);
873
-
874
- graphData.edges.forEach(edge => {
875
- const s = nodes.find(n => n.id === edge.source);
876
- const t = nodes.find(n => n.id === edge.target);
877
- if (!s || !t) return;
878
- if (edge.type === 'related') return;
879
- if (edge.type === 'similarity') {
880
- ctx.strokeStyle = '#fb7e81';
881
- ctx.lineWidth = 1.2;
882
- } else if (edge.type === 'dependency') {
883
- ctx.strokeStyle = '#84c1ff';
884
- ctx.lineWidth = 1.0;
885
- } else if (edge.type === 'reference') {
886
- ctx.strokeStyle = '#ffa500';
887
- ctx.lineWidth = 0.9;
888
- } else {
889
- ctx.strokeStyle = '#334155';
890
- ctx.lineWidth = 0.8;
891
- }
892
- ctx.beginPath();
893
- ctx.moveTo(s.x, s.y);
894
- ctx.lineTo(t.x, t.y);
895
- ctx.stroke();
896
- });
897
-
898
- const groups = {};
899
- nodes.forEach(n => {
900
- const g = n.group || '__default';
901
- if (!groups[g]) groups[g] = { minX: n.x, minY: n.y, maxX: n.x, maxY: n.y };
902
- groups[g].minX = Math.min(groups[g].minX, n.x);
903
- groups[g].minY = Math.min(groups[g].minY, n.y);
904
- groups[g].maxX = Math.max(groups[g].maxX, n.x);
905
- groups[g].maxY = Math.max(groups[g].maxY, n.y);
906
- });
907
-
908
- const groupRelations = {};
909
- graphData.edges.forEach(edge => {
910
- const sNode = nodes.find(n => n.id === edge.source);
911
- const tNode = nodes.find(n => n.id === edge.target);
912
- if (!sNode || !tNode) return;
913
- const g1 = sNode.group || '__default';
914
- const g2 = tNode.group || '__default';
915
- if (g1 === g2) return;
916
- const key = g1 < g2 ? g1 + '::' + g2 : g2 + '::' + g1;
917
- groupRelations[key] = (groupRelations[key] || 0) + 1;
918
- });
919
-
920
- Object.keys(groupRelations).forEach(k => {
921
- const count = groupRelations[k];
922
- const [ga, gb] = k.split('::');
923
- if (!groups[ga] || !groups[gb]) return;
924
- const ax = (groups[ga].minX + groups[ga].maxX) / 2;
925
- const ay = (groups[ga].minY + groups[ga].maxY) / 2;
926
- const bx = (groups[gb].minX + groups[gb].maxX) / 2;
927
- const by = (groups[gb].minY + groups[gb].maxY) / 2;
928
- ctx.beginPath();
929
- ctx.strokeStyle = 'rgba(148,163,184,0.25)';
930
- ctx.lineWidth = Math.min(6, 0.6 + Math.sqrt(count));
931
- ctx.moveTo(ax, ay);
932
- ctx.lineTo(bx, by);
933
- ctx.stroke();
934
- });
935
-
936
- Object.keys(groups).forEach(g => {
937
- if (g === '__default') return;
938
- const box = groups[g];
939
- const pad = 16;
940
- const x = box.minX - pad;
941
- const y = box.minY - pad;
942
- const w = (box.maxX - box.minX) + pad * 2;
943
- const h = (box.maxY - box.minY) + pad * 2;
944
- ctx.save();
945
- ctx.fillStyle = 'rgba(30,64,175,0.04)';
946
- ctx.strokeStyle = 'rgba(30,64,175,0.12)';
947
- ctx.lineWidth = 1.2;
948
- const r = 8;
949
- ctx.beginPath();
950
- ctx.moveTo(x + r, y);
951
- ctx.arcTo(x + w, y, x + w, y + h, r);
952
- ctx.arcTo(x + w, y + h, x, y + h, r);
953
- ctx.arcTo(x, y + h, x, y, r);
954
- ctx.arcTo(x, y, x + w, y, r);
955
- ctx.closePath();
956
- ctx.fill();
957
- ctx.stroke();
958
- ctx.restore();
959
- ctx.fillStyle = '#94a3b8';
960
- ctx.font = '11px sans-serif';
961
- ctx.fillText(g, x + 8, y + 14);
962
- });
963
-
964
- nodes.forEach(n => {
965
- const sizeVal = (n.size || n.value || 1);
966
- const r = 6 + (sizeVal / 2);
967
- ctx.beginPath();
968
- ctx.fillStyle = n.color || '#60a5fa';
969
- ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
970
- ctx.fill();
971
-
972
- ctx.fillStyle = '#e2e8f0';
973
- ctx.font = '11px sans-serif';
974
- ctx.textAlign = 'center';
975
- ctx.fillText(n.label || n.id.split('/').slice(-1)[0], n.x, n.y + r + 12);
976
- });
977
- }
978
-
979
- draw();
980
- </script>
981
- </body>
982
- </html>`;
983
- }
984
- function getReportTimestamp() {
985
- const now = /* @__PURE__ */ new Date();
986
- const pad = (n) => String(n).padStart(2, "0");
987
- return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
988
- }
989
- function findLatestScanReport(dirPath) {
990
- const aireadyDir = (0, import_path2.resolve)(dirPath, ".aiready");
991
- if (!(0, import_fs2.existsSync)(aireadyDir)) {
992
- return null;
993
- }
994
- const { readdirSync, statSync } = require("fs");
995
- let files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json"));
996
- if (files.length === 0) {
997
- files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-scan-") && f.endsWith(".json"));
998
- }
999
- if (files.length === 0) {
1000
- return null;
1001
- }
1002
- const sortedFiles = files.map((f) => ({ name: f, path: (0, import_path2.resolve)(aireadyDir, f), mtime: statSync((0, import_path2.resolve)(aireadyDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
1003
- return sortedFiles[0].path;
1004
- }
1005
- function warnIfGraphCapExceeded(report, dirPath) {
1006
- try {
1007
- const { loadConfig } = require("@aiready/core");
1008
- const { existsSync: existsSync2, readFileSync: readFileSync2 } = require("fs");
1009
- const { resolve } = require("path");
1010
- let graphConfig = { maxNodes: 400, maxEdges: 600 };
1011
- const configPath = resolve(dirPath, "aiready.json");
1012
- if (existsSync2(configPath)) {
1013
- try {
1014
- const rawConfig = JSON.parse(readFileSync2(configPath, "utf8"));
1015
- if (rawConfig.visualizer?.graph) {
1016
- graphConfig = {
1017
- maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
1018
- maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
1019
- };
1020
- }
1021
- } catch (e) {
1022
- }
1023
- }
1024
- const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
1025
- const edgeCount = report.context?.reduce((sum, ctx) => {
1026
- const relCount = ctx.relatedFiles?.length || 0;
1027
- const depCount = ctx.dependencies?.length || 0;
1028
- return sum + relCount + depCount;
1029
- }, 0) || 0;
1030
- if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
1031
- console.log("");
1032
- console.log(import_chalk.default.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
1033
- if (nodeCount > graphConfig.maxNodes) {
1034
- console.log(import_chalk.default.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
1035
- }
1036
- if (edgeCount > graphConfig.maxEdges) {
1037
- console.log(import_chalk.default.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
1038
- }
1039
- console.log(import_chalk.default.dim(` To increase limits, add to aiready.json:`));
1040
- console.log(import_chalk.default.dim(` {`));
1041
- console.log(import_chalk.default.dim(` "visualizer": {`));
1042
- console.log(import_chalk.default.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
1043
- console.log(import_chalk.default.dim(` }`));
1044
- console.log(import_chalk.default.dim(` }`));
1045
- }
1046
- } catch (e) {
1047
- }
1048
- }
1049
- async function handleVisualize(directory, options) {
966
+ // src/commands/visualize.ts
967
+ var import_chalk6 = __toESM(require("chalk"));
968
+ var import_fs3 = require("fs");
969
+ var import_path6 = require("path");
970
+ var import_child_process = require("child_process");
971
+ var import_core5 = require("@aiready/core");
972
+ var import_core6 = require("@aiready/core");
973
+ async function visualizeAction(directory, options) {
1050
974
  try {
1051
- const dirPath = (0, import_path2.resolve)(process.cwd(), directory || ".");
1052
- let reportPath = options.report ? (0, import_path2.resolve)(dirPath, options.report) : null;
1053
- if (!reportPath || !(0, import_fs2.existsSync)(reportPath)) {
975
+ const dirPath = (0, import_path6.resolve)(process.cwd(), directory || ".");
976
+ let reportPath = options.report ? (0, import_path6.resolve)(dirPath, options.report) : null;
977
+ if (!reportPath || !(0, import_fs3.existsSync)(reportPath)) {
1054
978
  const latestScan = findLatestScanReport(dirPath);
1055
979
  if (latestScan) {
1056
980
  reportPath = latestScan;
1057
- console.log(import_chalk.default.dim(`Found latest report: ${latestScan.split("/").pop()}`));
981
+ console.log(import_chalk6.default.dim(`Found latest report: ${latestScan.split("/").pop()}`));
1058
982
  } else {
1059
- console.error(import_chalk.default.red("\u274C No AI readiness report found"));
1060
- console.log(import_chalk.default.dim(`
983
+ console.error(import_chalk6.default.red("\u274C No AI readiness report found"));
984
+ console.log(import_chalk6.default.dim(`
1061
985
  Generate a report with:
1062
986
  aiready scan --output json
1063
987
 
@@ -1066,13 +990,13 @@ Or specify a custom report:
1066
990
  return;
1067
991
  }
1068
992
  }
1069
- const raw = (0, import_fs2.readFileSync)(reportPath, "utf8");
993
+ const raw = (0, import_fs3.readFileSync)(reportPath, "utf8");
1070
994
  const report = JSON.parse(raw);
1071
- const configPath = (0, import_path2.resolve)(dirPath, "aiready.json");
995
+ const configPath = (0, import_path6.resolve)(dirPath, "aiready.json");
1072
996
  let graphConfig = { maxNodes: 400, maxEdges: 600 };
1073
- if ((0, import_fs2.existsSync)(configPath)) {
997
+ if ((0, import_fs3.existsSync)(configPath)) {
1074
998
  try {
1075
- const rawConfig = JSON.parse((0, import_fs2.readFileSync)(configPath, "utf8"));
999
+ const rawConfig = JSON.parse((0, import_fs3.readFileSync)(configPath, "utf8"));
1076
1000
  if (rawConfig.visualizer?.graph) {
1077
1001
  graphConfig = {
1078
1002
  maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
@@ -1089,27 +1013,26 @@ Or specify a custom report:
1089
1013
  const graph = GraphBuilder.buildFromReport(report, dirPath);
1090
1014
  if (options.dev) {
1091
1015
  try {
1092
- const { spawn } = await import("child_process");
1093
- const monorepoWebDir = (0, import_path2.resolve)(dirPath, "packages/visualizer");
1016
+ const monorepoWebDir = (0, import_path6.resolve)(dirPath, "packages/visualizer");
1094
1017
  let webDir = "";
1095
1018
  let visualizerAvailable = false;
1096
- if ((0, import_fs2.existsSync)(monorepoWebDir)) {
1019
+ if ((0, import_fs3.existsSync)(monorepoWebDir)) {
1097
1020
  webDir = monorepoWebDir;
1098
1021
  visualizerAvailable = true;
1099
1022
  } else {
1100
1023
  const nodemodulesLocations = [
1101
- (0, import_path2.resolve)(dirPath, "node_modules", "@aiready", "visualizer"),
1102
- (0, import_path2.resolve)(process.cwd(), "node_modules", "@aiready", "visualizer")
1024
+ (0, import_path6.resolve)(dirPath, "node_modules", "@aiready", "visualizer"),
1025
+ (0, import_path6.resolve)(process.cwd(), "node_modules", "@aiready", "visualizer")
1103
1026
  ];
1104
1027
  let currentDir = dirPath;
1105
1028
  while (currentDir !== "/" && currentDir !== ".") {
1106
- nodemodulesLocations.push((0, import_path2.resolve)(currentDir, "node_modules", "@aiready", "visualizer"));
1107
- const parent = (0, import_path2.resolve)(currentDir, "..");
1029
+ nodemodulesLocations.push((0, import_path6.resolve)(currentDir, "node_modules", "@aiready", "visualizer"));
1030
+ const parent = (0, import_path6.resolve)(currentDir, "..");
1108
1031
  if (parent === currentDir) break;
1109
1032
  currentDir = parent;
1110
1033
  }
1111
1034
  for (const location of nodemodulesLocations) {
1112
- if ((0, import_fs2.existsSync)(location) && (0, import_fs2.existsSync)((0, import_path2.resolve)(location, "package.json"))) {
1035
+ if ((0, import_fs3.existsSync)(location) && (0, import_fs3.existsSync)((0, import_path6.resolve)(location, "package.json"))) {
1113
1036
  webDir = location;
1114
1037
  visualizerAvailable = true;
1115
1038
  break;
@@ -1118,7 +1041,7 @@ Or specify a custom report:
1118
1041
  if (!visualizerAvailable) {
1119
1042
  try {
1120
1043
  const vizPkgPath = require.resolve("@aiready/visualizer/package.json");
1121
- webDir = (0, import_path2.resolve)(vizPkgPath, "..");
1044
+ webDir = (0, import_path6.resolve)(vizPkgPath, "..");
1122
1045
  visualizerAvailable = true;
1123
1046
  } catch (e) {
1124
1047
  }
@@ -1126,17 +1049,17 @@ Or specify a custom report:
1126
1049
  }
1127
1050
  const spawnCwd = webDir || process.cwd();
1128
1051
  const nodeBinCandidate = process.execPath;
1129
- const nodeBin = (0, import_fs2.existsSync)(nodeBinCandidate) ? nodeBinCandidate : "node";
1052
+ const nodeBin = (0, import_fs3.existsSync)(nodeBinCandidate) ? nodeBinCandidate : "node";
1130
1053
  if (!visualizerAvailable) {
1131
- console.error(import_chalk.default.red("\u274C Cannot start dev server: @aiready/visualizer not available."));
1132
- console.log(import_chalk.default.dim("Install @aiready/visualizer in your project with:\n npm install @aiready/visualizer"));
1054
+ console.error(import_chalk6.default.red("\u274C Cannot start dev server: @aiready/visualizer not available."));
1055
+ console.log(import_chalk6.default.dim("Install @aiready/visualizer in your project with:\n npm install @aiready/visualizer"));
1133
1056
  return;
1134
1057
  }
1135
1058
  const { watch } = await import("fs");
1136
1059
  const copyReportToViz = () => {
1137
1060
  try {
1138
- const destPath = (0, import_path2.resolve)(spawnCwd, "web", "report-data.json");
1139
- (0, import_fs2.copyFileSync)(reportPath, destPath);
1061
+ const destPath = (0, import_path6.resolve)(spawnCwd, "web", "report-data.json");
1062
+ (0, import_fs3.copyFileSync)(reportPath, destPath);
1140
1063
  console.log(`\u{1F4CB} Report synced to ${destPath}`);
1141
1064
  } catch (e) {
1142
1065
  console.error("Failed to sync report:", e);
@@ -1153,7 +1076,7 @@ Or specify a custom report:
1153
1076
  AIREADY_REPORT_PATH: reportPath,
1154
1077
  AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1155
1078
  };
1156
- const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1079
+ const vite = (0, import_child_process.spawn)("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1157
1080
  const onExit = () => {
1158
1081
  try {
1159
1082
  reportWatcher.close();
@@ -1173,21 +1096,19 @@ Or specify a custom report:
1173
1096
  }
1174
1097
  }
1175
1098
  console.log("Generating HTML...");
1176
- const html = generateHTML(graph);
1177
- const outPath = (0, import_path2.resolve)(dirPath, options.output || "packages/visualizer/visualization.html");
1178
- (0, import_fs.writeFileSync)(outPath, html, "utf8");
1099
+ const html = (0, import_core6.generateHTML)(graph);
1100
+ const outPath = (0, import_path6.resolve)(dirPath, options.output || "packages/visualizer/visualization.html");
1101
+ (0, import_fs3.writeFileSync)(outPath, html, "utf8");
1179
1102
  console.log("Visualization written to:", outPath);
1180
1103
  if (options.open) {
1181
- const { exec } = await import("child_process");
1182
1104
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1183
- exec(`${opener} "${outPath}"`);
1105
+ (0, import_child_process.spawn)(opener, [`"${outPath}"`], { shell: true });
1184
1106
  }
1185
1107
  if (options.serve) {
1186
1108
  try {
1187
- const port = Number(options.serve) || 5173;
1109
+ const port = typeof options.serve === "number" ? options.serve : 5173;
1188
1110
  const http = await import("http");
1189
1111
  const fsp = await import("fs/promises");
1190
- const { exec } = await import("child_process");
1191
1112
  const server = http.createServer(async (req, res) => {
1192
1113
  try {
1193
1114
  const urlPath = req.url || "/";
@@ -1208,7 +1129,7 @@ Or specify a custom report:
1208
1129
  const addr = `http://localhost:${port}/`;
1209
1130
  console.log(`Local visualization server running at ${addr}`);
1210
1131
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1211
- exec(`${opener} "${addr}"`);
1132
+ (0, import_child_process.spawn)(opener, [`"${addr}"`], { shell: true });
1212
1133
  });
1213
1134
  process.on("SIGINT", () => {
1214
1135
  server.close();
@@ -1219,20 +1140,10 @@ Or specify a custom report:
1219
1140
  }
1220
1141
  }
1221
1142
  } catch (err) {
1222
- (0, import_core.handleCLIError)(err, "Visualization");
1143
+ (0, import_core5.handleCLIError)(err, "Visualization");
1223
1144
  }
1224
1145
  }
1225
- 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", `
1226
- EXAMPLES:
1227
- $ aiready visualise . # Auto-detects latest report
1228
- $ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
1229
- $ aiready visualise . --report report.json --dev
1230
- $ aiready visualise . --report report.json --serve 8080
1231
-
1232
- NOTES:
1233
- - Same options as 'visualize'. Use --dev for live reload and --serve to host a static HTML.
1234
- `).action(async (directory, options) => await handleVisualize(directory, options));
1235
- 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", `
1146
+ var visualizeHelpText = `
1236
1147
  EXAMPLES:
1237
1148
  $ aiready visualize . # Auto-detects latest report
1238
1149
  $ aiready visualize . --report .aiready/aiready-report-20260217-143022.json
@@ -1251,5 +1162,73 @@ NOTES:
1251
1162
  reduce clutter and improve interactivity on large graphs.
1252
1163
  - For very large graphs, consider narrowing the input with --include/--exclude or use --serve and
1253
1164
  allow the browser a moment to stabilize after load.
1254
- `).action(async (directory, options) => await handleVisualize(directory, options));
1165
+ `;
1166
+ var visualiseHelpText = `
1167
+ EXAMPLES:
1168
+ $ aiready visualise . # Auto-detects latest report
1169
+ $ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
1170
+ $ aiready visualise . --report report.json --dev
1171
+ $ aiready visualise . --report report.json --serve 8080
1172
+
1173
+ NOTES:
1174
+ - Same options as 'visualize'. Use --dev for live reload and --serve to host a static HTML.
1175
+ `;
1176
+
1177
+ // src/cli.ts
1178
+ var packageJson = JSON.parse((0, import_fs4.readFileSync)((0, import_path7.join)(__dirname, "../package.json"), "utf8"));
1179
+ var program = new import_commander.Command();
1180
+ program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText("after", `
1181
+ AI READINESS SCORING:
1182
+ Get a 0-100 score indicating how AI-ready your codebase is.
1183
+ Use --score flag with any analysis command for detailed breakdown.
1184
+
1185
+ EXAMPLES:
1186
+ $ aiready scan # Quick analysis of current directory
1187
+ $ aiready scan --score # Get AI Readiness Score (0-100)
1188
+ $ aiready scan --tools patterns # Run only pattern detection
1189
+ $ aiready patterns --similarity 0.6 # Custom similarity threshold
1190
+ $ aiready scan --output json --output-file results.json
1191
+
1192
+ GETTING STARTED:
1193
+ 1. Run 'aiready scan' to analyze your codebase
1194
+ 2. Use 'aiready scan --score' for AI readiness assessment
1195
+ 3. Create aiready.json for persistent configuration
1196
+ 4. Set up CI/CD with '--threshold' for quality gates
1197
+
1198
+ CONFIGURATION:
1199
+ Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
1200
+ CLI options override config file settings
1201
+
1202
+ Example aiready.json:
1203
+ {
1204
+ "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
1205
+ "tools": {
1206
+ "pattern-detect": { "minSimilarity": 0.5 },
1207
+ "context-analyzer": { "maxContextBudget": 15000 }
1208
+ },
1209
+ "output": { "format": "json", "directory": ".aiready" }
1210
+ }
1211
+
1212
+ VERSION: ${packageJson.version}
1213
+ DOCUMENTATION: https://aiready.dev/docs/cli
1214
+ GITHUB: https://github.com/caopengau/aiready-cli
1215
+ LANDING: https://github.com/caopengau/aiready-landing`);
1216
+ 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)", "patterns,context,consistency").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 (patterns:40,context:35,consistency:25)").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) => {
1217
+ await scanAction(directory, options);
1218
+ });
1219
+ 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) => {
1220
+ await patternsAction(directory, options);
1221
+ });
1222
+ 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) => {
1223
+ await contextAction(directory, options);
1224
+ });
1225
+ 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) => {
1226
+ await consistencyAction(directory, options);
1227
+ });
1228
+ 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) => {
1229
+ await visualizeAction(directory, options);
1230
+ });
1231
+ 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) => {
1232
+ await visualizeAction(directory, options);
1233
+ });
1255
1234
  program.parse();