@aiready/cli 0.7.18 → 0.7.20

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
@@ -68,6 +68,9 @@ async function analyzeUnified(options) {
68
68
  };
69
69
  if (tools.includes("patterns")) {
70
70
  const patternResult = await (0, import_pattern_detect.analyzePatterns)(options);
71
+ if (options.progressCallback) {
72
+ options.progressCallback({ tool: "patterns", data: patternResult });
73
+ }
71
74
  result.patterns = sortBySeverity(patternResult.results);
72
75
  result.duplicates = patternResult.duplicates;
73
76
  result.summary.totalIssues += patternResult.results.reduce(
@@ -77,6 +80,9 @@ async function analyzeUnified(options) {
77
80
  }
78
81
  if (tools.includes("context")) {
79
82
  const contextResults = await (0, import_context_analyzer.analyzeContext)(options);
83
+ if (options.progressCallback) {
84
+ options.progressCallback({ tool: "context", data: contextResults });
85
+ }
80
86
  result.context = contextResults.sort((a, b) => {
81
87
  const severityDiff = (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
82
88
  if (severityDiff !== 0) return severityDiff;
@@ -86,14 +92,16 @@ async function analyzeUnified(options) {
86
92
  result.summary.totalIssues += result.context?.length || 0;
87
93
  }
88
94
  if (tools.includes("consistency")) {
89
- const report = await (0, import_consistency.analyzeConsistency)({
95
+ const consistencyOptions = {
90
96
  rootDir: options.rootDir,
91
97
  include: options.include,
92
98
  exclude: options.exclude,
93
- checkNaming: true,
94
- checkPatterns: true,
95
- minSeverity: "info"
96
- });
99
+ ...options.consistency || {}
100
+ };
101
+ const report = await (0, import_consistency.analyzeConsistency)(consistencyOptions);
102
+ if (options.progressCallback) {
103
+ options.progressCallback({ tool: "consistency", data: report });
104
+ }
97
105
  if (report.results) {
98
106
  report.results = sortBySeverity(report.results);
99
107
  }
@@ -103,34 +111,6 @@ async function analyzeUnified(options) {
103
111
  result.summary.executionTime = Date.now() - startTime;
104
112
  return result;
105
113
  }
106
- function generateUnifiedSummary(result) {
107
- const { summary } = result;
108
- let output = `\u{1F680} AIReady Analysis Complete
109
-
110
- `;
111
- output += `\u{1F4CA} Summary:
112
- `;
113
- output += ` Tools run: ${summary.toolsRun.join(", ")}
114
- `;
115
- output += ` Total issues found: ${summary.totalIssues}
116
- `;
117
- output += ` Execution time: ${(summary.executionTime / 1e3).toFixed(2)}s
118
-
119
- `;
120
- if (result.patterns?.length) {
121
- output += `\u{1F50D} Pattern Analysis: ${result.patterns.length} issues
122
- `;
123
- }
124
- if (result.context?.length) {
125
- output += `\u{1F9E0} Context Analysis: ${result.context.length} issues
126
- `;
127
- }
128
- if (result.consistency) {
129
- output += `\u{1F3F7}\uFE0F Consistency Analysis: ${result.consistency.summary.totalIssues} issues
130
- `;
131
- }
132
- return output;
133
- }
134
114
 
135
115
  // src/cli.ts
136
116
  var import_chalk = __toESM(require("chalk"));
@@ -163,103 +143,210 @@ program.command("scan").description("Run unified analysis on a codebase").argume
163
143
  if (baseOptions.tools.includes("patterns")) {
164
144
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
165
145
  const patternSmartDefaults = await getSmartDefaults(directory, baseOptions);
166
- finalOptions = { ...patternSmartDefaults, ...finalOptions };
146
+ finalOptions = { ...patternSmartDefaults, ...finalOptions, ...baseOptions };
147
+ }
148
+ console.log(import_chalk.default.cyan("\n=== AIReady Run Preview ==="));
149
+ console.log(import_chalk.default.white("Tools to run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
150
+ console.log(import_chalk.default.white("Will use settings from config and defaults."));
151
+ const truncate = (arr, cap = 8) => {
152
+ if (!Array.isArray(arr)) return "";
153
+ const shown = arr.slice(0, cap).map((v) => String(v));
154
+ const more = arr.length - shown.length;
155
+ return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
156
+ };
157
+ console.log(import_chalk.default.white("\nGeneral settings:"));
158
+ if (finalOptions.rootDir) console.log(` rootDir: ${import_chalk.default.bold(String(finalOptions.rootDir))}`);
159
+ if (finalOptions.include) console.log(` include: ${import_chalk.default.bold(truncate(finalOptions.include, 6))}`);
160
+ if (finalOptions.exclude) console.log(` exclude: ${import_chalk.default.bold(truncate(finalOptions.exclude, 6))}`);
161
+ if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
162
+ const pd = finalOptions["pattern-detect"] || {
163
+ minSimilarity: finalOptions.minSimilarity,
164
+ minLines: finalOptions.minLines,
165
+ approx: finalOptions.approx,
166
+ minSharedTokens: finalOptions.minSharedTokens,
167
+ maxCandidatesPerBlock: finalOptions.maxCandidatesPerBlock,
168
+ batchSize: finalOptions.batchSize,
169
+ streamResults: finalOptions.streamResults,
170
+ severity: finalOptions.severity,
171
+ includeTests: finalOptions.includeTests
172
+ };
173
+ console.log(import_chalk.default.white("\nPattern-detect settings:"));
174
+ console.log(` minSimilarity: ${import_chalk.default.bold(pd.minSimilarity ?? "default")}`);
175
+ console.log(` minLines: ${import_chalk.default.bold(pd.minLines ?? "default")}`);
176
+ if (pd.approx !== void 0) console.log(` approx: ${import_chalk.default.bold(String(pd.approx))}`);
177
+ if (pd.minSharedTokens !== void 0) console.log(` minSharedTokens: ${import_chalk.default.bold(String(pd.minSharedTokens))}`);
178
+ if (pd.maxCandidatesPerBlock !== void 0) console.log(` maxCandidatesPerBlock: ${import_chalk.default.bold(String(pd.maxCandidatesPerBlock))}`);
179
+ if (pd.batchSize !== void 0) console.log(` batchSize: ${import_chalk.default.bold(String(pd.batchSize))}`);
180
+ if (pd.streamResults !== void 0) console.log(` streamResults: ${import_chalk.default.bold(String(pd.streamResults))}`);
181
+ if (pd.severity !== void 0) console.log(` severity: ${import_chalk.default.bold(String(pd.severity))}`);
182
+ if (pd.includeTests !== void 0) console.log(` includeTests: ${import_chalk.default.bold(String(pd.includeTests))}`);
167
183
  }
168
- const results = await analyzeUnified(finalOptions);
184
+ if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
185
+ const ca = finalOptions["context-analyzer"] || {
186
+ maxDepth: finalOptions.maxDepth,
187
+ maxContextBudget: finalOptions.maxContextBudget,
188
+ minCohesion: finalOptions.minCohesion,
189
+ maxFragmentation: finalOptions.maxFragmentation,
190
+ includeNodeModules: finalOptions.includeNodeModules
191
+ };
192
+ console.log(import_chalk.default.white("\nContext-analyzer settings:"));
193
+ console.log(` maxDepth: ${import_chalk.default.bold(ca.maxDepth ?? "default")}`);
194
+ console.log(` maxContextBudget: ${import_chalk.default.bold(ca.maxContextBudget ?? "default")}`);
195
+ if (ca.minCohesion !== void 0) console.log(` minCohesion: ${import_chalk.default.bold(String(ca.minCohesion))}`);
196
+ if (ca.maxFragmentation !== void 0) console.log(` maxFragmentation: ${import_chalk.default.bold(String(ca.maxFragmentation))}`);
197
+ if (ca.includeNodeModules !== void 0) console.log(` includeNodeModules: ${import_chalk.default.bold(String(ca.includeNodeModules))}`);
198
+ }
199
+ if (finalOptions.consistency) {
200
+ const c = finalOptions.consistency;
201
+ console.log(import_chalk.default.white("\nConsistency settings:"));
202
+ console.log(` checkNaming: ${import_chalk.default.bold(String(c.checkNaming ?? true))}`);
203
+ console.log(` checkPatterns: ${import_chalk.default.bold(String(c.checkPatterns ?? true))}`);
204
+ console.log(` checkArchitecture: ${import_chalk.default.bold(String(c.checkArchitecture ?? false))}`);
205
+ if (c.minSeverity) console.log(` minSeverity: ${import_chalk.default.bold(c.minSeverity)}`);
206
+ if (c.acceptedAbbreviations) console.log(` acceptedAbbreviations: ${import_chalk.default.bold(truncate(c.acceptedAbbreviations, 8))}`);
207
+ if (c.shortWords) console.log(` shortWords: ${import_chalk.default.bold(truncate(c.shortWords, 8))}`);
208
+ }
209
+ console.log(import_chalk.default.white("\nStarting analysis..."));
210
+ const progressCallback = (event) => {
211
+ console.log(import_chalk.default.cyan(`
212
+ --- ${event.tool.toUpperCase()} RESULTS ---`));
213
+ try {
214
+ if (event.tool === "patterns") {
215
+ const pr = event.data;
216
+ console.log(` Duplicate patterns: ${import_chalk.default.bold(String(pr.duplicates?.length || 0))}`);
217
+ console.log(` Files with pattern issues: ${import_chalk.default.bold(String(pr.results?.length || 0))}`);
218
+ if (pr.duplicates && pr.duplicates.length > 0) {
219
+ pr.duplicates.slice(0, 5).forEach((d, i) => {
220
+ console.log(` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`);
221
+ });
222
+ }
223
+ if (pr.results && pr.results.length > 0) {
224
+ console.log(` Top files with pattern issues:`);
225
+ const sortedByIssues = [...pr.results].sort((a, b) => (b.issues?.length || 0) - (a.issues?.length || 0));
226
+ sortedByIssues.slice(0, 5).forEach((r, i) => {
227
+ console.log(` ${i + 1}. ${r.fileName.split("/").pop()} - ${r.issues.length} issue(s)`);
228
+ });
229
+ }
230
+ if (pr.groups && pr.groups.length >= 0) {
231
+ 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`);
232
+ }
233
+ if (pr.clusters && pr.clusters.length >= 0) {
234
+ console.log(` \u2705 Created ${import_chalk.default.bold(String(pr.clusters.length))} refactor clusters`);
235
+ pr.clusters.slice(0, 3).forEach((cl, idx) => {
236
+ const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
237
+ console.log(` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`);
238
+ });
239
+ }
240
+ } else if (event.tool === "context") {
241
+ const cr = event.data;
242
+ console.log(` Context issues found: ${import_chalk.default.bold(String(cr.length || 0))}`);
243
+ cr.slice(0, 5).forEach((c, i) => {
244
+ const msg = c.message ? ` - ${c.message}` : "";
245
+ console.log(` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`);
246
+ });
247
+ } else if (event.tool === "consistency") {
248
+ const rep = event.data;
249
+ console.log(` Consistency totalIssues: ${import_chalk.default.bold(String(rep.summary?.totalIssues || 0))}`);
250
+ if (rep.results && rep.results.length > 0) {
251
+ const fileMap = /* @__PURE__ */ new Map();
252
+ rep.results.forEach((r) => {
253
+ (r.issues || []).forEach((issue) => {
254
+ const file = issue.location?.file || r.file || "unknown";
255
+ if (!fileMap.has(file)) fileMap.set(file, []);
256
+ fileMap.get(file).push(issue);
257
+ });
258
+ });
259
+ const files = Array.from(fileMap.entries()).sort((a, b) => b[1].length - a[1].length);
260
+ const topFiles = files.slice(0, 10);
261
+ topFiles.forEach(([file, issues], idx) => {
262
+ const counts = issues.reduce((acc, it) => {
263
+ const s = (it.severity || "info").toLowerCase();
264
+ acc[s] = (acc[s] || 0) + 1;
265
+ return acc;
266
+ }, {});
267
+ const sample = issues.find((it) => it.severity === "critical" || it.severity === "major") || issues[0];
268
+ const sampleMsg = sample ? ` \u2014 ${sample.message}` : "";
269
+ 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}`);
270
+ });
271
+ const remaining = files.length - topFiles.length;
272
+ if (remaining > 0) {
273
+ console.log(import_chalk.default.dim(` ... and ${remaining} more files with issues (use --output json for full details)`));
274
+ }
275
+ }
276
+ }
277
+ } catch (err) {
278
+ }
279
+ };
280
+ const results = await analyzeUnified({ ...finalOptions, progressCallback, suppressToolConfig: true });
281
+ console.log(import_chalk.default.cyan("\n=== AIReady Run Summary ==="));
282
+ console.log(import_chalk.default.white("Tools run:"), (finalOptions.tools || ["patterns", "context", "consistency"]).join(", "));
283
+ console.log(import_chalk.default.cyan("\nResults summary:"));
284
+ console.log(` Total issues (all tools): ${import_chalk.default.bold(String(results.summary.totalIssues || 0))}`);
285
+ if (results.duplicates) console.log(` Duplicate patterns found: ${import_chalk.default.bold(String(results.duplicates.length || 0))}`);
286
+ if (results.patterns) console.log(` Pattern files with issues: ${import_chalk.default.bold(String(results.patterns.length || 0))}`);
287
+ if (results.context) console.log(` Context issues: ${import_chalk.default.bold(String(results.context.length || 0))}`);
288
+ if (results.consistency) console.log(` Consistency issues: ${import_chalk.default.bold(String(results.consistency.summary.totalIssues || 0))}`);
289
+ console.log(import_chalk.default.cyan("===========================\n"));
169
290
  const elapsedTime = (0, import_core.getElapsedTime)(startTime);
170
291
  let scoringResult;
171
292
  if (options.score || finalOptions.scoring?.showBreakdown) {
172
293
  const toolScores = /* @__PURE__ */ new Map();
173
- if (results.patterns && baseOptions.tools.includes("patterns")) {
294
+ if (results.duplicates) {
174
295
  const { calculatePatternScore } = await import("@aiready/pattern-detect");
175
- const duplicates = results.duplicates || [];
176
- const score = calculatePatternScore(duplicates, results.patterns.length);
177
- toolScores.set("pattern-detect", score);
296
+ try {
297
+ const patternScore = calculatePatternScore(results.duplicates, results.patterns?.length || 0);
298
+ toolScores.set("pattern-detect", patternScore);
299
+ } catch (err) {
300
+ }
178
301
  }
179
- if (results.context && baseOptions.tools.includes("context")) {
180
- const { calculateContextScore } = await import("@aiready/context-analyzer");
181
- const summary = {
182
- avgContextBudget: results.context.reduce((sum, r) => sum + r.contextBudget, 0) / results.context.length,
183
- maxContextBudget: Math.max(...results.context.map((r) => r.contextBudget)),
184
- avgImportDepth: results.context.reduce((sum, r) => sum + r.importDepth, 0) / results.context.length,
185
- maxImportDepth: Math.max(...results.context.map((r) => r.importDepth)),
186
- avgFragmentation: results.context.reduce((sum, r) => sum + r.fragmentationScore, 0) / results.context.length,
187
- criticalIssues: results.context.filter((r) => r.severity === "critical").length,
188
- majorIssues: results.context.filter((r) => r.severity === "major").length
189
- };
190
- const score = calculateContextScore(summary);
191
- toolScores.set("context-analyzer", score);
302
+ if (results.context) {
303
+ const { generateSummary: genContextSummary, calculateContextScore } = await import("@aiready/context-analyzer");
304
+ try {
305
+ const ctxSummary = genContextSummary(results.context);
306
+ const contextScore = calculateContextScore(ctxSummary);
307
+ toolScores.set("context-analyzer", contextScore);
308
+ } catch (err) {
309
+ }
192
310
  }
193
- if (results.consistency && baseOptions.tools.includes("consistency")) {
311
+ if (results.consistency) {
194
312
  const { calculateConsistencyScore } = await import("@aiready/consistency");
195
- const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
196
- const score = calculateConsistencyScore(issues, results.consistency.summary.filesAnalyzed);
197
- toolScores.set("consistency", score);
198
- }
199
- const cliWeights = options.weights ? (0, import_core.parseWeightString)(options.weights) : void 0;
200
- scoringResult = (0, import_core.calculateOverallScore)(toolScores, finalOptions, cliWeights);
201
- if (options.threshold) {
202
- const threshold = parseFloat(options.threshold);
203
- if (scoringResult.overall < threshold) {
204
- console.error(import_chalk.default.red(`
205
- \u274C Score ${scoringResult.overall} is below threshold ${threshold}
206
- `));
207
- process.exit(1);
313
+ try {
314
+ const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
315
+ const totalFiles = results.consistency.summary?.filesAnalyzed || 0;
316
+ const consistencyScore = calculateConsistencyScore(issues, totalFiles);
317
+ toolScores.set("consistency", consistencyScore);
318
+ } catch (err) {
208
319
  }
209
320
  }
210
- }
211
- const outputFormat = options.output || finalOptions.output?.format || "console";
212
- const userOutputFile = options.outputFile || finalOptions.output?.file;
213
- if (outputFormat === "json") {
214
- const outputData = {
215
- ...results,
216
- summary: {
217
- ...results.summary,
218
- executionTime: parseFloat(elapsedTime)
219
- },
220
- ...scoringResult && { scoring: scoringResult }
221
- };
222
- const outputPath = (0, import_core.resolveOutputPath)(
223
- userOutputFile,
224
- `aiready-scan-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
225
- directory
226
- );
227
- (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
228
- } else {
229
- console.log(generateUnifiedSummary(results));
230
- if (scoringResult) {
231
- const terminalWidth = process.stdout.columns || 80;
232
- const dividerWidth = Math.min(60, terminalWidth - 2);
233
- const divider = "\u2501".repeat(dividerWidth);
234
- console.log(import_chalk.default.cyan("\n" + divider));
235
- console.log(import_chalk.default.bold.white(" AI READINESS SCORE"));
236
- console.log(import_chalk.default.cyan(divider) + "\n");
237
- const { emoji, color } = (0, import_core.getRatingDisplay)(scoringResult.rating);
238
- const scoreColor = color === "green" ? import_chalk.default.green : color === "blue" ? import_chalk.default.blue : color === "yellow" ? import_chalk.default.yellow : import_chalk.default.red;
239
- console.log(` ${emoji} Overall Score: ${scoreColor.bold(scoringResult.overall + "/100")} (${import_chalk.default.bold(scoringResult.rating)})`);
240
- console.log(` ${import_chalk.default.dim("Timestamp:")} ${new Date(scoringResult.timestamp).toLocaleString()}
241
- `);
242
- if (scoringResult.breakdown.length > 0) {
243
- console.log(import_chalk.default.bold(" Component Scores:\n"));
321
+ const cliWeights = (0, import_core.parseWeightString)(options.weights);
322
+ if (toolScores.size > 0) {
323
+ scoringResult = (0, import_core.calculateOverallScore)(toolScores, finalOptions, cliWeights.size ? cliWeights : void 0);
324
+ console.log(import_chalk.default.bold("\n\u{1F4CA} AI Readiness Overall Score"));
325
+ console.log(` ${(0, import_core.formatScore)(scoringResult)}`);
326
+ if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
327
+ console.log(import_chalk.default.bold("\nTool breakdown:"));
244
328
  scoringResult.breakdown.forEach((tool) => {
245
- const toolEmoji = tool.toolName === "pattern-detect" ? "\u{1F50D}" : tool.toolName === "context-analyzer" ? "\u{1F9E0}" : "\u{1F3F7}\uFE0F";
246
- const weight = scoringResult.calculation.weights[tool.toolName];
247
- console.log(` ${toolEmoji} ${import_chalk.default.white(tool.toolName.padEnd(20))} ${scoreColor(tool.score + "/100")} ${import_chalk.default.dim(`(weight: ${weight})`)}`);
329
+ const rating = (0, import_core.getRating)(tool.score);
330
+ const rd = (0, import_core.getRatingDisplay)(rating);
331
+ console.log(` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`);
248
332
  });
249
333
  console.log();
334
+ if (finalOptions.scoring?.showBreakdown) {
335
+ console.log(import_chalk.default.bold("Detailed tool breakdown:"));
336
+ scoringResult.breakdown.forEach((tool) => {
337
+ console.log((0, import_core.formatToolScore)(tool));
338
+ });
339
+ console.log();
340
+ }
250
341
  }
251
- console.log(import_chalk.default.dim(` Weighted Formula: ${scoringResult.calculation.formula}`));
252
- console.log(import_chalk.default.dim(` Normalized Score: ${scoringResult.calculation.normalized}
253
- `));
254
- const allRecommendations = scoringResult.breakdown.flatMap((tool) => tool.recommendations || []).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
255
- if (allRecommendations.length > 0) {
256
- console.log(import_chalk.default.bold(" Top Recommendations:\n"));
257
- allRecommendations.forEach((rec, i) => {
258
- const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
259
- console.log(` ${i + 1}. ${priorityIcon} ${rec.action}`);
260
- console.log(` ${import_chalk.default.dim(`Impact: +${rec.estimatedImpact} points`)}
261
- `);
262
- });
342
+ const outputFormat = options.output || finalOptions.output?.format || "console";
343
+ const userOutputFile = options.outputFile || finalOptions.output?.file;
344
+ if (outputFormat === "json") {
345
+ const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
346
+ const defaultFilename = `aiready-scan-${dateStr}.json`;
347
+ const outputPath = (0, import_core.resolveOutputPath)(userOutputFile, defaultFilename, directory);
348
+ const outputData = { ...results, scoring: scoringResult };
349
+ (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Summary saved to ${outputPath}`);
263
350
  }
264
351
  }
265
352
  }