@aiready/cli 0.9.40 → 0.9.41
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/.github/FUNDING.yml +2 -2
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-test.log +5 -5
- package/CONTRIBUTING.md +11 -2
- package/dist/chunk-HLBKROD3.mjs +237 -0
- package/dist/cli.js +688 -178
- package/dist/cli.mjs +679 -178
- package/dist/index.js +12 -3
- 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 +13 -8
- package/src/commands/consistency.ts +69 -29
- package/src/commands/context.ts +108 -38
- 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 +399 -126
- package/src/commands/testability.ts +22 -9
- package/src/commands/visualize.ts +102 -45
- package/src/index.ts +34 -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-HLBKROD3.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,126 @@ 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
|
+
suppressToolConfig: true
|
|
493
|
+
});
|
|
336
494
|
console.log(chalk2.cyan("\n=== AIReady Run Summary ==="));
|
|
337
|
-
console.log(
|
|
495
|
+
console.log(
|
|
496
|
+
chalk2.white("Tools run:"),
|
|
497
|
+
(finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
|
|
498
|
+
);
|
|
338
499
|
console.log(chalk2.cyan("\nResults summary:"));
|
|
339
|
-
console.log(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (results.
|
|
343
|
-
|
|
344
|
-
|
|
500
|
+
console.log(
|
|
501
|
+
` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
|
|
502
|
+
);
|
|
503
|
+
if (results.duplicates)
|
|
504
|
+
console.log(
|
|
505
|
+
` Duplicate patterns found: ${chalk2.bold(String(results.duplicates.length || 0))}`
|
|
506
|
+
);
|
|
507
|
+
if (results.patterns)
|
|
508
|
+
console.log(
|
|
509
|
+
` Pattern files with issues: ${chalk2.bold(String(results.patterns.length || 0))}`
|
|
510
|
+
);
|
|
511
|
+
if (results.context)
|
|
512
|
+
console.log(
|
|
513
|
+
` Context issues: ${chalk2.bold(String(results.context.length || 0))}`
|
|
514
|
+
);
|
|
515
|
+
console.log(
|
|
516
|
+
` Consistency issues: ${chalk2.bold(String(results.consistency?.summary?.totalIssues || 0))}`
|
|
517
|
+
);
|
|
518
|
+
if (results.changeAmplification)
|
|
519
|
+
console.log(
|
|
520
|
+
` Change amplification: ${chalk2.bold(String(results.changeAmplification.summary?.score || 0))}/100`
|
|
521
|
+
);
|
|
345
522
|
console.log(chalk2.cyan("===========================\n"));
|
|
346
523
|
const elapsedTime = getElapsedTime(startTime);
|
|
524
|
+
void elapsedTime;
|
|
347
525
|
let scoringResult;
|
|
348
526
|
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
349
527
|
const toolScores = /* @__PURE__ */ new Map();
|
|
350
528
|
if (results.duplicates) {
|
|
351
529
|
const { calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
352
530
|
try {
|
|
353
|
-
const patternScore = calculatePatternScore(
|
|
531
|
+
const patternScore = calculatePatternScore(
|
|
532
|
+
results.duplicates,
|
|
533
|
+
results.patterns?.length || 0
|
|
534
|
+
);
|
|
354
535
|
toolScores.set("pattern-detect", patternScore);
|
|
355
536
|
} catch (err) {
|
|
537
|
+
void err;
|
|
356
538
|
}
|
|
357
539
|
}
|
|
358
540
|
if (results.context) {
|
|
@@ -362,6 +544,7 @@ async function scanAction(directory, options) {
|
|
|
362
544
|
const contextScore = calculateContextScore(ctxSummary);
|
|
363
545
|
toolScores.set("context-analyzer", contextScore);
|
|
364
546
|
} catch (err) {
|
|
547
|
+
void err;
|
|
365
548
|
}
|
|
366
549
|
}
|
|
367
550
|
if (results.consistency) {
|
|
@@ -369,17 +552,24 @@ async function scanAction(directory, options) {
|
|
|
369
552
|
try {
|
|
370
553
|
const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
|
|
371
554
|
const totalFiles = results.consistency.summary?.filesAnalyzed || 0;
|
|
372
|
-
const consistencyScore = calculateConsistencyScore(
|
|
555
|
+
const consistencyScore = calculateConsistencyScore(
|
|
556
|
+
issues,
|
|
557
|
+
totalFiles
|
|
558
|
+
);
|
|
373
559
|
toolScores.set("consistency", consistencyScore);
|
|
374
560
|
} catch (err) {
|
|
561
|
+
void err;
|
|
375
562
|
}
|
|
376
563
|
}
|
|
377
564
|
if (results.aiSignalClarity) {
|
|
378
565
|
const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
|
|
379
566
|
try {
|
|
380
|
-
const hrScore = calculateAiSignalClarityScore(
|
|
567
|
+
const hrScore = calculateAiSignalClarityScore(
|
|
568
|
+
results.aiSignalClarity
|
|
569
|
+
);
|
|
381
570
|
toolScores.set("ai-signal-clarity", hrScore);
|
|
382
571
|
} catch (err) {
|
|
572
|
+
void err;
|
|
383
573
|
}
|
|
384
574
|
}
|
|
385
575
|
if (results.grounding) {
|
|
@@ -388,6 +578,7 @@ async function scanAction(directory, options) {
|
|
|
388
578
|
const agScore = calculateGroundingScore(results.grounding);
|
|
389
579
|
toolScores.set("agent-grounding", agScore);
|
|
390
580
|
} catch (err) {
|
|
581
|
+
void err;
|
|
391
582
|
}
|
|
392
583
|
}
|
|
393
584
|
if (results.testability) {
|
|
@@ -396,6 +587,7 @@ async function scanAction(directory, options) {
|
|
|
396
587
|
const tbScore = calculateTestabilityScore(results.testability);
|
|
397
588
|
toolScores.set("testability", tbScore);
|
|
398
589
|
} catch (err) {
|
|
590
|
+
void err;
|
|
399
591
|
}
|
|
400
592
|
}
|
|
401
593
|
if (results.docDrift) {
|
|
@@ -404,7 +596,13 @@ async function scanAction(directory, options) {
|
|
|
404
596
|
score: results.docDrift.summary.score,
|
|
405
597
|
rawMetrics: results.docDrift.rawData,
|
|
406
598
|
factors: [],
|
|
407
|
-
recommendations: (results.docDrift.recommendations || []).map(
|
|
599
|
+
recommendations: (results.docDrift.recommendations || []).map(
|
|
600
|
+
(action) => ({
|
|
601
|
+
action,
|
|
602
|
+
estimatedImpact: 5,
|
|
603
|
+
priority: "medium"
|
|
604
|
+
})
|
|
605
|
+
)
|
|
408
606
|
});
|
|
409
607
|
}
|
|
410
608
|
if (results.deps) {
|
|
@@ -413,7 +611,13 @@ async function scanAction(directory, options) {
|
|
|
413
611
|
score: results.deps.summary.score,
|
|
414
612
|
rawMetrics: results.deps.rawData,
|
|
415
613
|
factors: [],
|
|
416
|
-
recommendations: (results.deps.recommendations || []).map(
|
|
614
|
+
recommendations: (results.deps.recommendations || []).map(
|
|
615
|
+
(action) => ({
|
|
616
|
+
action,
|
|
617
|
+
estimatedImpact: 5,
|
|
618
|
+
priority: "medium"
|
|
619
|
+
})
|
|
620
|
+
)
|
|
417
621
|
});
|
|
418
622
|
}
|
|
419
623
|
if (results.changeAmplification) {
|
|
@@ -422,17 +626,28 @@ async function scanAction(directory, options) {
|
|
|
422
626
|
score: results.changeAmplification.summary.score,
|
|
423
627
|
rawMetrics: results.changeAmplification.rawData,
|
|
424
628
|
factors: [],
|
|
425
|
-
recommendations: (results.changeAmplification.recommendations || []).map((action) => ({
|
|
629
|
+
recommendations: (results.changeAmplification.recommendations || []).map((action) => ({
|
|
630
|
+
action,
|
|
631
|
+
estimatedImpact: 5,
|
|
632
|
+
priority: "medium"
|
|
633
|
+
}))
|
|
426
634
|
});
|
|
427
635
|
}
|
|
428
636
|
const cliWeights = parseWeightString(options.weights);
|
|
429
637
|
if (toolScores.size > 0) {
|
|
430
|
-
scoringResult = calculateOverallScore(
|
|
638
|
+
scoringResult = calculateOverallScore(
|
|
639
|
+
toolScores,
|
|
640
|
+
finalOptions,
|
|
641
|
+
cliWeights.size ? cliWeights : void 0
|
|
642
|
+
);
|
|
431
643
|
console.log(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
|
|
432
644
|
console.log(` ${formatScore(scoringResult)}`);
|
|
433
645
|
if (options.compareTo) {
|
|
434
646
|
try {
|
|
435
|
-
const prevReportStr = readFileSync2(
|
|
647
|
+
const prevReportStr = readFileSync2(
|
|
648
|
+
resolvePath2(process.cwd(), options.compareTo),
|
|
649
|
+
"utf8"
|
|
650
|
+
);
|
|
436
651
|
const prevReport = JSON.parse(prevReportStr);
|
|
437
652
|
const prevScore = prevReport.scoring?.score || prevReport.scoring?.overallScore;
|
|
438
653
|
if (typeof prevScore === "number") {
|
|
@@ -440,23 +655,44 @@ async function scanAction(directory, options) {
|
|
|
440
655
|
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
441
656
|
console.log();
|
|
442
657
|
if (diff > 0) {
|
|
443
|
-
console.log(
|
|
658
|
+
console.log(
|
|
659
|
+
chalk2.green(
|
|
660
|
+
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
661
|
+
)
|
|
662
|
+
);
|
|
444
663
|
} else if (diff < 0) {
|
|
445
|
-
console.log(
|
|
664
|
+
console.log(
|
|
665
|
+
chalk2.red(
|
|
666
|
+
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
667
|
+
)
|
|
668
|
+
);
|
|
446
669
|
} else {
|
|
447
|
-
console.log(
|
|
670
|
+
console.log(
|
|
671
|
+
chalk2.blue(
|
|
672
|
+
` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
673
|
+
)
|
|
674
|
+
);
|
|
448
675
|
}
|
|
449
676
|
scoringResult.trend = {
|
|
450
677
|
previousScore: prevScore,
|
|
451
678
|
difference: diff
|
|
452
679
|
};
|
|
453
680
|
} else {
|
|
454
|
-
console.log(
|
|
455
|
-
|
|
681
|
+
console.log(
|
|
682
|
+
chalk2.yellow(
|
|
683
|
+
`
|
|
684
|
+
\u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
|
|
685
|
+
)
|
|
686
|
+
);
|
|
456
687
|
}
|
|
457
688
|
} catch (e) {
|
|
458
|
-
|
|
459
|
-
|
|
689
|
+
void e;
|
|
690
|
+
console.log(
|
|
691
|
+
chalk2.yellow(
|
|
692
|
+
`
|
|
693
|
+
\u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
|
|
694
|
+
)
|
|
695
|
+
);
|
|
460
696
|
}
|
|
461
697
|
}
|
|
462
698
|
if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
|
|
@@ -464,7 +700,9 @@ async function scanAction(directory, options) {
|
|
|
464
700
|
scoringResult.breakdown.forEach((tool) => {
|
|
465
701
|
const rating = getRating(tool.score);
|
|
466
702
|
const rd = getRatingDisplay(rating);
|
|
467
|
-
console.log(
|
|
703
|
+
console.log(
|
|
704
|
+
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`
|
|
705
|
+
);
|
|
468
706
|
});
|
|
469
707
|
console.log();
|
|
470
708
|
if (finalOptions.scoring?.showBreakdown) {
|
|
@@ -482,20 +720,33 @@ async function scanAction(directory, options) {
|
|
|
482
720
|
if (outputFormat === "json") {
|
|
483
721
|
const timestamp = getReportTimestamp();
|
|
484
722
|
const defaultFilename = `aiready-report-${timestamp}.json`;
|
|
485
|
-
const outputPath = resolveOutputPath(
|
|
723
|
+
const outputPath = resolveOutputPath(
|
|
724
|
+
userOutputFile,
|
|
725
|
+
defaultFilename,
|
|
726
|
+
resolvedDir
|
|
727
|
+
);
|
|
486
728
|
const outputData = { ...results, scoring: scoringResult };
|
|
487
|
-
handleJSONOutput(
|
|
488
|
-
|
|
729
|
+
handleJSONOutput(
|
|
730
|
+
outputData,
|
|
731
|
+
outputPath,
|
|
732
|
+
`\u2705 Report saved to ${outputPath}`
|
|
733
|
+
);
|
|
734
|
+
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
489
735
|
} else {
|
|
490
736
|
const timestamp = getReportTimestamp();
|
|
491
737
|
const defaultFilename = `aiready-report-${timestamp}.json`;
|
|
492
|
-
const outputPath = resolveOutputPath(
|
|
738
|
+
const outputPath = resolveOutputPath(
|
|
739
|
+
userOutputFile,
|
|
740
|
+
defaultFilename,
|
|
741
|
+
resolvedDir
|
|
742
|
+
);
|
|
493
743
|
const outputData = { ...results, scoring: scoringResult };
|
|
494
744
|
try {
|
|
495
745
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
496
746
|
console.log(chalk2.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
497
|
-
warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
747
|
+
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
498
748
|
} catch (err) {
|
|
749
|
+
void err;
|
|
499
750
|
}
|
|
500
751
|
}
|
|
501
752
|
const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
|
@@ -513,16 +764,22 @@ async function scanAction(directory, options) {
|
|
|
513
764
|
}
|
|
514
765
|
console.log("::endgroup::");
|
|
515
766
|
if (threshold && scoringResult.overall < threshold) {
|
|
516
|
-
console.log(
|
|
767
|
+
console.log(
|
|
768
|
+
`::error::AI Readiness Score ${scoringResult.overall} is below threshold ${threshold}`
|
|
769
|
+
);
|
|
517
770
|
} else if (threshold) {
|
|
518
|
-
console.log(
|
|
771
|
+
console.log(
|
|
772
|
+
`::notice::AI Readiness Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
|
|
773
|
+
);
|
|
519
774
|
}
|
|
520
775
|
if (results.patterns) {
|
|
521
776
|
const criticalPatterns = results.patterns.flatMap(
|
|
522
777
|
(p) => p.issues.filter((i) => i.severity === "critical")
|
|
523
778
|
);
|
|
524
779
|
criticalPatterns.slice(0, 10).forEach((issue) => {
|
|
525
|
-
console.log(
|
|
780
|
+
console.log(
|
|
781
|
+
`::warning file=${issue.location?.file || "unknown"},line=${issue.location?.line || 1}::${issue.message}`
|
|
782
|
+
);
|
|
526
783
|
});
|
|
527
784
|
}
|
|
528
785
|
}
|
|
@@ -571,16 +828,30 @@ async function scanAction(directory, options) {
|
|
|
571
828
|
console.log(chalk2.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
|
|
572
829
|
console.log(chalk2.red(` Reason: ${failReason}`));
|
|
573
830
|
console.log(chalk2.dim("\n Remediation steps:"));
|
|
574
|
-
console.log(
|
|
831
|
+
console.log(
|
|
832
|
+
chalk2.dim(" 1. Run `aiready scan` locally to see detailed issues")
|
|
833
|
+
);
|
|
575
834
|
console.log(chalk2.dim(" 2. Fix the critical issues before merging"));
|
|
576
|
-
console.log(
|
|
835
|
+
console.log(
|
|
836
|
+
chalk2.dim(
|
|
837
|
+
" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
|
|
838
|
+
)
|
|
839
|
+
);
|
|
577
840
|
process.exit(1);
|
|
578
841
|
} else {
|
|
579
842
|
console.log(chalk2.green("\n\u2705 PR PASSED: AI Readiness Check"));
|
|
580
843
|
if (threshold) {
|
|
581
|
-
console.log(
|
|
844
|
+
console.log(
|
|
845
|
+
chalk2.green(
|
|
846
|
+
` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
|
|
847
|
+
)
|
|
848
|
+
);
|
|
582
849
|
}
|
|
583
|
-
console.log(
|
|
850
|
+
console.log(
|
|
851
|
+
chalk2.dim(
|
|
852
|
+
"\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
|
|
853
|
+
)
|
|
854
|
+
);
|
|
584
855
|
}
|
|
585
856
|
}
|
|
586
857
|
} catch (error) {
|
|
@@ -659,7 +930,11 @@ async function patternsAction(directory, options) {
|
|
|
659
930
|
if (options.minSharedTokens) {
|
|
660
931
|
cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
|
|
661
932
|
}
|
|
662
|
-
const finalOptions = await loadMergedConfig2(
|
|
933
|
+
const finalOptions = await loadMergedConfig2(
|
|
934
|
+
resolvedDir,
|
|
935
|
+
defaults,
|
|
936
|
+
cliOptions
|
|
937
|
+
);
|
|
663
938
|
const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
664
939
|
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
665
940
|
const elapsedTime = getElapsedTime2(startTime);
|
|
@@ -681,7 +956,11 @@ async function patternsAction(directory, options) {
|
|
|
681
956
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
682
957
|
resolvedDir
|
|
683
958
|
);
|
|
684
|
-
handleJSONOutput2(
|
|
959
|
+
handleJSONOutput2(
|
|
960
|
+
outputData,
|
|
961
|
+
outputPath,
|
|
962
|
+
`\u2705 Results saved to ${outputPath}`
|
|
963
|
+
);
|
|
685
964
|
} else {
|
|
686
965
|
const terminalWidth = process.stdout.columns || 80;
|
|
687
966
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
@@ -689,10 +968,22 @@ async function patternsAction(directory, options) {
|
|
|
689
968
|
console.log(chalk3.cyan(divider));
|
|
690
969
|
console.log(chalk3.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
691
970
|
console.log(chalk3.cyan(divider) + "\n");
|
|
692
|
-
console.log(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
console.log(
|
|
971
|
+
console.log(
|
|
972
|
+
chalk3.white(`\u{1F4C1} Files analyzed: ${chalk3.bold(results.length)}`)
|
|
973
|
+
);
|
|
974
|
+
console.log(
|
|
975
|
+
chalk3.yellow(
|
|
976
|
+
`\u26A0 Duplicate patterns found: ${chalk3.bold(summary.totalPatterns)}`
|
|
977
|
+
)
|
|
978
|
+
);
|
|
979
|
+
console.log(
|
|
980
|
+
chalk3.red(
|
|
981
|
+
`\u{1F4B0} Token cost (wasted): ${chalk3.bold(summary.totalTokenCost.toLocaleString())}`
|
|
982
|
+
)
|
|
983
|
+
);
|
|
984
|
+
console.log(
|
|
985
|
+
chalk3.gray(`\u23F1 Analysis time: ${chalk3.bold(elapsedTime + "s")}`)
|
|
986
|
+
);
|
|
696
987
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
697
988
|
if (sortedTypes.length > 0) {
|
|
698
989
|
console.log(chalk3.cyan("\n" + divider));
|
|
@@ -712,13 +1003,21 @@ async function patternsAction(directory, options) {
|
|
|
712
1003
|
const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
|
|
713
1004
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
714
1005
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
715
|
-
console.log(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1006
|
+
console.log(
|
|
1007
|
+
`${severityIcon} ${severity}: ${chalk3.bold(file1Name)} \u2194 ${chalk3.bold(file2Name)}`
|
|
1008
|
+
);
|
|
1009
|
+
console.log(
|
|
1010
|
+
` Similarity: ${chalk3.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk3.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1011
|
+
);
|
|
1012
|
+
console.log(
|
|
1013
|
+
` Lines: ${chalk3.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk3.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1014
|
+
`
|
|
1015
|
+
);
|
|
719
1016
|
});
|
|
720
1017
|
} else {
|
|
721
|
-
console.log(
|
|
1018
|
+
console.log(
|
|
1019
|
+
chalk3.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1020
|
+
);
|
|
722
1021
|
}
|
|
723
1022
|
if (patternScore) {
|
|
724
1023
|
console.log(chalk3.cyan(divider));
|
|
@@ -765,7 +1064,7 @@ async function contextAction(directory, options) {
|
|
|
765
1064
|
file: void 0
|
|
766
1065
|
}
|
|
767
1066
|
};
|
|
768
|
-
|
|
1067
|
+
const baseOptions = await loadMergedConfig3(resolvedDir, defaults, {
|
|
769
1068
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
|
|
770
1069
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
|
|
771
1070
|
include: options.include?.split(","),
|
|
@@ -773,13 +1072,20 @@ async function contextAction(directory, options) {
|
|
|
773
1072
|
});
|
|
774
1073
|
let finalOptions = { ...baseOptions };
|
|
775
1074
|
const { getSmartDefaults } = await import("@aiready/context-analyzer");
|
|
776
|
-
const contextSmartDefaults = await getSmartDefaults(
|
|
1075
|
+
const contextSmartDefaults = await getSmartDefaults(
|
|
1076
|
+
resolvedDir,
|
|
1077
|
+
baseOptions
|
|
1078
|
+
);
|
|
777
1079
|
finalOptions = { ...contextSmartDefaults, ...finalOptions };
|
|
778
1080
|
console.log("\u{1F4CB} Configuration:");
|
|
779
1081
|
console.log(` Max depth: ${finalOptions.maxDepth}`);
|
|
780
1082
|
console.log(` Max context budget: ${finalOptions.maxContextBudget}`);
|
|
781
|
-
console.log(
|
|
782
|
-
|
|
1083
|
+
console.log(
|
|
1084
|
+
` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`
|
|
1085
|
+
);
|
|
1086
|
+
console.log(
|
|
1087
|
+
` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`
|
|
1088
|
+
);
|
|
783
1089
|
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
784
1090
|
console.log("");
|
|
785
1091
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
@@ -803,7 +1109,11 @@ async function contextAction(directory, options) {
|
|
|
803
1109
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
804
1110
|
resolvedDir
|
|
805
1111
|
);
|
|
806
|
-
handleJSONOutput3(
|
|
1112
|
+
handleJSONOutput3(
|
|
1113
|
+
outputData,
|
|
1114
|
+
outputPath,
|
|
1115
|
+
`\u2705 Results saved to ${outputPath}`
|
|
1116
|
+
);
|
|
807
1117
|
} else {
|
|
808
1118
|
const terminalWidth = process.stdout.columns || 80;
|
|
809
1119
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
@@ -811,59 +1121,103 @@ async function contextAction(directory, options) {
|
|
|
811
1121
|
console.log(chalk4.cyan(divider));
|
|
812
1122
|
console.log(chalk4.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
813
1123
|
console.log(chalk4.cyan(divider) + "\n");
|
|
814
|
-
console.log(
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
console.log(
|
|
818
|
-
|
|
1124
|
+
console.log(
|
|
1125
|
+
chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(summary.totalFiles)}`)
|
|
1126
|
+
);
|
|
1127
|
+
console.log(
|
|
1128
|
+
chalk4.white(
|
|
1129
|
+
`\u{1F4CA} Total tokens: ${chalk4.bold(summary.totalTokens.toLocaleString())}`
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1132
|
+
console.log(
|
|
1133
|
+
chalk4.yellow(
|
|
1134
|
+
`\u{1F4B0} Avg context budget: ${chalk4.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1135
|
+
)
|
|
1136
|
+
);
|
|
1137
|
+
console.log(
|
|
1138
|
+
chalk4.white(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}
|
|
1139
|
+
`)
|
|
1140
|
+
);
|
|
819
1141
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
820
1142
|
if (totalIssues > 0) {
|
|
821
1143
|
console.log(chalk4.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
822
1144
|
if (summary.criticalIssues > 0) {
|
|
823
|
-
console.log(
|
|
1145
|
+
console.log(
|
|
1146
|
+
chalk4.red(` \u{1F534} Critical: ${chalk4.bold(summary.criticalIssues)}`)
|
|
1147
|
+
);
|
|
824
1148
|
}
|
|
825
1149
|
if (summary.majorIssues > 0) {
|
|
826
|
-
console.log(
|
|
1150
|
+
console.log(
|
|
1151
|
+
chalk4.yellow(` \u{1F7E1} Major: ${chalk4.bold(summary.majorIssues)}`)
|
|
1152
|
+
);
|
|
827
1153
|
}
|
|
828
1154
|
if (summary.minorIssues > 0) {
|
|
829
|
-
console.log(
|
|
1155
|
+
console.log(
|
|
1156
|
+
chalk4.blue(` \u{1F535} Minor: ${chalk4.bold(summary.minorIssues)}`)
|
|
1157
|
+
);
|
|
830
1158
|
}
|
|
831
|
-
console.log(
|
|
1159
|
+
console.log(
|
|
1160
|
+
chalk4.green(
|
|
1161
|
+
`
|
|
832
1162
|
\u{1F4A1} Potential savings: ${chalk4.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
833
|
-
`
|
|
1163
|
+
`
|
|
1164
|
+
)
|
|
1165
|
+
);
|
|
834
1166
|
} else {
|
|
835
1167
|
console.log(chalk4.green("\u2705 No significant issues found!\n"));
|
|
836
1168
|
}
|
|
837
1169
|
if (summary.deepFiles.length > 0) {
|
|
838
1170
|
console.log(chalk4.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
839
|
-
console.log(
|
|
840
|
-
|
|
841
|
-
|
|
1171
|
+
console.log(
|
|
1172
|
+
chalk4.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1173
|
+
);
|
|
1174
|
+
console.log(
|
|
1175
|
+
chalk4.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1176
|
+
`)
|
|
1177
|
+
);
|
|
842
1178
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
843
1179
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
844
|
-
console.log(
|
|
1180
|
+
console.log(
|
|
1181
|
+
` ${chalk4.cyan("\u2192")} ${chalk4.white(fileName)} ${chalk4.dim(`(depth: ${item.depth})`)}`
|
|
1182
|
+
);
|
|
845
1183
|
});
|
|
846
1184
|
console.log();
|
|
847
1185
|
}
|
|
848
1186
|
if (summary.fragmentedModules.length > 0) {
|
|
849
1187
|
console.log(chalk4.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
850
|
-
console.log(
|
|
851
|
-
|
|
1188
|
+
console.log(
|
|
1189
|
+
chalk4.gray(
|
|
1190
|
+
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1191
|
+
`
|
|
1192
|
+
)
|
|
1193
|
+
);
|
|
852
1194
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
853
|
-
console.log(
|
|
854
|
-
|
|
1195
|
+
console.log(
|
|
1196
|
+
` ${chalk4.yellow("\u25CF")} ${chalk4.white(module.domain)} - ${chalk4.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1197
|
+
);
|
|
1198
|
+
console.log(
|
|
1199
|
+
chalk4.dim(
|
|
1200
|
+
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1201
|
+
)
|
|
1202
|
+
);
|
|
855
1203
|
});
|
|
856
1204
|
console.log();
|
|
857
1205
|
}
|
|
858
1206
|
if (summary.lowCohesionFiles.length > 0) {
|
|
859
1207
|
console.log(chalk4.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
860
|
-
console.log(
|
|
861
|
-
|
|
1208
|
+
console.log(
|
|
1209
|
+
chalk4.gray(
|
|
1210
|
+
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1211
|
+
`
|
|
1212
|
+
)
|
|
1213
|
+
);
|
|
862
1214
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
863
1215
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
864
1216
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
865
1217
|
const color = item.score < 0.4 ? chalk4.red : chalk4.yellow;
|
|
866
|
-
console.log(
|
|
1218
|
+
console.log(
|
|
1219
|
+
` ${color("\u25CB")} ${chalk4.white(fileName)} ${chalk4.dim(`(${scorePercent}% cohesion)`)}`
|
|
1220
|
+
);
|
|
867
1221
|
});
|
|
868
1222
|
console.log();
|
|
869
1223
|
}
|
|
@@ -872,7 +1226,9 @@ async function contextAction(directory, options) {
|
|
|
872
1226
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
873
1227
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
874
1228
|
const severityColor = item.severity === "critical" ? chalk4.red : item.severity === "major" ? chalk4.yellow : chalk4.blue;
|
|
875
|
-
console.log(
|
|
1229
|
+
console.log(
|
|
1230
|
+
` ${severityColor("\u25CF")} ${chalk4.white(fileName)} ${chalk4.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1231
|
+
);
|
|
876
1232
|
});
|
|
877
1233
|
console.log();
|
|
878
1234
|
}
|
|
@@ -930,7 +1286,10 @@ async function consistencyAction(directory, options) {
|
|
|
930
1286
|
let consistencyScore;
|
|
931
1287
|
if (options.score) {
|
|
932
1288
|
const issues = report.results?.flatMap((r) => r.issues) || [];
|
|
933
|
-
consistencyScore = calculateConsistencyScore(
|
|
1289
|
+
consistencyScore = calculateConsistencyScore(
|
|
1290
|
+
issues,
|
|
1291
|
+
report.summary.filesAnalyzed
|
|
1292
|
+
);
|
|
934
1293
|
}
|
|
935
1294
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
936
1295
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
@@ -948,7 +1307,11 @@ async function consistencyAction(directory, options) {
|
|
|
948
1307
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
949
1308
|
resolvedDir
|
|
950
1309
|
);
|
|
951
|
-
handleJSONOutput4(
|
|
1310
|
+
handleJSONOutput4(
|
|
1311
|
+
outputData,
|
|
1312
|
+
outputPath,
|
|
1313
|
+
`\u2705 Results saved to ${outputPath}`
|
|
1314
|
+
);
|
|
952
1315
|
} else if (outputFormat === "markdown") {
|
|
953
1316
|
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
954
1317
|
const outputPath = resolveOutputPath4(
|
|
@@ -960,15 +1323,23 @@ async function consistencyAction(directory, options) {
|
|
|
960
1323
|
console.log(chalk5.green(`\u2705 Report saved to ${outputPath}`));
|
|
961
1324
|
} else {
|
|
962
1325
|
console.log(chalk5.bold("\n\u{1F4CA} Summary\n"));
|
|
963
|
-
console.log(
|
|
1326
|
+
console.log(
|
|
1327
|
+
`Files Analyzed: ${chalk5.cyan(report.summary.filesAnalyzed)}`
|
|
1328
|
+
);
|
|
964
1329
|
console.log(`Total Issues: ${chalk5.yellow(report.summary.totalIssues)}`);
|
|
965
1330
|
console.log(` Naming: ${chalk5.yellow(report.summary.namingIssues)}`);
|
|
966
1331
|
console.log(` Patterns: ${chalk5.yellow(report.summary.patternIssues)}`);
|
|
967
|
-
console.log(
|
|
1332
|
+
console.log(
|
|
1333
|
+
` Architecture: ${chalk5.yellow(report.summary.architectureIssues || 0)}`
|
|
1334
|
+
);
|
|
968
1335
|
console.log(`Analysis Time: ${chalk5.gray(elapsedTime + "s")}
|
|
969
1336
|
`);
|
|
970
1337
|
if (report.summary.totalIssues === 0) {
|
|
971
|
-
console.log(
|
|
1338
|
+
console.log(
|
|
1339
|
+
chalk5.green(
|
|
1340
|
+
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1341
|
+
)
|
|
1342
|
+
);
|
|
972
1343
|
} else {
|
|
973
1344
|
const namingResults = report.results.filter(
|
|
974
1345
|
(r) => r.issues.some((i) => i.category === "naming")
|
|
@@ -984,10 +1355,14 @@ async function consistencyAction(directory, options) {
|
|
|
984
1355
|
for (const issue of result.issues) {
|
|
985
1356
|
if (shown >= 5) break;
|
|
986
1357
|
const severityColor = issue.severity === "critical" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
|
|
987
|
-
console.log(
|
|
1358
|
+
console.log(
|
|
1359
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1360
|
+
);
|
|
988
1361
|
console.log(` ${issue.message}`);
|
|
989
1362
|
if (issue.suggestion) {
|
|
990
|
-
console.log(
|
|
1363
|
+
console.log(
|
|
1364
|
+
` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
|
|
1365
|
+
);
|
|
991
1366
|
}
|
|
992
1367
|
console.log();
|
|
993
1368
|
shown++;
|
|
@@ -1007,10 +1382,14 @@ async function consistencyAction(directory, options) {
|
|
|
1007
1382
|
for (const issue of result.issues) {
|
|
1008
1383
|
if (shown >= 5) break;
|
|
1009
1384
|
const severityColor = issue.severity === "critical" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
|
|
1010
|
-
console.log(
|
|
1385
|
+
console.log(
|
|
1386
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1387
|
+
);
|
|
1011
1388
|
console.log(` ${issue.message}`);
|
|
1012
1389
|
if (issue.suggestion) {
|
|
1013
|
-
console.log(
|
|
1390
|
+
console.log(
|
|
1391
|
+
` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
|
|
1392
|
+
);
|
|
1014
1393
|
}
|
|
1015
1394
|
console.log();
|
|
1016
1395
|
shown++;
|
|
@@ -1043,7 +1422,7 @@ async function consistencyAction(directory, options) {
|
|
|
1043
1422
|
|
|
1044
1423
|
// src/commands/visualize.ts
|
|
1045
1424
|
import chalk6 from "chalk";
|
|
1046
|
-
import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync
|
|
1425
|
+
import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
|
|
1047
1426
|
import { resolve as resolvePath6 } from "path";
|
|
1048
1427
|
import { spawn } from "child_process";
|
|
1049
1428
|
import { handleCLIError as handleCLIError5 } from "@aiready/core";
|
|
@@ -1056,15 +1435,21 @@ async function visualizeAction(directory, options) {
|
|
|
1056
1435
|
const latestScan = findLatestScanReport(dirPath);
|
|
1057
1436
|
if (latestScan) {
|
|
1058
1437
|
reportPath = latestScan;
|
|
1059
|
-
console.log(
|
|
1438
|
+
console.log(
|
|
1439
|
+
chalk6.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1440
|
+
);
|
|
1060
1441
|
} else {
|
|
1061
1442
|
console.error(chalk6.red("\u274C No AI readiness report found"));
|
|
1062
|
-
console.log(
|
|
1443
|
+
console.log(
|
|
1444
|
+
chalk6.dim(
|
|
1445
|
+
`
|
|
1063
1446
|
Generate a report with:
|
|
1064
1447
|
aiready scan --output json
|
|
1065
1448
|
|
|
1066
1449
|
Or specify a custom report:
|
|
1067
|
-
aiready visualise --report <path-to-report.json>`
|
|
1450
|
+
aiready visualise --report <path-to-report.json>`
|
|
1451
|
+
)
|
|
1452
|
+
);
|
|
1068
1453
|
return;
|
|
1069
1454
|
}
|
|
1070
1455
|
}
|
|
@@ -1082,6 +1467,7 @@ Or specify a custom report:
|
|
|
1082
1467
|
};
|
|
1083
1468
|
}
|
|
1084
1469
|
} catch (e) {
|
|
1470
|
+
void e;
|
|
1085
1471
|
}
|
|
1086
1472
|
}
|
|
1087
1473
|
const envVisualizerConfig = JSON.stringify(graphConfig);
|
|
@@ -1102,11 +1488,18 @@ Or specify a custom report:
|
|
|
1102
1488
|
} else {
|
|
1103
1489
|
const nodemodulesLocations = [
|
|
1104
1490
|
resolvePath6(dirPath, "node_modules", "@aiready", "visualizer"),
|
|
1105
|
-
resolvePath6(
|
|
1491
|
+
resolvePath6(
|
|
1492
|
+
process.cwd(),
|
|
1493
|
+
"node_modules",
|
|
1494
|
+
"@aiready",
|
|
1495
|
+
"visualizer"
|
|
1496
|
+
)
|
|
1106
1497
|
];
|
|
1107
1498
|
let currentDir = dirPath;
|
|
1108
1499
|
while (currentDir !== "/" && currentDir !== ".") {
|
|
1109
|
-
nodemodulesLocations.push(
|
|
1500
|
+
nodemodulesLocations.push(
|
|
1501
|
+
resolvePath6(currentDir, "node_modules", "@aiready", "visualizer")
|
|
1502
|
+
);
|
|
1110
1503
|
const parent = resolvePath6(currentDir, "..");
|
|
1111
1504
|
if (parent === currentDir) break;
|
|
1112
1505
|
currentDir = parent;
|
|
@@ -1123,7 +1516,8 @@ Or specify a custom report:
|
|
|
1123
1516
|
const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
|
|
1124
1517
|
webDir = resolvePath6(vizPkgPath, "..");
|
|
1125
1518
|
visualizerAvailable = true;
|
|
1126
|
-
} catch (
|
|
1519
|
+
} catch (err) {
|
|
1520
|
+
void err;
|
|
1127
1521
|
}
|
|
1128
1522
|
}
|
|
1129
1523
|
}
|
|
@@ -1134,7 +1528,7 @@ Or specify a custom report:
|
|
|
1134
1528
|
const copyReportToViz = () => {
|
|
1135
1529
|
try {
|
|
1136
1530
|
const destPath = resolvePath6(spawnCwd, "web", "report-data.json");
|
|
1137
|
-
|
|
1531
|
+
copyFileSync(reportPath, destPath);
|
|
1138
1532
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1139
1533
|
} catch (e) {
|
|
1140
1534
|
console.error("Failed to sync report:", e);
|
|
@@ -1151,30 +1545,46 @@ Or specify a custom report:
|
|
|
1151
1545
|
AIREADY_REPORT_PATH: reportPath,
|
|
1152
1546
|
AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
|
|
1153
1547
|
};
|
|
1154
|
-
const vite = spawn("pnpm", ["run", "dev:web"], {
|
|
1548
|
+
const vite = spawn("pnpm", ["run", "dev:web"], {
|
|
1549
|
+
cwd: spawnCwd,
|
|
1550
|
+
stdio: "inherit",
|
|
1551
|
+
shell: true,
|
|
1552
|
+
env: envForSpawn
|
|
1553
|
+
});
|
|
1155
1554
|
const onExit = () => {
|
|
1156
1555
|
try {
|
|
1157
1556
|
reportWatcher.close();
|
|
1158
|
-
} catch (
|
|
1557
|
+
} catch (err) {
|
|
1558
|
+
void err;
|
|
1159
1559
|
}
|
|
1160
1560
|
try {
|
|
1161
1561
|
vite.kill();
|
|
1162
|
-
} catch (
|
|
1562
|
+
} catch (err) {
|
|
1563
|
+
void err;
|
|
1163
1564
|
}
|
|
1164
1565
|
process.exit(0);
|
|
1165
1566
|
};
|
|
1166
1567
|
process.on("SIGINT", onExit);
|
|
1167
1568
|
process.on("SIGTERM", onExit);
|
|
1168
1569
|
devServerStarted = true;
|
|
1570
|
+
void devServerStarted;
|
|
1169
1571
|
return;
|
|
1170
1572
|
} else {
|
|
1171
|
-
console.log(
|
|
1172
|
-
|
|
1573
|
+
console.log(
|
|
1574
|
+
chalk6.yellow(
|
|
1575
|
+
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1576
|
+
)
|
|
1577
|
+
);
|
|
1578
|
+
console.log(
|
|
1579
|
+
chalk6.cyan(" Falling back to static HTML generation...\n")
|
|
1580
|
+
);
|
|
1173
1581
|
useDevMode = false;
|
|
1174
1582
|
}
|
|
1175
1583
|
} catch (err) {
|
|
1176
1584
|
console.error("Failed to start dev server:", err);
|
|
1177
|
-
console.log(
|
|
1585
|
+
console.log(
|
|
1586
|
+
chalk6.cyan(" Falling back to static HTML generation...\n")
|
|
1587
|
+
);
|
|
1178
1588
|
useDevMode = false;
|
|
1179
1589
|
}
|
|
1180
1590
|
}
|
|
@@ -1196,20 +1606,25 @@ Or specify a custom report:
|
|
|
1196
1606
|
const urlPath = req.url || "/";
|
|
1197
1607
|
if (urlPath === "/" || urlPath === "/index.html") {
|
|
1198
1608
|
const content = await fsp.readFile(outPath, "utf8");
|
|
1199
|
-
res.writeHead(200, {
|
|
1609
|
+
res.writeHead(200, {
|
|
1610
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
1611
|
+
});
|
|
1200
1612
|
res.end(content);
|
|
1201
1613
|
return;
|
|
1202
1614
|
}
|
|
1203
1615
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1204
1616
|
res.end("Not found");
|
|
1205
1617
|
} catch (e) {
|
|
1618
|
+
void e;
|
|
1206
1619
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
1207
1620
|
res.end("Server error");
|
|
1208
1621
|
}
|
|
1209
1622
|
});
|
|
1210
1623
|
server.listen(port, () => {
|
|
1211
1624
|
const addr = `http://localhost:${port}/`;
|
|
1212
|
-
console.log(
|
|
1625
|
+
console.log(
|
|
1626
|
+
chalk6.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1627
|
+
);
|
|
1213
1628
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1214
1629
|
});
|
|
1215
1630
|
process.on("SIGINT", () => {
|
|
@@ -1274,9 +1689,13 @@ var getDirname = () => {
|
|
|
1274
1689
|
if (typeof __dirname !== "undefined") return __dirname;
|
|
1275
1690
|
return dirname(fileURLToPath(import.meta.url));
|
|
1276
1691
|
};
|
|
1277
|
-
var packageJson = JSON.parse(
|
|
1692
|
+
var packageJson = JSON.parse(
|
|
1693
|
+
readFileSync4(join(getDirname(), "../package.json"), "utf8")
|
|
1694
|
+
);
|
|
1278
1695
|
var program = new Command();
|
|
1279
|
-
program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
|
|
1696
|
+
program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
|
|
1697
|
+
"after",
|
|
1698
|
+
`
|
|
1280
1699
|
AI READINESS SCORING:
|
|
1281
1700
|
Get a 0-100 score indicating how AI-ready your codebase is.
|
|
1282
1701
|
Use --score flag with any analysis command for detailed breakdown.
|
|
@@ -1311,23 +1730,105 @@ CONFIGURATION:
|
|
|
1311
1730
|
VERSION: ${packageJson.version}
|
|
1312
1731
|
DOCUMENTATION: https://aiready.dev/docs/cli
|
|
1313
1732
|
GITHUB: https://github.com/caopengau/aiready-cli
|
|
1314
|
-
LANDING: https://github.com/caopengau/aiready-landing`
|
|
1315
|
-
|
|
1733
|
+
LANDING: https://github.com/caopengau/aiready-landing`
|
|
1734
|
+
);
|
|
1735
|
+
program.command("scan").description(
|
|
1736
|
+
"Run comprehensive AI-readiness analysis (patterns + context + consistency)"
|
|
1737
|
+
).argument("[directory]", "Directory to analyze", ".").option(
|
|
1738
|
+
"-t, --tools <tools>",
|
|
1739
|
+
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
1740
|
+
).option(
|
|
1741
|
+
"--profile <type>",
|
|
1742
|
+
"Scan profile to use (agentic, cost, security, onboarding)"
|
|
1743
|
+
).option(
|
|
1744
|
+
"--compare-to <path>",
|
|
1745
|
+
"Compare results against a previous AIReady report JSON"
|
|
1746
|
+
).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(
|
|
1747
|
+
"--no-score",
|
|
1748
|
+
"Disable calculating AI Readiness Score (enabled by default)"
|
|
1749
|
+
).option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option(
|
|
1750
|
+
"--ci",
|
|
1751
|
+
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
1752
|
+
).option(
|
|
1753
|
+
"--fail-on <level>",
|
|
1754
|
+
"Fail on issues: critical, major, any",
|
|
1755
|
+
"critical"
|
|
1756
|
+
).addHelpText("after", scanHelpText).action(async (directory, options) => {
|
|
1316
1757
|
await scanAction(directory, options);
|
|
1317
1758
|
});
|
|
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(
|
|
1759
|
+
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(
|
|
1760
|
+
"--max-candidates <number>",
|
|
1761
|
+
"Maximum candidates per block (performance tuning)"
|
|
1762
|
+
).option(
|
|
1763
|
+
"--min-shared-tokens <number>",
|
|
1764
|
+
"Minimum shared tokens for candidates (performance tuning)"
|
|
1765
|
+
).option(
|
|
1766
|
+
"--full-scan",
|
|
1767
|
+
"Disable smart defaults for comprehensive analysis (slower)"
|
|
1768
|
+
).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(
|
|
1769
|
+
"--score",
|
|
1770
|
+
"Calculate and display AI Readiness Score for patterns (0-100)"
|
|
1771
|
+
).addHelpText("after", patternsHelpText).action(async (directory, options) => {
|
|
1319
1772
|
await patternsAction(directory, options);
|
|
1320
1773
|
});
|
|
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(
|
|
1774
|
+
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(
|
|
1775
|
+
"--max-context <number>",
|
|
1776
|
+
"Maximum acceptable context budget (tokens)",
|
|
1777
|
+
"10000"
|
|
1778
|
+
).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(
|
|
1779
|
+
"--score",
|
|
1780
|
+
"Calculate and display AI Readiness Score for context (0-100)"
|
|
1781
|
+
).action(async (directory, options) => {
|
|
1322
1782
|
await contextAction(directory, options);
|
|
1323
1783
|
});
|
|
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(
|
|
1784
|
+
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(
|
|
1785
|
+
"--min-severity <level>",
|
|
1786
|
+
"Minimum severity: info|minor|major|critical",
|
|
1787
|
+
"info"
|
|
1788
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
1789
|
+
"-o, --output <format>",
|
|
1790
|
+
"Output format: console, json, markdown",
|
|
1791
|
+
"console"
|
|
1792
|
+
).option("--output-file <path>", "Output file path (for json/markdown)").option(
|
|
1793
|
+
"--score",
|
|
1794
|
+
"Calculate and display AI Readiness Score for consistency (0-100)"
|
|
1795
|
+
).action(async (directory, options) => {
|
|
1325
1796
|
await consistencyAction(directory, options);
|
|
1326
1797
|
});
|
|
1327
|
-
program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
|
|
1798
|
+
program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
|
|
1799
|
+
"--report <path>",
|
|
1800
|
+
"Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
|
|
1801
|
+
).option(
|
|
1802
|
+
"-o, --output <path>",
|
|
1803
|
+
"Output HTML path (relative to directory)",
|
|
1804
|
+
"packages/visualizer/visualization.html"
|
|
1805
|
+
).option("--open", "Open generated HTML in default browser").option(
|
|
1806
|
+
"--serve [port]",
|
|
1807
|
+
"Start a local static server to serve the visualization (optional port number)",
|
|
1808
|
+
false
|
|
1809
|
+
).option(
|
|
1810
|
+
"--dev",
|
|
1811
|
+
"Start Vite dev server (live reload) for interactive development",
|
|
1812
|
+
true
|
|
1813
|
+
).addHelpText("after", visualiseHelpText).action(async (directory, options) => {
|
|
1328
1814
|
await visualizeAction(directory, options);
|
|
1329
1815
|
});
|
|
1330
|
-
program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option(
|
|
1816
|
+
program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option(
|
|
1817
|
+
"--report <path>",
|
|
1818
|
+
"Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
|
|
1819
|
+
).option(
|
|
1820
|
+
"-o, --output <path>",
|
|
1821
|
+
"Output HTML path (relative to directory)",
|
|
1822
|
+
"packages/visualizer/visualization.html"
|
|
1823
|
+
).option("--open", "Open generated HTML in default browser").option(
|
|
1824
|
+
"--serve [port]",
|
|
1825
|
+
"Start a local static server to serve the visualization (optional port number)",
|
|
1826
|
+
false
|
|
1827
|
+
).option(
|
|
1828
|
+
"--dev",
|
|
1829
|
+
"Start Vite dev server (live reload) for interactive development",
|
|
1830
|
+
false
|
|
1831
|
+
).addHelpText("after", visualizeHelpText).action(async (directory, options) => {
|
|
1331
1832
|
await visualizeAction(directory, options);
|
|
1332
1833
|
});
|
|
1333
1834
|
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) => {
|