@aiready/cli 0.7.19 → 0.7.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- analyzeUnified,
4
- generateUnifiedSummary
5
- } from "./chunk-P3XAXCTK.mjs";
3
+ analyzeUnified
4
+ } from "./chunk-3SG2GLFJ.mjs";
6
5
 
7
6
  // src/cli.ts
8
7
  import { Command } from "commander";
@@ -16,15 +15,58 @@ import {
16
15
  getElapsedTime,
17
16
  resolveOutputPath,
18
17
  calculateOverallScore,
18
+ formatScore,
19
19
  formatToolScore,
20
+ getRating,
20
21
  getRatingDisplay,
21
22
  parseWeightString
22
23
  } from "@aiready/core";
23
24
  import { readFileSync } from "fs";
24
25
  var packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
25
26
  var program = new Command();
26
- program.name("aiready").description("AIReady - Unified AI-readiness analysis tools").version(packageJson.version).addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings");
27
- program.command("scan").description("Run unified analysis on a codebase").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", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--weights <weights>", 'Override tool weights for scoring (e.g., "patterns:50,context:30,consistency:20")').option("--threshold <score>", "Minimum passing score for CI/CD (exits with code 1 if below)").action(async (directory, options) => {
27
+ program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText("after", `
28
+ AI READINESS SCORING:
29
+ Get a 0-100 score indicating how AI-ready your codebase is.
30
+ Use --score flag with any analysis command for detailed breakdown.
31
+
32
+ EXAMPLES:
33
+ $ aiready scan # Quick analysis of current directory
34
+ $ aiready scan --score # Get AI Readiness Score (0-100)
35
+ $ aiready scan --tools patterns # Run only pattern detection
36
+ $ aiready patterns --similarity 0.6 # Custom similarity threshold
37
+ $ aiready scan --output json --output-file results.json
38
+
39
+ GETTING STARTED:
40
+ 1. Run 'aiready scan' to analyze your codebase
41
+ 2. Use 'aiready scan --score' for AI readiness assessment
42
+ 3. Create aiready.json for persistent configuration
43
+ 4. Set up CI/CD with '--threshold' for quality gates
44
+
45
+ CONFIGURATION:
46
+ Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
47
+ CLI options override config file settings
48
+
49
+ Example aiready.json:
50
+ {
51
+ "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
52
+ "tools": {
53
+ "pattern-detect": { "minSimilarity": 0.5 },
54
+ "context-analyzer": { "maxContextBudget": 15000 }
55
+ },
56
+ "output": { "format": "json", "directory": ".aiready" }
57
+ }
58
+
59
+ VERSION: ${packageJson.version}
60
+ DOCUMENTATION: https://aiready.dev/docs/cli
61
+ GITHUB: https://github.com/caopengau/aiready-cli
62
+ LANDING: https://github.com/caopengau/aiready-landing`);
63
+ 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", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100) with breakdown").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", `
64
+ EXAMPLES:
65
+ $ aiready scan # Analyze all tools
66
+ $ aiready scan --tools patterns,context # Skip consistency
67
+ $ aiready scan --score --threshold 75 # CI/CD with threshold
68
+ $ aiready scan --output json --output-file report.json
69
+ `).action(async (directory, options) => {
28
70
  console.log(chalk.blue("\u{1F680} Starting AIReady unified analysis...\n"));
29
71
  const startTime = Date.now();
30
72
  try {
@@ -46,103 +88,210 @@ program.command("scan").description("Run unified analysis on a codebase").argume
46
88
  if (baseOptions.tools.includes("patterns")) {
47
89
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
48
90
  const patternSmartDefaults = await getSmartDefaults(directory, baseOptions);
49
- finalOptions = { ...patternSmartDefaults, ...finalOptions };
91
+ finalOptions = { ...patternSmartDefaults, ...finalOptions, ...baseOptions };
92
+ }
93
+ console.log(chalk.cyan("\n=== AIReady Run Preview ==="));
94
+ console.log(chalk.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
95
+ console.log(chalk.white("Will use settings from config and defaults."));
96
+ const truncate = (arr, cap = 8) => {
97
+ if (!Array.isArray(arr)) return "";
98
+ const shown = arr.slice(0, cap).map((v) => String(v));
99
+ const more = arr.length - shown.length;
100
+ return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
101
+ };
102
+ console.log(chalk.white("\nGeneral settings:"));
103
+ if (finalOptions.rootDir) console.log(` rootDir: ${chalk.bold(String(finalOptions.rootDir))}`);
104
+ if (finalOptions.include) console.log(` include: ${chalk.bold(truncate(finalOptions.include, 6))}`);
105
+ if (finalOptions.exclude) console.log(` exclude: ${chalk.bold(truncate(finalOptions.exclude, 6))}`);
106
+ if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
107
+ const pd = finalOptions["pattern-detect"] || {
108
+ minSimilarity: finalOptions.minSimilarity,
109
+ minLines: finalOptions.minLines,
110
+ approx: finalOptions.approx,
111
+ minSharedTokens: finalOptions.minSharedTokens,
112
+ maxCandidatesPerBlock: finalOptions.maxCandidatesPerBlock,
113
+ batchSize: finalOptions.batchSize,
114
+ streamResults: finalOptions.streamResults,
115
+ severity: finalOptions.severity,
116
+ includeTests: finalOptions.includeTests
117
+ };
118
+ console.log(chalk.white("\nPattern-detect settings:"));
119
+ console.log(` minSimilarity: ${chalk.bold(pd.minSimilarity ?? "default")}`);
120
+ console.log(` minLines: ${chalk.bold(pd.minLines ?? "default")}`);
121
+ if (pd.approx !== void 0) console.log(` approx: ${chalk.bold(String(pd.approx))}`);
122
+ if (pd.minSharedTokens !== void 0) console.log(` minSharedTokens: ${chalk.bold(String(pd.minSharedTokens))}`);
123
+ if (pd.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${chalk.bold(String(pd.maxCandidatesPerBlock))}`);
124
+ if (pd.batchSize !== void 0) console.log(` batchSize: ${chalk.bold(String(pd.batchSize))}`);
125
+ if (pd.streamResults !== void 0) console.log(` streamResults: ${chalk.bold(String(pd.streamResults))}`);
126
+ if (pd.severity !== void 0) console.log(` severity: ${chalk.bold(String(pd.severity))}`);
127
+ if (pd.includeTests !== void 0) console.log(` includeTests: ${chalk.bold(String(pd.includeTests))}`);
128
+ }
129
+ if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
130
+ const ca = finalOptions["context-analyzer"] || {
131
+ maxDepth: finalOptions.maxDepth,
132
+ maxContextBudget: finalOptions.maxContextBudget,
133
+ minCohesion: finalOptions.minCohesion,
134
+ maxFragmentation: finalOptions.maxFragmentation,
135
+ includeNodeModules: finalOptions.includeNodeModules
136
+ };
137
+ console.log(chalk.white("\nContext-analyzer settings:"));
138
+ console.log(` maxDepth: ${chalk.bold(ca.maxDepth ?? "default")}`);
139
+ console.log(` maxContextBudget: ${chalk.bold(ca.maxContextBudget ?? "default")}`);
140
+ if (ca.minCohesion !== void 0) console.log(` minCohesion: ${chalk.bold(String(ca.minCohesion))}`);
141
+ if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${chalk.bold(String(ca.maxFragmentation))}`);
142
+ if (ca.includeNodeModules !== void 0) console.log(` includeNodeModules: ${chalk.bold(String(ca.includeNodeModules))}`);
143
+ }
144
+ if (finalOptions.consistency) {
145
+ const c = finalOptions.consistency;
146
+ console.log(chalk.white("\nConsistency settings:"));
147
+ console.log(` checkNaming: ${chalk.bold(String(c.checkNaming ?? true))}`);
148
+ console.log(` checkPatterns: ${chalk.bold(String(c.checkPatterns ?? true))}`);
149
+ console.log(` checkArchitecture: ${chalk.bold(String(c.checkArchitecture ?? false))}`);
150
+ if (c.minSeverity) console.log(` minSeverity: ${chalk.bold(c.minSeverity)}`);
151
+ if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${chalk.bold(truncate(c.acceptedAbbreviations, 8))}`);
152
+ if (c.shortWords) console.log(` shortWords: ${chalk.bold(truncate(c.shortWords, 8))}`);
50
153
  }
