@aiready/cli 0.9.27 → 0.9.29

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,71 +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.
128
-
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
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
135
190
 
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
191
+ `;
192
+ markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
193
+ `;
194
+ markdown += `**Analysis Time:** ${elapsedTime}s
141
195
 
142
- CONFIGURATION:
143
- Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
144
- CLI options override config file settings
196
+ `;
197
+ markdown += `## Summary
145
198
 
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" }
154
- }
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}
155
207
 
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)").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", `
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 --ci --threshold 70 # GitHub Actions gatekeeper
166
- $ aiready scan --ci --fail-on major # Fail on major+ issues
167
- $ aiready scan --output json --output-file report.json
208
+ `;
209
+ if (report.recommendations.length > 0) {
210
+ markdown += `## Recommendations
168
211
 
169
- CI/CD INTEGRATION (Gatekeeper Mode):
170
- Use --ci for GitHub Actions integration:
171
- - Outputs GitHub Actions annotations for PR checks
172
- - Fails with exit code 1 if threshold not met
173
- - Shows clear "blocked" message with remediation steps
212
+ `;
213
+ report.recommendations.forEach((rec, i) => {
214
+ markdown += `${i + 1}. ${rec}
215
+ `;
216
+ });
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
+ }
174
226
 
175
- Example GitHub Actions workflow:
176
- - name: AI Readiness Check
177
- run: aiready scan --ci --threshold 70
178
- `).action(async (directory, options) => {
179
- 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"));
180
230
  const startTime = Date.now();
181
231
  const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
182
232
  try {
@@ -200,19 +250,13 @@ CI/CD INTEGRATION (Gatekeeper Mode):
200
250
  const patternSmartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
201
251
  finalOptions = { ...patternSmartDefaults, ...finalOptions, ...baseOptions };
202
252
  }
203
- console.log(import_chalk.default.cyan("\n=== AIReady Run Preview ==="));
204
- console.log(import_chalk.default.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
205
- console.log(import_chalk.default.white("Will use settings from config and defaults."));
206
- const truncate = (arr, cap = 8) => {
207
- if (!Array.isArray(arr)) return "";
208
- const shown = arr.slice(0, cap).map((v) => String(v));
209
- const more = arr.length - shown.length;
210
- return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
211
- };
212
- console.log(import_chalk.default.white("\nGeneral settings:"));
213
- if (finalOptions.rootDir) console.log(` rootDir: ${import_chalk.default.bold(String(finalOptions.rootDir))}`);
214
- if (finalOptions.include) console.log(` include: ${import_chalk.default.bold(truncate(finalOptions.include, 6))}`);
215
- 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))}`);
216
260
  if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
217
261
  const patternDetectConfig = finalOptions["pattern-detect"] || {
218
262
  minSimilarity: finalOptions.minSimilarity,
@@ -225,16 +269,16 @@ CI/CD INTEGRATION (Gatekeeper Mode):
225
269
  severity: finalOptions.severity,
226
270
  includeTests: finalOptions.includeTests
227
271
  };
228
- console.log(import_chalk.default.white("\nPattern-detect settings:"));
229
- console.log(` minSimilarity: ${import_chalk.default.bold(patternDetectConfig.minSimilarity ?? "default")}`);
230
- console.log(` minLines: ${import_chalk.default.bold(patternDetectConfig.minLines ?? "default")}`);
231
- if (patternDetectConfig.approx !== void 0) console.log(` approx: ${import_chalk.default.bold(String(patternDetectConfig.approx))}`);
232
- if (patternDetectConfig.minSharedTokens !== void 0) console.log(` minSharedTokens: ${import_chalk.default.bold(String(patternDetectConfig.minSharedTokens))}`);
233
- if (patternDetectConfig.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${import_chalk.default.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`);
234
- if (patternDetectConfig.batchSize !== void 0) console.log(` batchSize: ${import_chalk.default.bold(String(patternDetectConfig.batchSize))}`);
235
- if (patternDetectConfig.streamResults !== void 0) console.log(` streamResults: ${import_chalk.default.bold(String(patternDetectConfig.streamResults))}`);
236
- if (patternDetectConfig.severity !== void 0) console.log(` severity: ${import_chalk.default.bold(String(patternDetectConfig.severity))}`);
237
- 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))}`);
238
282
  }
239
283
  if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
240
284
  const ca = finalOptions["context-analyzer"] || {
@@ -244,32 +288,32 @@ CI/CD INTEGRATION (Gatekeeper Mode):
244
288
  maxFragmentation: finalOptions.maxFragmentation,
245
289
  includeNodeModules: finalOptions.includeNodeModules
246
290
  };
247
- console.log(import_chalk.default.white("\nContext-analyzer settings:"));
248
- console.log(` maxDepth: ${import_chalk.default.bold(ca.maxDepth ?? "default")}`);
249
- console.log(` maxContextBudget: ${import_chalk.default.bold(ca.maxContextBudget ?? "default")}`);
250
- if (ca.minCohesion !== void 0) console.log(` minCohesion: ${import_chalk.default.bold(String(ca.minCohesion))}`);
251
- if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${import_chalk.default.bold(String(ca.maxFragmentation))}`);
252
- 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))}`);
253
297
  }
254
298
  if (finalOptions.consistency) {
255
299
  const c = finalOptions.consistency;
256
- console.log(import_chalk.default.white("\nConsistency settings:"));
257
- console.log(` checkNaming: ${import_chalk.default.bold(String(c.checkNaming ?? true))}`);
258
- console.log(` checkPatterns: ${import_chalk.default.bold(String(c.checkPatterns ?? true))}`);
259
- console.log(` checkArchitecture: ${import_chalk.default.bold(String(c.checkArchitecture ?? false))}`);
260
- if (c.minSeverity) console.log(` minSeverity: ${import_chalk.default.bold(c.minSeverity)}`);
261
- if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${import_chalk.default.bold(truncate(c.acceptedAbbreviations, 8))}`);
262
- 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))}`);
263
307
  }
264
- console.log(import_chalk.default.white("\nStarting analysis..."));
308
+ console.log(import_chalk2.default.white("\nStarting analysis..."));
265
309
  const progressCallback = (event) => {
266
- console.log(import_chalk.default.cyan(`
310
+ console.log(import_chalk2.default.cyan(`
267
311
  --- ${event.tool.toUpperCase()} RESULTS ---`));
