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