@aiready/cli 0.14.21 → 0.14.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-HUSUJEQJ.mjs +298 -0
- package/dist/cli.js +931 -896
- package/dist/cli.mjs +600 -551
- package/dist/index.js +0 -9
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
package/dist/cli.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
__require,
|
|
4
4
|
analyzeUnified,
|
|
5
5
|
scoreUnified
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HUSUJEQJ.mjs";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -12,17 +12,14 @@ import { join as join2, dirname } from "path";
|
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
// src/commands/scan.ts
|
|
15
|
-
import
|
|
16
|
-
import { writeFileSync
|
|
17
|
-
import { resolve as
|
|
15
|
+
import chalk6 from "chalk";
|
|
16
|
+
import { writeFileSync } from "fs";
|
|
17
|
+
import { resolve as resolvePath4 } from "path";
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
handleJSONOutput,
|
|
19
|
+
handleJSONOutput as handleJSONOutput2,
|
|
21
20
|
handleCLIError as handleCLIError2,
|
|
22
21
|
resolveOutputPath,
|
|
23
22
|
getRepoMetadata,
|
|
24
|
-
calculateTokenBudget,
|
|
25
|
-
ToolName,
|
|
26
23
|
emitIssuesAsAnnotations
|
|
27
24
|
} from "@aiready/core";
|
|
28
25
|
|
|
@@ -31,12 +28,11 @@ import { resolve as resolvePath } from "path";
|
|
|
31
28
|
import { existsSync, readFileSync } from "fs";
|
|
32
29
|
import chalk from "chalk";
|
|
33
30
|
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
31
|
+
import {
|
|
32
|
+
findLatestReport,
|
|
33
|
+
getReportTimestamp,
|
|
34
|
+
handleJSONOutput
|
|
35
|
+
} from "@aiready/core";
|
|
40
36
|
async function warnIfGraphCapExceeded(report, dirPath) {
|
|
41
37
|
try {
|
|
42
38
|
const { loadConfig: loadConfig4 } = await import("@aiready/core");
|
|
@@ -247,19 +243,22 @@ function printScoring(scoringResult, scoringProfile) {
|
|
|
247
243
|
` ${progressBar} ${tool.score}/100 (${rating}) ${emoji} ${tool.toolName}`
|
|
248
244
|
);
|
|
249
245
|
});
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
});
|
|
262
|
-
|
|
246
|
+
printTopRecommendations(scoringResult.breakdown);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function printTopRecommendations(breakdown) {
|
|
250
|
+
const allRecs = breakdown.flatMap(
|
|
251
|
+
(t) => (t.recommendations ?? []).map((r) => ({ ...r, tool: t.toolName }))
|
|
252
|
+
).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
|
|
253
|
+
if (allRecs.length > 0) {
|
|
254
|
+
console.log(chalk2.bold("\n\u{1F3AF} Top Actionable Recommendations:"));
|
|
255
|
+
allRecs.forEach((rec, i) => {
|
|
256
|
+
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
257
|
+
console.log(` ${i + 1}. ${priorityIcon} ${chalk2.bold(rec.action)}`);
|
|
258
|
+
console.log(
|
|
259
|
+
` Impact: ${chalk2.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
|
|
260
|
+
);
|
|
261
|
+
});
|
|
263
262
|
}
|
|
264
263
|
}
|
|
265
264
|
function mapToUnifiedReport(res, scoring) {
|
|
@@ -388,7 +387,7 @@ async function uploadAction(file, options) {
|
|
|
388
387
|
handleCLIError(error, "Upload");
|
|
389
388
|
}
|
|
390
389
|
}
|
|
391
|
-
var
|
|
390
|
+
var UPLOAD_HELP_TEXT = `
|
|
392
391
|
EXAMPLES:
|
|
393
392
|
$ aiready upload report.json --api-key ar_...
|
|
394
393
|
$ aiready upload .aiready/latest.json
|
|
@@ -399,247 +398,285 @@ ENVIRONMENT VARIABLES:
|
|
|
399
398
|
AIREADY_SERVER Custom platform URL (default: https://dev.platform.getaiready.dev)
|
|
400
399
|
`;
|
|
401
400
|
|
|
402
|
-
// src/commands/scan.ts
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
ToolName.PatternDetect,
|
|
447
|
-
ToolName.ChangeAmplification
|
|
448
|
-
];
|
|
449
|
-
break;
|
|
450
|
-
case "ui":
|
|
451
|
-
profileTools = [
|
|
452
|
-
ToolName.NamingConsistency,
|
|
453
|
-
ToolName.ContextAnalyzer,
|
|
454
|
-
ToolName.PatternDetect,
|
|
455
|
-
ToolName.DocDrift,
|
|
456
|
-
ToolName.AiSignalClarity
|
|
457
|
-
];
|
|
458
|
-
break;
|
|
459
|
-
case "security":
|
|
460
|
-
profileTools = [
|
|
461
|
-
ToolName.NamingConsistency,
|
|
462
|
-
ToolName.TestabilityIndex
|
|
463
|
-
];
|
|
464
|
-
break;
|
|
465
|
-
case "onboarding":
|
|
466
|
-
profileTools = [
|
|
467
|
-
ToolName.ContextAnalyzer,
|
|
468
|
-
ToolName.NamingConsistency,
|
|
469
|
-
ToolName.AgentGrounding
|
|
470
|
-
];
|
|
471
|
-
break;
|
|
472
|
-
default:
|
|
473
|
-
console.log(
|
|
474
|
-
chalk4.yellow(
|
|
475
|
-
`
|
|
476
|
-
\u26A0\uFE0F Unknown profile '${options.profile}'. Using defaults.`
|
|
477
|
-
)
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const cliOverrides = {
|
|
482
|
-
include: options.include?.split(","),
|
|
483
|
-
exclude: options.exclude?.split(",")
|
|
484
|
-
};
|
|
485
|
-
if (profileTools) cliOverrides.tools = profileTools;
|
|
486
|
-
const baseOptions = await loadMergedConfig(
|
|
487
|
-
resolvedDir,
|
|
488
|
-
defaults,
|
|
489
|
-
cliOverrides
|
|
490
|
-
);
|
|
491
|
-
const finalOptions = { ...baseOptions };
|
|
492
|
-
if (baseOptions.tools.includes(ToolName.PatternDetect) || baseOptions.tools.includes("patterns")) {
|
|
493
|
-
const { getSmartDefaults } = await import("@aiready/pattern-detect");
|
|
494
|
-
const patternSmartDefaults = await getSmartDefaults(
|
|
495
|
-
resolvedDir,
|
|
496
|
-
finalOptions.toolConfigs?.[ToolName.PatternDetect] ?? {}
|
|
401
|
+
// src/commands/scan-config.ts
|
|
402
|
+
import { loadMergedConfig, ToolName as ToolName2 } from "@aiready/core";
|
|
403
|
+
|
|
404
|
+
// src/commands/scan-helpers.ts
|
|
405
|
+
import chalk4 from "chalk";
|
|
406
|
+
import { ToolName } from "@aiready/core";
|
|
407
|
+
function getProfileTools(profile) {
|
|
408
|
+
switch (profile.toLowerCase()) {
|
|
409
|
+
case "agentic":
|
|
410
|
+
return [
|
|
411
|
+
ToolName.AiSignalClarity,
|
|
412
|
+
ToolName.AgentGrounding,
|
|
413
|
+
ToolName.TestabilityIndex
|
|
414
|
+
];
|
|
415
|
+
case "cost":
|
|
416
|
+
return [ToolName.PatternDetect, ToolName.ContextAnalyzer];
|
|
417
|
+
case "logic":
|
|
418
|
+
return [
|
|
419
|
+
ToolName.TestabilityIndex,
|
|
420
|
+
ToolName.NamingConsistency,
|
|
421
|
+
ToolName.ContextAnalyzer,
|
|
422
|
+
ToolName.PatternDetect,
|
|
423
|
+
ToolName.ChangeAmplification
|
|
424
|
+
];
|
|
425
|
+
case "ui":
|
|
426
|
+
return [
|
|
427
|
+
ToolName.NamingConsistency,
|
|
428
|
+
ToolName.ContextAnalyzer,
|
|
429
|
+
ToolName.PatternDetect,
|
|
430
|
+
ToolName.DocDrift,
|
|
431
|
+
ToolName.AiSignalClarity
|
|
432
|
+
];
|
|
433
|
+
case "security":
|
|
434
|
+
return [ToolName.NamingConsistency, ToolName.TestabilityIndex];
|
|
435
|
+
case "onboarding":
|
|
436
|
+
return [
|
|
437
|
+
ToolName.ContextAnalyzer,
|
|
438
|
+
ToolName.NamingConsistency,
|
|
439
|
+
ToolName.AgentGrounding
|
|
440
|
+
];
|
|
441
|
+
default:
|
|
442
|
+
console.log(
|
|
443
|
+
chalk4.yellow(`
|
|
444
|
+
\u26A0\uFE0F Unknown profile '${profile}'. Using defaults.`)
|
|
497
445
|
);
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function createProgressCallback() {
|
|
450
|
+
return (event) => {
|
|
451
|
+
if (event.message) {
|
|
452
|
+
process.stdout.write(`\r\x1B[K [${event.tool}] ${event.message}`);
|
|
453
|
+
return;
|
|
503
454
|
}
|
|
504
|
-
|
|
505
|
-
console.log(
|
|
506
|
-
|
|
507
|
-
|
|
455
|
+
process.stdout.write("\r\x1B[K");
|
|
456
|
+
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
457
|
+
const toolResult = event.data;
|
|
458
|
+
if (toolResult && toolResult.summary) {
|
|
459
|
+
if (toolResult.summary.totalIssues !== void 0)
|
|
460
|
+
console.log(
|
|
461
|
+
` Issues found: ${chalk4.bold(toolResult.summary.totalIssues)}`
|
|
462
|
+
);
|
|
463
|
+
if (toolResult.summary.score !== void 0)
|
|
464
|
+
console.log(
|
|
465
|
+
` Tool Score: ${chalk4.bold(toolResult.summary.score)}/100`
|
|
466
|
+
);
|
|
467
|
+
if (toolResult.summary.totalFiles !== void 0)
|
|
468
|
+
console.log(
|
|
469
|
+
` Files analyzed: ${chalk4.bold(toolResult.summary.totalFiles)}`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// src/commands/scan-config.ts
|
|
476
|
+
var SCAN_DEFAULTS = {
|
|
477
|
+
tools: [
|
|
478
|
+
"pattern-detect",
|
|
479
|
+
"context-analyzer",
|
|
480
|
+
"naming-consistency",
|
|
481
|
+
"ai-signal-clarity",
|
|
482
|
+
"agent-grounding",
|
|
483
|
+
"testability-index",
|
|
484
|
+
"doc-drift",
|
|
485
|
+
"dependency-health",
|
|
486
|
+
"change-amplification"
|
|
487
|
+
],
|
|
488
|
+
include: void 0,
|
|
489
|
+
exclude: void 0,
|
|
490
|
+
output: {
|
|
491
|
+
format: "console",
|
|
492
|
+
file: void 0
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
async function resolveScanConfig(resolvedDir, options) {
|
|
496
|
+
let profileTools = options.tools ? options.tools.split(",").map((t) => t.trim()) : void 0;
|
|
497
|
+
if (options.profile) {
|
|
498
|
+
profileTools = getProfileTools(options.profile);
|
|
499
|
+
}
|
|
500
|
+
const cliOverrides = {
|
|
501
|
+
include: options.include?.split(","),
|
|
502
|
+
exclude: options.exclude?.split(",")
|
|
503
|
+
};
|
|
504
|
+
if (profileTools) cliOverrides.tools = profileTools;
|
|
505
|
+
const baseOptions = await loadMergedConfig(
|
|
506
|
+
resolvedDir,
|
|
507
|
+
SCAN_DEFAULTS,
|
|
508
|
+
cliOverrides
|
|
509
|
+
);
|
|
510
|
+
const finalOptions = { ...baseOptions };
|
|
511
|
+
if (baseOptions.tools.includes(ToolName2.PatternDetect) || baseOptions.tools.includes("patterns")) {
|
|
512
|
+
const { getSmartDefaults } = await import("@aiready/pattern-detect");
|
|
513
|
+
const patternSmartDefaults = await getSmartDefaults(
|
|
514
|
+
resolvedDir,
|
|
515
|
+
finalOptions.toolConfigs?.[ToolName2.PatternDetect] ?? {}
|
|
508
516
|
);
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
process.stdout.write("\r\x1B[K");
|
|
515
|
-
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
516
|
-
const res = event.data;
|
|
517
|
-
if (res && res.summary) {
|
|
518
|
-
if (res.summary.totalIssues !== void 0)
|
|
519
|
-
console.log(` Issues found: ${chalk4.bold(res.summary.totalIssues)}`);
|
|
520
|
-
if (res.summary.score !== void 0)
|
|
521
|
-
console.log(` Tool Score: ${chalk4.bold(res.summary.score)}/100`);
|
|
522
|
-
if (res.summary.totalFiles !== void 0)
|
|
523
|
-
console.log(
|
|
524
|
-
` Files analyzed: ${chalk4.bold(res.summary.totalFiles)}`
|
|
525
|
-
);
|
|
526
|
-
}
|
|
517
|
+
if (!finalOptions.toolConfigs) finalOptions.toolConfigs = {};
|
|
518
|
+
finalOptions.toolConfigs[ToolName2.PatternDetect] = {
|
|
519
|
+
...patternSmartDefaults,
|
|
520
|
+
...finalOptions.toolConfigs[ToolName2.PatternDetect]
|
|
527
521
|
};
|
|
528
|
-
|
|
529
|
-
|
|
522
|
+
}
|
|
523
|
+
return finalOptions;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/commands/scan-orchestrator.ts
|
|
527
|
+
import chalk5 from "chalk";
|
|
528
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
529
|
+
import { resolve as resolvePath3 } from "path";
|
|
530
|
+
import { calculateTokenBudget } from "@aiready/core";
|
|
531
|
+
async function runUnifiedScan(resolvedDir, finalOptions, options, startTime) {
|
|
532
|
+
console.log(chalk5.cyan("\n=== AIReady Run Preview ==="));
|
|
533
|
+
console.log(
|
|
534
|
+
chalk5.white("Tools to run:"),
|
|
535
|
+
(finalOptions.tools ?? []).join(", ")
|
|
536
|
+
);
|
|
537
|
+
const progressCallback = createProgressCallback();
|
|
538
|
+
const scoringProfile = options.profile ?? finalOptions.scoring?.profile ?? "default";
|
|
539
|
+
const results = await analyzeUnified({
|
|
540
|
+
...finalOptions,
|
|
541
|
+
progressCallback,
|
|
542
|
+
onProgress: () => {
|
|
543
|
+
},
|
|
544
|
+
suppressToolConfig: true
|
|
545
|
+
});
|
|
546
|
+
printScanSummary(results, startTime);
|
|
547
|
+
let scoringResult;
|
|
548
|
+
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
549
|
+
scoringResult = await scoreUnified(results, {
|
|
530
550
|
...finalOptions,
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
551
|
+
scoring: {
|
|
552
|
+
...finalOptions.scoring,
|
|
553
|
+
profile: scoringProfile
|
|
554
|
+
}
|
|
535
555
|
});
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
556
|
+
printScoring(scoringResult, scoringProfile);
|
|
557
|
+
if (options.compareTo) {
|
|
558
|
+
handleTrendComparison(options.compareTo, scoringResult);
|
|
559
|
+
}
|
|
560
|
+
await handleBusinessImpactMetrics(
|
|
561
|
+
results,
|
|
562
|
+
scoringResult,
|
|
563
|
+
options.model ?? "gpt-5.4-mini"
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
return { results, scoringResult };
|
|
567
|
+
}
|
|
568
|
+
function handleTrendComparison(baselinePath, currentScoring) {
|
|
569
|
+
try {
|
|
570
|
+
const prevReport = JSON.parse(
|
|
571
|
+
readFileSync2(resolvePath3(process.cwd(), baselinePath), "utf8")
|
|
572
|
+
);
|
|
573
|
+
const prevScore = prevReport.scoring?.overall ?? prevReport.scoring?.score;
|
|
574
|
+
if (typeof prevScore === "number") {
|
|
575
|
+
const diff = currentScoring.overall - prevScore;
|
|
576
|
+
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
577
|
+
if (diff > 0)
|
|
578
|
+
console.log(
|
|
579
|
+
chalk5.green(
|
|
580
|
+
` \u{1F4C8} Trend: ${diffStr} compared to ${baselinePath} (${prevScore} \u2192 ${currentScoring.overall})`
|
|
581
|
+
)
|
|
582
|
+
);
|
|
583
|
+
else if (diff < 0)
|
|
584
|
+
console.log(
|
|
585
|
+
chalk5.red(
|
|
586
|
+
` \u{1F4C9} Trend: ${diffStr} compared to ${baselinePath} (${prevScore} \u2192 ${currentScoring.overall})`
|
|
587
|
+
)
|
|
588
|
+
);
|
|
589
|
+
else
|
|
590
|
+
console.log(
|
|
591
|
+
chalk5.blue(
|
|
592
|
+
` \u2796 Trend: No change (${prevScore} \u2192 ${currentScoring.overall})`
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
} catch (e) {
|
|
597
|
+
void e;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async function handleBusinessImpactMetrics(results, scoringResult, modelId) {
|
|
601
|
+
const totalWastedDuplication = (scoringResult.breakdown ?? []).reduce(
|
|
602
|
+
(sum, s) => sum + (s.tokenBudget?.wastedTokens?.bySource?.duplication ?? 0),
|
|
603
|
+
0
|
|
604
|
+
);
|
|
605
|
+
const totalWastedFragmentation = (scoringResult.breakdown ?? []).reduce(
|
|
606
|
+
(sum, s) => sum + (s.tokenBudget?.wastedTokens?.bySource?.fragmentation ?? 0),
|
|
607
|
+
0
|
|
608
|
+
);
|
|
609
|
+
const totalContext = Math.max(
|
|
610
|
+
...(scoringResult.breakdown ?? []).map(
|
|
611
|
+
(s) => s.tokenBudget?.totalContextTokens ?? 0
|
|
612
|
+
),
|
|
613
|
+
0
|
|
614
|
+
);
|
|
615
|
+
if (totalContext > 0) {
|
|
616
|
+
const unifiedBudget = calculateTokenBudget({
|
|
617
|
+
totalContextTokens: totalContext,
|
|
618
|
+
wastedTokens: {
|
|
619
|
+
duplication: totalWastedDuplication,
|
|
620
|
+
fragmentation: totalWastedFragmentation,
|
|
621
|
+
chattiness: totalContext * 0.1
|
|
622
|
+
// Default chattiness
|
|
578
623
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
);
|
|
587
|
-
const totalContext = Math.max(
|
|
588
|
-
...(scoringResult.breakdown ?? []).map(
|
|
589
|
-
(s) => s.tokenBudget?.totalContextTokens ?? 0
|
|
590
|
-
),
|
|
591
|
-
0
|
|
592
|
-
);
|
|
593
|
-
if (totalContext > 0) {
|
|
594
|
-
const unifiedBudget = calculateTokenBudget({
|
|
595
|
-
totalContextTokens: totalContext,
|
|
596
|
-
wastedTokens: {
|
|
597
|
-
duplication: totalWastedDuplication,
|
|
598
|
-
fragmentation: totalWastedFragmentation,
|
|
599
|
-
chattiness: totalContext * 0.1
|
|
600
|
-
// Default chattiness
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
const allIssues = [];
|
|
604
|
-
for (const toolId of results.summary.toolsRun) {
|
|
605
|
-
if (results[toolId]?.results) {
|
|
606
|
-
results[toolId].results.forEach((fileRes) => {
|
|
607
|
-
if (fileRes.issues) {
|
|
608
|
-
allIssues.push(...fileRes.issues);
|
|
609
|
-
}
|
|
610
|
-
});
|
|
624
|
+
});
|
|
625
|
+
const allIssues = [];
|
|
626
|
+
for (const toolId of results.summary.toolsRun) {
|
|
627
|
+
if (results[toolId]?.results) {
|
|
628
|
+
results[toolId].results.forEach((fileRes) => {
|
|
629
|
+
if (fileRes.issues) {
|
|
630
|
+
allIssues.push(...fileRes.issues);
|
|
611
631
|
}
|
|
612
|
-
}
|
|
613
|
-
const modelId = options.model ?? "gpt-5.4-mini";
|
|
614
|
-
const roi = (await import("@aiready/core")).calculateBusinessROI({
|
|
615
|
-
tokenWaste: unifiedBudget.wastedTokens.total,
|
|
616
|
-
issues: allIssues,
|
|
617
|
-
modelId
|
|
618
632
|
});
|
|
619
|
-
printBusinessImpact(roi, unifiedBudget);
|
|
620
|
-
results.summary.businessImpact = {
|
|
621
|
-
estimatedMonthlyWaste: roi.monthlySavings,
|
|
622
|
-
potentialSavings: roi.monthlySavings,
|
|
623
|
-
productivityHours: roi.productivityGainHours
|
|
624
|
-
};
|
|
625
|
-
scoringResult.tokenBudget = unifiedBudget;
|
|
626
|
-
scoringResult.businessROI = roi;
|
|
627
633
|
}
|
|
628
634
|
}
|
|
635
|
+
const { calculateBusinessROI } = await import("@aiready/core");
|
|
636
|
+
const roi = calculateBusinessROI({
|
|
637
|
+
tokenWaste: unifiedBudget.wastedTokens.total,
|
|
638
|
+
issues: allIssues,
|
|
639
|
+
modelId
|
|
640
|
+
});
|
|
641
|
+
printBusinessImpact(roi, unifiedBudget);
|
|
642
|
+
results.summary.businessImpact = {
|
|
643
|
+
estimatedMonthlyWaste: roi.monthlySavings,
|
|
644
|
+
potentialSavings: roi.monthlySavings,
|
|
645
|
+
productivityHours: roi.productivityGainHours
|
|
646
|
+
};
|
|
647
|
+
scoringResult.tokenBudget = unifiedBudget;
|
|
648
|
+
scoringResult.businessROI = roi;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/commands/scan.ts
|
|
653
|
+
async function scanAction(directory, options) {
|
|
654
|
+
console.log(chalk6.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
655
|
+
const startTime = Date.now();
|
|
656
|
+
const resolvedDir = resolvePath4(process.cwd(), directory ?? ".");
|
|
657
|
+
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
658
|
+
try {
|
|
659
|
+
const finalOptions = await resolveScanConfig(resolvedDir, options);
|
|
660
|
+
const { results, scoringResult } = await runUnifiedScan(
|
|
661
|
+
resolvedDir,
|
|
662
|
+
finalOptions,
|
|
663
|
+
options,
|
|
664
|
+
startTime
|
|
665
|
+
);
|
|
629
666
|
console.log(
|
|
630
|
-
|
|
667
|
+
chalk6.dim(
|
|
631
668
|
"\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
632
669
|
)
|
|
633
670
|
);
|
|
634
|
-
console.log(
|
|
635
|
-
console.log(
|
|
671
|
+
console.log(chalk6.dim("\u{1F4AC} Found a bug or have a metric idea?"));
|
|
672
|
+
console.log(chalk6.dim("\u{1F449} Copy/paste this to your AI agent:"));
|
|
636
673
|
console.log(
|
|
637
|
-
|
|
674
|
+
chalk6.cyan(
|
|
638
675
|
` "Any feedback for the tools? Please use 'aiready bug' to report \u2764\uFE0F"`
|
|
639
676
|
)
|
|
640
677
|
);
|
|
641
678
|
console.log(
|
|
642
|
-
|
|
679
|
+
chalk6.dim(
|
|
643
680
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
644
681
|
)
|
|
645
682
|
);
|
|
@@ -654,7 +691,7 @@ async function scanAction(directory, options) {
|
|
|
654
691
|
resolvedDir
|
|
655
692
|
);
|
|
656
693
|
if (outputFormat === "json") {
|
|
657
|
-
|
|
694
|
+
handleJSONOutput2(
|
|
658
695
|
outputData,
|
|
659
696
|
outputPath,
|
|
660
697
|
`\u2705 Report saved to ${outputPath}`
|
|
@@ -662,7 +699,7 @@ async function scanAction(directory, options) {
|
|
|
662
699
|
} else {
|
|
663
700
|
try {
|
|
664
701
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
665
|
-
console.log(
|
|
702
|
+
console.log(chalk6.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
666
703
|
} catch (err) {
|
|
667
704
|
void err;
|
|
668
705
|
}
|
|
@@ -674,61 +711,83 @@ async function scanAction(directory, options) {
|
|
|
674
711
|
});
|
|
675
712
|
}
|
|
676
713
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
677
|
-
|
|
678
|
-
const threshold = options.threshold ? parseInt(options.threshold) : void 0;
|
|
679
|
-
const failOnLevel = options.failOn ?? "critical";
|
|
680
|
-
const isCI = options.ci ?? process.env.CI === "true";
|
|
681
|
-
let shouldFail = false;
|
|
682
|
-
let failReason = "";
|
|
683
|
-
const report = mapToUnifiedReport(results, scoringResult);
|
|
684
|
-
if (isCI && report.results && report.results.length > 0) {
|
|
685
|
-
console.log(
|
|
686
|
-
chalk4.cyan(
|
|
687
|
-
`
|
|
688
|
-
\u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
689
|
-
)
|
|
690
|
-
);
|
|
691
|
-
emitIssuesAsAnnotations(report.results);
|
|
692
|
-
}
|
|
693
|
-
if (threshold && scoringResult.overall < threshold) {
|
|
694
|
-
shouldFail = true;
|
|
695
|
-
failReason = `Score ${scoringResult.overall} < threshold ${threshold}`;
|
|
696
|
-
}
|
|
697
|
-
if (failOnLevel !== "none") {
|
|
698
|
-
if (failOnLevel === "critical" && report.summary.criticalIssues > 0) {
|
|
699
|
-
shouldFail = true;
|
|
700
|
-
failReason = `Found ${report.summary.criticalIssues} critical issues`;
|
|
701
|
-
} else if (failOnLevel === "major" && report.summary.criticalIssues + report.summary.majorIssues > 0) {
|
|
702
|
-
shouldFail = true;
|
|
703
|
-
failReason = `Found ${report.summary.criticalIssues} critical and ${report.summary.majorIssues} major issues`;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
if (shouldFail) {
|
|
707
|
-
console.log(chalk4.red(`
|
|
708
|
-
\u{1F6AB} SCAN FAILED: ${failReason}`));
|
|
709
|
-
process.exit(1);
|
|
710
|
-
} else {
|
|
711
|
-
console.log(chalk4.green("\n\u2705 SCAN PASSED"));
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
+
await handleGatekeeper(outputData, scoringResult, options, results);
|
|
714
715
|
} catch (error) {
|
|
715
716
|
handleCLIError2(error, "Analysis");
|
|
716
717
|
}
|
|
717
718
|
}
|
|
718
|
-
|
|
719
|
+
async function handleGatekeeper(outputData, scoringResult, options, results) {
|
|
720
|
+
if (!scoringResult) return;
|
|
721
|
+
const threshold = options.threshold ? parseInt(options.threshold) : void 0;
|
|
722
|
+
const failOnLevel = options.failOn ?? "critical";
|
|
723
|
+
const isCI = options.ci ?? process.env.CI === "true";
|
|
724
|
+
let shouldFail = false;
|
|
725
|
+
let failReason = "";
|
|
726
|
+
const report = mapToUnifiedReport(results, scoringResult);
|
|
727
|
+
if (isCI && report.results && report.results.length > 0) {
|
|
728
|
+
console.log(
|
|
729
|
+
chalk6.cyan(
|
|
730
|
+
`
|
|
731
|
+
\u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
732
|
+
)
|
|
733
|
+
);
|
|
734
|
+
emitIssuesAsAnnotations(report.results);
|
|
735
|
+
}
|
|
736
|
+
if (threshold && scoringResult.overall < threshold) {
|
|
737
|
+
shouldFail = true;
|
|
738
|
+
failReason = `Score ${scoringResult.overall} < threshold ${threshold}`;
|
|
739
|
+
}
|
|
740
|
+
if (failOnLevel !== "none") {
|
|
741
|
+
if (failOnLevel === "critical" && report.summary.criticalIssues > 0) {
|
|
742
|
+
shouldFail = true;
|
|
743
|
+
failReason = `Found ${report.summary.criticalIssues} critical issues`;
|
|
744
|
+
} else if (failOnLevel === "major" && report.summary.criticalIssues + report.summary.majorIssues > 0) {
|
|
745
|
+
shouldFail = true;
|
|
746
|
+
failReason = `Found ${report.summary.criticalIssues} critical and ${report.summary.majorIssues} major issues`;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (shouldFail) {
|
|
750
|
+
console.log(chalk6.red(`
|
|
751
|
+
\u{1F6AB} SCAN FAILED: ${failReason}`));
|
|
752
|
+
process.exit(1);
|
|
753
|
+
} else {
|
|
754
|
+
console.log(chalk6.green("\n\u2705 SCAN PASSED"));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
var SCAN_HELP_TEXT = `
|
|
758
|
+
Run a comprehensive AI-readiness scan of your codebase.
|
|
759
|
+
|
|
760
|
+
${chalk6.bold("Examples:")}
|
|
761
|
+
$ aiready scan .
|
|
762
|
+
$ aiready scan src --profile agentic
|
|
763
|
+
$ aiready scan . --threshold 80 --fail-on critical
|
|
764
|
+
$ aiready scan . --output json --output-file report.json
|
|
765
|
+
|
|
766
|
+
${chalk6.bold("Profiles:")}
|
|
767
|
+
agentic - Focus on AI signal clarity and agent grounding
|
|
768
|
+
cost - Focus on token budget and pattern reuse
|
|
769
|
+
logic - Focus on testability and naming consistency
|
|
770
|
+
ui - Focus on component naming and documentation
|
|
771
|
+
security - Focus on naming and testability
|
|
772
|
+
onboarding - Focus on context and grounding
|
|
773
|
+
|
|
774
|
+
${chalk6.bold("CI/CD Integration:")}
|
|
775
|
+
Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
|
|
776
|
+
When running in GitHub Actions, it will automatically emit annotations for found issues.
|
|
777
|
+
`;
|
|
719
778
|
|
|
720
779
|
// src/commands/init.ts
|
|
721
780
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
722
781
|
import { join } from "path";
|
|
723
|
-
import
|
|
724
|
-
import { ToolName as
|
|
782
|
+
import chalk7 from "chalk";
|
|
783
|
+
import { ToolName as ToolName3 } from "@aiready/core";
|
|
725
784
|
async function initAction(options) {
|
|
726
785
|
const fileExt = options.format === "js" ? "js" : "json";
|
|
727
786
|
const fileName = fileExt === "js" ? "aiready.config.js" : "aiready.json";
|
|
728
787
|
const filePath = join(process.cwd(), fileName);
|
|
729
788
|
if (existsSync2(filePath) && !options.force) {
|
|
730
789
|
console.error(
|
|
731
|
-
|
|
790
|
+
chalk7.red(`Error: ${fileName} already exists. Use --force to overwrite.`)
|
|
732
791
|
);
|
|
733
792
|
process.exit(1);
|
|
734
793
|
}
|
|
@@ -752,15 +811,15 @@ async function initAction(options) {
|
|
|
752
811
|
"**/*.spec.ts"
|
|
753
812
|
],
|
|
754
813
|
tools: [
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
814
|
+
ToolName3.PatternDetect,
|
|
815
|
+
ToolName3.ContextAnalyzer,
|
|
816
|
+
ToolName3.NamingConsistency,
|
|
817
|
+
ToolName3.AiSignalClarity,
|
|
818
|
+
ToolName3.AgentGrounding,
|
|
819
|
+
ToolName3.TestabilityIndex,
|
|
820
|
+
ToolName3.DocDrift,
|
|
821
|
+
ToolName3.DependencyHealth,
|
|
822
|
+
ToolName3.ChangeAmplification
|
|
764
823
|
]
|
|
765
824
|
},
|
|
766
825
|
// Output preferences
|
|
@@ -775,7 +834,7 @@ async function initAction(options) {
|
|
|
775
834
|
},
|
|
776
835
|
// Tool-specific configurations
|
|
777
836
|
tools: {
|
|
778
|
-
[
|
|
837
|
+
[ToolName3.PatternDetect]: {
|
|
779
838
|
// Core detection thresholds
|
|
780
839
|
minSimilarity: 0.4,
|
|
781
840
|
// Jaccard similarity threshold (0-1)
|
|
@@ -805,7 +864,7 @@ async function initAction(options) {
|
|
|
805
864
|
// Add any additional advanced options here
|
|
806
865
|
} : {}
|
|
807
866
|
},
|
|
808
|
-
[
|
|
867
|
+
[ToolName3.ContextAnalyzer]: {
|
|
809
868
|
// Smart defaults are generated dynamically based on repository size
|
|
810
869
|
// These are fallback values for when smart defaults can't be calculated
|
|
811
870
|
maxContextBudget: 25e3,
|
|
@@ -822,7 +881,7 @@ async function initAction(options) {
|
|
|
822
881
|
includeNodeModules: false
|
|
823
882
|
// Whether to include node_modules in analysis
|
|
824
883
|
},
|
|
825
|
-
[
|
|
884
|
+
[ToolName3.NamingConsistency]: {
|
|
826
885
|
// Core checks
|
|
827
886
|
checkNaming: true,
|
|
828
887
|
// Check naming conventions and quality
|
|
@@ -869,7 +928,7 @@ async function initAction(options) {
|
|
|
869
928
|
],
|
|
870
929
|
...options.full ? { disableChecks: [] } : {}
|
|
871
930
|
},
|
|
872
|
-
[
|
|
931
|
+
[ToolName3.AiSignalClarity]: {
|
|
873
932
|
// All signal clarity checks enabled by default
|
|
874
933
|
checkMagicLiterals: true,
|
|
875
934
|
// Detect magic numbers and strings
|
|
@@ -888,7 +947,7 @@ async function initAction(options) {
|
|
|
888
947
|
checkLargeFiles: true
|
|
889
948
|
// Detect files that are too large
|
|
890
949
|
},
|
|
891
|
-
[
|
|
950
|
+
[ToolName3.AgentGrounding]: {
|
|
892
951
|
// Structure clarity
|
|
893
952
|
maxRecommendedDepth: 4,
|
|
894
953
|
// Max directory depth before flagging as "too deep"
|
|
@@ -899,7 +958,7 @@ async function initAction(options) {
|
|
|
899
958
|
additionalVagueNames: ["stuff", "misc", "temp", "test"]
|
|
900
959
|
// Custom vague file names
|
|
901
960
|
},
|
|
902
|
-
[
|
|
961
|
+
[ToolName3.TestabilityIndex]: {
|
|
903
962
|
// Coverage thresholds
|
|
904
963
|
minCoverageRatio: 0.3,
|
|
905
964
|
// Minimum acceptable test/source ratio
|
|
@@ -909,19 +968,19 @@ async function initAction(options) {
|
|
|
909
968
|
maxDepth: 10
|
|
910
969
|
// Maximum scan depth
|
|
911
970
|
},
|
|
912
|
-
[
|
|
971
|
+
[ToolName3.DocDrift]: {
|
|
913
972
|
// Drift detection
|
|
914
973
|
maxCommits: 50,
|
|
915
974
|
// Maximum commit distance to check for drift
|
|
916
975
|
staleMonths: 3
|
|
917
976
|
// Consider comments older than this as outdated
|
|
918
977
|
},
|
|
919
|
-
[
|
|
978
|
+
[ToolName3.DependencyHealth]: {
|
|
920
979
|
// Training cutoff for AI knowledge assessment
|
|
921
980
|
trainingCutoffYear: 2023
|
|
922
981
|
// Year cutoff for AI training data
|
|
923
982
|
},
|
|
924
|
-
[
|
|
983
|
+
[ToolName3.ChangeAmplification]: {
|
|
925
984
|
// Change amplification primarily relies on global scan settings
|
|
926
985
|
// No additional tool-specific configuration required
|
|
927
986
|
}
|
|
@@ -950,35 +1009,36 @@ module.exports = ${JSON.stringify(defaultConfig, null, 2)};
|
|
|
950
1009
|
try {
|
|
951
1010
|
writeFileSync2(filePath, content, "utf8");
|
|
952
1011
|
console.log(
|
|
953
|
-
|
|
954
|
-
\u2705 Created default configuration: ${
|
|
1012
|
+
chalk7.green(`
|
|
1013
|
+
\u2705 Created default configuration: ${chalk7.bold(fileName)}`)
|
|
955
1014
|
);
|
|
956
1015
|
console.log(
|
|
957
|
-
|
|
1016
|
+
chalk7.cyan("You can now fine-tune your settings and run AIReady with:")
|
|
958
1017
|
);
|
|
959
|
-
console.log(
|
|
1018
|
+
console.log(chalk7.white(` $ aiready scan
|
|
960
1019
|
`));
|
|
961
1020
|
} catch (error) {
|
|
962
|
-
console.error(
|
|
1021
|
+
console.error(chalk7.red(`Failed to write configuration file: ${error}`));
|
|
963
1022
|
process.exit(1);
|
|
964
1023
|
}
|
|
965
1024
|
}
|
|
966
1025
|
|
|
967
1026
|
// src/commands/patterns.ts
|
|
968
|
-
import
|
|
969
|
-
import { resolve as resolvePath4 } from "path";
|
|
1027
|
+
import chalk8 from "chalk";
|
|
970
1028
|
import {
|
|
971
|
-
loadMergedConfig as loadMergedConfig2,
|
|
972
|
-
handleJSONOutput as handleJSONOutput2,
|
|
973
1029
|
handleCLIError as handleCLIError3,
|
|
974
1030
|
getElapsedTime,
|
|
975
|
-
|
|
976
|
-
|
|
1031
|
+
formatToolScore,
|
|
1032
|
+
printTerminalHeader,
|
|
1033
|
+
getTerminalDivider,
|
|
1034
|
+
prepareActionConfig,
|
|
1035
|
+
resolveOutputFormat,
|
|
1036
|
+
formatStandardReport,
|
|
1037
|
+
handleStandardJSONOutput
|
|
977
1038
|
} from "@aiready/core";
|
|
978
1039
|
async function patternsAction(directory, options) {
|
|
979
|
-
console.log(
|
|
1040
|
+
console.log(chalk8.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
980
1041
|
const startTime = Date.now();
|
|
981
|
-
const resolvedDir = resolvePath4(process.cwd(), directory ?? ".");
|
|
982
1042
|
try {
|
|
983
1043
|
const useSmartDefaults = !options.fullScan;
|
|
984
1044
|
const defaults = {
|
|
@@ -1007,8 +1067,8 @@ async function patternsAction(directory, options) {
|
|
|
1007
1067
|
if (options.minSharedTokens) {
|
|
1008
1068
|
cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
|
|
1009
1069
|
}
|
|
1010
|
-
const finalOptions = await
|
|
1011
|
-
|
|
1070
|
+
const { resolvedDir, finalOptions } = await prepareActionConfig(
|
|
1071
|
+
directory,
|
|
1012
1072
|
defaults,
|
|
1013
1073
|
cliOptions
|
|
1014
1074
|
);
|
|
@@ -1022,60 +1082,53 @@ async function patternsAction(directory, options) {
|
|
|
1022
1082
|
if (options.score) {
|
|
1023
1083
|
patternScore = calculatePatternScore(duplicates, results.length);
|
|
1024
1084
|
}
|
|
1025
|
-
const outputFormat
|
|
1026
|
-
|
|
1085
|
+
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat(
|
|
1086
|
+
options,
|
|
1087
|
+
finalOptions
|
|
1088
|
+
);
|
|
1027
1089
|
if (outputFormat === "json") {
|
|
1028
|
-
const outputData = {
|
|
1090
|
+
const outputData = formatStandardReport({
|
|
1029
1091
|
results,
|
|
1030
|
-
summary
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
`aiready-report-${getReportTimestamp()}.json`,
|
|
1036
|
-
resolvedDir
|
|
1037
|
-
);
|
|
1038
|
-
handleJSONOutput2(
|
|
1092
|
+
summary,
|
|
1093
|
+
elapsedTime,
|
|
1094
|
+
score: patternScore
|
|
1095
|
+
});
|
|
1096
|
+
handleStandardJSONOutput({
|
|
1039
1097
|
outputData,
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
);
|
|
1098
|
+
outputFile: userOutputFile,
|
|
1099
|
+
resolvedDir
|
|
1100
|
+
});
|
|
1043
1101
|
} else {
|
|
1044
|
-
|
|
1045
|
-
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1046
|
-
const divider = "\u2501".repeat(dividerWidth);
|
|
1047
|
-
console.log(chalk6.cyan(divider));
|
|
1048
|
-
console.log(chalk6.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
1049
|
-
console.log(chalk6.cyan(divider) + "\n");
|
|
1102
|
+
printTerminalHeader("PATTERN ANALYSIS SUMMARY");
|
|
1050
1103
|
console.log(
|
|
1051
|
-
|
|
1104
|
+
chalk8.white(`\u{1F4C1} Files analyzed: ${chalk8.bold(results.length)}`)
|
|
1052
1105
|
);
|
|
1053
1106
|
console.log(
|
|
1054
|
-
|
|
1055
|
-
`\u26A0 Duplicate patterns found: ${
|
|
1107
|
+
chalk8.yellow(
|
|
1108
|
+
`\u26A0 Duplicate patterns found: ${chalk8.bold(summary.totalPatterns)}`
|
|
1056
1109
|
)
|
|
1057
1110
|
);
|
|
1058
1111
|
console.log(
|
|
1059
|
-
|
|
1060
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
1112
|
+
chalk8.red(
|
|
1113
|
+
`\u{1F4B0} Token cost (wasted): ${chalk8.bold(summary.totalTokenCost.toLocaleString())}`
|
|
1061
1114
|
)
|
|
1062
1115
|
);
|
|
1063
1116
|
console.log(
|
|
1064
|
-
|
|
1117
|
+
chalk8.gray(`\u23F1 Analysis time: ${chalk8.bold(elapsedTime + "s")}`)
|
|
1065
1118
|
);
|
|
1066
1119
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
1067
1120
|
if (sortedTypes.length > 0) {
|
|
1068
|
-
console.log(
|
|
1069
|
-
console.log(
|
|
1070
|
-
console.log(
|
|
1121
|
+
console.log("\n" + getTerminalDivider());
|
|
1122
|
+
console.log(chalk8.bold.white(" PATTERNS BY TYPE"));
|
|
1123
|
+
console.log(getTerminalDivider() + "\n");
|
|
1071
1124
|
sortedTypes.forEach(([type, count]) => {
|
|
1072
|
-
console.log(` ${
|
|
1125
|
+
console.log(` ${chalk8.white(type.padEnd(15))} ${chalk8.bold(count)}`);
|
|
1073
1126
|
});
|
|
1074
1127
|
}
|
|
1075
1128
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
1076
|
-
console.log(
|
|
1077
|
-
console.log(
|
|
1078
|
-
console.log(
|
|
1129
|
+
console.log("\n" + getTerminalDivider());
|
|
1130
|
+
console.log(chalk8.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
1131
|
+
console.log(getTerminalDivider() + "\n");
|
|
1079
1132
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
1080
1133
|
topDuplicates.forEach((dup) => {
|
|
1081
1134
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -1083,25 +1136,25 @@ async function patternsAction(directory, options) {
|
|
|
1083
1136
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
1084
1137
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
1085
1138
|
console.log(
|
|
1086
|
-
`${severityIcon} ${severity}: ${
|
|
1139
|
+
`${severityIcon} ${severity}: ${chalk8.bold(file1Name)} \u2194 ${chalk8.bold(file2Name)}`
|
|
1087
1140
|
);
|
|
1088
1141
|
console.log(
|
|
1089
|
-
` Similarity: ${
|
|
1142
|
+
` Similarity: ${chalk8.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk8.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1090
1143
|
);
|
|
1091
1144
|
console.log(
|
|
1092
|
-
` Lines: ${
|
|
1145
|
+
` Lines: ${chalk8.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk8.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1093
1146
|
`
|
|
1094
1147
|
);
|
|
1095
1148
|
});
|
|
1096
1149
|
} else {
|
|
1097
1150
|
console.log(
|
|
1098
|
-
|
|
1151
|
+
chalk8.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1099
1152
|
);
|
|
1100
1153
|
}
|
|
1101
1154
|
if (patternScore) {
|
|
1102
|
-
console.log(
|
|
1103
|
-
console.log(
|
|
1104
|
-
console.log(
|
|
1155
|
+
console.log(getTerminalDivider());
|
|
1156
|
+
console.log(chalk8.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
1157
|
+
console.log(getTerminalDivider() + "\n");
|
|
1105
1158
|
console.log(formatToolScore(patternScore));
|
|
1106
1159
|
console.log();
|
|
1107
1160
|
}
|
|
@@ -1110,7 +1163,7 @@ async function patternsAction(directory, options) {
|
|
|
1110
1163
|
handleCLIError3(error, "Pattern analysis");
|
|
1111
1164
|
}
|
|
1112
1165
|
}
|
|
1113
|
-
var
|
|
1166
|
+
var PATTERNS_HELP_TEXT = `
|
|
1114
1167
|
EXAMPLES:
|
|
1115
1168
|
$ aiready patterns # Default analysis
|
|
1116
1169
|
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
@@ -1118,20 +1171,19 @@ EXAMPLES:
|
|
|
1118
1171
|
`;
|
|
1119
1172
|
|
|
1120
1173
|
// src/commands/context.ts
|
|
1121
|
-
import
|
|
1122
|
-
import { resolve as resolvePath5 } from "path";
|
|
1174
|
+
import chalk9 from "chalk";
|
|
1123
1175
|
import {
|
|
1124
|
-
loadMergedConfig as loadMergedConfig3,
|
|
1125
|
-
handleJSONOutput as handleJSONOutput3,
|
|
1126
1176
|
handleCLIError as handleCLIError4,
|
|
1127
1177
|
getElapsedTime as getElapsedTime2,
|
|
1128
|
-
|
|
1129
|
-
|
|
1178
|
+
formatToolScore as formatToolScore2,
|
|
1179
|
+
prepareActionConfig as prepareActionConfig2,
|
|
1180
|
+
resolveOutputFormat as resolveOutputFormat2,
|
|
1181
|
+
formatStandardReport as formatStandardReport2,
|
|
1182
|
+
handleStandardJSONOutput as handleStandardJSONOutput2
|
|
1130
1183
|
} from "@aiready/core";
|
|
1131
1184
|
async function contextAction(directory, options) {
|
|
1132
|
-
console.log(
|
|
1185
|
+
console.log(chalk9.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
1133
1186
|
const startTime = Date.now();
|
|
1134
|
-
const resolvedDir = resolvePath5(process.cwd(), directory ?? ".");
|
|
1135
1187
|
try {
|
|
1136
1188
|
const defaults = {
|
|
1137
1189
|
maxDepth: 5,
|
|
@@ -1143,7 +1195,7 @@ async function contextAction(directory, options) {
|
|
|
1143
1195
|
file: void 0
|
|
1144
1196
|
}
|
|
1145
1197
|
};
|
|
1146
|
-
const baseOptions = await
|
|
1198
|
+
const { resolvedDir, finalOptions: baseOptions } = await prepareActionConfig2(directory, defaults, {
|
|
1147
1199
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
|
|
1148
1200
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
|
|
1149
1201
|
include: options.include?.split(","),
|
|
@@ -1175,107 +1227,105 @@ async function contextAction(directory, options) {
|
|
|
1175
1227
|
if (options.score) {
|
|
1176
1228
|
contextScore = calculateContextScore(summary);
|
|
1177
1229
|
}
|
|
1178
|
-
const outputFormat
|
|
1179
|
-
|
|
1230
|
+
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat2(
|
|
1231
|
+
options,
|
|
1232
|
+
finalOptions
|
|
1233
|
+
);
|
|
1180
1234
|
if (outputFormat === "json") {
|
|
1181
|
-
const outputData = {
|
|
1235
|
+
const outputData = formatStandardReport2({
|
|
1182
1236
|
results,
|
|
1183
|
-
summary
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
`aiready-report-${getReportTimestamp()}.json`,
|
|
1189
|
-
resolvedDir
|
|
1190
|
-
);
|
|
1191
|
-
handleJSONOutput3(
|
|
1237
|
+
summary,
|
|
1238
|
+
elapsedTime,
|
|
1239
|
+
score: contextScore
|
|
1240
|
+
});
|
|
1241
|
+
handleStandardJSONOutput2({
|
|
1192
1242
|
outputData,
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
);
|
|
1243
|
+
outputFile: userOutputFile,
|
|
1244
|
+
resolvedDir
|
|
1245
|
+
});
|
|
1196
1246
|
} else {
|
|
1197
1247
|
const terminalWidth = process.stdout.columns ?? 80;
|
|
1198
1248
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1199
1249
|
const divider = "\u2501".repeat(dividerWidth);
|
|
1200
|
-
console.log(
|
|
1201
|
-
console.log(
|
|
1202
|
-
console.log(
|
|
1250
|
+
console.log(chalk9.cyan(divider));
|
|
1251
|
+
console.log(chalk9.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1252
|
+
console.log(chalk9.cyan(divider) + "\n");
|
|
1203
1253
|
console.log(
|
|
1204
|
-
|
|
1254
|
+
chalk9.white(`\u{1F4C1} Files analyzed: ${chalk9.bold(summary.totalFiles)}`)
|
|
1205
1255
|
);
|
|
1206
1256
|
console.log(
|
|
1207
|
-
|
|
1208
|
-
`\u{1F4CA} Total tokens: ${
|
|
1257
|
+
chalk9.white(
|
|
1258
|
+
`\u{1F4CA} Total tokens: ${chalk9.bold(summary.totalTokens.toLocaleString())}`
|
|
1209
1259
|
)
|
|
1210
1260
|
);
|
|
1211
1261
|
console.log(
|
|
1212
|
-
|
|
1213
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1262
|
+
chalk9.yellow(
|
|
1263
|
+
`\u{1F4B0} Avg context budget: ${chalk9.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1214
1264
|
)
|
|
1215
1265
|
);
|
|
1216
1266
|
console.log(
|
|
1217
|
-
|
|
1267
|
+
chalk9.white(`\u23F1 Analysis time: ${chalk9.bold(elapsedTime + "s")}
|
|
1218
1268
|
`)
|
|
1219
1269
|
);
|
|
1220
1270
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1221
1271
|
if (totalIssues > 0) {
|
|
1222
|
-
console.log(
|
|
1272
|
+
console.log(chalk9.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1223
1273
|
if (summary.criticalIssues > 0) {
|
|
1224
1274
|
console.log(
|
|
1225
|
-
|
|
1275
|
+
chalk9.red(` \u{1F534} Critical: ${chalk9.bold(summary.criticalIssues)}`)
|
|
1226
1276
|
);
|
|
1227
1277
|
}
|
|
1228
1278
|
if (summary.majorIssues > 0) {
|
|
1229
1279
|
console.log(
|
|
1230
|
-
|
|
1280
|
+
chalk9.yellow(` \u{1F7E1} Major: ${chalk9.bold(summary.majorIssues)}`)
|
|
1231
1281
|
);
|
|
1232
1282
|
}
|
|
1233
1283
|
if (summary.minorIssues > 0) {
|
|
1234
1284
|
console.log(
|
|
1235
|
-
|
|
1285
|
+
chalk9.blue(` \u{1F535} Minor: ${chalk9.bold(summary.minorIssues)}`)
|
|
1236
1286
|
);
|
|
1237
1287
|
}
|
|
1238
1288
|
console.log(
|
|
1239
|
-
|
|
1289
|
+
chalk9.green(
|
|
1240
1290
|
`
|
|
1241
|
-
\u{1F4A1} Potential savings: ${
|
|
1291
|
+
\u{1F4A1} Potential savings: ${chalk9.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1242
1292
|
`
|
|
1243
1293
|
)
|
|
1244
1294
|
);
|
|
1245
1295
|
} else {
|
|
1246
|
-
console.log(
|
|
1296
|
+
console.log(chalk9.green("\u2705 No significant issues found!\n"));
|
|
1247
1297
|
}
|
|
1248
1298
|
if (summary.deepFiles.length > 0) {
|
|
1249
|
-
console.log(
|
|
1299
|
+
console.log(chalk9.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1250
1300
|
console.log(
|
|
1251
|
-
|
|
1301
|
+
chalk9.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1252
1302
|
);
|
|
1253
1303
|
console.log(
|
|
1254
|
-
|
|
1304
|
+
chalk9.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1255
1305
|
`)
|
|
1256
1306
|
);
|
|
1257
1307
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1258
1308
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1259
1309
|
console.log(
|
|
1260
|
-
` ${
|
|
1310
|
+
` ${chalk9.cyan("\u2192")} ${chalk9.white(fileName)} ${chalk9.dim(`(depth: ${item.depth})`)}`
|
|
1261
1311
|
);
|
|
1262
1312
|
});
|
|
1263
1313
|
console.log();
|
|
1264
1314
|
}
|
|
1265
1315
|
if (summary.fragmentedModules.length > 0) {
|
|
1266
|
-
console.log(
|
|
1316
|
+
console.log(chalk9.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1267
1317
|
console.log(
|
|
1268
|
-
|
|
1318
|
+
chalk9.gray(
|
|
1269
1319
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1270
1320
|
`
|
|
1271
1321
|
)
|
|
1272
1322
|
);
|
|
1273
1323
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1274
1324
|
console.log(
|
|
1275
|
-
` ${
|
|
1325
|
+
` ${chalk9.yellow("\u25CF")} ${chalk9.white(module.domain)} - ${chalk9.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1276
1326
|
);
|
|
1277
1327
|
console.log(
|
|
1278
|
-
|
|
1328
|
+
chalk9.dim(
|
|
1279
1329
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1280
1330
|
)
|
|
1281
1331
|
);
|
|
@@ -1283,9 +1333,9 @@ async function contextAction(directory, options) {
|
|
|
1283
1333
|
console.log();
|
|
1284
1334
|
}
|
|
1285
1335
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1286
|
-
console.log(
|
|
1336
|
+
console.log(chalk9.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1287
1337
|
console.log(
|
|
1288
|
-
|
|
1338
|
+
chalk9.gray(
|
|
1289
1339
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1290
1340
|
`
|
|
1291
1341
|
)
|
|
@@ -1293,28 +1343,28 @@ async function contextAction(directory, options) {
|
|
|
1293
1343
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1294
1344
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1295
1345
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1296
|
-
const color = item.score < 0.4 ?
|
|
1346
|
+
const color = item.score < 0.4 ? chalk9.red : chalk9.yellow;
|
|
1297
1347
|
console.log(
|
|
1298
|
-
` ${color("\u25CB")} ${
|
|
1348
|
+
` ${color("\u25CB")} ${chalk9.white(fileName)} ${chalk9.dim(`(${scorePercent}% cohesion)`)}`
|
|
1299
1349
|
);
|
|
1300
1350
|
});
|
|
1301
1351
|
console.log();
|
|
1302
1352
|
}
|
|
1303
1353
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1304
|
-
console.log(
|
|
1354
|
+
console.log(chalk9.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1305
1355
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1306
1356
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1307
|
-
const severityColor = item.severity === "critical" ?
|
|
1357
|
+
const severityColor = item.severity === "critical" ? chalk9.red : item.severity === "major" ? chalk9.yellow : chalk9.blue;
|
|
1308
1358
|
console.log(
|
|
1309
|
-
` ${severityColor("\u25CF")} ${
|
|
1359
|
+
` ${severityColor("\u25CF")} ${chalk9.white(fileName)} ${chalk9.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1310
1360
|
);
|
|
1311
1361
|
});
|
|
1312
1362
|
console.log();
|
|
1313
1363
|
}
|
|
1314
1364
|
if (contextScore) {
|
|
1315
|
-
console.log(
|
|
1316
|
-
console.log(
|
|
1317
|
-
console.log(
|
|
1365
|
+
console.log(chalk9.cyan(divider));
|
|
1366
|
+
console.log(chalk9.bold.white(" AI READINESS SCORE (Context)"));
|
|
1367
|
+
console.log(chalk9.cyan(divider) + "\n");
|
|
1318
1368
|
console.log(formatToolScore2(contextScore));
|
|
1319
1369
|
console.log();
|
|
1320
1370
|
}
|
|
@@ -1325,21 +1375,21 @@ async function contextAction(directory, options) {
|
|
|
1325
1375
|
}
|
|
1326
1376
|
|
|
1327
1377
|
// src/commands/consistency.ts
|
|
1328
|
-
import
|
|
1378
|
+
import chalk10 from "chalk";
|
|
1329
1379
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1330
|
-
import { resolve as resolvePath6 } from "path";
|
|
1331
1380
|
import {
|
|
1332
|
-
loadMergedConfig as loadMergedConfig4,
|
|
1333
|
-
handleJSONOutput as handleJSONOutput4,
|
|
1334
1381
|
handleCLIError as handleCLIError5,
|
|
1335
1382
|
getElapsedTime as getElapsedTime3,
|
|
1336
|
-
resolveOutputPath as
|
|
1337
|
-
formatToolScore as formatToolScore3
|
|
1383
|
+
resolveOutputPath as resolveOutputPath2,
|
|
1384
|
+
formatToolScore as formatToolScore3,
|
|
1385
|
+
prepareActionConfig as prepareActionConfig3,
|
|
1386
|
+
resolveOutputFormat as resolveOutputFormat3,
|
|
1387
|
+
formatStandardReport as formatStandardReport3,
|
|
1388
|
+
handleStandardJSONOutput as handleStandardJSONOutput3
|
|
1338
1389
|
} from "@aiready/core";
|
|
1339
1390
|
async function consistencyAction(directory, options) {
|
|
1340
|
-
console.log(
|
|
1391
|
+
console.log(chalk10.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1341
1392
|
const startTime = Date.now();
|
|
1342
|
-
const resolvedDir = resolvePath6(process.cwd(), directory ?? ".");
|
|
1343
1393
|
try {
|
|
1344
1394
|
const defaults = {
|
|
1345
1395
|
checkNaming: true,
|
|
@@ -1352,13 +1402,17 @@ async function consistencyAction(directory, options) {
|
|
|
1352
1402
|
file: void 0
|
|
1353
1403
|
}
|
|
1354
1404
|
};
|
|
1355
|
-
const finalOptions = await
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1405
|
+
const { resolvedDir, finalOptions } = await prepareActionConfig3(
|
|
1406
|
+
directory,
|
|
1407
|
+
defaults,
|
|
1408
|
+
{
|
|
1409
|
+
checkNaming: options.naming !== false,
|
|
1410
|
+
checkPatterns: options.patterns !== false,
|
|
1411
|
+
minSeverity: options.minSeverity,
|
|
1412
|
+
include: options.include?.split(","),
|
|
1413
|
+
exclude: options.exclude?.split(",")
|
|
1414
|
+
}
|
|
1415
|
+
);
|
|
1362
1416
|
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1363
1417
|
const report = await analyzeConsistency(finalOptions);
|
|
1364
1418
|
const elapsedTime = getElapsedTime3(startTime);
|
|
@@ -1370,52 +1424,47 @@ async function consistencyAction(directory, options) {
|
|
|
1370
1424
|
report.summary.filesAnalyzed
|
|
1371
1425
|
);
|
|
1372
1426
|
}
|
|
1373
|
-
const outputFormat
|
|
1374
|
-
|
|
1427
|
+
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat3(
|
|
1428
|
+
options,
|
|
1429
|
+
finalOptions
|
|
1430
|
+
);
|
|
1375
1431
|
if (outputFormat === "json") {
|
|
1376
|
-
const outputData = {
|
|
1377
|
-
|
|
1378
|
-
summary:
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
};
|
|
1384
|
-
const outputPath = resolveOutputPath4(
|
|
1385
|
-
userOutputFile,
|
|
1386
|
-
`aiready-report-${getReportTimestamp()}.json`,
|
|
1387
|
-
resolvedDir
|
|
1388
|
-
);
|
|
1389
|
-
handleJSONOutput4(
|
|
1432
|
+
const outputData = formatStandardReport3({
|
|
1433
|
+
report,
|
|
1434
|
+
summary: report.summary,
|
|
1435
|
+
elapsedTime,
|
|
1436
|
+
score: consistencyScore
|
|
1437
|
+
});
|
|
1438
|
+
handleStandardJSONOutput3({
|
|
1390
1439
|
outputData,
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
);
|
|
1440
|
+
outputFile: userOutputFile,
|
|
1441
|
+
resolvedDir
|
|
1442
|
+
});
|
|
1394
1443
|
} else if (outputFormat === "markdown") {
|
|
1395
1444
|
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1396
|
-
const outputPath =
|
|
1445
|
+
const outputPath = resolveOutputPath2(
|
|
1397
1446
|
userOutputFile,
|
|
1398
1447
|
`aiready-report-${getReportTimestamp()}.md`,
|
|
1399
1448
|
resolvedDir
|
|
1400
1449
|
);
|
|
1401
1450
|
writeFileSync3(outputPath, markdown);
|
|
1402
|
-
console.log(
|
|
1451
|
+
console.log(chalk10.green(`\u2705 Report saved to ${outputPath}`));
|
|
1403
1452
|
} else {
|
|
1404
|
-
console.log(
|
|
1453
|
+
console.log(chalk10.bold("\n\u{1F4CA} Summary\n"));
|
|
1405
1454
|
console.log(
|
|
1406
|
-
`Files Analyzed: ${
|
|
1455
|
+
`Files Analyzed: ${chalk10.cyan(report.summary.filesAnalyzed)}`
|
|
1407
1456
|
);
|
|
1408
|
-
console.log(`Total Issues: ${
|
|
1409
|
-
console.log(` Naming: ${
|
|
1410
|
-
console.log(` Patterns: ${
|
|
1457
|
+
console.log(`Total Issues: ${chalk10.yellow(report.summary.totalIssues)}`);
|
|
1458
|
+
console.log(` Naming: ${chalk10.yellow(report.summary.namingIssues)}`);
|
|
1459
|
+
console.log(` Patterns: ${chalk10.yellow(report.summary.patternIssues)}`);
|
|
1411
1460
|
console.log(
|
|
1412
|
-
` Architecture: ${
|
|
1461
|
+
` Architecture: ${chalk10.yellow(report.summary.architectureIssues ?? 0)}`
|
|
1413
1462
|
);
|
|
1414
|
-
console.log(`Analysis Time: ${
|
|
1463
|
+
console.log(`Analysis Time: ${chalk10.gray(elapsedTime + "s")}
|
|
1415
1464
|
`);
|
|
1416
1465
|
if (report.summary.totalIssues === 0) {
|
|
1417
1466
|
console.log(
|
|
1418
|
-
|
|
1467
|
+
chalk10.green(
|
|
1419
1468
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1420
1469
|
)
|
|
1421
1470
|
);
|
|
@@ -1427,20 +1476,20 @@ async function consistencyAction(directory, options) {
|
|
|
1427
1476
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1428
1477
|
);
|
|
1429
1478
|
if (namingResults.length > 0) {
|
|
1430
|
-
console.log(
|
|
1479
|
+
console.log(chalk10.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1431
1480
|
let shown = 0;
|
|
1432
|
-
for (const
|
|
1481
|
+
for (const namingFileResult of namingResults) {
|
|
1433
1482
|
if (shown >= 5) break;
|
|
1434
|
-
for (const issue of
|
|
1483
|
+
for (const issue of namingFileResult.issues) {
|
|
1435
1484
|
if (shown >= 5) break;
|
|
1436
|
-
const severityColor = issue.severity === "critical" ?
|
|
1485
|
+
const severityColor = issue.severity === "critical" ? chalk10.red : issue.severity === "major" ? chalk10.yellow : issue.severity === "minor" ? chalk10.blue : chalk10.gray;
|
|
1437
1486
|
console.log(
|
|
1438
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1487
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk10.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1439
1488
|
);
|
|
1440
1489
|
console.log(` ${issue.message}`);
|
|
1441
1490
|
if (issue.suggestion) {
|
|
1442
1491
|
console.log(
|
|
1443
|
-
` ${
|
|
1492
|
+
` ${chalk10.dim("\u2192")} ${chalk10.italic(issue.suggestion)}`
|
|
1444
1493
|
);
|
|
1445
1494
|
}
|
|
1446
1495
|
console.log();
|
|
@@ -1449,25 +1498,25 @@ async function consistencyAction(directory, options) {
|
|
|
1449
1498
|
}
|
|
1450
1499
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1451
1500
|
if (remaining > 0) {
|
|
1452
|
-
console.log(
|
|
1501
|
+
console.log(chalk10.dim(` ... and ${remaining} more issues
|
|
1453
1502
|
`));
|
|
1454
1503
|
}
|
|
1455
1504
|
}
|
|
1456
1505
|
if (patternResults.length > 0) {
|
|
1457
|
-
console.log(
|
|
1506
|
+
console.log(chalk10.bold("\u{1F504} Pattern Issues\n"));
|
|
1458
1507
|
let shown = 0;
|
|
1459
|
-
for (const
|
|
1508
|
+
for (const patternFileResult of patternResults) {
|
|
1460
1509
|
if (shown >= 5) break;
|
|
1461
|
-
for (const issue of
|
|
1510
|
+
for (const issue of patternFileResult.issues) {
|
|
1462
1511
|
if (shown >= 5) break;
|
|
1463
|
-
const severityColor = issue.severity === "critical" ?
|
|
1512
|
+
const severityColor = issue.severity === "critical" ? chalk10.red : issue.severity === "major" ? chalk10.yellow : issue.severity === "minor" ? chalk10.blue : chalk10.gray;
|
|
1464
1513
|
console.log(
|
|
1465
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1514
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk10.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1466
1515
|
);
|
|
1467
1516
|
console.log(` ${issue.message}`);
|
|
1468
1517
|
if (issue.suggestion) {
|
|
1469
1518
|
console.log(
|
|
1470
|
-
` ${
|
|
1519
|
+
` ${chalk10.dim("\u2192")} ${chalk10.italic(issue.suggestion)}`
|
|
1471
1520
|
);
|
|
1472
1521
|
}
|
|
1473
1522
|
console.log();
|
|
@@ -1476,12 +1525,12 @@ async function consistencyAction(directory, options) {
|
|
|
1476
1525
|
}
|
|
1477
1526
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1478
1527
|
if (remaining > 0) {
|
|
1479
|
-
console.log(
|
|
1528
|
+
console.log(chalk10.dim(` ... and ${remaining} more issues
|
|
1480
1529
|
`));
|
|
1481
1530
|
}
|
|
1482
1531
|
}
|
|
1483
1532
|
if (report.recommendations.length > 0) {
|
|
1484
|
-
console.log(
|
|
1533
|
+
console.log(chalk10.bold("\u{1F4A1} Recommendations\n"));
|
|
1485
1534
|
report.recommendations.forEach((rec, i) => {
|
|
1486
1535
|
console.log(`${i + 1}. ${rec}`);
|
|
1487
1536
|
});
|
|
@@ -1489,7 +1538,7 @@ async function consistencyAction(directory, options) {
|
|
|
1489
1538
|
}
|
|
1490
1539
|
}
|
|
1491
1540
|
if (consistencyScore) {
|
|
1492
|
-
console.log(
|
|
1541
|
+
console.log(chalk10.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1493
1542
|
console.log(formatToolScore3(consistencyScore));
|
|
1494
1543
|
console.log();
|
|
1495
1544
|
}
|
|
@@ -1500,27 +1549,27 @@ async function consistencyAction(directory, options) {
|
|
|
1500
1549
|
}
|
|
1501
1550
|
|
|
1502
1551
|
// src/commands/visualize.ts
|
|
1503
|
-
import
|
|
1552
|
+
import chalk11 from "chalk";
|
|
1504
1553
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1505
|
-
import { resolve as
|
|
1554
|
+
import { resolve as resolvePath5 } from "path";
|
|
1506
1555
|
import { spawn } from "child_process";
|
|
1507
1556
|
import { handleCLIError as handleCLIError6 } from "@aiready/core";
|
|
1508
1557
|
import { generateHTML, findLatestReport as findLatestReport2 } from "@aiready/core";
|
|
1509
1558
|
async function visualizeAction(directory, options) {
|
|
1510
1559
|
try {
|
|
1511
|
-
const dirPath =
|
|
1512
|
-
let reportPath = options.report ?
|
|
1560
|
+
const dirPath = resolvePath5(process.cwd(), directory ?? ".");
|
|
1561
|
+
let reportPath = options.report ? resolvePath5(dirPath, options.report) : null;
|
|
1513
1562
|
if (!reportPath || !existsSync3(reportPath)) {
|
|
1514
1563
|
const latestScan = findLatestReport2(dirPath);
|
|
1515
1564
|
if (latestScan) {
|
|
1516
1565
|
reportPath = latestScan;
|
|
1517
1566
|
console.log(
|
|
1518
|
-
|
|
1567
|
+
chalk11.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1519
1568
|
);
|
|
1520
1569
|
} else {
|
|
1521
|
-
console.error(
|
|
1570
|
+
console.error(chalk11.red("\u274C No AI readiness report found"));
|
|
1522
1571
|
console.log(
|
|
1523
|
-
|
|
1572
|
+
chalk11.dim(
|
|
1524
1573
|
`
|
|
1525
1574
|
Generate a report with:
|
|
1526
1575
|
aiready scan --output json
|
|
@@ -1534,7 +1583,7 @@ Or specify a custom report:
|
|
|
1534
1583
|
}
|
|
1535
1584
|
const raw = readFileSync3(reportPath, "utf8");
|
|
1536
1585
|
const report = JSON.parse(raw);
|
|
1537
|
-
const configPath =
|
|
1586
|
+
const configPath = resolvePath5(dirPath, "aiready.json");
|
|
1538
1587
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1539
1588
|
if (existsSync3(configPath)) {
|
|
1540
1589
|
try {
|
|
@@ -1558,7 +1607,7 @@ Or specify a custom report:
|
|
|
1558
1607
|
let devServerStarted = false;
|
|
1559
1608
|
if (useDevMode) {
|
|
1560
1609
|
try {
|
|
1561
|
-
const localWebDir =
|
|
1610
|
+
const localWebDir = resolvePath5(dirPath, "packages/visualizer");
|
|
1562
1611
|
let webDir = "";
|
|
1563
1612
|
let visualizerAvailable = false;
|
|
1564
1613
|
if (existsSync3(localWebDir)) {
|
|
@@ -1566,8 +1615,8 @@ Or specify a custom report:
|
|
|
1566
1615
|
visualizerAvailable = true;
|
|
1567
1616
|
} else {
|
|
1568
1617
|
const nodemodulesLocations = [
|
|
1569
|
-
|
|
1570
|
-
|
|
1618
|
+
resolvePath5(dirPath, "node_modules", "@aiready", "visualizer"),
|
|
1619
|
+
resolvePath5(
|
|
1571
1620
|
process.cwd(),
|
|
1572
1621
|
"node_modules",
|
|
1573
1622
|
"@aiready",
|
|
@@ -1577,14 +1626,14 @@ Or specify a custom report:
|
|
|
1577
1626
|
let currentDir = dirPath;
|
|
1578
1627
|
while (currentDir !== "/" && currentDir !== ".") {
|
|
1579
1628
|
nodemodulesLocations.push(
|
|
1580
|
-
|
|
1629
|
+
resolvePath5(currentDir, "node_modules", "@aiready", "visualizer")
|
|
1581
1630
|
);
|
|
1582
|
-
const parent =
|
|
1631
|
+
const parent = resolvePath5(currentDir, "..");
|
|
1583
1632
|
if (parent === currentDir) break;
|
|
1584
1633
|
currentDir = parent;
|
|
1585
1634
|
}
|
|
1586
1635
|
for (const location of nodemodulesLocations) {
|
|
1587
|
-
if (existsSync3(location) && existsSync3(
|
|
1636
|
+
if (existsSync3(location) && existsSync3(resolvePath5(location, "package.json"))) {
|
|
1588
1637
|
webDir = location;
|
|
1589
1638
|
visualizerAvailable = true;
|
|
1590
1639
|
break;
|
|
@@ -1593,20 +1642,20 @@ Or specify a custom report:
|
|
|
1593
1642
|
if (!visualizerAvailable) {
|
|
1594
1643
|
try {
|
|
1595
1644
|
const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
|
|
1596
|
-
webDir =
|
|
1645
|
+
webDir = resolvePath5(vizPkgPath, "..");
|
|
1597
1646
|
visualizerAvailable = true;
|
|
1598
1647
|
} catch (err) {
|
|
1599
1648
|
void err;
|
|
1600
1649
|
}
|
|
1601
1650
|
}
|
|
1602
1651
|
}
|
|
1603
|
-
const webViteConfigExists = webDir && existsSync3(
|
|
1652
|
+
const webViteConfigExists = webDir && existsSync3(resolvePath5(webDir, "web", "vite.config.ts"));
|
|
1604
1653
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1605
1654
|
const spawnCwd = webDir;
|
|
1606
1655
|
const { watch } = await import("fs");
|
|
1607
1656
|
const copyReportToViz = () => {
|
|
1608
1657
|
try {
|
|
1609
|
-
const destPath =
|
|
1658
|
+
const destPath = resolvePath5(spawnCwd, "web", "report-data.json");
|
|
1610
1659
|
copyFileSync(reportPath, destPath);
|
|
1611
1660
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1612
1661
|
} catch (e) {
|
|
@@ -1650,19 +1699,19 @@ Or specify a custom report:
|
|
|
1650
1699
|
return;
|
|
1651
1700
|
} else {
|
|
1652
1701
|
console.log(
|
|
1653
|
-
|
|
1702
|
+
chalk11.yellow(
|
|
1654
1703
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1655
1704
|
)
|
|
1656
1705
|
);
|
|
1657
1706
|
console.log(
|
|
1658
|
-
|
|
1707
|
+
chalk11.cyan(" Falling back to static HTML generation...\n")
|
|
1659
1708
|
);
|
|
1660
1709
|
useDevMode = false;
|
|
1661
1710
|
}
|
|
1662
1711
|
} catch (err) {
|
|
1663
1712
|
console.error("Failed to start dev server:", err);
|
|
1664
1713
|
console.log(
|
|
1665
|
-
|
|
1714
|
+
chalk11.cyan(" Falling back to static HTML generation...\n")
|
|
1666
1715
|
);
|
|
1667
1716
|
useDevMode = false;
|
|
1668
1717
|
}
|
|
@@ -1670,9 +1719,9 @@ Or specify a custom report:
|
|
|
1670
1719
|
console.log("Generating HTML...");
|
|
1671
1720
|
const html = generateHTML(graph);
|
|
1672
1721
|
const defaultOutput = "visualization.html";
|
|
1673
|
-
const outPath =
|
|
1722
|
+
const outPath = resolvePath5(dirPath, options.output ?? defaultOutput);
|
|
1674
1723
|
writeFileSync4(outPath, html, "utf8");
|
|
1675
|
-
console.log(
|
|
1724
|
+
console.log(chalk11.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1676
1725
|
if (options.open || options.serve) {
|
|
1677
1726
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1678
1727
|
if (options.serve) {
|
|
@@ -1702,7 +1751,7 @@ Or specify a custom report:
|
|
|
1702
1751
|
server.listen(port, () => {
|
|
1703
1752
|
const addr = `http://localhost:${port}/`;
|
|
1704
1753
|
console.log(
|
|
1705
|
-
|
|
1754
|
+
chalk11.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1706
1755
|
);
|
|
1707
1756
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1708
1757
|
});
|
|
@@ -1721,7 +1770,7 @@ Or specify a custom report:
|
|
|
1721
1770
|
handleCLIError6(err, "Visualization");
|
|
1722
1771
|
}
|
|
1723
1772
|
}
|
|
1724
|
-
var
|
|
1773
|
+
var VISUALIZE_HELP_TEXT = `
|
|
1725
1774
|
EXAMPLES:
|
|
1726
1775
|
$ aiready visualize . # Auto-detects latest report, generates HTML
|
|
1727
1776
|
$ aiready visualize . --report .aiready/aiready-report-20260217-143022.json
|
|
@@ -1738,7 +1787,7 @@ NOTES:
|
|
|
1738
1787
|
- --dev starts a Vite dev server with live reload (requires local @aiready/visualizer installation).
|
|
1739
1788
|
When --dev is not available, it falls back to static HTML generation.
|
|
1740
1789
|
`;
|
|
1741
|
-
var
|
|
1790
|
+
var VISUALISE_HELP_TEXT = `
|
|
1742
1791
|
EXAMPLES:
|
|
1743
1792
|
$ aiready visualise . # Auto-detects latest report
|
|
1744
1793
|
$ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
|
|
@@ -1749,14 +1798,14 @@ NOTES:
|
|
|
1749
1798
|
`;
|
|
1750
1799
|
|
|
1751
1800
|
// src/commands/shared/standard-tool-actions.ts
|
|
1752
|
-
import
|
|
1801
|
+
import chalk12 from "chalk";
|
|
1753
1802
|
|
|
1754
1803
|
// src/commands/agent-grounding.ts
|
|
1755
|
-
import
|
|
1804
|
+
import chalk13 from "chalk";
|
|
1756
1805
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1757
1806
|
|
|
1758
1807
|
// src/commands/testability.ts
|
|
1759
|
-
import
|
|
1808
|
+
import chalk14 from "chalk";
|
|
1760
1809
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1761
1810
|
async function testabilityAction(directory, options) {
|
|
1762
1811
|
const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
|
|
@@ -1781,28 +1830,28 @@ async function testabilityAction(directory, options) {
|
|
|
1781
1830
|
"blind-risk": "\u{1F480}"
|
|
1782
1831
|
};
|
|
1783
1832
|
const safetyColors = {
|
|
1784
|
-
safe:
|
|
1785
|
-
"moderate-risk":
|
|
1786
|
-
"high-risk":
|
|
1787
|
-
"blind-risk":
|
|
1833
|
+
safe: chalk14.green,
|
|
1834
|
+
"moderate-risk": chalk14.yellow,
|
|
1835
|
+
"high-risk": chalk14.red,
|
|
1836
|
+
"blind-risk": chalk14.bgRed.white
|
|
1788
1837
|
};
|
|
1789
1838
|
const safety = report.summary.aiChangeSafetyRating;
|
|
1790
1839
|
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1791
|
-
const color = safetyColors[safety] ??
|
|
1840
|
+
const color = safetyColors[safety] ?? chalk14.white;
|
|
1792
1841
|
console.log(
|
|
1793
|
-
` \u{1F9EA} Testability: ${
|
|
1842
|
+
` \u{1F9EA} Testability: ${chalk14.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1794
1843
|
);
|
|
1795
1844
|
console.log(
|
|
1796
1845
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1797
1846
|
);
|
|
1798
1847
|
console.log(
|
|
1799
|
-
|
|
1848
|
+
chalk14.dim(
|
|
1800
1849
|
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1801
1850
|
)
|
|
1802
1851
|
);
|
|
1803
1852
|
if (safety === "blind-risk") {
|
|
1804
1853
|
console.log(
|
|
1805
|
-
|
|
1854
|
+
chalk14.red.bold(
|
|
1806
1855
|
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1807
1856
|
)
|
|
1808
1857
|
);
|
|
@@ -1814,7 +1863,7 @@ async function testabilityAction(directory, options) {
|
|
|
1814
1863
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1815
1864
|
|
|
1816
1865
|
// src/commands/bug.ts
|
|
1817
|
-
import
|
|
1866
|
+
import chalk15 from "chalk";
|
|
1818
1867
|
import { execSync } from "child_process";
|
|
1819
1868
|
async function bugAction(message, options) {
|
|
1820
1869
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1832,35 +1881,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1832
1881
|
Type: ${type}
|
|
1833
1882
|
`.trim();
|
|
1834
1883
|
if (options.submit) {
|
|
1835
|
-
console.log(
|
|
1884
|
+
console.log(chalk15.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1836
1885
|
try {
|
|
1837
1886
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1838
1887
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1839
1888
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1840
|
-
console.log(
|
|
1841
|
-
console.log(
|
|
1889
|
+
console.log(chalk15.green("\u2705 Issue Created Successfully!"));
|
|
1890
|
+
console.log(chalk15.cyan(output));
|
|
1842
1891
|
return;
|
|
1843
1892
|
} catch {
|
|
1844
|
-
console.error(
|
|
1893
|
+
console.error(chalk15.red("\n\u274C Failed to submit via gh CLI."));
|
|
1845
1894
|
console.log(
|
|
1846
|
-
|
|
1895
|
+
chalk15.yellow(
|
|
1847
1896
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1848
1897
|
)
|
|
1849
1898
|
);
|
|
1850
|
-
console.log(
|
|
1899
|
+
console.log(chalk15.dim(" Falling back to URL generation..."));
|
|
1851
1900
|
}
|
|
1852
1901
|
}
|
|
1853
1902
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1854
1903
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1855
|
-
console.log(
|
|
1856
|
-
console.log(
|
|
1857
|
-
console.log(
|
|
1858
|
-
console.log(
|
|
1859
|
-
console.log(
|
|
1860
|
-
console.log(
|
|
1861
|
-
console.log(
|
|
1904
|
+
console.log(chalk15.green("\u{1F680} Issue Draft Prepared!\n"));
|
|
1905
|
+
console.log(chalk15.bold("Title: ") + title);
|
|
1906
|
+
console.log(chalk15.bold("Type: ") + type);
|
|
1907
|
+
console.log(chalk15.bold("\nClick the link below to submit this issue:"));
|
|
1908
|
+
console.log(chalk15.cyan(fullUrl));
|
|
1909
|
+
console.log(chalk15.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1910
|
+
console.log(chalk15.dim(" You have successfully prepared a report."));
|
|
1862
1911
|
console.log(
|
|
1863
|
-
|
|
1912
|
+
chalk15.dim(
|
|
1864
1913
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1865
1914
|
)
|
|
1866
1915
|
);
|
|
@@ -1869,19 +1918,19 @@ Type: ${type}
|
|
|
1869
1918
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1870
1919
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1871
1920
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1872
|
-
console.log(
|
|
1873
|
-
console.log(` Report a Bug: ${
|
|
1874
|
-
console.log(` Request a Feature: ${
|
|
1875
|
-
console.log(` Suggest a Metric: ${
|
|
1876
|
-
console.log(
|
|
1877
|
-
console.log(
|
|
1921
|
+
console.log(chalk15.blue("\u{1F4AC} Feedback & Bug Reports\n"));
|
|
1922
|
+
console.log(` Report a Bug: ${chalk15.cyan(bugUrl)}`);
|
|
1923
|
+
console.log(` Request a Feature: ${chalk15.cyan(featureUrl)}`);
|
|
1924
|
+
console.log(` Suggest a Metric: ${chalk15.cyan(metricUrl)}`);
|
|
1925
|
+
console.log(chalk15.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1926
|
+
console.log(chalk15.dim(" To prepare a specific report, run:"));
|
|
1878
1927
|
console.log(
|
|
1879
|
-
|
|
1928
|
+
chalk15.cyan(
|
|
1880
1929
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1881
1930
|
)
|
|
1882
1931
|
);
|
|
1883
1932
|
}
|
|
1884
|
-
var
|
|
1933
|
+
var BUG_HELP_TEXT = `
|
|
1885
1934
|
EXAMPLES:
|
|
1886
1935
|
$ aiready bug # Show general links
|
|
1887
1936
|
$ aiready bug "Naming check is too slow" # Prepare a pre-filled bug report
|
|
@@ -1950,14 +1999,14 @@ program.command("scan").description(
|
|
|
1950
1999
|
).option(
|
|
1951
2000
|
"--compare-to <path>",
|
|
1952
2001
|
"Compare results against a previous AIReady report JSON"
|
|
1953
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)"
|
|
2002
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option(
|
|
1954
2003
|
"--ci",
|
|
1955
2004
|
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
1956
2005
|
).option(
|
|
1957
2006
|
"--fail-on <level>",
|
|
1958
2007
|
"Fail on issues: critical, major, any",
|
|
1959
2008
|
"critical"
|
|
1960
|
-
).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",
|
|
2009
|
+
).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", SCAN_HELP_TEXT).action(async (directory, options) => {
|
|
1961
2010
|
await scanAction(directory, options);
|
|
1962
2011
|
});
|
|
1963
2012
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
@@ -1976,7 +2025,7 @@ program.command("patterns").description("Detect duplicate code patterns that con
|
|
|
1976
2025
|
).option(
|
|
1977
2026
|
"--full-scan",
|
|
1978
2027
|
"Disable smart defaults for comprehensive analysis (slower)"
|
|
1979
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)"
|
|
2028
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after", PATTERNS_HELP_TEXT).action(async (directory, options) => {
|
|
1980
2029
|
await patternsAction(directory, options);
|
|
1981
2030
|
});
|
|
1982
2031
|
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(
|
|
@@ -2012,7 +2061,7 @@ program.command("visualise").description("Alias for visualize (British spelling)
|
|
|
2012
2061
|
"--dev",
|
|
2013
2062
|
"Start Vite dev server (live reload) for interactive development",
|
|
2014
2063
|
true
|
|
2015
|
-
).addHelpText("after",
|
|
2064
|
+
).addHelpText("after", VISUALISE_HELP_TEXT).action(async (directory, options) => {
|
|
2016
2065
|
await visualizeAction(directory, options);
|
|
2017
2066
|
});
|
|
2018
2067
|
program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option(
|
|
@@ -2030,7 +2079,7 @@ program.command("visualize").description("Generate interactive visualization fro
|
|
|
2030
2079
|
"--dev",
|
|
2031
2080
|
"Start Vite dev server (live reload) for interactive development",
|
|
2032
2081
|
false
|
|
2033
|
-
).addHelpText("after",
|
|
2082
|
+
).addHelpText("after", VISUALIZE_HELP_TEXT).action(async (directory, options) => {
|
|
2034
2083
|
await visualizeAction(directory, options);
|
|
2035
2084
|
});
|
|
2036
2085
|
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) => {
|
|
@@ -2039,10 +2088,10 @@ program.command("change-amplification").description("Analyze graph metrics for c
|
|
|
2039
2088
|
program.command("testability").description("Analyze test coverage and AI readiness").argument("[directory]", "Directory to analyze", ".").option("--min-coverage <ratio>", "Minimum acceptable coverage ratio", "0.3").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) => {
|
|
2040
2089
|
await testabilityAction(directory, options);
|
|
2041
2090
|
});
|
|
2042
|
-
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",
|
|
2091
|
+
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", UPLOAD_HELP_TEXT).action(async (file, options) => {
|
|
2043
2092
|
await uploadAction(file, options);
|
|
2044
2093
|
});
|
|
2045
|
-
program.command("bug").description("Report a bug or provide feedback (Agent-friendly)").argument("[message]", "Short description of the issue").option("-t, --type <type>", "Issue type: bug, feature, metric", "bug").option("--submit", "Submit the issue directly using the GitHub CLI (gh)").addHelpText("after",
|
|
2094
|
+
program.command("bug").description("Report a bug or provide feedback (Agent-friendly)").argument("[message]", "Short description of the issue").option("-t, --type <type>", "Issue type: bug, feature, metric", "bug").option("--submit", "Submit the issue directly using the GitHub CLI (gh)").addHelpText("after", BUG_HELP_TEXT).action(async (message, options) => {
|
|
2046
2095
|
await bugAction(message, options);
|
|
2047
2096
|
});
|
|
2048
2097
|
program.parse();
|