268
312
  try {
269
313
  if (event.tool === "patterns") {
270
314
  const pr = event.data;
271
- console.log(` Duplicate patterns: ${import_chalk.default.bold(String(pr.duplicates?.length || 0))}`);
272
- 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))}`);
273
317
  if (pr.duplicates && pr.duplicates.length > 0) {
274
318
  pr.duplicates.slice(0, 5).forEach((d, i) => {
275
319
  console.log(` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`);
@@ -283,10 +327,10 @@ CI/CD INTEGRATION (Gatekeeper Mode):
283
327
  });
284
328
  }
285
329
  if (pr.groups && pr.groups.length >= 0) {
286
- 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`);
287
331
  }
288
332
  if (pr.clusters && pr.clusters.length >= 0) {
289
- 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`);
290
334
  pr.clusters.slice(0, 3).forEach((cl, idx) => {
291
335
  const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
292
336
  console.log(` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`);
@@ -294,14 +338,14 @@ CI/CD INTEGRATION (Gatekeeper Mode):
294
338
  }
295
339
  } else if (event.tool === "context") {
296
340
  const cr = event.data;
297
- 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))}`);
298
342
  cr.slice(0, 5).forEach((c, i) => {
299
343
  const msg = c.message ? ` - ${c.message}` : "";
300
344
  console.log(` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`);
301
345
  });
302
346
  } else if (event.tool === "consistency") {
303
347
  const rep = event.data;
304
- 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))}`);
305
349
  if (rep.results && rep.results.length > 0) {
306
350
  const fileMap = /* @__PURE__ */ new Map();
307
351
  rep.results.forEach((r) => {
@@ -325,7 +369,7 @@ CI/CD INTEGRATION (Gatekeeper Mode):
325
369
  });
326
370
  const remaining = files.length - topFiles.length;
327
371
  if (remaining > 0) {
328
- 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)`));
329
373
  }
330
374
  }
331
375
  }
@@ -333,15 +377,15 @@ CI/CD INTEGRATION (Gatekeeper Mode):
333
377
  }
334
378
  };
335
379
  const results = await analyzeUnified({ ...finalOptions, progressCallback, suppressToolConfig: true });
336
- console.log(import_chalk.default.cyan("\n=== AIReady Run Summary ==="));
337
- console.log(import_chalk.default.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
338
- console.log(import_chalk.default.cyan("\nResults summary:"));
339
- console.log(` Total issues (all tools): ${import_chalk.default.bold(String(results.summary.totalIssues || 0))}`);
340
- if (results.duplicates) console.log(` Duplicate patterns found: ${import_chalk.default.bold(String(results.duplicates.length || 0))}`);
341
- if (results.patterns) console.log(` Pattern files with issues: ${import_chalk.default.bold(String(results.patterns.length || 0))}`);
342
- if (results.context) console.log(` Context issues: ${import_chalk.default.bold(String(results.context.length || 0))}`);
343
- if (results.consistency) console.log(` Consistency issues: ${import_chalk.default.bold(String(results.consistency.summary.totalIssues || 0))}`);
344
- 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"));
345
389
  const elapsedTime = (0, import_core.getElapsedTime)(startTime);
346
390
  let scoringResult;
347
391
  if (options.score || finalOptions.scoring?.showBreakdown) {
@@ -376,10 +420,10 @@ CI/CD INTEGRATION (Gatekeeper Mode):
376
420
  const cliWeights = (0, import_core.parseWeightString)(options.weights);
377
421
  if (toolScores.size > 0) {
378
422
  scoringResult = (0, import_core.calculateOverallScore)(toolScores, finalOptions, cliWeights.size ? cliWeights : void 0);
379
- 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"));
380
424
  console.log(` ${(0, import_core.formatScore)(scoringResult)}`);
381
425
  if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
382
- console.log(import_chalk.default.bold("\nTool breakdown:"));
426
+ console.log(import_chalk2.default.bold("\nTool breakdown:"));
383
427
  scoringResult.breakdown.forEach((tool) => {
384
428
  const rating = (0, import_core.getRating)(tool.score);
385
429
  const rd = (0, import_core.getRatingDisplay)(rating);
@@ -387,7 +431,7 @@ CI/CD INTEGRATION (Gatekeeper Mode):
387
431
  });
388
432
  console.log();
389
433
  if (finalOptions.scoring?.showBreakdown) {
390
- console.log(import_chalk.default.bold("Detailed tool breakdown:"));
434
+ console.log(import_chalk2.default.bold("Detailed tool breakdown:"));
391
435
  scoringResult.breakdown.forEach((tool) => {
392
436
  console.log((0, import_core.formatToolScore)(tool));
393
437
  });
@@ -476,34 +520,53 @@ CI/CD INTEGRATION (Gatekeeper Mode):
476
520
  }
477
521
  }
478
522
  if (shouldFail) {
479
- console.log(import_chalk.default.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
480
- console.log(import_chalk.default.red(` Reason: ${failReason}`));
481
- console.log(import_chalk.default.dim("\n Remediation steps:"));
482
- console.log(import_chalk.default.dim(" 1. Run `aiready scan` locally to see detailed issues"));
483
- console.log(import_chalk.default.dim(" 2. Fix the critical issues before merging"));
484
- console.log(import_chalk.default.dim(" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"));
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"));
485
529
  process.exit(1);
486
530
  } else {
487
- console.log(import_chalk.default.green("\n\u2705 PR PASSED: AI Readiness Check"));
531
+ console.log(import_chalk2.default.green("\n\u2705 PR PASSED: AI Readiness Check"));
488
532
  if (threshold) {
489
- console.log(import_chalk.default.green(` Score: ${scoringResult.overallScore}/100 (threshold: ${threshold})`));
533
+ console.log(import_chalk2.default.green(` Score: ${scoringResult.overallScore}/100 (threshold: ${threshold})`));
490
534
  }
491
- console.log(import_chalk.default.dim("\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"));
535
+ console.log(import_chalk2.default.dim("\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"));
492
536
  }
493
537
  }
494
538
  } catch (error) {
495
539
  (0, import_core.handleCLIError)(error, "Analysis");
496
540
  }
497
- });
498
- 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 = `
499
543
  EXAMPLES:
500
- $ aiready patterns # Default analysis
501
- $ aiready patterns --similarity 0.6 # Stricter matching
502
- $ aiready patterns --min-lines 10 # Larger patterns only
503
- `).action(async (directory, options) => {
504
- 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"));
505
568
  const startTime = Date.now();
506
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
569
+ const resolvedDir = (0, import_path3.resolve)(process.cwd(), directory || ".");
507
570
  try {
508
571
  const useSmartDefaults = !options.fullScan;
509
572
  const defaults = {
@@ -532,10 +595,10 @@ EXAMPLES:
532
595
  if (options.minSharedTokens) {
533
596
  cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
534
597
  }
535
- const finalOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, cliOptions);
598
+ const finalOptions = await (0, import_core2.loadMergedConfig)(resolvedDir, defaults, cliOptions);
536
599
  const { analyzePatterns: analyzePatterns2, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
537
600
  const { results, duplicates } = await analyzePatterns2(finalOptions);
538
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
601
+ const elapsedTime = (0, import_core2.getElapsedTime)(startTime);
539
602
  const summary = generateSummary(results);
540
603
  let patternScore;
541
604
  if (options.score) {
@@ -549,66 +612,77 @@ EXAMPLES:
549
612
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
550
613
  ...patternScore && { scoring: patternScore }
551
614
  };
552
- const outputPath = (0, import_core.resolveOutputPath)(
615
+ const outputPath = (0, import_core2.resolveOutputPath)(
553
616
  userOutputFile,
554
617
  `aiready-report-${getReportTimestamp()}.json`,
555
618
  resolvedDir
556
619
  );
557
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
620
+ (0, import_core2.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
558
621
  } else {
559
622
  const terminalWidth = process.stdout.columns || 80;
560
623
  const dividerWidth = Math.min(60, terminalWidth - 2);
561
624
  const divider = "\u2501".repeat(dividerWidth);
562
- console.log(import_chalk.default.cyan(divider));
563
- console.log(import_chalk.default.bold.white(" PATTERN ANALYSIS SUMMARY"));
564
- console.log(import_chalk.default.cyan(divider) + "\n");
565
- console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(results.length)}`));
566
- console.log(import_chalk.default.yellow(`\u26A0 Duplicate patterns found: ${import_chalk.default.bold(summary.totalPatterns)}`));
567
- console.log(import_chalk.default.red(`\u{1F4B0} Token cost (wasted): ${import_chalk.default.bold(summary.totalTokenCost.toLocaleString())}`));
568
- 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")}`));
569
632
  const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
570
633
  if (sortedTypes.length > 0) {
571
- console.log(import_chalk.default.cyan("\n" + divider));
572
- console.log(import_chalk.default.bold.white(" PATTERNS BY TYPE"));
573
- 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");
574
637
  sortedTypes.forEach(([type, count]) => {
575
- 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)}`);
576
639
  });
577
640
  }
578
641
  if (summary.totalPatterns > 0 && duplicates.length > 0) {
579
- console.log(import_chalk.default.cyan("\n" + divider));
580
- console.log(import_chalk.default.bold.white(" TOP DUPLICATE PATTERNS"));
581
- 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");
582
645
  const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
583
646
  topDuplicates.forEach((dup) => {
584
647
  const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
585
648
  const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
586
649
  const file1Name = dup.file1.split("/").pop() || dup.file1;
587
650
  const file2Name = dup.file2.split("/").pop() || dup.file2;
588
- console.log(`${severityIcon} ${severity}: ${import_chalk.default.bold(file1Name)} \u2194 ${import_chalk.default.bold(file2Name)}`);
589
- console.log(` Similarity: ${import_chalk.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk.default.bold(dup.tokenCost.toLocaleString())} tokens each`);
590
- 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)}
591
654
  `);
592
655
  });
593
656
  } else {
594
- 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"));
595
658
  }