51
- const results = await analyzeUnified(finalOptions);
154
+ console.log(chalk.white("\nStarting analysis..."));
155
+ const progressCallback = (event) => {
156
+ console.log(chalk.cyan(`
157
+ --- ${event.tool.toUpperCase()} RESULTS ---`));
158
+ try {
159
+ if (event.tool === "patterns") {
160
+ const pr = event.data;
161
+ console.log(` Duplicate patterns: ${chalk.bold(String(pr.duplicates?.length || 0))}`);
162
+ console.log(` Files with pattern issues: ${chalk.bold(String(pr.results?.length || 0))}`);
163
+ if (pr.duplicates && pr.duplicates.length > 0) {
164
+ pr.duplicates.slice(0, 5).forEach((d, i) => {
165
+ console.log(` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`);
166
+ });
167
+ }
168
+ if (pr.results && pr.results.length > 0) {
169
+ console.log(` Top files with pattern issues:`);
170
+ const sortedByIssues = [...pr.results].sort((a, b) => (b.issues?.length || 0) - (a.issues?.length || 0));
171
+ sortedByIssues.slice(0, 5).forEach((r, i) => {
172
+ console.log(` ${i + 1}. ${r.fileName.split("/").pop()} - ${r.issues.length} issue(s)`);
173
+ });
174
+ }
175
+ if (pr.groups && pr.groups.length >= 0) {
176
+ console.log(` \u2705 Grouped ${chalk.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk.bold(String(pr.groups.length))} file pairs`);
177
+ }
178
+ if (pr.clusters && pr.clusters.length >= 0) {
179
+ console.log(` \u2705 Created ${chalk.bold(String(pr.clusters.length))} refactor clusters`);
180
+ pr.clusters.slice(0, 3).forEach((cl, idx) => {
181
+ const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
182
+ console.log(` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`);
183
+ });
184
+ }
185
+ } else if (event.tool === "context") {
186
+ const cr = event.data;
187
+ console.log(` Context issues found: ${chalk.bold(String(cr.length || 0))}`);
188
+ cr.slice(0, 5).forEach((c, i) => {
189
+ const msg = c.message ? ` - ${c.message}` : "";
190
+ console.log(` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`);
191
+ });
192
+ } else if (event.tool === "consistency") {
193
+ const rep = event.data;
194
+ console.log(` Consistency totalIssues: ${chalk.bold(String(rep.summary?.totalIssues || 0))}`);
195
+ if (rep.results && rep.results.length > 0) {
196
+ const fileMap = /* @__PURE__ */ new Map();
197
+ rep.results.forEach((r) => {
198
+ (r.issues || []).forEach((issue) => {
199
+ const file = issue.location?.file || r.file || "unknown";
200
+ if (!fileMap.has(file)) fileMap.set(file, []);
201
+ fileMap.get(file).push(issue);
202
+ });
203
+ });
204
+ const files = Array.from(fileMap.entries()).sort((a, b) => b[1].length - a[1].length);
205
+ const topFiles = files.slice(0, 10);
206
+ topFiles.forEach(([file, issues], idx) => {
207
+ const counts = issues.reduce((acc, it) => {
208
+ const s = (it.severity || "info").toLowerCase();
209
+ acc[s] = (acc[s] || 0) + 1;
210
+ return acc;
211
+ }, {});
212
+ const sample = issues.find((it) => it.severity === "critical" || it.severity === "major") || issues[0];
213
+ const sampleMsg = sample ? ` \u2014 ${sample.message}` : "";
214
+ console.log(` ${idx + 1}. ${file} \u2014 ${issues.length} issue(s) (critical:${counts.critical || 0} major:${counts.major || 0} minor:${counts.minor || 0} info:${counts.info || 0})${sampleMsg}`);
215
+ });
216
+ const remaining = files.length - topFiles.length;
217
+ if (remaining > 0) {
218
+ console.log(chalk.dim(` ... and ${remaining} more files with issues (use --output json for full details)`));
219
+ }
220
+ }
221
+ }
222
+ } catch (err) {
223
+ }
224
+ };
225
+ const results = await analyzeUnified({ ...finalOptions, progressCallback, suppressToolConfig: true });
226
+ console.log(chalk.cyan("\n=== AIReady Run Summary ==="));
227
+ console.log(chalk.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
228
+ console.log(chalk.cyan("\nResults summary:"));
229
+ console.log(` Total issues (all tools): ${chalk.bold(String(results.summary.totalIssues || 0))}`);
230
+ if (results.duplicates) console.log(` Duplicate patterns found: ${chalk.bold(String(results.duplicates.length || 0))}`);
231
+ if (results.patterns) console.log(` Pattern files with issues: ${chalk.bold(String(results.patterns.length || 0))}`);
232
+ if (results.context) console.log(` Context issues: ${chalk.bold(String(results.context.length || 0))}`);
233
+ if (results.consistency) console.log(` Consistency issues: ${chalk.bold(String(results.consistency.summary.totalIssues || 0))}`);
234
+ console.log(chalk.cyan("===========================\n"));
52
235
  const elapsedTime = getElapsedTime(startTime);
53
236
  let scoringResult;
54
237
  if (options.score || finalOptions.scoring?.showBreakdown) {
55
238
  const toolScores = /* @__PURE__ */ new Map();
56
- if (results.patterns && baseOptions.tools.includes("patterns")) {
239
+ if (results.duplicates) {
57
240
  const { calculatePatternScore } = await import("@aiready/pattern-detect");
58
- const duplicates = results.duplicates || [];
59
- const score = calculatePatternScore(duplicates, results.patterns.length);
60
- toolScores.set("pattern-detect", score);
241
+ try {
242
+ const patternScore = calculatePatternScore(results.duplicates, results.patterns?.length || 0);
243
+ toolScores.set("pattern-detect", patternScore);
244
+ } catch (err) {
245
+ }
61
246
  }
62
- if (results.context && baseOptions.tools.includes("context")) {
63
- const { calculateContextScore } = await import("@aiready/context-analyzer");
64
- const summary = {
65
- avgContextBudget: results.context.reduce((sum, r) => sum + r.contextBudget, 0) / results.context.length,
66
- maxContextBudget: Math.max(...results.context.map((r) => r.contextBudget)),
67
- avgImportDepth: results.context.reduce((sum, r) => sum + r.importDepth, 0) / results.context.length,
68
- maxImportDepth: Math.max(...results.context.map((r) => r.importDepth)),
69
- avgFragmentation: results.context.reduce((sum, r) => sum + r.fragmentationScore, 0) / results.context.length,
70
- criticalIssues: results.context.filter((r) => r.severity === "critical").length,
71
- majorIssues: results.context.filter((r) => r.severity === "major").length
72
- };
73
- const score = calculateContextScore(summary);
74
- toolScores.set("context-analyzer", score);
247
+ if (results.context) {
248
+ const { generateSummary: genContextSummary, calculateContextScore } = await import("@aiready/context-analyzer");
249
+ try {
250
+ const ctxSummary = genContextSummary(results.context);
251
+ const contextScore = calculateContextScore(ctxSummary);
252
+ toolScores.set("context-analyzer", contextScore);
253
+ } catch (err) {
254
+ }
75
255
  }
76
- if (results.consistency && baseOptions.tools.includes("consistency")) {
256
+ if (results.consistency) {
77
257
  const { calculateConsistencyScore } = await import("@aiready/consistency");
78
- const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
79
- const score = calculateConsistencyScore(issues, results.consistency.summary.filesAnalyzed);
80
- toolScores.set("consistency", score);
81
- }
82
- const cliWeights = options.weights ? parseWeightString(options.weights) : void 0;
83
- scoringResult = calculateOverallScore(toolScores, finalOptions, cliWeights);
84
- if (options.threshold) {
85
- const threshold = parseFloat(options.threshold);
86
- if (scoringResult.overall < threshold) {
87
- console.error(chalk.red(`
88
- \u274C Score ${scoringResult.overall} is below threshold ${threshold}
89
- `));
90
- process.exit(1);
258
+ try {
259
+ const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
260
+ const totalFiles = results.consistency.summary?.filesAnalyzed || 0;
261
+ const consistencyScore = calculateConsistencyScore(issues, totalFiles);
262
+ toolScores.set("consistency", consistencyScore);
263
+ } catch (err) {
91
264
  }
92
265
  }
93
- }
94
- const outputFormat = options.output || finalOptions.output?.format || "console";
95
- const userOutputFile = options.outputFile || finalOptions.output?.file;
96
- if (outputFormat === "json") {
97
- const outputData = {
98
- ...results,
99
- summary: {
100
- ...results.summary,
101
- executionTime: parseFloat(elapsedTime)
102
- },
103
- ...scoringResult && { scoring: scoringResult }
104
- };
105
- const outputPath = resolveOutputPath(
106
- userOutputFile,
107
- `aiready-scan-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
108
- directory
109
- );
110
- handleJSONOutput(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
111
- } else {
112
- console.log(generateUnifiedSummary(results));
113
- if (scoringResult) {
114
- const terminalWidth = process.stdout.columns || 80;
115
- const dividerWidth = Math.min(60, terminalWidth - 2);
116
- const divider = "\u2501".repeat(dividerWidth);
117
- console.log(chalk.cyan("\n" + divider));
118
- console.log(chalk.bold.white(" AI READINESS SCORE"));
119
- console.log(chalk.cyan(divider) + "\n");
120
- const { emoji, color } = getRatingDisplay(scoringResult.rating);
121
- const scoreColor = color === "green" ? chalk.green : color === "blue" ? chalk.blue : color === "yellow" ? chalk.yellow : chalk.red;
122
- console.log(` ${emoji} Overall Score: ${scoreColor.bold(scoringResult.overall + "/100")} (${chalk.bold(scoringResult.rating)})`);
123
- console.log(` ${chalk.dim("Timestamp:")} ${new Date(scoringResult.timestamp).toLocaleString()}
124
- `);
125
- if (scoringResult.breakdown.length > 0) {
126
- console.log(chalk.bold(" Component Scores:\n"));
266
+ const cliWeights = parseWeightString(options.weights);
267
+ if (toolScores.size > 0) {
268
+ scoringResult = calculateOverallScore(toolScores, finalOptions, cliWeights.size ? cliWeights : void 0);
269
+ console.log(chalk.bold("\n\u{1F4CA} AI Readiness Overall Score"));
270
+ console.log(` ${formatScore(scoringResult)}`);
271
+ if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
272
+ console.log(chalk.bold("\nTool breakdown:"));
127
273
  scoringResult.breakdown.forEach((tool) => {
128
- const toolEmoji = tool.toolName === "pattern-detect" ? "\u{1F50D}" : tool.toolName === "context-analyzer" ? "\u{1F9E0}" : "\u{1F3F7}\uFE0F";
129
- const weight = scoringResult.calculation.weights[tool.toolName];
130
- console.log(` ${toolEmoji} ${chalk.white(tool.toolName.padEnd(20))} ${scoreColor(tool.score + "/100")} ${chalk.dim(`(weight: ${weight})`)}`);
274
+ const rating = getRating(tool.score);
275
+ const rd = getRatingDisplay(rating);
276
+ console.log(` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`);
131
277
  });
132
278
  console.log();
279
+ if (finalOptions.scoring?.showBreakdown) {
280
+ console.log(chalk.bold("Detailed tool breakdown:"));
281
+ scoringResult.breakdown.forEach((tool) => {
282
+ console.log(formatToolScore(tool));
283
+ });
284
+ console.log();
285
+ }
133
286
  }
134
- console.log(chalk.dim(` Weighted Formula: ${scoringResult.calculation.formula}`));
135
- console.log(chalk.dim(` Normalized Score: ${scoringResult.calculation.normalized}
136
- `));
137
- const allRecommendations = scoringResult.breakdown.flatMap((tool) => tool.recommendations || []).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
138
- if (allRecommendations.length > 0) {
139
- console.log(chalk.bold(" Top Recommendations:\n"));
140
- allRecommendations.forEach((rec, i) => {
141
- const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
142
- console.log(` ${i + 1}. ${priorityIcon} ${rec.action}`);
143
- console.log(` ${chalk.dim(`Impact: +${rec.estimatedImpact} points`)}
144
- `);
145
- });
287
+ const outputFormat = options.output || finalOptions.output?.format || "console";
288
+ const userOutputFile = options.outputFile || finalOptions.output?.file;
289
+ if (outputFormat === "json") {
290
+ const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
291
+ const defaultFilename = `aiready-scan-${dateStr}.json`;
292
+ const outputPath = resolveOutputPath(userOutputFile, defaultFilename, directory);
293
+ const outputData = { ...results, scoring: scoringResult };
294
+ handleJSONOutput(outputData, outputPath, `\u2705 Summary saved to ${outputPath}`);
146
295
  }
147
296
  }
148
297
  }
@@ -150,7 +299,12 @@ program.command("scan").description("Run unified analysis on a codebase").argume
150
299
  handleCLIError(error, "Analysis");
151
300
  }
152
301
  });
153
- program.command("patterns").description("Run pattern detection analysis").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)").action(async (directory, options) => {
302
+ 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", `
303
+ EXAMPLES:
304
+ $ aiready patterns # Default analysis
305
+ $ aiready patterns --similarity 0.6 # Stricter matching
306
+ $ aiready patterns --min-lines 10 # Larger patterns only
307
+ `).action(async (directory, options) => {
154
308
  console.log(chalk.blue("\u{1F50D} Analyzing patterns...\n"));
155
309
  const startTime = Date.now();
156
310
  try {
@@ -254,7 +408,7 @@ program.command("patterns").description("Run pattern detection analysis").argume
254
408
  handleCLIError(error, "Pattern analysis");
255
409
  }
256
410
  });
257
- program.command("context").description("Run context window cost analysis").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) => {
411
+ 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) => {
258
412
  console.log(chalk.blue("\u{1F9E0} Analyzing context costs...\n"));
259
413
  const startTime = Date.now();
260
414
  try {
@@ -391,7 +545,7 @@ program.command("context").description("Run context window cost analysis").argum
391
545
  handleCLIError(error, "Context analysis");
392
546
  }
393
547
  });
394
- program.command("consistency").description("Check naming, patterns, and architecture 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) => {
548
+ 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) => {
395
549
  console.log(chalk.blue("\u{1F50D} Analyzing consistency...\n"));
396
550
  const startTime = Date.now();
397
551
  try {
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ScanOptions, AnalysisResult } from '@aiready/core';
2
2
  import { ContextAnalysisResult } from '@aiready/context-analyzer';
3
3
  import { DuplicatePattern } from '@aiready/pattern-detect';
4
- import { ConsistencyReport } from '@aiready/consistency';
4
+ import { ConsistencyOptions, ConsistencyReport } from '@aiready/consistency';
5
5
 
6
6
  interface UnifiedAnalysisOptions extends ScanOptions {
7
7
  tools?: ('patterns' | 'context' | 'consistency')[];
@@ -10,6 +10,11 @@ interface UnifiedAnalysisOptions extends ScanOptions {
10
10
  maxCandidatesPerBlock?: number;
11
11
  minSharedTokens?: number;
12
12
  useSmartDefaults?: boolean;
13
+ consistency?: Partial<ConsistencyOptions>;
14
+ progressCallback?: (event: {
15
+ tool: string;
16
+ data: any;
17
+ }) => void;
13
18
  }
14
19
  interface UnifiedAnalysisResult {
15
20
  patterns?: AnalysisResult[];
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ScanOptions, AnalysisResult } from '@aiready/core';
2
2
  import { ContextAnalysisResult } from '@aiready/context-analyzer';
3
3
  import { DuplicatePattern } from '@aiready/pattern-detect';
4
- import { ConsistencyReport } from '@aiready/consistency';
4
+ import { ConsistencyOptions, ConsistencyReport } from '@aiready/consistency';
5
5
 
6
6
  interface UnifiedAnalysisOptions extends ScanOptions {
7
7
  tools?: ('patterns' | 'context' | 'consistency')[];
@@ -10,6 +10,11 @@ interface UnifiedAnalysisOptions extends ScanOptions {
10
10
  maxCandidatesPerBlock?: number;
11
11
  minSharedTokens?: number;
12
12
  useSmartDefaults?: boolean;
13
+ consistency?: Partial<ConsistencyOptions>;
14
+ progressCallback?: (event: {
15
+ tool: string;
16
+ data: any;
17
+ }) => void;
13
18
  }
14
19
  interface UnifiedAnalysisResult {
15
20
  patterns?: AnalysisResult[];
package/dist/index.js CHANGED
@@ -65,6 +65,9 @@ async function analyzeUnified(options) {
65
65
  };
66
66
  if (tools.includes("patterns")) {
67
67
  const patternResult = await (0, import_pattern_detect.analyzePatterns)(options);
68
+ if (options.progressCallback) {
69
+ options.progressCallback({ tool: "patterns", data: patternResult });
70
+ }
68
71
  result.patterns = sortBySeverity(patternResult.results);
69
72
  result.duplicates = patternResult.duplicates;
70
73
  result.summary.totalIssues += patternResult.results.reduce(
@@ -74,6 +77,9 @@ async function analyzeUnified(options) {
74
77
  }
75
78
  if (tools.includes("context")) {
76
79
  const contextResults = await (0, import_context_analyzer.analyzeContext)(options);
80
+ if (options.progressCallback) {
81
+ options.progressCallback({ tool: "context", data: contextResults });
82
+ }
77
83
  result.context = contextResults.sort((a, b) => {
78
84
  const severityDiff = (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
79
85
  if (severityDiff !== 0) return severityDiff;
@@ -83,14 +89,16 @@ async function analyzeUnified(options) {
83
89
  result.summary.totalIssues += result.context?.length || 0;
84
90
  }
85
91
  if (tools.includes("consistency")) {
86
- const report = await (0, import_consistency.analyzeConsistency)({
92
+ const consistencyOptions = {
87
93
  rootDir: options.rootDir,
88
94
  include: options.include,
89
95
  exclude: options.exclude,
90
- checkNaming: true,
91
- checkPatterns: true,
92
- minSeverity: "info"
93
- });
96
+ ...options.consistency || {}
97
+ };
98
+ const report = await (0, import_consistency.analyzeConsistency)(consistencyOptions);
99
+ if (options.progressCallback) {
100
+ options.progressCallback({ tool: "consistency", data: report });
101
+ }
94
102
  if (report.results) {
95
103
  report.results = sortBySeverity(report.results);
96
104
  }
@@ -114,11 +122,11 @@ function generateUnifiedSummary(result) {
114
122
  output += ` Execution time: ${(summary.executionTime / 1e3).toFixed(2)}s
115
123
 
116
124
  `;
117
- if (result.patterns?.length) {
125
+ if (result.patterns) {
118
126
  output += `\u{1F50D} Pattern Analysis: ${result.patterns.length} issues
119
127
  `;
120
128
  }
121
- if (result.context?.length) {
129
+ if (result.context) {
122
130
  output += `\u{1F9E0} Context Analysis: ${result.context.length} issues
123
131
  `;
124
132
  }
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  analyzeUnified,
3
3
  generateUnifiedSummary
4
- } from "./chunk-P3XAXCTK.mjs";
4
+ } from "./chunk-3SG2GLFJ.mjs";
5
5
  export {
6
6
  analyzeUnified,
7
7
  generateUnifiedSummary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.7.19",
3
+ "version": "0.7.21",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,10 +11,10 @@
11
11
  "dependencies": {
12
12
  "commander": "^14.0.0",
13
13
  "chalk": "^5.3.0",
14
- "@aiready/pattern-detect": "0.9.21",
15
- "@aiready/core": "0.7.12",
16
- "@aiready/context-analyzer": "0.7.17",
17
- "@aiready/consistency": "0.6.15"
14
+ "@aiready/pattern-detect": "0.9.23",
15
+ "@aiready/core": "0.7.14",
16
+ "@aiready/context-analyzer": "0.7.19",
17
+ "@aiready/consistency": "0.6.17"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsup": "^8.3.5",