@aiready/cli 0.9.41 → 0.9.45
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-20260228-003433.json +7939 -0
- package/.aiready/aiready-report-20260228-003613.json +771 -0
- package/.turbo/turbo-build.log +11 -11
- package/.turbo/turbo-lint.log +5 -0
- package/.turbo/turbo-test.log +4 -4
- package/README.md +22 -1
- package/dist/chunk-LLJMKNBI.mjs +243 -0
- package/dist/cli.js +424 -263
- package/dist/cli.mjs +373 -212
- package/dist/index.js +12 -6
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
- package/src/cli.ts +18 -0
- package/src/commands/ai-signal-clarity.ts +2 -2
- package/src/commands/consistency.ts +1 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/index.ts +1 -0
- package/src/commands/scan.ts +125 -7
- package/src/commands/upload.ts +87 -0
- package/src/index.ts +6 -0
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";
|
|
@@ -11,21 +11,25 @@ import { join, dirname } from "path";
|
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
|
|
13
13
|
// src/commands/scan.ts
|
|
14
|
-
import
|
|
14
|
+
import chalk3 from "chalk";
|
|
15
15
|
import { writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
16
|
-
import { resolve as
|
|
16
|
+
import { resolve as resolvePath3 } from "path";
|
|
17
17
|
import {
|
|
18
18
|
loadMergedConfig,
|
|
19
19
|
handleJSONOutput,
|
|
20
|
-
handleCLIError,
|
|
20
|
+
handleCLIError as handleCLIError2,
|
|
21
21
|
getElapsedTime,
|
|
22
22
|
resolveOutputPath,
|
|
23
23
|
calculateOverallScore,
|
|
24
24
|
formatScore,
|
|
25
25
|
formatToolScore,
|
|
26
|
+
calculateTokenBudget,
|
|
27
|
+
estimateCostFromBudget,
|
|
28
|
+
getModelPreset,
|
|
26
29
|
getRating,
|
|
27
30
|
getRatingDisplay,
|
|
28
|
-
parseWeightString
|
|
31
|
+
parseWeightString,
|
|
32
|
+
getRepoMetadata
|
|
29
33
|
} from "@aiready/core";
|
|
30
34
|
|
|
31
35
|
// src/utils/helpers.ts
|
|
@@ -152,11 +156,81 @@ function truncateArray(arr, cap = 8) {
|
|
|
152
156
|
return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
|
|
153
157
|
}
|
|
154
158
|
|
|
159
|
+
// src/commands/upload.ts
|
|
160
|
+
import fs from "fs";
|
|
161
|
+
import { resolve as resolvePath2 } from "path";
|
|
162
|
+
import chalk2 from "chalk";
|
|
163
|
+
import {
|
|
164
|
+
handleCLIError
|
|
165
|
+
} from "@aiready/core";
|
|
166
|
+
async function uploadAction(file, options) {
|
|
167
|
+
const startTime = Date.now();
|
|
168
|
+
const filePath = resolvePath2(process.cwd(), file);
|
|
169
|
+
const serverUrl = options.server || process.env.AIREADY_SERVER || "https://dev.platform.getaiready.dev";
|
|
170
|
+
const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
|
|
171
|
+
if (!apiKey) {
|
|
172
|
+
console.error(chalk2.red("\u274C API Key is required for upload."));
|
|
173
|
+
console.log(chalk2.dim(" Set AIREADY_API_KEY environment variable or use --api-key flag."));
|
|
174
|
+
console.log(chalk2.dim(" Get an API key from https://getaiready.dev/dashboard"));
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
if (!fs.existsSync(filePath)) {
|
|
178
|
+
console.error(chalk2.red(`\u274C File not found: ${filePath}`));
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
console.log(chalk2.blue(`\u{1F680} Uploading report to ${serverUrl}...`));
|
|
183
|
+
const reportData = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
184
|
+
const repoId = options.repoId || reportData.repository?.repoId;
|
|
185
|
+
const res = await fetch(`${serverUrl}/api/analysis/upload`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
"Authorization": `Bearer ${apiKey}`
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify({
|
|
192
|
+
data: reportData,
|
|
193
|
+
repoId
|
|
194
|
+
// Might be null, server will handle mapping
|
|
195
|
+
})
|
|
196
|
+
});
|
|
197
|
+
const result = await res.json();
|
|
198
|
+
if (!res.ok) {
|
|
199
|
+
console.error(chalk2.red(`\u274C Upload failed: ${result.error || res.statusText}`));
|
|
200
|
+
if (res.status === 401) {
|
|
201
|
+
console.log(chalk2.dim(" Hint: Your API key may be invalid or expired."));
|
|
202
|
+
}
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
206
|
+
console.log(chalk2.green(`
|
|
207
|
+
\u2705 Upload successful! (${duration}s)`));
|
|
208
|
+
console.log(chalk2.cyan(` View results: ${serverUrl}/dashboard`));
|
|
209
|
+
if (result.analysis) {
|
|
210
|
+
console.log(chalk2.dim(` Analysis ID: ${result.analysis.id}`));
|
|
211
|
+
console.log(chalk2.dim(` Score: ${result.analysis.aiScore}/100`));
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
handleCLIError(error, "Upload");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
var uploadHelpText = `
|
|
218
|
+
EXAMPLES:
|
|
219
|
+
$ aiready upload report.json --api-key ar_...
|
|
220
|
+
$ aiready upload .aiready/latest.json
|
|
221
|
+
$ AIREADY_API_KEY=ar_... aiready upload report.json
|
|
222
|
+
|
|
223
|
+
ENVIRONMENT VARIABLES:
|
|
224
|
+
AIREADY_API_KEY Your platform API key
|
|
225
|
+
AIREADY_SERVER Custom platform URL (default: https://dev.platform.getaiready.dev)
|
|
226
|
+
`;
|
|
227
|
+
|
|
155
228
|
// src/commands/scan.ts
|
|
156
229
|
async function scanAction(directory, options) {
|
|
157
|
-
console.log(
|
|
230
|
+
console.log(chalk3.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
158
231
|
const startTime = Date.now();
|
|
159
|
-
const resolvedDir =
|
|
232
|
+
const resolvedDir = resolvePath3(process.cwd(), directory || ".");
|
|
233
|
+
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
160
234
|
try {
|
|
161
235
|
const defaults = {
|
|
162
236
|
tools: [
|
|
@@ -199,7 +273,7 @@ async function scanAction(directory, options) {
|
|
|
199
273
|
break;
|
|
200
274
|
default:
|
|
201
275
|
console.log(
|
|
202
|
-
|
|
276
|
+
chalk3.yellow(
|
|
203
277
|
`
|
|
204
278
|
\u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`
|
|
205
279
|
)
|
|
@@ -231,22 +305,22 @@ async function scanAction(directory, options) {
|
|
|
231
305
|
...baseOptions
|
|
232
306
|
};
|
|
233
307
|
}
|
|
234
|
-
console.log(
|
|
308
|
+
console.log(chalk3.cyan("\n=== AIReady Run Preview ==="));
|
|
235
309
|
console.log(
|
|
236
|
-
|
|
310
|
+
chalk3.white("Tools to run:"),
|
|
237
311
|
(finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
|
|
238
312
|
);
|
|
239
|
-
console.log(
|
|
240
|
-
console.log(
|
|
313
|
+
console.log(chalk3.white("Will use settings from config and defaults."));
|
|
314
|
+
console.log(chalk3.white("\nGeneral settings:"));
|
|
241
315
|
if (finalOptions.rootDir)
|
|
242
|
-
console.log(` rootDir: ${
|
|
316
|
+
console.log(` rootDir: ${chalk3.bold(String(finalOptions.rootDir))}`);
|
|
243
317
|
if (finalOptions.include)
|
|
244
318
|
console.log(
|
|
245
|
-
` include: ${
|
|
319
|
+
` include: ${chalk3.bold(truncateArray(finalOptions.include, 6))}`
|
|
246
320
|
);
|
|
247
321
|
if (finalOptions.exclude)
|
|
248
322
|
console.log(
|
|
249
|
-
` exclude: ${
|
|
323
|
+
` exclude: ${chalk3.bold(truncateArray(finalOptions.exclude, 6))}`
|
|
250
324
|
);
|
|
251
325
|
if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
|
|
252
326
|
const patternDetectConfig = finalOptions["pattern-detect"] || {
|
|
@@ -260,40 +334,40 @@ async function scanAction(directory, options) {
|
|
|
260
334
|
severity: finalOptions.severity,
|
|
261
335
|
includeTests: finalOptions.includeTests
|
|
262
336
|
};
|
|
263
|
-
console.log(
|
|
337
|
+
console.log(chalk3.white("\nPattern-detect settings:"));
|
|
264
338
|
console.log(
|
|
265
|
-
` minSimilarity: ${
|
|
339
|
+
` minSimilarity: ${chalk3.bold(patternDetectConfig.minSimilarity ?? "default")}`
|
|
266
340
|
);
|
|
267
341
|
console.log(
|
|
268
|
-
` minLines: ${
|
|
342
|
+
` minLines: ${chalk3.bold(patternDetectConfig.minLines ?? "default")}`
|
|
269
343
|
);
|
|
270
344
|
if (patternDetectConfig.approx !== void 0)
|
|
271
345
|
console.log(
|
|
272
|
-
` approx: ${
|
|
346
|
+
` approx: ${chalk3.bold(String(patternDetectConfig.approx))}`
|
|
273
347
|
);
|
|
274
348
|
if (patternDetectConfig.minSharedTokens !== void 0)
|
|
275
349
|
console.log(
|
|
276
|
-
` minSharedTokens: ${
|
|
350
|
+
` minSharedTokens: ${chalk3.bold(String(patternDetectConfig.minSharedTokens))}`
|
|
277
351
|
);
|
|
278
352
|
if (patternDetectConfig.maxCandidatesPerBlock !== void 0)
|
|
279
353
|
console.log(
|
|
280
|
-
` maxCandidatesPerBlock: ${
|
|
354
|
+
` maxCandidatesPerBlock: ${chalk3.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
|
|
281
355
|
);
|
|
282
356
|
if (patternDetectConfig.batchSize !== void 0)
|
|
283
357
|
console.log(
|
|
284
|
-
` batchSize: ${
|
|
358
|
+
` batchSize: ${chalk3.bold(String(patternDetectConfig.batchSize))}`
|
|
285
359
|
);
|
|
286
360
|
if (patternDetectConfig.streamResults !== void 0)
|
|
287
361
|
console.log(
|
|
288
|
-
` streamResults: ${
|
|
362
|
+
` streamResults: ${chalk3.bold(String(patternDetectConfig.streamResults))}`
|
|
289
363
|
);
|
|
290
364
|
if (patternDetectConfig.severity !== void 0)
|
|
291
365
|
console.log(
|
|
292
|
-
` severity: ${
|
|
366
|
+
` severity: ${chalk3.bold(String(patternDetectConfig.severity))}`
|
|
293
367
|
);
|
|
294
368
|
if (patternDetectConfig.includeTests !== void 0)
|
|
295
369
|
console.log(
|
|
296
|
-
` includeTests: ${
|
|
370
|
+
` includeTests: ${chalk3.bold(String(patternDetectConfig.includeTests))}`
|
|
297
371
|
);
|
|
298
372
|
}
|
|
299
373
|
if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
|
|
@@ -304,57 +378,57 @@ async function scanAction(directory, options) {
|
|
|
304
378
|
maxFragmentation: finalOptions.maxFragmentation,
|
|
305
379
|
includeNodeModules: finalOptions.includeNodeModules
|
|
306
380
|
};
|
|
307
|
-
console.log(
|
|
308
|
-
console.log(` maxDepth: ${
|
|
381
|
+
console.log(chalk3.white("\nContext-analyzer settings:"));
|
|
382
|
+
console.log(` maxDepth: ${chalk3.bold(ca.maxDepth ?? "default")}`);
|
|
309
383
|
console.log(
|
|
310
|
-
` maxContextBudget: ${
|
|
384
|
+
` maxContextBudget: ${chalk3.bold(ca.maxContextBudget ?? "default")}`
|
|
311
385
|
);
|
|
312
386
|
if (ca.minCohesion !== void 0)
|
|
313
|
-
console.log(` minCohesion: ${
|
|
387
|
+
console.log(` minCohesion: ${chalk3.bold(String(ca.minCohesion))}`);
|
|
314
388
|
if (ca.maxFragmentation !== void 0)
|
|
315
389
|
console.log(
|
|
316
|
-
` maxFragmentation: ${
|
|
390
|
+
` maxFragmentation: ${chalk3.bold(String(ca.maxFragmentation))}`
|
|
317
391
|
);
|
|
318
392
|
if (ca.includeNodeModules !== void 0)
|
|
319
393
|
console.log(
|
|
320
|
-
` includeNodeModules: ${
|
|
394
|
+
` includeNodeModules: ${chalk3.bold(String(ca.includeNodeModules))}`
|
|
321
395
|
);
|
|
322
396
|
}
|
|
323
397
|
if (finalOptions.consistency) {
|
|
324
398
|
const c = finalOptions.consistency;
|
|
325
|
-
console.log(
|
|
399
|
+
console.log(chalk3.white("\nConsistency settings:"));
|
|
326
400
|
console.log(
|
|
327
|
-
` checkNaming: ${
|
|
401
|
+
` checkNaming: ${chalk3.bold(String(c.checkNaming ?? true))}`
|
|
328
402
|
);
|
|
329
403
|
console.log(
|
|
330
|
-
` checkPatterns: ${
|
|
404
|
+
` checkPatterns: ${chalk3.bold(String(c.checkPatterns ?? true))}`
|
|
331
405
|
);
|
|
332
406
|
console.log(
|
|
333
|
-
` checkArchitecture: ${
|
|
407
|
+
` checkArchitecture: ${chalk3.bold(String(c.checkArchitecture ?? false))}`
|
|
334
408
|
);
|
|
335
409
|
if (c.minSeverity)
|
|
336
|
-
console.log(` minSeverity: ${
|
|
410
|
+
console.log(` minSeverity: ${chalk3.bold(c.minSeverity)}`);
|
|
337
411
|
if (c.acceptedAbbreviations)
|
|
338
412
|
console.log(
|
|
339
|
-
` acceptedAbbreviations: ${
|
|
413
|
+
` acceptedAbbreviations: ${chalk3.bold(truncateArray(c.acceptedAbbreviations, 8))}`
|
|
340
414
|
);
|
|
341
415
|
if (c.shortWords)
|
|
342
416
|
console.log(
|
|
343
|
-
` shortWords: ${
|
|
417
|
+
` shortWords: ${chalk3.bold(truncateArray(c.shortWords, 8))}`
|
|
344
418
|
);
|
|
345
419
|
}
|
|
346
|
-
console.log(
|
|
420
|
+
console.log(chalk3.white("\nStarting analysis..."));
|
|
347
421
|
const progressCallback = (event) => {
|
|
348
|
-
console.log(
|
|
422
|
+
console.log(chalk3.cyan(`
|
|
349
423
|
--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
350
424
|
try {
|
|
351
425
|
if (event.tool === "patterns") {
|
|
352
426
|
const pr = event.data;
|
|
353
427
|
console.log(
|
|
354
|
-
` Duplicate patterns: ${
|
|
428
|
+
` Duplicate patterns: ${chalk3.bold(String(pr.duplicates?.length || 0))}`
|
|
355
429
|
);
|
|
356
430
|
console.log(
|
|
357
|
-
` Files with pattern issues: ${
|
|
431
|
+
` Files with pattern issues: ${chalk3.bold(String(pr.results?.length || 0))}`
|
|
358
432
|
);
|
|
359
433
|
if (pr.duplicates && pr.duplicates.length > 0) {
|
|
360
434
|
pr.duplicates.slice(0, 5).forEach((d, i) => {
|
|
@@ -376,12 +450,12 @@ async function scanAction(directory, options) {
|
|
|
376
450
|
}
|
|
377
451
|
if (pr.groups && pr.groups.length >= 0) {
|
|
378
452
|
console.log(
|
|
379
|
-
` \u2705 Grouped ${
|
|
453
|
+
` \u2705 Grouped ${chalk3.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk3.bold(String(pr.groups.length))} file pairs`
|
|
380
454
|
);
|
|
381
455
|
}
|
|
382
456
|
if (pr.clusters && pr.clusters.length >= 0) {
|
|
383
457
|
console.log(
|
|
384
|
-
` \u2705 Created ${
|
|
458
|
+
` \u2705 Created ${chalk3.bold(String(pr.clusters.length))} refactor clusters`
|
|
385
459
|
);
|
|
386
460
|
pr.clusters.slice(0, 3).forEach((cl, idx) => {
|
|
387
461
|
const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
|
|
@@ -393,7 +467,7 @@ async function scanAction(directory, options) {
|
|
|
393
467
|
} else if (event.tool === "context") {
|
|
394
468
|
const cr = event.data;
|
|
395
469
|
console.log(
|
|
396
|
-
` Context issues found: ${
|
|
470
|
+
` Context issues found: ${chalk3.bold(String(cr.length || 0))}`
|
|
397
471
|
);
|
|
398
472
|
cr.slice(0, 5).forEach((c, i) => {
|
|
399
473
|
const msg = c.message ? ` - ${c.message}` : "";
|
|
@@ -404,7 +478,7 @@ async function scanAction(directory, options) {
|
|
|
404
478
|
} else if (event.tool === "consistency") {
|
|
405
479
|
const rep = event.data;
|
|
406
480
|
console.log(
|
|
407
|
-
` Consistency totalIssues: ${
|
|
481
|
+
` Consistency totalIssues: ${chalk3.bold(String(rep.summary?.totalIssues || 0))}`
|
|
408
482
|
);
|
|
409
483
|
if (rep.results && rep.results.length > 0) {
|
|
410
484
|
const fileMap = /* @__PURE__ */ new Map();
|
|
@@ -439,7 +513,7 @@ async function scanAction(directory, options) {
|
|
|
439
513
|
const remaining = files.length - topFiles.length;
|
|
440
514
|
if (remaining > 0) {
|
|
441
515
|
console.log(
|
|
442
|
-
|
|
516
|
+
chalk3.dim(
|
|
443
517
|
` ... and ${remaining} more files with issues (use --output json for full details)`
|
|
444
518
|
)
|
|
445
519
|
);
|
|
@@ -448,37 +522,37 @@ async function scanAction(directory, options) {
|
|
|
448
522
|
} else if (event.tool === "doc-drift") {
|
|
449
523
|
const dr = event.data;
|
|
450
524
|
console.log(
|
|
451
|
-
` Issues found: ${
|
|
525
|
+
` Issues found: ${chalk3.bold(String(dr.issues?.length || 0))}`
|
|
452
526
|
);
|
|
453
527
|
if (dr.rawData) {
|
|
454
528
|
console.log(
|
|
455
|
-
` Signature Mismatches: ${
|
|
529
|
+
` Signature Mismatches: ${chalk3.bold(dr.rawData.outdatedComments || 0)}`
|
|
456
530
|
);
|
|
457
531
|
console.log(
|
|
458
|
-
` Undocumented Complexity: ${
|
|
532
|
+
` Undocumented Complexity: ${chalk3.bold(dr.rawData.undocumentedComplexity || 0)}`
|
|
459
533
|
);
|
|
460
534
|
}
|
|
461
535
|
} else if (event.tool === "deps-health") {
|
|
462
536
|
const dr = event.data;
|
|
463
537
|
console.log(
|
|
464
|
-
` Packages Analyzed: ${
|
|
538
|
+
` Packages Analyzed: ${chalk3.bold(String(dr.summary?.packagesAnalyzed || 0))}`
|
|
465
539
|
);
|
|
466
540
|
if (dr.rawData) {
|
|
467
541
|
console.log(
|
|
468
|
-
` Deprecated Packages: ${
|
|
542
|
+
` Deprecated Packages: ${chalk3.bold(dr.rawData.deprecatedPackages || 0)}`
|
|
469
543
|
);
|
|
470
544
|
console.log(
|
|
471
|
-
` AI Cutoff Skew Score: ${
|
|
545
|
+
` AI Cutoff Skew Score: ${chalk3.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`
|
|
472
546
|
);
|
|
473
547
|
}
|
|
474
548
|
} else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
|
|
475
549
|
const dr = event.data;
|
|
476
550
|
console.log(
|
|
477
|
-
` Coupling issues: ${
|
|
551
|
+
` Coupling issues: ${chalk3.bold(String(dr.issues?.length || 0))}`
|
|
478
552
|
);
|
|
479
553
|
if (dr.summary) {
|
|
480
554
|
console.log(
|
|
481
|
-
` Complexity Score: ${
|
|
555
|
+
` Complexity Score: ${chalk3.bold(dr.summary.score || 0)}/100`
|
|
482
556
|
);
|
|
483
557
|
}
|
|
484
558
|
}
|
|
@@ -489,37 +563,45 @@ async function scanAction(directory, options) {
|
|
|
489
563
|
const results = await analyzeUnified({
|
|
490
564
|
...finalOptions,
|
|
491
565
|
progressCallback,
|
|
566
|
+
onProgress: (processed, total, message) => {
|
|
567
|
+
process.stdout.write(
|
|
568
|
+
`\r\x1B[K [${processed}/${total}] ${message}...`
|
|
569
|
+
);
|
|
570
|
+
if (processed === total) {
|
|
571
|
+
process.stdout.write("\n");
|
|
572
|
+
}
|
|
573
|
+
},
|
|
492
574
|
suppressToolConfig: true
|
|
493
575
|
});
|
|
494
|
-
console.log(
|
|
576
|
+
console.log(chalk3.cyan("\n=== AIReady Run Summary ==="));
|
|
495
577
|
console.log(
|
|
496
|
-
|
|
578
|
+
chalk3.white("Tools run:"),
|
|
497
579
|
(finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
|
|
498
580
|
);
|
|
499
|
-
console.log(
|
|
581
|
+
console.log(chalk3.cyan("\nResults summary:"));
|
|
500
582
|
console.log(
|
|
501
|
-
` Total issues (all tools): ${
|
|
583
|
+
` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
|
|
502
584
|
);
|
|
503
585
|
if (results.duplicates)
|
|
504
586
|
console.log(
|
|
505
|
-
` Duplicate patterns found: ${
|
|
587
|
+
` Duplicate patterns found: ${chalk3.bold(String(results.duplicates.length || 0))}`
|
|
506
588
|
);
|
|
507
589
|
if (results.patterns)
|
|
508
590
|
console.log(
|
|
509
|
-
` Pattern files with issues: ${
|
|
591
|
+
` Pattern files with issues: ${chalk3.bold(String(results.patterns.length || 0))}`
|
|
510
592
|
);
|
|
511
593
|
if (results.context)
|
|
512
594
|
console.log(
|
|
513
|
-
` Context issues: ${
|
|
595
|
+
` Context issues: ${chalk3.bold(String(results.context.length || 0))}`
|
|
514
596
|
);
|
|
515
597
|
console.log(
|
|
516
|
-
` Consistency issues: ${
|
|
598
|
+
` Consistency issues: ${chalk3.bold(String(results.consistency?.summary?.totalIssues || 0))}`
|
|
517
599
|
);
|
|
518
600
|
if (results.changeAmplification)
|
|
519
601
|
console.log(
|
|
520
|
-
` Change amplification: ${
|
|
602
|
+
` Change amplification: ${chalk3.bold(String(results.changeAmplification.summary?.score || 0))}/100`
|
|
521
603
|
);
|
|
522
|
-
console.log(
|
|
604
|
+
console.log(chalk3.cyan("===========================\n"));
|
|
523
605
|
const elapsedTime = getElapsedTime(startTime);
|
|
524
606
|
void elapsedTime;
|
|
525
607
|
let scoringResult;
|
|
@@ -532,6 +614,16 @@ async function scanAction(directory, options) {
|
|
|
532
614
|
results.duplicates,
|
|
533
615
|
results.patterns?.length || 0
|
|
534
616
|
);
|
|
617
|
+
const wastedTokens = results.duplicates.reduce((sum, d) => sum + (d.tokenCost || 0), 0);
|
|
618
|
+
patternScore.tokenBudget = calculateTokenBudget({
|
|
619
|
+
totalContextTokens: wastedTokens * 2,
|
|
620
|
+
// Estimated context
|
|
621
|
+
wastedTokens: {
|
|
622
|
+
duplication: wastedTokens,
|
|
623
|
+
fragmentation: 0,
|
|
624
|
+
chattiness: 0
|
|
625
|
+
}
|
|
626
|
+
});
|
|
535
627
|
toolScores.set("pattern-detect", patternScore);
|
|
536
628
|
} catch (err) {
|
|
537
629
|
void err;
|
|
@@ -542,6 +634,14 @@ async function scanAction(directory, options) {
|
|
|
542
634
|
try {
|
|
543
635
|
const ctxSummary = genContextSummary(results.context);
|
|
544
636
|
const contextScore = calculateContextScore(ctxSummary);
|
|
637
|
+
contextScore.tokenBudget = calculateTokenBudget({
|
|
638
|
+
totalContextTokens: ctxSummary.totalTokens,
|
|
639
|
+
wastedTokens: {
|
|
640
|
+
duplication: 0,
|
|
641
|
+
fragmentation: ctxSummary.totalPotentialSavings || 0,
|
|
642
|
+
chattiness: 0
|
|
643
|
+
}
|
|
644
|
+
});
|
|
545
645
|
toolScores.set("context-analyzer", contextScore);
|
|
546
646
|
} catch (err) {
|
|
547
647
|
void err;
|
|
@@ -640,12 +740,12 @@ async function scanAction(directory, options) {
|
|
|
640
740
|
finalOptions,
|
|
641
741
|
cliWeights.size ? cliWeights : void 0
|
|
642
742
|
);
|
|
643
|
-
console.log(
|
|
743
|
+
console.log(chalk3.bold("\n\u{1F4CA} AI Readiness Overall Score"));
|
|
644
744
|
console.log(` ${formatScore(scoringResult)}`);
|
|
645
745
|
if (options.compareTo) {
|
|
646
746
|
try {
|
|
647
747
|
const prevReportStr = readFileSync2(
|
|
648
|
-
|
|
748
|
+
resolvePath3(process.cwd(), options.compareTo),
|
|
649
749
|
"utf8"
|
|
650
750
|
);
|
|
651
751
|
const prevReport = JSON.parse(prevReportStr);
|
|
@@ -656,19 +756,19 @@ async function scanAction(directory, options) {
|
|
|
656
756
|
console.log();
|
|
657
757
|
if (diff > 0) {
|
|
658
758
|
console.log(
|
|
659
|
-
|
|
759
|
+
chalk3.green(
|
|
660
760
|
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
661
761
|
)
|
|
662
762
|
);
|
|
663
763
|
} else if (diff < 0) {
|
|
664
764
|
console.log(
|
|
665
|
-
|
|
765
|
+
chalk3.red(
|
|
666
766
|
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
667
767
|
)
|
|
668
768
|
);
|
|
669
769
|
} else {
|
|
670
770
|
console.log(
|
|
671
|
-
|
|
771
|
+
chalk3.blue(
|
|
672
772
|
` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
673
773
|
)
|
|
674
774
|
);
|
|
@@ -679,7 +779,7 @@ async function scanAction(directory, options) {
|
|
|
679
779
|
};
|
|
680
780
|
} else {
|
|
681
781
|
console.log(
|
|
682
|
-
|
|
782
|
+
chalk3.yellow(
|
|
683
783
|
`
|
|
684
784
|
\u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
|
|
685
785
|
)
|
|
@@ -688,15 +788,49 @@ async function scanAction(directory, options) {
|
|
|
688
788
|
} catch (e) {
|
|
689
789
|
void e;
|
|
690
790
|
console.log(
|
|
691
|
-
|
|
791
|
+
chalk3.yellow(
|
|
692
792
|
`
|
|
693
793
|
\u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
|
|
694
794
|
)
|
|
695
795
|
);
|
|
696
796
|
}
|
|
697
797
|
}
|
|
798
|
+
const totalWastedDuplication = Array.from(toolScores.values()).reduce((sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.duplication || 0), 0);
|
|
799
|
+
const totalWastedFragmentation = Array.from(toolScores.values()).reduce((sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.fragmentation || 0), 0);
|
|
800
|
+
const totalContext = Math.max(...Array.from(toolScores.values()).map((s) => s.tokenBudget?.totalContextTokens || 0));
|
|
801
|
+
if (totalContext > 0) {
|
|
802
|
+
const unifiedBudget = calculateTokenBudget({
|
|
803
|
+
totalContextTokens: totalContext,
|
|
804
|
+
wastedTokens: {
|
|
805
|
+
duplication: totalWastedDuplication,
|
|
806
|
+
fragmentation: totalWastedFragmentation,
|
|
807
|
+
chattiness: 0
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
const targetModel = options.model || "claude-4.6";
|
|
811
|
+
const modelPreset = getModelPreset(targetModel);
|
|
812
|
+
const costEstimate = estimateCostFromBudget(unifiedBudget, modelPreset);
|
|
813
|
+
const barWidth = 20;
|
|
814
|
+
const filled = Math.round(unifiedBudget.efficiencyRatio * barWidth);
|
|
815
|
+
const bar = chalk3.green("\u2588".repeat(filled)) + chalk3.dim("\u2591".repeat(barWidth - filled));
|
|
816
|
+
console.log(chalk3.bold("\n\u{1F4CA} AI Token Budget Analysis (v0.13)"));
|
|
817
|
+
console.log(` Efficiency: [${bar}] ${(unifiedBudget.efficiencyRatio * 100).toFixed(0)}%`);
|
|
818
|
+
console.log(` Total Context: ${chalk3.bold(unifiedBudget.totalContextTokens.toLocaleString())} tokens`);
|
|
819
|
+
console.log(` Wasted Tokens: ${chalk3.red(unifiedBudget.wastedTokens.total.toLocaleString())} (${(unifiedBudget.wastedTokens.total / unifiedBudget.totalContextTokens * 100).toFixed(1)}%)`);
|
|
820
|
+
console.log(` Waste Breakdown:`);
|
|
821
|
+
console.log(` \u2022 Duplication: ${unifiedBudget.wastedTokens.bySource.duplication.toLocaleString()} tokens`);
|
|
822
|
+
console.log(` \u2022 Fragmentation: ${unifiedBudget.wastedTokens.bySource.fragmentation.toLocaleString()} tokens`);
|
|
823
|
+
console.log(` Potential Savings: ${chalk3.green(unifiedBudget.potentialRetrievableTokens.toLocaleString())} tokens retrievable`);
|
|
824
|
+
console.log(`
|
|
825
|
+
Est. Monthly Cost (${modelPreset.name}): ${chalk3.bold("$" + costEstimate.total)} [range: $${costEstimate.range[0]}-$${costEstimate.range[1]}]`);
|
|
826
|
+
scoringResult.tokenBudget = unifiedBudget;
|
|
827
|
+
scoringResult.costEstimate = {
|
|
828
|
+
model: modelPreset.name,
|
|
829
|
+
...costEstimate
|
|
830
|
+
};
|
|
831
|
+
}
|
|
698
832
|
if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
|
|
699
|
-
console.log(
|
|
833
|
+
console.log(chalk3.bold("\nTool breakdown:"));
|
|
700
834
|
scoringResult.breakdown.forEach((tool) => {
|
|
701
835
|
const rating = getRating(tool.score);
|
|
702
836
|
const rd = getRatingDisplay(rating);
|
|
@@ -706,7 +840,7 @@ async function scanAction(directory, options) {
|
|
|
706
840
|
});
|
|
707
841
|
console.log();
|
|
708
842
|
if (finalOptions.scoring?.showBreakdown) {
|
|
709
|
-
console.log(
|
|
843
|
+
console.log(chalk3.bold("Detailed tool breakdown:"));
|
|
710
844
|
scoringResult.breakdown.forEach((tool) => {
|
|
711
845
|
console.log(formatToolScore(tool));
|
|
712
846
|
});
|
|
@@ -725,12 +859,23 @@ async function scanAction(directory, options) {
|
|
|
725
859
|
defaultFilename,
|
|
726
860
|
resolvedDir
|
|
727
861
|
);
|
|
728
|
-
const outputData = {
|
|
862
|
+
const outputData = {
|
|
863
|
+
...results,
|
|
864
|
+
scoring: scoringResult,
|
|
865
|
+
repository: repoMetadata
|
|
866
|
+
};
|
|
729
867
|
handleJSONOutput(
|
|
730
868
|
outputData,
|
|
731
869
|
outputPath,
|
|
732
870
|
`\u2705 Report saved to ${outputPath}`
|
|
733
871
|
);
|
|
872
|
+
if (options.upload) {
|
|
873
|
+
console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
|
|
874
|
+
await uploadAction(outputPath, {
|
|
875
|
+
apiKey: options.apiKey,
|
|
876
|
+
server: options.server
|
|
877
|
+
});
|
|
878
|
+
}
|
|
734
879
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
735
880
|
} else {
|
|
736
881
|
const timestamp = getReportTimestamp();
|
|
@@ -740,10 +885,21 @@ async function scanAction(directory, options) {
|
|
|
740
885
|
defaultFilename,
|
|
741
886
|
resolvedDir
|
|
742
887
|
);
|
|
743
|
-
const outputData = {
|
|
888
|
+
const outputData = {
|
|
889
|
+
...results,
|
|
890
|
+
scoring: scoringResult,
|
|
891
|
+
repository: repoMetadata
|
|
892
|
+
};
|
|
744
893
|
try {
|
|
745
894
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
746
|
-
console.log(
|
|
895
|
+
console.log(chalk3.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
896
|
+
if (options.upload) {
|
|
897
|
+
console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
|
|
898
|
+
await uploadAction(outputPath, {
|
|
899
|
+
apiKey: options.apiKey,
|
|
900
|
+
server: options.server
|
|
901
|
+
});
|
|
902
|
+
}
|
|
747
903
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
748
904
|
} catch (err) {
|
|
749
905
|
void err;
|
|
@@ -825,37 +981,37 @@ async function scanAction(directory, options) {
|
|
|
825
981
|
}
|
|
826
982
|
}
|
|
827
983
|
if (shouldFail) {
|
|
828
|
-
console.log(
|
|
829
|
-
console.log(
|
|
830
|
-
console.log(
|
|
984
|
+
console.log(chalk3.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
|
|
985
|
+
console.log(chalk3.red(` Reason: ${failReason}`));
|
|
986
|
+
console.log(chalk3.dim("\n Remediation steps:"));
|
|
831
987
|
console.log(
|
|
832
|
-
|
|
988
|
+
chalk3.dim(" 1. Run `aiready scan` locally to see detailed issues")
|
|
833
989
|
);
|
|
834
|
-
console.log(
|
|
990
|
+
console.log(chalk3.dim(" 2. Fix the critical issues before merging"));
|
|
835
991
|
console.log(
|
|
836
|
-
|
|
992
|
+
chalk3.dim(
|
|
837
993
|
" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
|
|
838
994
|
)
|
|
839
995
|
);
|
|
840
996
|
process.exit(1);
|
|
841
997
|
} else {
|
|
842
|
-
console.log(
|
|
998
|
+
console.log(chalk3.green("\n\u2705 PR PASSED: AI Readiness Check"));
|
|
843
999
|
if (threshold) {
|
|
844
1000
|
console.log(
|
|
845
|
-
|
|
1001
|
+
chalk3.green(
|
|
846
1002
|
` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
|
|
847
1003
|
)
|
|
848
1004
|
);
|
|
849
1005
|
}
|
|
850
1006
|
console.log(
|
|
851
|
-
|
|
1007
|
+
chalk3.dim(
|
|
852
1008
|
"\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
|
|
853
1009
|
)
|
|
854
1010
|
);
|
|
855
1011
|
}
|
|
856
1012
|
}
|
|
857
1013
|
} catch (error) {
|
|
858
|
-
|
|
1014
|
+
handleCLIError2(error, "Analysis");
|
|
859
1015
|
}
|
|
860
1016
|
}
|
|
861
1017
|
var scanHelpText = `
|
|
@@ -869,6 +1025,8 @@ EXAMPLES:
|
|
|
869
1025
|
$ aiready scan --ci --threshold 70 # GitHub Actions gatekeeper
|
|
870
1026
|
$ aiready scan --ci --fail-on major # Fail on major+ issues
|
|
871
1027
|
$ aiready scan --output json --output-file report.json
|
|
1028
|
+
$ aiready scan --upload --api-key ar_... # Automatic platform upload
|
|
1029
|
+
$ aiready scan --upload --server custom-url.com # Upload to custom platform
|
|
872
1030
|
|
|
873
1031
|
PROFILES:
|
|
874
1032
|
agentic: aiSignalClarity, grounding, testability
|
|
@@ -888,20 +1046,20 @@ CI/CD INTEGRATION (Gatekeeper Mode):
|
|
|
888
1046
|
`;
|
|
889
1047
|
|
|
890
1048
|
// src/commands/patterns.ts
|
|
891
|
-
import
|
|
892
|
-
import { resolve as
|
|
1049
|
+
import chalk4 from "chalk";
|
|
1050
|
+
import { resolve as resolvePath4 } from "path";
|
|
893
1051
|
import {
|
|
894
1052
|
loadMergedConfig as loadMergedConfig2,
|
|
895
1053
|
handleJSONOutput as handleJSONOutput2,
|
|
896
|
-
handleCLIError as
|
|
1054
|
+
handleCLIError as handleCLIError3,
|
|
897
1055
|
getElapsedTime as getElapsedTime2,
|
|
898
1056
|
resolveOutputPath as resolveOutputPath2,
|
|
899
1057
|
formatToolScore as formatToolScore2
|
|
900
1058
|
} from "@aiready/core";
|
|
901
1059
|
async function patternsAction(directory, options) {
|
|
902
|
-
console.log(
|
|
1060
|
+
console.log(chalk4.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
903
1061
|
const startTime = Date.now();
|
|
904
|
-
const resolvedDir =
|
|
1062
|
+
const resolvedDir = resolvePath4(process.cwd(), directory || ".");
|
|
905
1063
|
try {
|
|
906
1064
|
const useSmartDefaults = !options.fullScan;
|
|
907
1065
|
const defaults = {
|
|
@@ -965,38 +1123,38 @@ async function patternsAction(directory, options) {
|
|
|
965
1123
|
const terminalWidth = process.stdout.columns || 80;
|
|
966
1124
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
967
1125
|
const divider = "\u2501".repeat(dividerWidth);
|
|
968
|
-
console.log(
|
|
969
|
-
console.log(
|
|
970
|
-
console.log(
|
|
1126
|
+
console.log(chalk4.cyan(divider));
|
|
1127
|
+
console.log(chalk4.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
1128
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
971
1129
|
console.log(
|
|
972
|
-
|
|
1130
|
+
chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(results.length)}`)
|
|
973
1131
|
);
|
|
974
1132
|
console.log(
|
|
975
|
-
|
|
976
|
-
`\u26A0 Duplicate patterns found: ${
|
|
1133
|
+
chalk4.yellow(
|
|
1134
|
+
`\u26A0 Duplicate patterns found: ${chalk4.bold(summary.totalPatterns)}`
|
|
977
1135
|
)
|
|
978
1136
|
);
|
|
979
1137
|
console.log(
|
|
980
|
-
|
|
981
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
1138
|
+
chalk4.red(
|
|
1139
|
+
`\u{1F4B0} Token cost (wasted): ${chalk4.bold(summary.totalTokenCost.toLocaleString())}`
|
|
982
1140
|
)
|
|
983
1141
|
);
|
|
984
1142
|
console.log(
|
|
985
|
-
|
|
1143
|
+
chalk4.gray(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}`)
|
|
986
1144
|
);
|
|
987
1145
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
988
1146
|
if (sortedTypes.length > 0) {
|
|
989
|
-
console.log(
|
|
990
|
-
console.log(
|
|
991
|
-
console.log(
|
|
1147
|
+
console.log(chalk4.cyan("\n" + divider));
|
|
1148
|
+
console.log(chalk4.bold.white(" PATTERNS BY TYPE"));
|
|
1149
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
992
1150
|
sortedTypes.forEach(([type, count]) => {
|
|
993
|
-
console.log(` ${
|
|
1151
|
+
console.log(` ${chalk4.white(type.padEnd(15))} ${chalk4.bold(count)}`);
|
|
994
1152
|
});
|
|
995
1153
|
}
|
|
996
1154
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
997
|
-
console.log(
|
|
998
|
-
console.log(
|
|
999
|
-
console.log(
|
|
1155
|
+
console.log(chalk4.cyan("\n" + divider));
|
|
1156
|
+
console.log(chalk4.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
1157
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
1000
1158
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
1001
1159
|
topDuplicates.forEach((dup) => {
|
|
1002
1160
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -1004,31 +1162,31 @@ async function patternsAction(directory, options) {
|
|
|
1004
1162
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
1005
1163
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
1006
1164
|
console.log(
|
|
1007
|
-
`${severityIcon} ${severity}: ${
|
|
1165
|
+
`${severityIcon} ${severity}: ${chalk4.bold(file1Name)} \u2194 ${chalk4.bold(file2Name)}`
|
|
1008
1166
|
);
|
|
1009
1167
|
console.log(
|
|
1010
|
-
` Similarity: ${
|
|
1168
|
+
` Similarity: ${chalk4.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk4.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1011
1169
|
);
|
|
1012
1170
|
console.log(
|
|
1013
|
-
` Lines: ${
|
|
1171
|
+
` Lines: ${chalk4.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk4.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1014
1172
|
`
|
|
1015
1173
|
);
|
|
1016
1174
|
});
|
|
1017
1175
|
} else {
|
|
1018
1176
|
console.log(
|
|
1019
|
-
|
|
1177
|
+
chalk4.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1020
1178
|
);
|
|
1021
1179
|
}
|
|
1022
1180
|
if (patternScore) {
|
|
1023
|
-
console.log(
|
|
1024
|
-
console.log(
|
|
1025
|
-
console.log(
|
|
1181
|
+
console.log(chalk4.cyan(divider));
|
|
1182
|
+
console.log(chalk4.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
1183
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
1026
1184
|
console.log(formatToolScore2(patternScore));
|
|
1027
1185
|
console.log();
|
|
1028
1186
|
}
|
|
1029
1187
|
}
|
|
1030
1188
|
} catch (error) {
|
|
1031
|
-
|
|
1189
|
+
handleCLIError3(error, "Pattern analysis");
|
|
1032
1190
|
}
|
|
1033
1191
|
}
|
|
1034
1192
|
var patternsHelpText = `
|
|
@@ -1039,20 +1197,20 @@ EXAMPLES:
|
|
|
1039
1197
|
`;
|
|
1040
1198
|
|
|
1041
1199
|
// src/commands/context.ts
|
|
1042
|
-
import
|
|
1043
|
-
import { resolve as
|
|
1200
|
+
import chalk5 from "chalk";
|
|
1201
|
+
import { resolve as resolvePath5 } from "path";
|
|
1044
1202
|
import {
|
|
1045
1203
|
loadMergedConfig as loadMergedConfig3,
|
|
1046
1204
|
handleJSONOutput as handleJSONOutput3,
|
|
1047
|
-
handleCLIError as
|
|
1205
|
+
handleCLIError as handleCLIError4,
|
|
1048
1206
|
getElapsedTime as getElapsedTime3,
|
|
1049
1207
|
resolveOutputPath as resolveOutputPath3,
|
|
1050
1208
|
formatToolScore as formatToolScore3
|
|
1051
1209
|
} from "@aiready/core";
|
|
1052
1210
|
async function contextAction(directory, options) {
|
|
1053
|
-
console.log(
|
|
1211
|
+
console.log(chalk5.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
1054
1212
|
const startTime = Date.now();
|
|
1055
|
-
const resolvedDir =
|
|
1213
|
+
const resolvedDir = resolvePath5(process.cwd(), directory || ".");
|
|
1056
1214
|
try {
|
|
1057
1215
|
const defaults = {
|
|
1058
1216
|
maxDepth: 5,
|
|
@@ -1118,85 +1276,85 @@ async function contextAction(directory, options) {
|
|
|
1118
1276
|
const terminalWidth = process.stdout.columns || 80;
|
|
1119
1277
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1120
1278
|
const divider = "\u2501".repeat(dividerWidth);
|
|
1121
|
-
console.log(
|
|
1122
|
-
console.log(
|
|
1123
|
-
console.log(
|
|
1279
|
+
console.log(chalk5.cyan(divider));
|
|
1280
|
+
console.log(chalk5.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1281
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
1124
1282
|
console.log(
|
|
1125
|
-
|
|
1283
|
+
chalk5.white(`\u{1F4C1} Files analyzed: ${chalk5.bold(summary.totalFiles)}`)
|
|
1126
1284
|
);
|
|
1127
1285
|
console.log(
|
|
1128
|
-
|
|
1129
|
-
`\u{1F4CA} Total tokens: ${
|
|
1286
|
+
chalk5.white(
|
|
1287
|
+
`\u{1F4CA} Total tokens: ${chalk5.bold(summary.totalTokens.toLocaleString())}`
|
|
1130
1288
|
)
|
|
1131
1289
|
);
|
|
1132
1290
|
console.log(
|
|
1133
|
-
|
|
1134
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1291
|
+
chalk5.yellow(
|
|
1292
|
+
`\u{1F4B0} Avg context budget: ${chalk5.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1135
1293
|
)
|
|
1136
1294
|
);
|
|
1137
1295
|
console.log(
|
|
1138
|
-
|
|
1296
|
+
chalk5.white(`\u23F1 Analysis time: ${chalk5.bold(elapsedTime + "s")}
|
|
1139
1297
|
`)
|
|
1140
1298
|
);
|
|
1141
1299
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1142
1300
|
if (totalIssues > 0) {
|
|
1143
|
-
console.log(
|
|
1301
|
+
console.log(chalk5.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1144
1302
|
if (summary.criticalIssues > 0) {
|
|
1145
1303
|
console.log(
|
|
1146
|
-
|
|
1304
|
+
chalk5.red(` \u{1F534} Critical: ${chalk5.bold(summary.criticalIssues)}`)
|
|
1147
1305
|
);
|
|
1148
1306
|
}
|
|
1149
1307
|
if (summary.majorIssues > 0) {
|
|
1150
1308
|
console.log(
|
|
1151
|
-
|
|
1309
|
+
chalk5.yellow(` \u{1F7E1} Major: ${chalk5.bold(summary.majorIssues)}`)
|
|
1152
1310
|
);
|
|
1153
1311
|
}
|
|
1154
1312
|
if (summary.minorIssues > 0) {
|
|
1155
1313
|
console.log(
|
|
1156
|
-
|
|
1314
|
+
chalk5.blue(` \u{1F535} Minor: ${chalk5.bold(summary.minorIssues)}`)
|
|
1157
1315
|
);
|
|
1158
1316
|
}
|
|
1159
1317
|
console.log(
|
|
1160
|
-
|
|
1318
|
+
chalk5.green(
|
|
1161
1319
|
`
|
|
1162
|
-
\u{1F4A1} Potential savings: ${
|
|
1320
|
+
\u{1F4A1} Potential savings: ${chalk5.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1163
1321
|
`
|
|
1164
1322
|
)
|
|
1165
1323
|
);
|
|
1166
1324
|
} else {
|
|
1167
|
-
console.log(
|
|
1325
|
+
console.log(chalk5.green("\u2705 No significant issues found!\n"));
|
|
1168
1326
|
}
|
|
1169
1327
|
if (summary.deepFiles.length > 0) {
|
|
1170
|
-
console.log(
|
|
1328
|
+
console.log(chalk5.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1171
1329
|
console.log(
|
|
1172
|
-
|
|
1330
|
+
chalk5.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1173
1331
|
);
|
|
1174
1332
|
console.log(
|
|
1175
|
-
|
|
1333
|
+
chalk5.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1176
1334
|
`)
|
|
1177
1335
|
);
|
|
1178
1336
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1179
1337
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1180
1338
|
console.log(
|
|
1181
|
-
` ${
|
|
1339
|
+
` ${chalk5.cyan("\u2192")} ${chalk5.white(fileName)} ${chalk5.dim(`(depth: ${item.depth})`)}`
|
|
1182
1340
|
);
|
|
1183
1341
|
});
|
|
1184
1342
|
console.log();
|
|
1185
1343
|
}
|
|
1186
1344
|
if (summary.fragmentedModules.length > 0) {
|
|
1187
|
-
console.log(
|
|
1345
|
+
console.log(chalk5.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1188
1346
|
console.log(
|
|
1189
|
-
|
|
1347
|
+
chalk5.gray(
|
|
1190
1348
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1191
1349
|
`
|
|
1192
1350
|
)
|
|
1193
1351
|
);
|
|
1194
1352
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1195
1353
|
console.log(
|
|
1196
|
-
` ${
|
|
1354
|
+
` ${chalk5.yellow("\u25CF")} ${chalk5.white(module.domain)} - ${chalk5.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1197
1355
|
);
|
|
1198
1356
|
console.log(
|
|
1199
|
-
|
|
1357
|
+
chalk5.dim(
|
|
1200
1358
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1201
1359
|
)
|
|
1202
1360
|
);
|
|
@@ -1204,9 +1362,9 @@ async function contextAction(directory, options) {
|
|
|
1204
1362
|
console.log();
|
|
1205
1363
|
}
|
|
1206
1364
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1207
|
-
console.log(
|
|
1365
|
+
console.log(chalk5.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1208
1366
|
console.log(
|
|
1209
|
-
|
|
1367
|
+
chalk5.gray(
|
|
1210
1368
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1211
1369
|
`
|
|
1212
1370
|
)
|
|
@@ -1214,53 +1372,53 @@ async function contextAction(directory, options) {
|
|
|
1214
1372
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1215
1373
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1216
1374
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1217
|
-
const color = item.score < 0.4 ?
|
|
1375
|
+
const color = item.score < 0.4 ? chalk5.red : chalk5.yellow;
|
|
1218
1376
|
console.log(
|
|
1219
|
-
` ${color("\u25CB")} ${
|
|
1377
|
+
` ${color("\u25CB")} ${chalk5.white(fileName)} ${chalk5.dim(`(${scorePercent}% cohesion)`)}`
|
|
1220
1378
|
);
|
|
1221
1379
|
});
|
|
1222
1380
|
console.log();
|
|
1223
1381
|
}
|
|
1224
1382
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1225
|
-
console.log(
|
|
1383
|
+
console.log(chalk5.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1226
1384
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1227
1385
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1228
|
-
const severityColor = item.severity === "critical" ?
|
|
1386
|
+
const severityColor = item.severity === "critical" ? chalk5.red : item.severity === "major" ? chalk5.yellow : chalk5.blue;
|
|
1229
1387
|
console.log(
|
|
1230
|
-
` ${severityColor("\u25CF")} ${
|
|
1388
|
+
` ${severityColor("\u25CF")} ${chalk5.white(fileName)} ${chalk5.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1231
1389
|
);
|
|
1232
1390
|
});
|
|
1233
1391
|
console.log();
|
|
1234
1392
|
}
|
|
1235
1393
|
if (contextScore) {
|
|
1236
|
-
console.log(
|
|
1237
|
-
console.log(
|
|
1238
|
-
console.log(
|
|
1394
|
+
console.log(chalk5.cyan(divider));
|
|
1395
|
+
console.log(chalk5.bold.white(" AI READINESS SCORE (Context)"));
|
|
1396
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
1239
1397
|
console.log(formatToolScore3(contextScore));
|
|
1240
1398
|
console.log();
|
|
1241
1399
|
}
|
|
1242
1400
|
}
|
|
1243
1401
|
} catch (error) {
|
|
1244
|
-
|
|
1402
|
+
handleCLIError4(error, "Context analysis");
|
|
1245
1403
|
}
|
|
1246
1404
|
}
|
|
1247
1405
|
|
|
1248
1406
|
// src/commands/consistency.ts
|
|
1249
|
-
import
|
|
1407
|
+
import chalk6 from "chalk";
|
|
1250
1408
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
1251
|
-
import { resolve as
|
|
1409
|
+
import { resolve as resolvePath6 } from "path";
|
|
1252
1410
|
import {
|
|
1253
1411
|
loadMergedConfig as loadMergedConfig4,
|
|
1254
1412
|
handleJSONOutput as handleJSONOutput4,
|
|
1255
|
-
handleCLIError as
|
|
1413
|
+
handleCLIError as handleCLIError5,
|
|
1256
1414
|
getElapsedTime as getElapsedTime4,
|
|
1257
1415
|
resolveOutputPath as resolveOutputPath4,
|
|
1258
1416
|
formatToolScore as formatToolScore4
|
|
1259
1417
|
} from "@aiready/core";
|
|
1260
1418
|
async function consistencyAction(directory, options) {
|
|
1261
|
-
console.log(
|
|
1419
|
+
console.log(chalk6.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1262
1420
|
const startTime = Date.now();
|
|
1263
|
-
const resolvedDir =
|
|
1421
|
+
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1264
1422
|
try {
|
|
1265
1423
|
const defaults = {
|
|
1266
1424
|
checkNaming: true,
|
|
@@ -1320,23 +1478,23 @@ async function consistencyAction(directory, options) {
|
|
|
1320
1478
|
resolvedDir
|
|
1321
1479
|
);
|
|
1322
1480
|
writeFileSync2(outputPath, markdown);
|
|
1323
|
-
console.log(
|
|
1481
|
+
console.log(chalk6.green(`\u2705 Report saved to ${outputPath}`));
|
|
1324
1482
|
} else {
|
|
1325
|
-
console.log(
|
|
1483
|
+
console.log(chalk6.bold("\n\u{1F4CA} Summary\n"));
|
|
1326
1484
|
console.log(
|
|
1327
|
-
`Files Analyzed: ${
|
|
1485
|
+
`Files Analyzed: ${chalk6.cyan(report.summary.filesAnalyzed)}`
|
|
1328
1486
|
);
|
|
1329
|
-
console.log(`Total Issues: ${
|
|
1330
|
-
console.log(` Naming: ${
|
|
1331
|
-
console.log(` Patterns: ${
|
|
1487
|
+
console.log(`Total Issues: ${chalk6.yellow(report.summary.totalIssues)}`);
|
|
1488
|
+
console.log(` Naming: ${chalk6.yellow(report.summary.namingIssues)}`);
|
|
1489
|
+
console.log(` Patterns: ${chalk6.yellow(report.summary.patternIssues)}`);
|
|
1332
1490
|
console.log(
|
|
1333
|
-
` Architecture: ${
|
|
1491
|
+
` Architecture: ${chalk6.yellow(report.summary.architectureIssues || 0)}`
|
|
1334
1492
|
);
|
|
1335
|
-
console.log(`Analysis Time: ${
|
|
1493
|
+
console.log(`Analysis Time: ${chalk6.gray(elapsedTime + "s")}
|
|
1336
1494
|
`);
|
|
1337
1495
|
if (report.summary.totalIssues === 0) {
|
|
1338
1496
|
console.log(
|
|
1339
|
-
|
|
1497
|
+
chalk6.green(
|
|
1340
1498
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1341
1499
|
)
|
|
1342
1500
|
);
|
|
@@ -1348,20 +1506,20 @@ async function consistencyAction(directory, options) {
|
|
|
1348
1506
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1349
1507
|
);
|
|
1350
1508
|
if (namingResults.length > 0) {
|
|
1351
|
-
console.log(
|
|
1509
|
+
console.log(chalk6.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1352
1510
|
let shown = 0;
|
|
1353
1511
|
for (const result of namingResults) {
|
|
1354
1512
|
if (shown >= 5) break;
|
|
1355
1513
|
for (const issue of result.issues) {
|
|
1356
1514
|
if (shown >= 5) break;
|
|
1357
|
-
const severityColor = issue.severity === "critical" ?
|
|
1515
|
+
const severityColor = issue.severity === "critical" ? chalk6.red : issue.severity === "major" ? chalk6.yellow : issue.severity === "minor" ? chalk6.blue : chalk6.gray;
|
|
1358
1516
|
console.log(
|
|
1359
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1517
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk6.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1360
1518
|
);
|
|
1361
1519
|
console.log(` ${issue.message}`);
|
|
1362
1520
|
if (issue.suggestion) {
|
|
1363
1521
|
console.log(
|
|
1364
|
-
` ${
|
|
1522
|
+
` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
|
|
1365
1523
|
);
|
|
1366
1524
|
}
|
|
1367
1525
|
console.log();
|
|
@@ -1370,25 +1528,25 @@ async function consistencyAction(directory, options) {
|
|
|
1370
1528
|
}
|
|
1371
1529
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1372
1530
|
if (remaining > 0) {
|
|
1373
|
-
console.log(
|
|
1531
|
+
console.log(chalk6.dim(` ... and ${remaining} more issues
|
|
1374
1532
|
`));
|
|
1375
1533
|
}
|
|
1376
1534
|
}
|
|
1377
1535
|
if (patternResults.length > 0) {
|
|
1378
|
-
console.log(
|
|
1536
|
+
console.log(chalk6.bold("\u{1F504} Pattern Issues\n"));
|
|
1379
1537
|
let shown = 0;
|
|
1380
1538
|
for (const result of patternResults) {
|
|
1381
1539
|
if (shown >= 5) break;
|
|
1382
1540
|
for (const issue of result.issues) {
|
|
1383
1541
|
if (shown >= 5) break;
|
|
1384
|
-
const severityColor = issue.severity === "critical" ?
|
|
1542
|
+
const severityColor = issue.severity === "critical" ? chalk6.red : issue.severity === "major" ? chalk6.yellow : issue.severity === "minor" ? chalk6.blue : chalk6.gray;
|
|
1385
1543
|
console.log(
|
|
1386
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1544
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk6.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1387
1545
|
);
|
|
1388
1546
|
console.log(` ${issue.message}`);
|
|
1389
1547
|
if (issue.suggestion) {
|
|
1390
1548
|
console.log(
|
|
1391
|
-
` ${
|
|
1549
|
+
` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
|
|
1392
1550
|
);
|
|
1393
1551
|
}
|
|
1394
1552
|
console.log();
|
|
@@ -1397,12 +1555,12 @@ async function consistencyAction(directory, options) {
|
|
|
1397
1555
|
}
|
|
1398
1556
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1399
1557
|
if (remaining > 0) {
|
|
1400
|
-
console.log(
|
|
1558
|
+
console.log(chalk6.dim(` ... and ${remaining} more issues
|
|
1401
1559
|
`));
|
|
1402
1560
|
}
|
|
1403
1561
|
}
|
|
1404
1562
|
if (report.recommendations.length > 0) {
|
|
1405
|
-
console.log(
|
|
1563
|
+
console.log(chalk6.bold("\u{1F4A1} Recommendations\n"));
|
|
1406
1564
|
report.recommendations.forEach((rec, i) => {
|
|
1407
1565
|
console.log(`${i + 1}. ${rec}`);
|
|
1408
1566
|
});
|
|
@@ -1410,38 +1568,38 @@ async function consistencyAction(directory, options) {
|
|
|
1410
1568
|
}
|
|
1411
1569
|
}
|
|
1412
1570
|
if (consistencyScore) {
|
|
1413
|
-
console.log(
|
|
1571
|
+
console.log(chalk6.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1414
1572
|
console.log(formatToolScore4(consistencyScore));
|
|
1415
1573
|
console.log();
|
|
1416
1574
|
}
|
|
1417
1575
|
}
|
|
1418
1576
|
} catch (error) {
|
|
1419
|
-
|
|
1577
|
+
handleCLIError5(error, "Consistency analysis");
|
|
1420
1578
|
}
|
|
1421
1579
|
}
|
|
1422
1580
|
|
|
1423
1581
|
// src/commands/visualize.ts
|
|
1424
|
-
import
|
|
1582
|
+
import chalk7 from "chalk";
|
|
1425
1583
|
import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
|
|
1426
|
-
import { resolve as
|
|
1584
|
+
import { resolve as resolvePath7 } from "path";
|
|
1427
1585
|
import { spawn } from "child_process";
|
|
1428
|
-
import { handleCLIError as
|
|
1586
|
+
import { handleCLIError as handleCLIError6 } from "@aiready/core";
|
|
1429
1587
|
import { generateHTML } from "@aiready/core";
|
|
1430
1588
|
async function visualizeAction(directory, options) {
|
|
1431
1589
|
try {
|
|
1432
|
-
const dirPath =
|
|
1433
|
-
let reportPath = options.report ?
|
|
1590
|
+
const dirPath = resolvePath7(process.cwd(), directory || ".");
|
|
1591
|
+
let reportPath = options.report ? resolvePath7(dirPath, options.report) : null;
|
|
1434
1592
|
if (!reportPath || !existsSync2(reportPath)) {
|
|
1435
1593
|
const latestScan = findLatestScanReport(dirPath);
|
|
1436
1594
|
if (latestScan) {
|
|
1437
1595
|
reportPath = latestScan;
|
|
1438
1596
|
console.log(
|
|
1439
|
-
|
|
1597
|
+
chalk7.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1440
1598
|
);
|
|
1441
1599
|
} else {
|
|
1442
|
-
console.error(
|
|
1600
|
+
console.error(chalk7.red("\u274C No AI readiness report found"));
|
|
1443
1601
|
console.log(
|
|
1444
|
-
|
|
1602
|
+
chalk7.dim(
|
|
1445
1603
|
`
|
|
1446
1604
|
Generate a report with:
|
|
1447
1605
|
aiready scan --output json
|
|
@@ -1455,7 +1613,7 @@ Or specify a custom report:
|
|
|
1455
1613
|
}
|
|
1456
1614
|
const raw = readFileSync3(reportPath, "utf8");
|
|
1457
1615
|
const report = JSON.parse(raw);
|
|
1458
|
-
const configPath =
|
|
1616
|
+
const configPath = resolvePath7(dirPath, "aiready.json");
|
|
1459
1617
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1460
1618
|
if (existsSync2(configPath)) {
|
|
1461
1619
|
try {
|
|
@@ -1479,7 +1637,7 @@ Or specify a custom report:
|
|
|
1479
1637
|
let devServerStarted = false;
|
|
1480
1638
|
if (useDevMode) {
|
|
1481
1639
|
try {
|
|
1482
|
-
const monorepoWebDir =
|
|
1640
|
+
const monorepoWebDir = resolvePath7(dirPath, "packages/visualizer");
|
|
1483
1641
|
let webDir = "";
|
|
1484
1642
|
let visualizerAvailable = false;
|
|
1485
1643
|
if (existsSync2(monorepoWebDir)) {
|
|
@@ -1487,8 +1645,8 @@ Or specify a custom report:
|
|
|
1487
1645
|
visualizerAvailable = true;
|
|
1488
1646
|
} else {
|
|
1489
1647
|
const nodemodulesLocations = [
|
|
1490
|
-
|
|
1491
|
-
|
|
1648
|
+
resolvePath7(dirPath, "node_modules", "@aiready", "visualizer"),
|
|
1649
|
+
resolvePath7(
|
|
1492
1650
|
process.cwd(),
|
|
1493
1651
|
"node_modules",
|
|
1494
1652
|
"@aiready",
|
|
@@ -1498,14 +1656,14 @@ Or specify a custom report:
|
|
|
1498
1656
|
let currentDir = dirPath;
|
|
1499
1657
|
while (currentDir !== "/" && currentDir !== ".") {
|
|
1500
1658
|
nodemodulesLocations.push(
|
|
1501
|
-
|
|
1659
|
+
resolvePath7(currentDir, "node_modules", "@aiready", "visualizer")
|
|
1502
1660
|
);
|
|
1503
|
-
const parent =
|
|
1661
|
+
const parent = resolvePath7(currentDir, "..");
|
|
1504
1662
|
if (parent === currentDir) break;
|
|
1505
1663
|
currentDir = parent;
|
|
1506
1664
|
}
|
|
1507
1665
|
for (const location of nodemodulesLocations) {
|
|
1508
|
-
if (existsSync2(location) && existsSync2(
|
|
1666
|
+
if (existsSync2(location) && existsSync2(resolvePath7(location, "package.json"))) {
|
|
1509
1667
|
webDir = location;
|
|
1510
1668
|
visualizerAvailable = true;
|
|
1511
1669
|
break;
|
|
@@ -1514,20 +1672,20 @@ Or specify a custom report:
|
|
|
1514
1672
|
if (!visualizerAvailable) {
|
|
1515
1673
|
try {
|
|
1516
1674
|
const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
|
|
1517
|
-
webDir =
|
|
1675
|
+
webDir = resolvePath7(vizPkgPath, "..");
|
|
1518
1676
|
visualizerAvailable = true;
|
|
1519
1677
|
} catch (err) {
|
|
1520
1678
|
void err;
|
|
1521
1679
|
}
|
|
1522
1680
|
}
|
|
1523
1681
|
}
|
|
1524
|
-
const webViteConfigExists = webDir && existsSync2(
|
|
1682
|
+
const webViteConfigExists = webDir && existsSync2(resolvePath7(webDir, "web", "vite.config.ts"));
|
|
1525
1683
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1526
1684
|
const spawnCwd = webDir;
|
|
1527
1685
|
const { watch } = await import("fs");
|
|
1528
1686
|
const copyReportToViz = () => {
|
|
1529
1687
|
try {
|
|
1530
|
-
const destPath =
|
|
1688
|
+
const destPath = resolvePath7(spawnCwd, "web", "report-data.json");
|
|
1531
1689
|
copyFileSync(reportPath, destPath);
|
|
1532
1690
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1533
1691
|
} catch (e) {
|
|
@@ -1571,19 +1729,19 @@ Or specify a custom report:
|
|
|
1571
1729
|
return;
|
|
1572
1730
|
} else {
|
|
1573
1731
|
console.log(
|
|
1574
|
-
|
|
1732
|
+
chalk7.yellow(
|
|
1575
1733
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1576
1734
|
)
|
|
1577
1735
|
);
|
|
1578
1736
|
console.log(
|
|
1579
|
-
|
|
1737
|
+
chalk7.cyan(" Falling back to static HTML generation...\n")
|
|
1580
1738
|
);
|
|
1581
1739
|
useDevMode = false;
|
|
1582
1740
|
}
|
|
1583
1741
|
} catch (err) {
|
|
1584
1742
|
console.error("Failed to start dev server:", err);
|
|
1585
1743
|
console.log(
|
|
1586
|
-
|
|
1744
|
+
chalk7.cyan(" Falling back to static HTML generation...\n")
|
|
1587
1745
|
);
|
|
1588
1746
|
useDevMode = false;
|
|
1589
1747
|
}
|
|
@@ -1591,9 +1749,9 @@ Or specify a custom report:
|
|
|
1591
1749
|
console.log("Generating HTML...");
|
|
1592
1750
|
const html = generateHTML(graph);
|
|
1593
1751
|
const defaultOutput = "visualization.html";
|
|
1594
|
-
const outPath =
|
|
1752
|
+
const outPath = resolvePath7(dirPath, options.output || defaultOutput);
|
|
1595
1753
|
writeFileSync3(outPath, html, "utf8");
|
|
1596
|
-
console.log(
|
|
1754
|
+
console.log(chalk7.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1597
1755
|
if (options.open || options.serve) {
|
|
1598
1756
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1599
1757
|
if (options.serve) {
|
|
@@ -1623,7 +1781,7 @@ Or specify a custom report:
|
|
|
1623
1781
|
server.listen(port, () => {
|
|
1624
1782
|
const addr = `http://localhost:${port}/`;
|
|
1625
1783
|
console.log(
|
|
1626
|
-
|
|
1784
|
+
chalk7.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1627
1785
|
);
|
|
1628
1786
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1629
1787
|
});
|
|
@@ -1639,7 +1797,7 @@ Or specify a custom report:
|
|
|
1639
1797
|
}
|
|
1640
1798
|
}
|
|
1641
1799
|
} catch (err) {
|
|
1642
|
-
|
|
1800
|
+
handleCLIError6(err, "Visualization");
|
|
1643
1801
|
}
|
|
1644
1802
|
}
|
|
1645
1803
|
var visualizeHelpText = `
|
|
@@ -1670,15 +1828,15 @@ NOTES:
|
|
|
1670
1828
|
`;
|
|
1671
1829
|
|
|
1672
1830
|
// src/commands/ai-signal-clarity.ts
|
|
1673
|
-
import
|
|
1831
|
+
import chalk8 from "chalk";
|
|
1674
1832
|
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
1675
1833
|
|
|
1676
1834
|
// src/commands/agent-grounding.ts
|
|
1677
|
-
import
|
|
1835
|
+
import chalk9 from "chalk";
|
|
1678
1836
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1679
1837
|
|
|
1680
1838
|
// src/commands/testability.ts
|
|
1681
|
-
import
|
|
1839
|
+
import chalk10 from "chalk";
|
|
1682
1840
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1683
1841
|
|
|
1684
1842
|
// src/commands/change-amplification.ts
|
|
@@ -1753,7 +1911,7 @@ program.command("scan").description(
|
|
|
1753
1911
|
"--fail-on <level>",
|
|
1754
1912
|
"Fail on issues: critical, major, any",
|
|
1755
1913
|
"critical"
|
|
1756
|
-
).addHelpText("after", scanHelpText).action(async (directory, options) => {
|
|
1914
|
+
).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", scanHelpText).action(async (directory, options) => {
|
|
1757
1915
|
await scanAction(directory, options);
|
|
1758
1916
|
});
|
|
1759
1917
|
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(
|
|
@@ -1834,4 +1992,7 @@ program.command("visualize").description("Generate interactive visualization fro
|
|
|
1834
1992
|
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) => {
|
|
1835
1993
|
await changeAmplificationAction(directory, options);
|
|
1836
1994
|
});
|
|
1995
|
+
program.command("upload").description("Upload an AIReady report JSON to the platform").argument("<file>", "Report JSON file to upload").option("--api-key <key>", "Platform API key").option("--repo-id <id>", "Platform repository ID (optional)").option("--server <url>", "Custom platform URL").addHelpText("after", uploadHelpText).action(async (file, options) => {
|
|
1996
|
+
await uploadAction(file, options);
|
|
1997
|
+
});
|
|
1837
1998
|
program.parse();
|