596
659
  if (patternScore) {
597
- console.log(import_chalk.default.cyan(divider));
598
- console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Patterns)"));
599
- console.log(import_chalk.default.cyan(divider) + "\n");
600
- 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));
601
664
  console.log();
602
665
  }
603
666
  }
604
667
  } catch (error) {
605
- (0, import_core.handleCLIError)(error, "Pattern analysis");
668
+ (0, import_core2.handleCLIError)(error, "Pattern analysis");
606
669
  }
607
- });
608
- 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) => {
609
- 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"));
610
684
  const startTime = Date.now();
611
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
685
+ const resolvedDir = (0, import_path4.resolve)(process.cwd(), directory || ".");
612
686
  try {
613
687
  const defaults = {
614
688
  maxDepth: 5,
@@ -620,7 +694,7 @@ program.command("context").description("Analyze context window costs and depende
620
694
  file: void 0
621
695
  }
622
696
  };
623
- let baseOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, {
697
+ let baseOptions = await (0, import_core3.loadMergedConfig)(resolvedDir, defaults, {
624
698
  maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
625
699
  maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
626
700
  include: options.include?.split(","),
@@ -639,7 +713,7 @@ program.command("context").description("Analyze context window costs and depende
639
713
  console.log("");
640
714
  const { analyzeContext: analyzeContext2, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
641
715
  const results = await analyzeContext2(finalOptions);
642
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
716
+ const elapsedTime = (0, import_core3.getElapsedTime)(startTime);
643
717
  const summary = generateSummary(results);
644
718
  let contextScore;
645
719
  if (options.score) {
@@ -653,100 +727,106 @@ program.command("context").description("Analyze context window costs and depende
653
727
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
654
728
  ...contextScore && { scoring: contextScore }
655
729
  };
656
- const outputPath = (0, import_core.resolveOutputPath)(
730
+ const outputPath = (0, import_core3.resolveOutputPath)(
657
731
  userOutputFile,
658
732
  `aiready-report-${getReportTimestamp()}.json`,
659
733
  resolvedDir
660
734
  );
661
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
735
+ (0, import_core3.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
662
736
  } else {
663
737
  const terminalWidth = process.stdout.columns || 80;
664
738
  const dividerWidth = Math.min(60, terminalWidth - 2);
665
739
  const divider = "\u2501".repeat(dividerWidth);
666
- console.log(import_chalk.default.cyan(divider));
667
- console.log(import_chalk.default.bold.white(" CONTEXT ANALYSIS SUMMARY"));
668
- console.log(import_chalk.default.cyan(divider) + "\n");
669
- console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(summary.totalFiles)}`));
670
- console.log(import_chalk.default.white(`\u{1F4CA} Total tokens: ${import_chalk.default.bold(summary.totalTokens.toLocaleString())}`));
671
- console.log(import_chalk.default.yellow(`\u{1F4B0} Avg context budget: ${import_chalk.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
672
- 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")}
673
747
  `));
674
748
  const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
675
749
  if (totalIssues > 0) {
676
- console.log(import_chalk.default.bold("\u26A0\uFE0F Issues Found:\n"));
750
+ console.log(import_chalk4.default.bold("\u26A0\uFE0F Issues Found:\n"));
677
751
  if (summary.criticalIssues > 0) {
678
- 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)}`));
679
753
  }
680
754
  if (summary.majorIssues > 0) {
681
- 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)}`));
682
756
  }
683
757
  if (summary.minorIssues > 0) {
684
- 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)}`));
685
759
  }
686
- console.log(import_chalk.default.green(`
687
- \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
688
762
  `));
689
763
  } else {
690
- 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"));
691
765
  }
692
766
  if (summary.deepFiles.length > 0) {
693
- console.log(import_chalk.default.bold("\u{1F4CF} Deep Import Chains:\n"));
694
- console.log(import_chalk.default.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
695
- 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}
696
770
  `));
697
771
  summary.deepFiles.slice(0, 10).forEach((item) => {
698
772
  const fileName = item.file.split("/").slice(-2).join("/");
699
- 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})`)}`);
700
774
  });
701
775
  console.log();
702
776
  }
703
777
  if (summary.fragmentedModules.length > 0) {
704
- console.log(import_chalk.default.bold("\u{1F9E9} Fragmented Modules:\n"));
705
- 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)}%
706
780
  `));
707
781
  summary.fragmentedModules.slice(0, 10).forEach((module2) => {
708
- 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`)}`);
709
- 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)}%`));
710
784
  });
711
785
  console.log();
712
786
  }
713
787
  if (summary.lowCohesionFiles.length > 0) {
714
- console.log(import_chalk.default.bold("\u{1F500} Low Cohesion Files:\n"));
715
- 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)}%
716
790
  `));
717
791
  summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
718
792
  const fileName = item.file.split("/").slice(-2).join("/");
719
793
  const scorePercent = (item.score * 100).toFixed(0);
720
- const color = item.score < 0.4 ? import_chalk.default.red : import_chalk.default.yellow;
721
- 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)`)}`);
722
796
  });
723
797
  console.log();
724
798
  }
725
799
  if (summary.topExpensiveFiles.length > 0) {
726
- 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"));
727
801
  summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
728
802
  const fileName = item.file.split("/").slice(-2).join("/");
729
- const severityColor = item.severity === "critical" ? import_chalk.default.red : item.severity === "major" ? import_chalk.default.yellow : import_chalk.default.blue;
730
- 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)`)}`);
731
805
  });
732
806
  console.log();
733
807
  }
734
808
  if (contextScore) {
735
- console.log(import_chalk.default.cyan(divider));
736
- console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Context)"));
737
- console.log(import_chalk.default.cyan(divider) + "\n");
738
- 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));
739
813
  console.log();
740
814
  }
741
815
  }
742
816
  } catch (error) {
743
- (0, import_core.handleCLIError)(error, "Context analysis");
817
+ (0, import_core3.handleCLIError)(error, "Context analysis");
744
818
  }
745
- });
746
- 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) => {
747
- 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"));
748
828
  const startTime = Date.now();
749
- const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
829
+ const resolvedDir = (0, import_path5.resolve)(process.cwd(), directory || ".");
750
830
  try {
751
831
  const defaults = {
752
832
  checkNaming: true,
@@ -759,7 +839,7 @@ program.command("consistency").description("Check naming conventions and archite
759
839
  file: void 0
760
840
  }
761
841
  };
762
- const finalOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, {
842
+ const finalOptions = await (0, import_core4.loadMergedConfig)(resolvedDir, defaults, {
763
843
  checkNaming: options.naming !== false,
764
844
  checkPatterns: options.patterns !== false,
765
845
  minSeverity: options.minSeverity,
@@ -768,7 +848,7 @@ program.command("consistency").description("Check naming conventions and archite
768
848
  });
769
849
  const { analyzeConsistency: analyzeConsistency2, calculateConsistencyScore } = await import("@aiready/consistency");
770
850
  const report = await analyzeConsistency2(finalOptions);
771
- const elapsedTime = (0, import_core.getElapsedTime)(startTime);
851
+ const elapsedTime = (0, import_core4.getElapsedTime)(startTime);
772
852
  let consistencyScore;
773
853
  if (options.score) {
774
854
  const issues = report.results?.flatMap((r) => r.issues) || [];
@@ -785,32 +865,32 @@ program.command("consistency").description("Check naming conventions and archite
785
865
  },
786
866
  ...consistencyScore && { scoring: consistencyScore }
787
867
  };
788
- const outputPath = (0, import_core.resolveOutputPath)(
868
+ const outputPath = (0, import_core4.resolveOutputPath)(
789
869
  userOutputFile,
790
870
  `aiready-report-${getReportTimestamp()}.json`,
791
871
  resolvedDir
792
872
  );
793
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
873
+ (0, import_core4.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
794
874
  } else if (outputFormat === "markdown") {
795
875
  const markdown = generateMarkdownReport(report, elapsedTime);
796
- const outputPath = (0, import_core.resolveOutputPath)(
876
+ const outputPath = (0, import_core4.resolveOutputPath)(
797
877
  userOutputFile,
798
878
  `aiready-report-${getReportTimestamp()}.md`,
799
879
  resolvedDir
800
880
  );
801
- (0, import_fs.writeFileSync)(outputPath, markdown);
802
- 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}`));
803
883
  } else {
804
- console.log(import_chalk.default.bold("\n\u{1F4CA} Summary\n"));
805
- console.log(`Files Analyzed: ${import_chalk.default.cyan(report.summary.filesAnalyzed)}`);
806
- console.log(`Total Issues: ${import_chalk.default.yellow(report.summary.totalIssues)}`);
807
- console.log(` Naming: ${import_chalk.default.yellow(report.summary.namingIssues)}`);
808
- console.log(` Patterns: ${import_chalk.default.yellow(report.summary.patternIssues)}`);
809
- console.log(` Architecture: ${import_chalk.default.yellow(report.summary.architectureIssues || 0)}`);
810
- 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")}
811
891
  `);
812
892
  if (report.summary.totalIssues === 0) {
813
- 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"));
814
894
  } else {
815
895
  const namingResults = report.results.filter(
816
896
  (r) => r.issues.some((i) => i.category === "naming")
@@ -819,17 +899,17 @@ program.command("consistency").description("Check naming conventions and archite
819
899
  (r) => r.issues.some((i) => i.category === "patterns")
820
900
  );
821
901
  if (namingResults.length > 0) {
822
- 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"));
823
903
  let shown = 0;
824
904
  for (const result of namingResults) {
825
905
  if (shown >= 5) break;
826
906
  for (const issue of result.issues) {
827
907
  if (shown >= 5) break;
828
- 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;
829
- 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}`)}`);
830
910
  console.log(` ${issue.message}`);
831
911
  if (issue.suggestion) {
832
- 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)}`);
833
913
  }
834
914
  console.log();
835
915
  shown++;
@@ -837,22 +917,22 @@ program.command("consistency").description("Check naming conventions and archite
837
917
  }
838
918
  const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
839
919
  if (remaining > 0) {
840
- console.log(import_chalk.default.dim(` ... and ${remaining} more issues
920
+ console.log(import_chalk5.default.dim(` ... and ${remaining} more issues
841
921
  `));
842
922
  }
843
923
  }
844
924
  if (patternResults.length > 0) {
845
- console.log(import_chalk.default.bold("\u{1F504} Pattern Issues\n"));
925
+ console.log(import_chalk5.default.bold("\u{1F504} Pattern Issues\n"));
846
926
  let shown = 0;
847
927
  for (const result of patternResults) {
848
928
  if (shown >= 5) break;
849
929
  for (const issue of result.issues) {
850
930
  if (shown >= 5) break;
851
- 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;
852
- 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}`)}`);
853
933
  console.log(` ${issue.message}`);
854
934
  if (issue.suggestion) {
855
- 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)}`);
856
936
  }
857
937
  console.log();
858
938
  shown++;
@@ -860,12 +940,12 @@ program.command("consistency").description("Check naming conventions and archite
860
940
  }
861
941
  const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
862
942
  if (remaining > 0) {
863
- console.log(import_chalk.default.dim(` ... and ${remaining} more issues
943
+ console.log(import_chalk5.default.dim(` ... and ${remaining} more issues
864
944
  `));
865
945
  }
866
946
  }
867
947
  if (report.recommendations.length > 0) {
868
- console.log(import_chalk.default.bold("\u{1F4A1} Recommendations\n"));
948
+ console.log(import_chalk5.default.bold("\u{1F4A1} Recommendations\n"));
869
949
  report.recommendations.forEach((rec, i) => {
870
950
  console.log(`${i + 1}. ${rec}`);
871
951
  });
@@ -873,124 +953,35 @@ program.command("consistency").description("Check naming conventions and archite
873
953
  }
874
954
  }
875
955
  if (consistencyScore) {
876
- console.log(import_chalk.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
877
- 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));
878
958
  console.log();
879
959
  }
880
960
  }
881
961
  } catch (error) {
882
- (0, import_core.handleCLIError)(error, "Consistency analysis");
883
- }
884
- });
885
- function generateMarkdownReport(report, elapsedTime) {
886
- let markdown = `# Consistency Analysis Report
887
-
888
- `;
889
- markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
890
- `;
891
- markdown += `**Analysis Time:** ${elapsedTime}s
892
-
893
- `;
894
- markdown += `## Summary
895
-
896
- `;
897
- markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
898
- `;
899
- markdown += `- **Total Issues:** ${report.summary.totalIssues}
900
- `;
901
- markdown += ` - Naming: ${report.summary.namingIssues}
902
- `;
903
- markdown += ` - Patterns: ${report.summary.patternIssues}
904
-
905
- `;
906
- if (report.recommendations.length > 0) {
907
- markdown += `## Recommendations
908
-
909
- `;
910
- report.recommendations.forEach((rec, i) => {
911
- markdown += `${i + 1}. ${rec}
912
- `;
913
- });
914
- }
915
- return markdown;
916
- }
917
- function getReportTimestamp() {
918
- const now = /* @__PURE__ */ new Date();
919
- const pad = (n) => String(n).padStart(2, "0");
920
- return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
921
- }
922
- function findLatestScanReport(dirPath) {
923
- const aireadyDir = (0, import_path2.resolve)(dirPath, ".aiready");
924
- if (!(0, import_fs2.existsSync)(aireadyDir)) {
925
- return null;
926
- }
927
- const { readdirSync, statSync } = require("fs");
928
- let files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json"));
929
- if (files.length === 0) {
930
- files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-scan-") && f.endsWith(".json"));
931
- }
932
- if (files.length === 0) {
933
- return null;
934
- }
935
- 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());
936
- return sortedFiles[0].path;
937
- }
938
- function warnIfGraphCapExceeded(report, dirPath) {
939
- try {
940
- const { loadConfig } = require("@aiready/core");
941
- const { existsSync: existsSync2, readFileSync: readFileSync2 } = require("fs");
942
- const { resolve } = require("path");
943
- let graphConfig = { maxNodes: 400, maxEdges: 600 };
944
- const configPath = resolve(dirPath, "aiready.json");
945
- if (existsSync2(configPath)) {
946
- try {
947
- const rawConfig = JSON.parse(readFileSync2(configPath, "utf8"));
948
- if (rawConfig.visualizer?.graph) {
949
- graphConfig = {
950
- maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
951
- maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
952
- };
953
- }
954
- } catch (e) {
955
- }
956
- }
957
- const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
958
- const edgeCount = report.context?.reduce((sum, ctx) => {
959
- const relCount = ctx.relatedFiles?.length || 0;
960
- const depCount = ctx.dependencies?.length || 0;
961
- return sum + relCount + depCount;
962
- }, 0) || 0;
963
- if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
964
- console.log("");
965
- console.log(import_chalk.default.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
966
- if (nodeCount > graphConfig.maxNodes) {
967
- console.log(import_chalk.default.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
968
- }
969
- if (edgeCount > graphConfig.maxEdges) {
970
- console.log(import_chalk.default.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
971
- }
972
- console.log(import_chalk.default.dim(` To increase limits, add to aiready.json:`));
973
- console.log(import_chalk.default.dim(` {`));
974
- console.log(import_chalk.default.dim(` "visualizer": {`));
975
- console.log(import_chalk.default.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
976
- console.log(import_chalk.default.dim(` }`));
977
- console.log(import_chalk.default.dim(` }`));
978
- }
979
- } catch (e) {
962
+ (0, import_core4.handleCLIError)(error, "Consistency analysis");
980
963
  }
981
964
  }
982
- async function handleVisualize(directory, options) {
965
+
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) {
983
974
  try {
984
- const dirPath = (0, import_path2.resolve)(process.cwd(), directory || ".");
985
- let reportPath = options.report ? (0, import_path2.resolve)(dirPath, options.report) : null;
986
- 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)) {
987
978
  const latestScan = findLatestScanReport(dirPath);
988
979
  if (latestScan) {
989
980
  reportPath = latestScan;
990
- 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()}`));
991
982
  } else {
992
- console.error(import_chalk.default.red("\u274C No AI readiness report found"));
993
- 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(`
994
985
  Generate a report with:
995
986
  aiready scan --output json
996
987
 
@@ -999,13 +990,13 @@ Or specify a custom report:
999
990
  return;
1000
991
  }
1001
992
  }
1002
- const raw = (0, import_fs2.readFileSync)(reportPath, "utf8");
993
+ const raw = (0, import_fs3.readFileSync)(reportPath, "utf8");
1003
994
  const report = JSON.parse(raw);
1004
- const configPath = (0, import_path2.resolve)(dirPath, "aiready.json");
995
+ const configPath = (0, import_path6.resolve)(dirPath, "aiready.json");
1005
996
  let graphConfig = { maxNodes: 400, maxEdges: 600 };
1006
- if ((0, import_fs2.existsSync)(configPath)) {
997
+ if ((0, import_fs3.existsSync)(configPath)) {
1007
998
  try {
1008
- const rawConfig = JSON.parse((0, import_fs2.readFileSync)(configPath, "utf8"));
999
+ const rawConfig = JSON.parse((0, import_fs3.readFileSync)(configPath, "utf8"));
1009
1000
  if (rawConfig.visualizer?.graph) {
1010
1001
  graphConfig = {
1011
1002
  maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
@@ -1020,29 +1011,30 @@ Or specify a custom report:
1020
1011
  console.log("Building graph from report...");
1021
1012
  const { GraphBuilder } = await import("@aiready/visualizer/graph");
1022
1013
  const graph = GraphBuilder.buildFromReport(report, dirPath);
1023
- if (options.dev) {
1014
+ let useDevMode = options.dev || false;
1015
+ let devServerStarted = false;
1016
+ if (useDevMode) {
1024
1017
  try {
1025
- const { spawn } = await import("child_process");
1026
- const monorepoWebDir = (0, import_path2.resolve)(dirPath, "packages/visualizer");
1018
+ const monorepoWebDir = (0, import_path6.resolve)(dirPath, "packages/visualizer");
1027
1019
  let webDir = "";
1028
1020
  let visualizerAvailable = false;
1029
- if ((0, import_fs2.existsSync)(monorepoWebDir)) {
1021
+ if ((0, import_fs3.existsSync)(monorepoWebDir)) {
1030
1022
  webDir = monorepoWebDir;
1031
1023
  visualizerAvailable = true;
1032
1024
  } else {
1033
1025
  const nodemodulesLocations = [
1034
- (0, import_path2.resolve)(dirPath, "node_modules", "@aiready", "visualizer"),
1035
- (0, import_path2.resolve)(process.cwd(), "node_modules", "@aiready", "visualizer")
1026
+ (0, import_path6.resolve)(dirPath, "node_modules", "@aiready", "visualizer"),
1027
+ (0, import_path6.resolve)(process.cwd(), "node_modules", "@aiready", "visualizer")
1036
1028
  ];
1037
1029
  let currentDir = dirPath;
1038
1030
  while (currentDir !== "/" && currentDir !== ".") {
1039
- nodemodulesLocations.push((0, import_path2.resolve)(currentDir, "node_modules", "@aiready", "visualizer"));
1040
- const parent = (0, import_path2.resolve)(currentDir, "..");
1031
+ nodemodulesLocations.push((0, import_path6.resolve)(currentDir, "node_modules", "@aiready", "visualizer"));
1032
+ const parent = (0, import_path6.resolve)(currentDir, "..");
1041
1033
  if (parent === currentDir) break;
1042
1034
  currentDir = parent;
1043
1035
  }
1044
1036
  for (const location of nodemodulesLocations) {
1045
- if ((0, import_fs2.existsSync)(location) && (0, import_fs2.existsSync)((0, import_path2.resolve)(location, "package.json"))) {
1037
+ if ((0, import_fs3.existsSync)(location) && (0, import_fs3.existsSync)((0, import_path6.resolve)(location, "package.json"))) {
1046
1038
  webDir = location;
1047
1039
  visualizerAvailable = true;
1048
1040
  break;
@@ -1051,123 +1043,115 @@ Or specify a custom report:
1051
1043
  if (!visualizerAvailable) {
1052
1044
  try {
1053
1045
  const vizPkgPath = require.resolve("@aiready/visualizer/package.json");
1054
- webDir = (0, import_path2.resolve)(vizPkgPath, "..");
1046
+ webDir = (0, import_path6.resolve)(vizPkgPath, "..");
1055
1047
  visualizerAvailable = true;
1056
1048
  } catch (e) {
1057
1049
  }
1058
1050
  }
1059
1051
  }
1060
- const spawnCwd = webDir || process.cwd();
1061
- const nodeBinCandidate = process.execPath;
1062
- const nodeBin = (0, import_fs2.existsSync)(nodeBinCandidate) ? nodeBinCandidate : "node";
1063
- if (!visualizerAvailable) {
1064
- console.error(import_chalk.default.red("\u274C Cannot start dev server: @aiready/visualizer not available."));
1065
- console.log(import_chalk.default.dim("Install @aiready/visualizer in your project with:\n npm install @aiready/visualizer"));
1052
+ const webViteConfigExists = webDir && (0, import_fs3.existsSync)((0, import_path6.resolve)(webDir, "web", "vite.config.ts"));
1053
+ if (visualizerAvailable && webViteConfigExists) {
1054
+ const spawnCwd = webDir;
1055
+ const { watch } = await import("fs");
1056
+ const copyReportToViz = () => {
1057
+ try {
1058
+ const destPath = (0, import_path6.resolve)(spawnCwd, "web", "report-data.json");
1059
+ (0, import_fs3.copyFileSync)(reportPath, destPath);
1060
+ console.log(`\u{1F4CB} Report synced to ${destPath}`);
1061
+ } catch (e) {
1062
+ console.error("Failed to sync report:", e);
1063
+ }
1064
+ };
1065
+ copyReportToViz();
1066
+ let watchTimeout = null;
1067
+ const reportWatcher = watch(reportPath, () => {
1068
+ if (watchTimeout) clearTimeout(watchTimeout);
1069
+ watchTimeout = setTimeout(copyReportToViz, 100);
1070
+ });
1071
+ const envForSpawn = {
1072
+ ...process.env,
1073
+ AIREADY_REPORT_PATH: reportPath,
1074
+ AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1075
+ };
1076
+ const vite = (0, import_child_process.spawn)("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1077
+ const onExit = () => {
1078
+ try {
1079
+ reportWatcher.close();
1080
+ } catch (e) {
1081
+ }
1082
+ try {
1083
+ vite.kill();
1084
+ } catch (e) {
1085
+ }
1086
+ process.exit(0);
1087
+ };
1088
+ process.on("SIGINT", onExit);
1089
+ process.on("SIGTERM", onExit);
1090
+ devServerStarted = true;
1066
1091
  return;
1092
+ } else {
1093
+ console.log(import_chalk6.default.yellow("\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."));
1094
+ console.log(import_chalk6.default.cyan(" Falling back to static HTML generation...\n"));
1095
+ useDevMode = false;
1067
1096
  }
1068
- const { watch } = await import("fs");
1069
- const copyReportToViz = () => {
1070
- try {
1071
- const destPath = (0, import_path2.resolve)(spawnCwd, "web", "report-data.json");
1072
- (0, import_fs2.copyFileSync)(reportPath, destPath);
1073
- console.log(`\u{1F4CB} Report synced to ${destPath}`);
1074
- } catch (e) {
1075
- console.error("Failed to sync report:", e);
1076
- }
1077
- };
1078
- copyReportToViz();
1079
- let watchTimeout = null;
1080
- const reportWatcher = watch(reportPath, () => {
1081
- if (watchTimeout) clearTimeout(watchTimeout);
1082
- watchTimeout = setTimeout(copyReportToViz, 100);
1083
- });
1084
- const envForSpawn = {
1085
- ...process.env,
1086
- AIREADY_REPORT_PATH: reportPath,
1087
- AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1088
- };
1089
- const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1090
- const onExit = () => {
1091
- try {
1092
- reportWatcher.close();
1093
- } catch (e) {
1094
- }
1095
- try {
1096
- vite.kill();
1097
- } catch (e) {
1098
- }
1099
- process.exit(0);
1100
- };
1101
- process.on("SIGINT", onExit);
1102
- process.on("SIGTERM", onExit);
1103
- return;
1104
1097
  } catch (err) {
1105
1098
  console.error("Failed to start dev server:", err);
1099
+ console.log(import_chalk6.default.cyan(" Falling back to static HTML generation...\n"));
1100
+ useDevMode = false;
1106
1101
  }
1107
1102
  }
1108
1103
  console.log("Generating HTML...");
1109
- const html = (0, import_core.generateHTML)(graph);
1110
- const outPath = (0, import_path2.resolve)(dirPath, options.output || "packages/visualizer/visualization.html");
1111
- (0, import_fs.writeFileSync)(outPath, html, "utf8");
1112
- console.log("Visualization written to:", outPath);
1113
- if (options.open) {
1114
- const { exec } = await import("child_process");
1104
+ const html = (0, import_core6.generateHTML)(graph);
1105
+ const defaultOutput = "visualization.html";
1106
+ const outPath = (0, import_path6.resolve)(dirPath, options.output || defaultOutput);
1107
+ (0, import_fs3.writeFileSync)(outPath, html, "utf8");
1108
+ console.log(import_chalk6.default.green(`\u2705 Visualization written to: ${outPath}`));
1109
+ if (options.open || options.serve) {
1115
1110
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1116
- exec(`${opener} "${outPath}"`);
1117
- }
1118
- if (options.serve) {
1119
- try {
1120
- const port = typeof options.serve === "number" ? options.serve : 5173;
1121
- const http = await import("http");
1122
- const fsp = await import("fs/promises");
1123
- const { exec } = await import("child_process");
1124
- const server = http.createServer(async (req, res) => {
1125
- try {
1126
- const urlPath = req.url || "/";
1127
- if (urlPath === "/" || urlPath === "/index.html") {
1128
- const content = await fsp.readFile(outPath, "utf8");
1129
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1130
- res.end(content);
1131
- return;
1111
+ if (options.serve) {
1112
+ try {
1113
+ const port = typeof options.serve === "number" ? options.serve : 5173;
1114
+ const http = await import("http");
1115
+ const fsp = await import("fs/promises");
1116
+ const server = http.createServer(async (req, res) => {
1117
+ try {
1118
+ const urlPath = req.url || "/";
1119
+ if (urlPath === "/" || urlPath === "/index.html") {
1120
+ const content = await fsp.readFile(outPath, "utf8");
1121
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1122
+ res.end(content);
1123
+ return;
1124
+ }
1125
+ res.writeHead(404, { "Content-Type": "text/plain" });
1126
+ res.end("Not found");
1127
+ } catch (e) {
1128
+ res.writeHead(500, { "Content-Type": "text/plain" });
1129
+ res.end("Server error");
1132
1130
  }
1133
- res.writeHead(404, { "Content-Type": "text/plain" });
1134
- res.end("Not found");
1135
- } catch (e) {
1136
- res.writeHead(500, { "Content-Type": "text/plain" });
1137
- res.end("Server error");
1138
- }
1139
- });
1140
- server.listen(port, () => {
1141
- const addr = `http://localhost:${port}/`;
1142
- console.log(`Local visualization server running at ${addr}`);
1143
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1144
- exec(`${opener} "${addr}"`);
1145
- });
1146
- process.on("SIGINT", () => {
1147
- server.close();
1148
- process.exit(0);
1149
- });
1150
- } catch (err) {
1151
- console.error("Failed to start local server:", err);
1131
+ });
1132
+ server.listen(port, () => {
1133
+ const addr = `http://localhost:${port}/`;
1134
+ console.log(import_chalk6.default.cyan(`\u{1F310} Local visualization server running at ${addr}`));
1135
+ (0, import_child_process.spawn)(opener, [`"${addr}"`], { shell: true });
1136
+ });
1137
+ process.on("SIGINT", () => {
1138
+ server.close();
1139
+ process.exit(0);
1140
+ });
1141
+ } catch (err) {
1142
+ console.error("Failed to start local server:", err);
1143
+ }
1144
+ } else if (options.open) {
1145
+ (0, import_child_process.spawn)(opener, [`"${outPath}"`], { shell: true });
1152
1146
  }
1153
1147
  }
1154
1148
  } catch (err) {
1155
- (0, import_core.handleCLIError)(err, "Visualization");
1149
+ (0, import_core5.handleCLIError)(err, "Visualization");
1156
1150
  }
1157
1151
  }
1158
- 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", `
1152
+ var visualizeHelpText = `
1159
1153
  EXAMPLES:
1160
- $ aiready visualise . # Auto-detects latest report
1161
- $ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
1162
- $ aiready visualise . --report report.json --dev
1163
- $ aiready visualise . --report report.json --serve 8080
1164
-
1165
- NOTES:
1166
- - Same options as 'visualize'. Use --dev for live reload and --serve to host a static HTML.
1167
- `).action(async (directory, options) => await handleVisualize(directory, options));
1168
- 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", `
1169
- EXAMPLES:
1170
- $ aiready visualize . # Auto-detects latest report
1154
+ $ aiready visualize . # Auto-detects latest report, generates HTML
1171
1155
  $ aiready visualize . --report .aiready/aiready-report-20260217-143022.json
1172
1156
  $ aiready visualize . --report report.json -o out/visualization.html --open
1173
1157
  $ aiready visualize . --report report.json --serve
@@ -1177,12 +1161,76 @@ EXAMPLES:
1177
1161
  NOTES:
1178
1162
  - The value passed to --report is interpreted relative to the directory argument (first positional).
1179
1163
  If the report is not found, the CLI will suggest running 'aiready scan' to generate it.
1180
- - Default output path: packages/visualizer/visualization.html (relative to the directory argument).
1164
+ - Default output path: visualization.html (in the current directory).
1181
1165
  - --serve starts a tiny single-file HTTP server (default port: 5173) and opens your browser.
1182
- It serves only the generated HTML (no additional asset folders).
1183
- - Relatedness is represented by node proximity and size; explicit 'related' edges are not drawn to
1184
- reduce clutter and improve interactivity on large graphs.
1185
- - For very large graphs, consider narrowing the input with --include/--exclude or use --serve and
1186
- allow the browser a moment to stabilize after load.
1187
- `).action(async (directory, options) => await handleVisualize(directory, options));
1166
+ - --dev starts a Vite dev server with live reload (requires local @aiready/visualizer installation).
1167
+ When --dev is not available, it falls back to static HTML generation.
1168
+ `;
1169
+ var visualiseHelpText = `
1170
+ EXAMPLES:
1171
+ $ aiready visualise . # Auto-detects latest report
1172
+ $ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
1173
+ $ aiready visualise . --report report.json --serve 8080
1174
+
1175
+ NOTES:
1176
+ - Same options as 'visualize'. Use --serve to host the static HTML, or --dev for live reload.
1177
+ `;
1178
+
1179
+ // src/cli.ts
1180
+ var packageJson = JSON.parse((0, import_fs4.readFileSync)((0, import_path7.join)(__dirname, "../package.json"), "utf8"));
1181
+ var program = new import_commander.Command();
1182
+ program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText("after", `
1183
+ AI READINESS SCORING:
1184
+ Get a 0-100 score indicating how AI-ready your codebase is.
1185
+ Use --score flag with any analysis command for detailed breakdown.
1186
+
1187
+ EXAMPLES:
1188
+ $ aiready scan # Quick analysis of current directory
1189
+ $ aiready scan --score # Get AI Readiness Score (0-100)
1190
+ $ aiready scan --tools patterns # Run only pattern detection
1191
+ $ aiready patterns --similarity 0.6 # Custom similarity threshold
1192
+ $ aiready scan --output json --output-file results.json
1193
+
1194
+ GETTING STARTED:
1195
+ 1. Run 'aiready scan' to analyze your codebase
1196
+ 2. Use 'aiready scan --score' for AI readiness assessment
1197
+ 3. Create aiready.json for persistent configuration
1198
+ 4. Set up CI/CD with '--threshold' for quality gates
1199
+
1200
+ CONFIGURATION:
1201
+ Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
1202
+ CLI options override config file settings
1203
+
1204
+ Example aiready.json:
1205
+ {
1206
+ "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
1207
+ "tools": {
1208
+ "pattern-detect": { "minSimilarity": 0.5 },
1209
+ "context-analyzer": { "maxContextBudget": 15000 }
1210
+ },
1211
+ "output": { "format": "json", "directory": ".aiready" }
1212
+ }
1213
+
1214
+ VERSION: ${packageJson.version}
1215
+ DOCUMENTATION: https://aiready.dev/docs/cli
1216
+ GITHUB: https://github.com/caopengau/aiready-cli
1217
+ LANDING: https://github.com/caopengau/aiready-landing`);
1218
+ 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) => {
1219
+ await scanAction(directory, options);
1220
+ });
1221
+ 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) => {
1222
+ await patternsAction(directory, options);
1223
+ });
1224
+ 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) => {
1225
+ await contextAction(directory, options);
1226
+ });
1227
+ 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) => {
1228
+ await consistencyAction(directory, options);
1229
+ });
1230
+ 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) => {
1231
+ await visualizeAction(directory, options);
1232
+ });
1233
+ 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) => {
1234
+ await visualizeAction(directory, options);
1235
+ });
1188
1236
  program.parse();