@aiready/core 0.23.23 → 0.24.0
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-2ILVUVRK.mjs +860 -0
- package/dist/chunk-G4Z45SMK.mjs +852 -0
- package/dist/chunk-LTAZ7Z62.mjs +855 -0
- package/dist/chunk-SM6INS52.mjs +501 -0
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +20 -12
- package/dist/client/index.mjs +1 -1
- package/dist/index-CkM98qn1.d.mts +1299 -0
- package/dist/index-CkM98qn1.d.ts +1299 -0
- package/dist/index-DLHCsiAk.d.mts +1294 -0
- package/dist/index-DLHCsiAk.d.ts +1294 -0
- package/dist/index-EQ2jRSlB.d.mts +1301 -0
- package/dist/index-EQ2jRSlB.d.ts +1301 -0
- package/dist/index.d.mts +177 -139
- package/dist/index.d.ts +177 -139
- package/dist/index.js +483 -253
- package/dist/index.mjs +451 -240
- package/dist/python-parser-SJ3LFZFJ.mjs +8 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -52,13 +52,13 @@ import {
|
|
|
52
52
|
getToolWeight,
|
|
53
53
|
normalizeToolName,
|
|
54
54
|
parseWeightString
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-2ILVUVRK.mjs";
|
|
56
56
|
import {
|
|
57
57
|
TypeScriptParser
|
|
58
58
|
} from "./chunk-UTCRW3N7.mjs";
|
|
59
59
|
import {
|
|
60
60
|
PythonParser
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-SM6INS52.mjs";
|
|
62
62
|
import {
|
|
63
63
|
JavaParser
|
|
64
64
|
} from "./chunk-SWZOT67M.mjs";
|
|
@@ -391,14 +391,14 @@ async function scanFiles(options) {
|
|
|
391
391
|
ignoreFromFile = [];
|
|
392
392
|
}
|
|
393
393
|
}
|
|
394
|
-
const
|
|
394
|
+
const TEST_PATTERNS2 = [
|
|
395
395
|
"**/*.test.*",
|
|
396
396
|
"**/*.spec.*",
|
|
397
397
|
"**/__tests__/**",
|
|
398
398
|
"**/test/**",
|
|
399
399
|
"**/tests/**"
|
|
400
400
|
];
|
|
401
|
-
const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !
|
|
401
|
+
const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS2.includes(p)) : DEFAULT_EXCLUDE;
|
|
402
402
|
const finalExclude = [
|
|
403
403
|
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
|
|
404
404
|
];
|
|
@@ -460,14 +460,14 @@ async function scanEntries(options) {
|
|
|
460
460
|
ignoreFromFile = [];
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
|
-
const
|
|
463
|
+
const TEST_PATTERNS2 = [
|
|
464
464
|
"**/*.test.*",
|
|
465
465
|
"**/*.spec.*",
|
|
466
466
|
"**/__tests__/**",
|
|
467
467
|
"**/test/**",
|
|
468
468
|
"**/tests/**"
|
|
469
469
|
];
|
|
470
|
-
const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !
|
|
470
|
+
const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS2.includes(p)) : DEFAULT_EXCLUDE;
|
|
471
471
|
const finalExclude = [
|
|
472
472
|
.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
|
|
473
473
|
];
|
|
@@ -522,7 +522,7 @@ function isSourceFile(filePath) {
|
|
|
522
522
|
return ["ts", "tsx", "js", "jsx", "py", "java", "go", "rs"].includes(ext);
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
// src/utils/
|
|
525
|
+
// src/utils/fs-utils.ts
|
|
526
526
|
import {
|
|
527
527
|
writeFileSync,
|
|
528
528
|
mkdirSync,
|
|
@@ -531,23 +531,12 @@ import {
|
|
|
531
531
|
statSync
|
|
532
532
|
} from "fs";
|
|
533
533
|
import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
|
|
534
|
-
import chalk from "chalk";
|
|
535
534
|
function ensureDir(path) {
|
|
536
535
|
const dir = dirname2(path);
|
|
537
536
|
if (!existsSync2(dir)) {
|
|
538
537
|
mkdirSync(dir, { recursive: true });
|
|
539
538
|
}
|
|
540
539
|
}
|
|
541
|
-
function normalizeSeverity(s) {
|
|
542
|
-
if (!s) return null;
|
|
543
|
-
const lower = s.toLowerCase();
|
|
544
|
-
if (["critical", "high-risk", "blind-risk"].includes(lower))
|
|
545
|
-
return "critical" /* Critical */;
|
|
546
|
-
if (["major", "moderate-risk"].includes(lower)) return "major" /* Major */;
|
|
547
|
-
if (["minor", "safe"].includes(lower)) return "minor" /* Minor */;
|
|
548
|
-
if (lower === "info") return "info" /* Info */;
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
540
|
function getFilesByPattern(dir, pattern) {
|
|
552
541
|
if (!existsSync2(dir)) return [];
|
|
553
542
|
try {
|
|
@@ -574,16 +563,6 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
|
|
|
574
563
|
ensureDir(outputPath);
|
|
575
564
|
return outputPath;
|
|
576
565
|
}
|
|
577
|
-
async function loadMergedConfig(directory, defaults, cliOptions) {
|
|
578
|
-
const config = await loadConfig(directory);
|
|
579
|
-
const mergedConfig = mergeConfigWithDefaults(config, defaults);
|
|
580
|
-
const result = {
|
|
581
|
-
...mergedConfig,
|
|
582
|
-
...cliOptions,
|
|
583
|
-
rootDir: directory
|
|
584
|
-
};
|
|
585
|
-
return result;
|
|
586
|
-
}
|
|
587
566
|
function handleJSONOutput(data, outputFile, successMessage) {
|
|
588
567
|
if (outputFile) {
|
|
589
568
|
ensureDir(outputFile);
|
|
@@ -593,63 +572,54 @@ function handleJSONOutput(data, outputFile, successMessage) {
|
|
|
593
572
|
console.log(JSON.stringify(data, null, 2));
|
|
594
573
|
}
|
|
595
574
|
}
|
|
596
|
-
function
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
function printTerminalHeader(title, color = chalk.cyan) {
|
|
602
|
-
const divider = getTerminalDivider(color);
|
|
603
|
-
console.log(divider);
|
|
604
|
-
console.log(chalk.bold.white(` ${title.toUpperCase()}`));
|
|
605
|
-
console.log(divider + "\n");
|
|
606
|
-
}
|
|
607
|
-
function handleCLIError(error, commandName) {
|
|
608
|
-
console.error(`\u274C ${commandName} failed:`, error);
|
|
609
|
-
process.exit(1);
|
|
610
|
-
}
|
|
611
|
-
function getElapsedTime(startTime) {
|
|
612
|
-
return ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
613
|
-
}
|
|
614
|
-
function getScoreBar(val) {
|
|
615
|
-
const clamped = Math.max(0, Math.min(100, val));
|
|
616
|
-
return "\u2588".repeat(Math.round(clamped / 10)).padEnd(10, "\u2591");
|
|
617
|
-
}
|
|
618
|
-
function getSafetyIcon(rating) {
|
|
619
|
-
switch (rating) {
|
|
620
|
-
case "safe":
|
|
621
|
-
return "\u2705";
|
|
622
|
-
case "moderate-risk":
|
|
623
|
-
return "\u26A0\uFE0F ";
|
|
624
|
-
case "high-risk":
|
|
625
|
-
return "\u{1F534}";
|
|
626
|
-
case "blind-risk":
|
|
627
|
-
return "\u{1F480}";
|
|
628
|
-
default:
|
|
629
|
-
return "\u2753";
|
|
575
|
+
function findLatestReport(dirPath) {
|
|
576
|
+
const aireadyDir = resolvePath(dirPath, ".aiready");
|
|
577
|
+
let files = getFilesByPattern(aireadyDir, /^aiready-report-.*\.json$/);
|
|
578
|
+
if (files.length === 0) {
|
|
579
|
+
files = getFilesByPattern(aireadyDir, /^aiready-scan-.*\.json$/);
|
|
630
580
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (!onProgress) return;
|
|
634
|
-
if (processed % throttleCount === 0 || processed === total) {
|
|
635
|
-
onProgress(processed, total, `${message} (${processed}/${total})`);
|
|
581
|
+
if (files.length === 0) {
|
|
582
|
+
return null;
|
|
636
583
|
}
|
|
584
|
+
const sortedFiles = files.map((f) => ({
|
|
585
|
+
name: f,
|
|
586
|
+
path: resolvePath(aireadyDir, f),
|
|
587
|
+
mtime: statSync(resolvePath(aireadyDir, f)).mtime
|
|
588
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
589
|
+
return sortedFiles[0].path;
|
|
637
590
|
}
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
return
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
591
|
+
function findLatestScanReport(scanReportsDir, reportFilePrefix) {
|
|
592
|
+
try {
|
|
593
|
+
const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
|
|
594
|
+
const reportFiles = getFilesByPattern(scanReportsDir, prefixRegex);
|
|
595
|
+
if (reportFiles.length === 0) return null;
|
|
596
|
+
reportFiles.sort((a, b) => {
|
|
597
|
+
const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
|
|
598
|
+
const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
|
|
599
|
+
return idB - idA;
|
|
600
|
+
});
|
|
601
|
+
return join2(scanReportsDir, reportFiles[0]);
|
|
602
|
+
} catch {
|
|
603
|
+
console.error("Error while finding latest scan report");
|
|
604
|
+
return null;
|
|
651
605
|
}
|
|
652
606
|
}
|
|
607
|
+
|
|
608
|
+
// src/utils/terminal-utils.ts
|
|
609
|
+
import chalk2 from "chalk";
|
|
610
|
+
|
|
611
|
+
// src/utils/severity-utils.ts
|
|
612
|
+
import chalk from "chalk";
|
|
613
|
+
function normalizeSeverity(s) {
|
|
614
|
+
if (!s) return null;
|
|
615
|
+
const lower = s.toLowerCase();
|
|
616
|
+
if (["critical", "high-risk", "blind-risk"].includes(lower))
|
|
617
|
+
return "critical" /* Critical */;
|
|
618
|
+
if (["major", "moderate-risk"].includes(lower)) return "major" /* Major */;
|
|
619
|
+
if (["minor", "safe"].includes(lower)) return "minor" /* Minor */;
|
|
620
|
+
if (lower === "info") return "info" /* Info */;
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
653
623
|
function getSeverityValue(s) {
|
|
654
624
|
const normalized = normalizeSeverity(s);
|
|
655
625
|
switch (normalized) {
|
|
@@ -668,23 +638,6 @@ function getSeverityValue(s) {
|
|
|
668
638
|
function getSeverityLevel(s) {
|
|
669
639
|
return getSeverityValue(s);
|
|
670
640
|
}
|
|
671
|
-
function getSeverityBadge(severity, chalkInstance = chalk) {
|
|
672
|
-
const val = getSeverityValue(
|
|
673
|
-
typeof severity === "string" ? severity : severity
|
|
674
|
-
);
|
|
675
|
-
switch (val) {
|
|
676
|
-
case 4:
|
|
677
|
-
return chalkInstance.bgRed.white.bold(" CRITICAL ");
|
|
678
|
-
case 3:
|
|
679
|
-
return chalkInstance.bgYellow.black.bold(" MAJOR ");
|
|
680
|
-
case 2:
|
|
681
|
-
return chalkInstance.bgBlue.white.bold(" MINOR ");
|
|
682
|
-
case 1:
|
|
683
|
-
return chalkInstance.bgCyan.black(" INFO ");
|
|
684
|
-
default:
|
|
685
|
-
return chalkInstance.bgCyan.black(" INFO ");
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
641
|
function getSeverityEnum(s) {
|
|
689
642
|
const level = getSeverityLevel(s);
|
|
690
643
|
switch (level) {
|
|
@@ -698,39 +651,125 @@ function getSeverityEnum(s) {
|
|
|
698
651
|
return "info";
|
|
699
652
|
}
|
|
700
653
|
}
|
|
701
|
-
function
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
654
|
+
function getSeverityColor(severity, chalkInstance = chalk) {
|
|
655
|
+
const normalized = normalizeSeverity(severity);
|
|
656
|
+
switch (normalized) {
|
|
657
|
+
case "critical" /* Critical */:
|
|
658
|
+
return chalkInstance.red;
|
|
659
|
+
case "major" /* Major */:
|
|
660
|
+
return chalkInstance.yellow;
|
|
661
|
+
case "minor" /* Minor */:
|
|
662
|
+
return chalkInstance.green;
|
|
663
|
+
case "info" /* Info */:
|
|
664
|
+
return chalkInstance.blue;
|
|
665
|
+
default:
|
|
666
|
+
return chalkInstance.white;
|
|
706
667
|
}
|
|
707
|
-
|
|
708
|
-
|
|
668
|
+
}
|
|
669
|
+
function getSeverityBadge(severity, chalkInstance = chalk) {
|
|
670
|
+
const normalized = normalizeSeverity(severity);
|
|
671
|
+
switch (normalized) {
|
|
672
|
+
case "critical" /* Critical */:
|
|
673
|
+
return chalkInstance.bgRed.white.bold(" CRITICAL ");
|
|
674
|
+
case "major" /* Major */:
|
|
675
|
+
return chalkInstance.bgYellow.black.bold(" MAJOR ");
|
|
676
|
+
case "minor" /* Minor */:
|
|
677
|
+
return chalkInstance.bgGreen.black.bold(" MINOR ");
|
|
678
|
+
case "info" /* Info */:
|
|
679
|
+
return chalkInstance.bgBlue.white.bold(" INFO ");
|
|
680
|
+
default:
|
|
681
|
+
return chalkInstance.bgCyan.black(" UNKNOWN ");
|
|
709
682
|
}
|
|
710
|
-
const sortedFiles = files.map((f) => ({
|
|
711
|
-
name: f,
|
|
712
|
-
path: resolvePath(aireadyDir, f),
|
|
713
|
-
mtime: statSync(resolvePath(aireadyDir, f)).mtime
|
|
714
|
-
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
715
|
-
return sortedFiles[0].path;
|
|
716
683
|
}
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
684
|
+
function getSeverityLabel(severity) {
|
|
685
|
+
const labels = {
|
|
686
|
+
["critical" /* Critical */]: "\u{1F534} CRITICAL",
|
|
687
|
+
["major" /* Major */]: "\u{1F7E1} MAJOR",
|
|
688
|
+
["minor" /* Minor */]: "\u{1F535} MINOR",
|
|
689
|
+
["info" /* Info */]: "\u2139\uFE0F INFO"
|
|
690
|
+
};
|
|
691
|
+
return labels[severity];
|
|
692
|
+
}
|
|
693
|
+
function filterBySeverity(items, minSeverity) {
|
|
694
|
+
const severityOrder = [
|
|
695
|
+
"info" /* Info */,
|
|
696
|
+
"minor" /* Minor */,
|
|
697
|
+
"major" /* Major */,
|
|
698
|
+
"critical" /* Critical */
|
|
699
|
+
];
|
|
700
|
+
const minIndex = severityOrder.indexOf(minSeverity);
|
|
701
|
+
if (minIndex === -1) return items;
|
|
702
|
+
return items.filter((item) => {
|
|
703
|
+
const itemIndex = severityOrder.indexOf(item.severity);
|
|
704
|
+
return itemIndex >= minIndex;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/utils/terminal-utils.ts
|
|
709
|
+
function getSafetyIcon(rating) {
|
|
710
|
+
switch (rating) {
|
|
711
|
+
case "safe":
|
|
712
|
+
return "\u2705";
|
|
713
|
+
case "moderate-risk":
|
|
714
|
+
return "\u26A0\uFE0F ";
|
|
715
|
+
case "high-risk":
|
|
716
|
+
return "\u{1F534}";
|
|
717
|
+
case "blind-risk":
|
|
718
|
+
return "\u{1F480}";
|
|
719
|
+
default:
|
|
720
|
+
return "\u2753";
|
|
731
721
|
}
|
|
732
722
|
}
|
|
733
723
|
|
|
724
|
+
// src/utils/terminal-ui.ts
|
|
725
|
+
import chalk3 from "chalk";
|
|
726
|
+
function printTerminalHeader(title, colorFn = chalk3.cyan.bold, width = 80) {
|
|
727
|
+
const divider = "\u2501".repeat(width);
|
|
728
|
+
console.log(colorFn(`
|
|
729
|
+
${divider}`));
|
|
730
|
+
console.log(colorFn(` ${title.toUpperCase()}`));
|
|
731
|
+
console.log(colorFn(`${divider}
|
|
732
|
+
`));
|
|
733
|
+
}
|
|
734
|
+
function getTerminalDivider(colorFn = chalk3.gray, width = 80) {
|
|
735
|
+
return colorFn("\u2501".repeat(width));
|
|
736
|
+
}
|
|
737
|
+
function getScoreBar(score, width = 10) {
|
|
738
|
+
const normalized = Math.max(0, Math.min(100, score));
|
|
739
|
+
const solid = Math.round(normalized / 100 * width);
|
|
740
|
+
const empty = width - solid;
|
|
741
|
+
return "\u2588".repeat(solid) + "\u2591".repeat(empty);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/utils/progress-utils.ts
|
|
745
|
+
function emitProgress(processed, total, toolId, message, onProgress, throttleCount = 50) {
|
|
746
|
+
if (!onProgress) return;
|
|
747
|
+
if (processed % throttleCount === 0 || processed === total) {
|
|
748
|
+
onProgress(processed, total, `${message} (${processed}/${total})`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// src/utils/cli-utils.ts
|
|
753
|
+
function getElapsedTime(startTime) {
|
|
754
|
+
return ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
755
|
+
}
|
|
756
|
+
function handleCLIError(error, commandName) {
|
|
757
|
+
console.error(`\u274C ${commandName} failed:`, error);
|
|
758
|
+
process.exit(1);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/utils/cli-helpers.ts
|
|
762
|
+
async function loadMergedConfig(directory, defaults, cliOptions) {
|
|
763
|
+
const config = await loadConfig(directory);
|
|
764
|
+
const mergedConfig = mergeConfigWithDefaults(config, defaults);
|
|
765
|
+
const result = {
|
|
766
|
+
...mergedConfig,
|
|
767
|
+
...cliOptions,
|
|
768
|
+
rootDir: directory
|
|
769
|
+
};
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
|
|
734
773
|
// src/utils/cli-action-helpers.ts
|
|
735
774
|
import { resolve as resolvePath2 } from "path";
|
|
736
775
|
function getReportTimestamp() {
|
|
@@ -873,7 +912,7 @@ var ParserFactory = class _ParserFactory {
|
|
|
873
912
|
return new TypeScriptParser2();
|
|
874
913
|
});
|
|
875
914
|
this.registerLazyParser("python" /* Python */, async () => {
|
|
876
|
-
const { PythonParser: PythonParser2 } = await import("./python-parser-
|
|
915
|
+
const { PythonParser: PythonParser2 } = await import("./python-parser-SJ3LFZFJ.mjs");
|
|
877
916
|
return new PythonParser2();
|
|
878
917
|
});
|
|
879
918
|
this.registerLazyParser("java" /* Java */, async () => {
|
|
@@ -1318,6 +1357,9 @@ function mergeConfigWithDefaults(userConfig, defaults) {
|
|
|
1318
1357
|
if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
|
|
1319
1358
|
if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
|
|
1320
1359
|
}
|
|
1360
|
+
if (userConfig.threshold !== void 0)
|
|
1361
|
+
mergedConfig.threshold = userConfig.threshold;
|
|
1362
|
+
if (userConfig.failOn !== void 0) mergedConfig.failOn = userConfig.failOn;
|
|
1321
1363
|
if (userConfig.tools) {
|
|
1322
1364
|
if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
|
|
1323
1365
|
for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
|
|
@@ -1335,114 +1377,148 @@ function mergeConfigWithDefaults(userConfig, defaults) {
|
|
|
1335
1377
|
return mergedConfig;
|
|
1336
1378
|
}
|
|
1337
1379
|
|
|
1380
|
+
// src/utils/report-styles.ts
|
|
1381
|
+
var REPORT_STYLES = `
|
|
1382
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 2rem; background-color: #f9f9f9; }
|
|
1383
|
+
h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
|
|
1384
|
+
.card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 2rem; border: 1px solid #eaeaea; }
|
|
1385
|
+
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
|
|
1386
|
+
.stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
|
|
1387
|
+
.stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
|
|
1388
|
+
.stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
|
|
1389
|
+
.hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; }
|
|
1390
|
+
.hero h1 { border: none; color: white; margin: 0; }
|
|
1391
|
+
.hero p { margin: 10px 0 0 0; opacity: 0.9; }
|
|
1392
|
+
table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
|
|
1393
|
+
th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
|
|
1394
|
+
th { background-color: #f8fafc; font-weight: 600; color: #475569; }
|
|
1395
|
+
tr:last-child td { border-bottom: none; }
|
|
1396
|
+
.critical { color: #dc2626; font-weight: bold; }
|
|
1397
|
+
.major { color: #ea580c; font-weight: bold; }
|
|
1398
|
+
.minor { color: #2563eb; }
|
|
1399
|
+
.issue-critical { color: #dc2626; font-weight: bold; text-transform: uppercase; }
|
|
1400
|
+
.issue-major { color: #ea580c; font-weight: bold; text-transform: uppercase; }
|
|
1401
|
+
.issue-minor { color: #2563eb; font-weight: bold; text-transform: uppercase; }
|
|
1402
|
+
code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
|
|
1403
|
+
.footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
|
|
1404
|
+
a { color: #2563eb; text-decoration: none; }
|
|
1405
|
+
a:hover { text-decoration: underline; }
|
|
1406
|
+
`;
|
|
1407
|
+
|
|
1338
1408
|
// src/utils/report-formatters.ts
|
|
1339
|
-
function
|
|
1409
|
+
function tag(name, content = "", attrs = {}) {
|
|
1410
|
+
const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${v}"`).join("");
|
|
1411
|
+
return `<${name}${attrStr}>${content}</${name}>`;
|
|
1412
|
+
}
|
|
1413
|
+
function generateReportHead(title, styles = REPORT_STYLES) {
|
|
1340
1414
|
return `<!DOCTYPE html>
|
|
1341
1415
|
<html lang="en">
|
|
1342
1416
|
<head>
|
|
1343
1417
|
<meta charset="UTF-8">
|
|
1344
1418
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 2rem; background-color: #f9f9f9; }
|
|
1348
|
-
h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
|
|
1349
|
-
.card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 2rem; border: 1px solid #eaeaea; }
|
|
1350
|
-
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
|
|
1351
|
-
.stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
|
|
1352
|
-
.stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
|
|
1353
|
-
.stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
|
|
1354
|
-
.hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; }
|
|
1355
|
-
.hero h1 { border: none; color: white; margin: 0; }
|
|
1356
|
-
.hero p { margin: 10px 0 0 0; opacity: 0.9; }
|
|
1357
|
-
table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
|
|
1358
|
-
th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
|
|
1359
|
-
th { background-color: #f8fafc; font-weight: 600; color: #475569; }
|
|
1360
|
-
tr:last-child td { border-bottom: none; }
|
|
1361
|
-
.critical { color: #dc2626; font-weight: bold; }
|
|
1362
|
-
.major { color: #ea580c; font-weight: bold; }
|
|
1363
|
-
.minor { color: #2563eb; }
|
|
1364
|
-
.issue-critical { color: #dc2626; font-weight: bold; text-transform: uppercase; }
|
|
1365
|
-
.issue-major { color: #ea580c; font-weight: bold; text-transform: uppercase; }
|
|
1366
|
-
.issue-minor { color: #2563eb; font-weight: bold; text-transform: uppercase; }
|
|
1367
|
-
code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
|
|
1368
|
-
.footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
|
|
1369
|
-
a { color: #2563eb; text-decoration: none; }
|
|
1370
|
-
a:hover { text-decoration: underline; }
|
|
1371
|
-
</style>
|
|
1419
|
+
${tag("title", title)}
|
|
1420
|
+
${tag("style", styles)}
|
|
1372
1421
|
</head>`;
|
|
1373
1422
|
}
|
|
1374
1423
|
function generateReportHero(title, subtitle) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
${subtitleHtml}
|
|
1379
|
-
</div>`;
|
|
1424
|
+
return tag("div", tag("h1", title) + (subtitle ? tag("p", subtitle) : ""), {
|
|
1425
|
+
class: "hero"
|
|
1426
|
+
});
|
|
1380
1427
|
}
|
|
1381
1428
|
function generateStatCards(cards) {
|
|
1382
1429
|
const cardsHtml = cards.map(
|
|
1383
|
-
(
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1430
|
+
(c) => tag(
|
|
1431
|
+
"div",
|
|
1432
|
+
tag("div", String(c.value), {
|
|
1433
|
+
class: "stat-value",
|
|
1434
|
+
...c.color ? { style: `color: ${c.color}` } : {}
|
|
1435
|
+
}) + tag("div", c.label, { class: "stat-label" }),
|
|
1436
|
+
{ class: "stat-card" }
|
|
1437
|
+
)
|
|
1388
1438
|
).join("");
|
|
1389
|
-
return
|
|
1390
|
-
}
|
|
1391
|
-
function generateScoreCard(value, label) {
|
|
1392
|
-
return `<div class="stat-card" style="margin-bottom: 2rem;">
|
|
1393
|
-
<div class="stat-label">${label}</div>
|
|
1394
|
-
<div class="stat-value">${value}</div>
|
|
1395
|
-
</div>`;
|
|
1439
|
+
return tag("div", cardsHtml, { class: "stats" });
|
|
1396
1440
|
}
|
|
1397
1441
|
function generateTable(config) {
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
${
|
|
1415
|
-
|
|
1442
|
+
const head = tag(
|
|
1443
|
+
"thead",
|
|
1444
|
+
tag("tr", config.headers.map((h) => tag("th", h)).join(""))
|
|
1445
|
+
);
|
|
1446
|
+
const body = tag(
|
|
1447
|
+
"tbody",
|
|
1448
|
+
config.rows.map(
|
|
1449
|
+
(row) => tag("tr", row.map((cell) => tag("td", cell)).join(""))
|
|
1450
|
+
).join("")
|
|
1451
|
+
);
|
|
1452
|
+
return tag("table", head + body);
|
|
1453
|
+
}
|
|
1454
|
+
function generateIssueSummary(crit, maj, min, savings) {
|
|
1455
|
+
const details = [
|
|
1456
|
+
tag("span", `\u{1F534} Critical: ${crit}`, { class: "critical" }),
|
|
1457
|
+
tag("span", `\u{1F7E1} Major: ${maj}`, { class: "major" }),
|
|
1458
|
+
tag("span", `\u{1F535} Minor: ${min}`, { class: "minor" })
|
|
1459
|
+
].join(" ");
|
|
1460
|
+
const savingsHtml = savings ? tag(
|
|
1461
|
+
"p",
|
|
1462
|
+
tag("strong", "Potential Savings: ") + savings.toLocaleString() + " tokens"
|
|
1463
|
+
) : "";
|
|
1464
|
+
return tag(
|
|
1465
|
+
"div",
|
|
1466
|
+
tag("h2", "\u26A0\uFE0F Issues Summary") + tag("p", details) + savingsHtml,
|
|
1467
|
+
{ class: "card", style: "margin-bottom: 30px;" }
|
|
1468
|
+
);
|
|
1416
1469
|
}
|
|
1417
1470
|
function generateReportFooter(options) {
|
|
1418
|
-
const
|
|
1471
|
+
const version = options.version ? ` v${options.version}` : "";
|
|
1419
1472
|
const links = [];
|
|
1420
|
-
if (options.packageUrl)
|
|
1421
|
-
links.push(
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1473
|
+
if (options.packageUrl)
|
|
1474
|
+
links.push(
|
|
1475
|
+
tag(
|
|
1476
|
+
"p",
|
|
1477
|
+
`Like AIReady? ${tag("a", "Star us on GitHub", { href: options.packageUrl })}`
|
|
1478
|
+
)
|
|
1479
|
+
);
|
|
1480
|
+
if (options.bugUrl)
|
|
1481
|
+
links.push(
|
|
1482
|
+
tag(
|
|
1483
|
+
"p",
|
|
1484
|
+
`Like AIReady? ${tag("a", "Report it here", { href: options.bugUrl })}`
|
|
1485
|
+
)
|
|
1486
|
+
);
|
|
1487
|
+
return tag(
|
|
1488
|
+
"div",
|
|
1489
|
+
tag(
|
|
1490
|
+
"p",
|
|
1491
|
+
`Generated by ${tag("strong", "@aiready/" + options.packageName)}` + version
|
|
1492
|
+
) + links.join(""),
|
|
1493
|
+
{ class: "footer" }
|
|
1494
|
+
);
|
|
1431
1495
|
}
|
|
1432
1496
|
function wrapInCard(content, title) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1497
|
+
return tag("div", (title ? tag("h2", title) : "") + content, {
|
|
1498
|
+
class: "card"
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
function generateCompleteReport(options, body) {
|
|
1502
|
+
return generateReportHead(options.title) + tag("body", body + generateReportFooter(options));
|
|
1503
|
+
}
|
|
1504
|
+
function generateStandardHtmlReport(options, stats, sections, score) {
|
|
1505
|
+
const hero = generateReportHero(
|
|
1506
|
+
`${options.emoji || "\u{1F50D}"} AIReady ${options.title}`,
|
|
1507
|
+
`Generated on ${(/* @__PURE__ */ new Date()).toLocaleString()}`
|
|
1508
|
+
);
|
|
1509
|
+
const scoreCard = score ? tag(
|
|
1510
|
+
"div",
|
|
1511
|
+
tag("div", String(score.value), { class: "score-value" }) + tag("div", score.label, { class: "score-label" }),
|
|
1512
|
+
{ class: "score-card" }
|
|
1513
|
+
) : "";
|
|
1514
|
+
const statsCards = generateStatCards(stats);
|
|
1515
|
+
const bodyContent = `
|
|
1516
|
+
${hero}
|
|
1517
|
+
${scoreCard}
|
|
1518
|
+
${statsCards}
|
|
1519
|
+
${sections.map((s) => wrapInCard(s.content, s.title)).join("\n")}
|
|
1520
|
+
`;
|
|
1521
|
+
return generateCompleteReport(options, bodyContent);
|
|
1446
1522
|
}
|
|
1447
1523
|
|
|
1448
1524
|
// src/utils/scoring-helpers.ts
|
|
@@ -1539,12 +1615,12 @@ function calculateHeuristicConfidence(similarity, tokens, lines) {
|
|
|
1539
1615
|
}
|
|
1540
1616
|
|
|
1541
1617
|
// src/utils/cli-factory.ts
|
|
1542
|
-
import
|
|
1618
|
+
import chalk4 from "chalk";
|
|
1543
1619
|
function createStandardProgressCallback(toolName) {
|
|
1544
1620
|
return (processed, total, message) => {
|
|
1545
1621
|
const percent = Math.round(processed / Math.max(1, total) * 100);
|
|
1546
1622
|
process.stdout.write(
|
|
1547
|
-
`\r\x1B[K [${toolName}] ${
|
|
1623
|
+
`\r\x1B[K [${toolName}] ${chalk4.cyan(`${percent}%`)} ${message}`
|
|
1548
1624
|
);
|
|
1549
1625
|
if (processed === total) {
|
|
1550
1626
|
process.stdout.write("\n");
|
|
@@ -1552,12 +1628,12 @@ function createStandardProgressCallback(toolName) {
|
|
|
1552
1628
|
};
|
|
1553
1629
|
}
|
|
1554
1630
|
function formatStandardCliResult(toolName, score, issuesCount) {
|
|
1555
|
-
const scoreColor = score >= 75 ?
|
|
1631
|
+
const scoreColor = score >= 75 ? chalk4.green : score >= 50 ? chalk4.yellow : chalk4.red;
|
|
1556
1632
|
console.log(`
|
|
1557
|
-
${
|
|
1633
|
+
${chalk4.bold(toolName.toUpperCase())} Analysis Complete`);
|
|
1558
1634
|
console.log(` Overall Score: ${scoreColor(score)}/100`);
|
|
1559
1635
|
console.log(
|
|
1560
|
-
` Issues Found: ${issuesCount > 0 ?
|
|
1636
|
+
` Issues Found: ${issuesCount > 0 ? chalk4.red(issuesCount) : chalk4.green("None")}`
|
|
1561
1637
|
);
|
|
1562
1638
|
}
|
|
1563
1639
|
async function runStandardCliAction(toolName, action) {
|
|
@@ -1566,7 +1642,7 @@ async function runStandardCliAction(toolName, action) {
|
|
|
1566
1642
|
formatStandardCliResult(toolName, score, issuesCount);
|
|
1567
1643
|
} catch (error) {
|
|
1568
1644
|
console.error(
|
|
1569
|
-
|
|
1645
|
+
chalk4.red(`
|
|
1570
1646
|
\u274C [${toolName}] critical error: ${error.message}`)
|
|
1571
1647
|
);
|
|
1572
1648
|
process.exit(1);
|
|
@@ -1574,13 +1650,13 @@ async function runStandardCliAction(toolName, action) {
|
|
|
1574
1650
|
}
|
|
1575
1651
|
|
|
1576
1652
|
// src/utils/reporting.ts
|
|
1577
|
-
import
|
|
1653
|
+
import chalk5 from "chalk";
|
|
1578
1654
|
function getScoreColor(score) {
|
|
1579
|
-
if (score >= 85) return
|
|
1580
|
-
if (score >= 70) return
|
|
1581
|
-
if (score >= 50) return
|
|
1582
|
-
if (score >= 30) return
|
|
1583
|
-
return
|
|
1655
|
+
if (score >= 85) return chalk5.green;
|
|
1656
|
+
if (score >= 70) return chalk5.cyan;
|
|
1657
|
+
if (score >= 50) return chalk5.yellow;
|
|
1658
|
+
if (score >= 30) return chalk5.red;
|
|
1659
|
+
return chalk5.bgRed.white;
|
|
1584
1660
|
}
|
|
1585
1661
|
function displayStandardConsoleReport(data) {
|
|
1586
1662
|
const {
|
|
@@ -1595,27 +1671,27 @@ function displayStandardConsoleReport(data) {
|
|
|
1595
1671
|
noIssuesMessage = "\u2728 No issues found!",
|
|
1596
1672
|
safetyRating
|
|
1597
1673
|
} = data;
|
|
1598
|
-
console.log(
|
|
1674
|
+
console.log(chalk5.bold(`
|
|
1599
1675
|
${title}
|
|
1600
1676
|
`));
|
|
1601
1677
|
if (safetyRating) {
|
|
1602
1678
|
if (safetyRating === "blind-risk" || safetyRating === "\u{1F480} blind-risk") {
|
|
1603
1679
|
console.log(
|
|
1604
|
-
|
|
1680
|
+
chalk5.bgRed.white.bold(
|
|
1605
1681
|
" \u{1F480} BLIND RISK \u2014 NO TESTS DETECTED. AI-GENERATED CHANGES CANNOT BE VERIFIED. "
|
|
1606
1682
|
)
|
|
1607
1683
|
);
|
|
1608
1684
|
console.log();
|
|
1609
1685
|
} else if (safetyRating === "high-risk" || safetyRating === "\u{1F534} high-risk") {
|
|
1610
1686
|
console.log(
|
|
1611
|
-
|
|
1687
|
+
chalk5.red.bold(
|
|
1612
1688
|
` \u{1F534} HIGH RISK \u2014 Insufficient test coverage. AI changes may introduce silent bugs.`
|
|
1613
1689
|
)
|
|
1614
1690
|
);
|
|
1615
1691
|
console.log();
|
|
1616
1692
|
}
|
|
1617
1693
|
}
|
|
1618
|
-
const safetyColor = safetyRating ? getSeverityColor(safetyRating,
|
|
1694
|
+
const safetyColor = safetyRating ? getSeverityColor(safetyRating, chalk5) : getScoreColor(score);
|
|
1619
1695
|
if (safetyRating) {
|
|
1620
1696
|
console.log(
|
|
1621
1697
|
`AI Change Safety: ${safetyColor(`${getSafetyIcon(safetyRating)} ${safetyRating.toUpperCase()}`)}`
|
|
@@ -1625,12 +1701,12 @@ ${title}
|
|
|
1625
1701
|
`Score: ${getScoreColor(score)(score + "/100")} (${rating.toUpperCase()})`
|
|
1626
1702
|
);
|
|
1627
1703
|
if (stats.length > 0) {
|
|
1628
|
-
const statsStr = stats.map((s) => `${s.label}: ${
|
|
1704
|
+
const statsStr = stats.map((s) => `${s.label}: ${chalk5.cyan(s.value)}`).join(" ");
|
|
1629
1705
|
console.log(statsStr);
|
|
1630
1706
|
}
|
|
1631
|
-
console.log(`Analysis Time: ${
|
|
1707
|
+
console.log(`Analysis Time: ${chalk5.gray(elapsedTime + "s")}
|
|
1632
1708
|
`);
|
|
1633
|
-
console.log(
|
|
1709
|
+
console.log(chalk5.bold("\u{1F4D0} Dimension Scores\n"));
|
|
1634
1710
|
for (const dim of dimensions) {
|
|
1635
1711
|
const color = getScoreColor(dim.value);
|
|
1636
1712
|
console.log(
|
|
@@ -1638,24 +1714,24 @@ ${title}
|
|
|
1638
1714
|
);
|
|
1639
1715
|
}
|
|
1640
1716
|
if (issues.length > 0) {
|
|
1641
|
-
console.log(
|
|
1717
|
+
console.log(chalk5.bold("\n\u26A0\uFE0F Issues\n"));
|
|
1642
1718
|
for (const issue of issues) {
|
|
1643
|
-
const sev = getSeverityColor(issue.severity,
|
|
1719
|
+
const sev = getSeverityColor(issue.severity, chalk5);
|
|
1644
1720
|
console.log(`${sev(issue.severity.toUpperCase())} ${issue.message}`);
|
|
1645
1721
|
if (issue.suggestion) {
|
|
1646
1722
|
console.log(
|
|
1647
|
-
` ${
|
|
1723
|
+
` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
|
|
1648
1724
|
);
|
|
1649
1725
|
}
|
|
1650
1726
|
console.log();
|
|
1651
1727
|
}
|
|
1652
1728
|
} else {
|
|
1653
|
-
console.log(
|
|
1729
|
+
console.log(chalk5.green(`
|
|
1654
1730
|
${noIssuesMessage}
|
|
1655
1731
|
`));
|
|
1656
1732
|
}
|
|
1657
1733
|
if (recommendations.length > 0) {
|
|
1658
|
-
console.log(
|
|
1734
|
+
console.log(chalk5.bold("\u{1F4A1} Recommendations\n"));
|
|
1659
1735
|
recommendations.forEach((rec, i) => {
|
|
1660
1736
|
console.log(`${i + 1}. ${rec}`);
|
|
1661
1737
|
});
|
|
@@ -2636,7 +2712,17 @@ function calculateAiSignalClarity(params) {
|
|
|
2636
2712
|
rating: "minimal",
|
|
2637
2713
|
signals: [],
|
|
2638
2714
|
topRisk: "No symbols to analyze",
|
|
2639
|
-
recommendations: []
|
|
2715
|
+
recommendations: [],
|
|
2716
|
+
dimensions: {
|
|
2717
|
+
overloadingScore: 100,
|
|
2718
|
+
magicLiteralScore: 100,
|
|
2719
|
+
booleanTrapScore: 100,
|
|
2720
|
+
implicitSideEffectScore: 100,
|
|
2721
|
+
deepCallbackScore: 100,
|
|
2722
|
+
ambiguityScore: 100,
|
|
2723
|
+
documentationScore: 100,
|
|
2724
|
+
sizeScore: 100
|
|
2725
|
+
}
|
|
2640
2726
|
};
|
|
2641
2727
|
}
|
|
2642
2728
|
const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
|
|
@@ -2749,7 +2835,23 @@ function calculateAiSignalClarity(params) {
|
|
|
2749
2835
|
rating,
|
|
2750
2836
|
signals: signals.filter((s) => s.count > 0),
|
|
2751
2837
|
topRisk,
|
|
2752
|
-
recommendations
|
|
2838
|
+
recommendations,
|
|
2839
|
+
dimensions: {
|
|
2840
|
+
overloadingScore: Math.max(0, 100 - overloadSignal.riskContribution * 5),
|
|
2841
|
+
magicLiteralScore: Math.max(0, 100 - magicSignal.riskContribution * 6.6),
|
|
2842
|
+
booleanTrapScore: Math.max(0, 100 - trapSignal.riskContribution * 6.6),
|
|
2843
|
+
implicitSideEffectScore: Math.max(
|
|
2844
|
+
0,
|
|
2845
|
+
100 - sideEffectSignal.riskContribution * 10
|
|
2846
|
+
),
|
|
2847
|
+
deepCallbackScore: Math.max(
|
|
2848
|
+
0,
|
|
2849
|
+
100 - callbackSignal.riskContribution * 10
|
|
2850
|
+
),
|
|
2851
|
+
ambiguityScore: Math.max(0, 100 - ambiguousSignal.riskContribution * 20),
|
|
2852
|
+
documentationScore: Math.max(0, 100 - undocSignal.riskContribution * 20),
|
|
2853
|
+
sizeScore: Math.max(0, 100 - largeFileSignal.riskContribution * 4)
|
|
2854
|
+
}
|
|
2753
2855
|
};
|
|
2754
2856
|
}
|
|
2755
2857
|
|
|
@@ -3352,6 +3454,104 @@ function emitIssuesAsAnnotations(issues) {
|
|
|
3352
3454
|
});
|
|
3353
3455
|
});
|
|
3354
3456
|
}
|
|
3457
|
+
|
|
3458
|
+
// src/utils/analysis-orchestrator.ts
|
|
3459
|
+
async function runBatchAnalysis(items, label, toolId, onProgress, processFn, onResult) {
|
|
3460
|
+
let processed = 0;
|
|
3461
|
+
for (const item of items) {
|
|
3462
|
+
processed++;
|
|
3463
|
+
emitProgress(processed, items.length, toolId, label, onProgress);
|
|
3464
|
+
const result = await processFn(item);
|
|
3465
|
+
onResult(result);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3469
|
+
// src/utils/spoke-cli-helpers.ts
|
|
3470
|
+
import chalk6 from "chalk";
|
|
3471
|
+
async function executeSpokeCli(name, description, options, analyzeFn) {
|
|
3472
|
+
console.log(chalk6.cyan(`Analyzing ${description.toLowerCase()}...`));
|
|
3473
|
+
try {
|
|
3474
|
+
const report = await analyzeFn({
|
|
3475
|
+
rootDir: process.cwd(),
|
|
3476
|
+
...options
|
|
3477
|
+
});
|
|
3478
|
+
console.log(chalk6.bold(`
|
|
3479
|
+
${name} Analysis Results:`));
|
|
3480
|
+
console.log(
|
|
3481
|
+
`Rating: ${report.summary.rating.toUpperCase()} (Score: ${report.summary.score})`
|
|
3482
|
+
);
|
|
3483
|
+
if (report.issues && report.issues.length > 0) {
|
|
3484
|
+
console.log(chalk6.red(`
|
|
3485
|
+
Found ${report.issues.length} issues.`));
|
|
3486
|
+
} else {
|
|
3487
|
+
console.log(chalk6.green("\nNo issues detected."));
|
|
3488
|
+
}
|
|
3489
|
+
return report;
|
|
3490
|
+
} catch (err) {
|
|
3491
|
+
console.error(chalk6.red(`Error during ${name} analysis: ${err.message}`));
|
|
3492
|
+
process.exit(1);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
// src/utils/test-utils.ts
|
|
3497
|
+
import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
|
|
3498
|
+
import { join as join5 } from "path";
|
|
3499
|
+
var TEST_PATTERNS = [
|
|
3500
|
+
/\.(test|spec)\.(ts|tsx|js|jsx)$/,
|
|
3501
|
+
/_test\.go$/,
|
|
3502
|
+
/test_.*\.py$/,
|
|
3503
|
+
/.*_test\.py$/,
|
|
3504
|
+
/.*Test\.java$/,
|
|
3505
|
+
/.*Tests\.cs$/,
|
|
3506
|
+
/__tests__\//,
|
|
3507
|
+
/\/tests?\//,
|
|
3508
|
+
/\/e2e\//,
|
|
3509
|
+
/\/fixtures\//
|
|
3510
|
+
];
|
|
3511
|
+
function isTestFile(filePath, extraPatterns) {
|
|
3512
|
+
if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
|
|
3513
|
+
if (extraPatterns) return extraPatterns.some((p) => filePath.includes(p));
|
|
3514
|
+
return false;
|
|
3515
|
+
}
|
|
3516
|
+
function detectTestFramework(rootDir) {
|
|
3517
|
+
const manifests = [
|
|
3518
|
+
{
|
|
3519
|
+
file: "package.json",
|
|
3520
|
+
deps: [
|
|
3521
|
+
"jest",
|
|
3522
|
+
"vitest",
|
|
3523
|
+
"mocha",
|
|
3524
|
+
"jasmine",
|
|
3525
|
+
"ava",
|
|
3526
|
+
"tap",
|
|
3527
|
+
"playwright",
|
|
3528
|
+
"cypress"
|
|
3529
|
+
]
|
|
3530
|
+
},
|
|
3531
|
+
{ file: "requirements.txt", deps: ["pytest", "unittest", "nose"] },
|
|
3532
|
+
{ file: "pyproject.toml", deps: ["pytest"] },
|
|
3533
|
+
{ file: "pom.xml", deps: ["junit", "testng"] },
|
|
3534
|
+
{ file: "build.gradle", deps: ["junit", "testng"] },
|
|
3535
|
+
{ file: "go.mod", deps: ["testing"] }
|
|
3536
|
+
// go testing is built-in
|
|
3537
|
+
];
|
|
3538
|
+
for (const m of manifests) {
|
|
3539
|
+
const p = join5(rootDir, m.file);
|
|
3540
|
+
if (existsSync5(p)) {
|
|
3541
|
+
if (m.file === "go.mod") return true;
|
|
3542
|
+
try {
|
|
3543
|
+
const content = readFileSync3(p, "utf-8");
|
|
3544
|
+
if (m.deps.some((d) => content.includes(d))) return true;
|
|
3545
|
+
} catch {
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
return false;
|
|
3550
|
+
}
|
|
3551
|
+
function isBuildArtifact(filePath) {
|
|
3552
|
+
const lower = filePath.toLowerCase();
|
|
3553
|
+
return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/") || lower.includes("/target/") || lower.includes("/bin/");
|
|
3554
|
+
}
|
|
3355
3555
|
export {
|
|
3356
3556
|
AIReadyConfigSchema,
|
|
3357
3557
|
AnalysisResultSchema,
|
|
@@ -3396,6 +3596,7 @@ export {
|
|
|
3396
3596
|
SeveritySchema,
|
|
3397
3597
|
SpokeOutputSchema,
|
|
3398
3598
|
SpokeSummarySchema,
|
|
3599
|
+
TEST_PATTERNS,
|
|
3399
3600
|
TOOL_NAME_MAP,
|
|
3400
3601
|
ToolName,
|
|
3401
3602
|
ToolNameSchema,
|
|
@@ -3435,14 +3636,18 @@ export {
|
|
|
3435
3636
|
clearHistory,
|
|
3436
3637
|
createProvider,
|
|
3437
3638
|
createStandardProgressCallback,
|
|
3639
|
+
detectTestFramework,
|
|
3438
3640
|
displayStandardConsoleReport,
|
|
3439
3641
|
emitAnnotation,
|
|
3440
3642
|
emitIssuesAsAnnotations,
|
|
3441
3643
|
emitProgress,
|
|
3644
|
+
ensureDir,
|
|
3442
3645
|
estimateCostFromBudget,
|
|
3443
3646
|
estimateTokens,
|
|
3647
|
+
executeSpokeCli,
|
|
3444
3648
|
exportHistory,
|
|
3445
3649
|
extractCodeBlocks,
|
|
3650
|
+
filterBySeverity,
|
|
3446
3651
|
findLatestReport,
|
|
3447
3652
|
findLatestScanReport,
|
|
3448
3653
|
formatAcceptanceRate,
|
|
@@ -3458,13 +3663,14 @@ export {
|
|
|
3458
3663
|
generateReportFooter,
|
|
3459
3664
|
generateReportHead,
|
|
3460
3665
|
generateReportHero,
|
|
3461
|
-
|
|
3666
|
+
generateStandardHtmlReport,
|
|
3462
3667
|
generateStatCards,
|
|
3463
3668
|
generateTable,
|
|
3464
3669
|
generateValueChain,
|
|
3465
3670
|
getElapsedTime,
|
|
3466
3671
|
getFileCommitTimestamps,
|
|
3467
3672
|
getFileExtension,
|
|
3673
|
+
getFilesByPattern,
|
|
3468
3674
|
getHistorySummary,
|
|
3469
3675
|
getLineRangeLastModifiedCached,
|
|
3470
3676
|
getModelPreset,
|
|
@@ -3487,6 +3693,7 @@ export {
|
|
|
3487
3693
|
getSeverityBadge,
|
|
3488
3694
|
getSeverityColor,
|
|
3489
3695
|
getSeverityEnum,
|
|
3696
|
+
getSeverityLabel,
|
|
3490
3697
|
getSeverityLevel,
|
|
3491
3698
|
getSeverityValue,
|
|
3492
3699
|
getSupportedLanguages,
|
|
@@ -3501,8 +3708,10 @@ export {
|
|
|
3501
3708
|
inferPatternType,
|
|
3502
3709
|
initTreeSitter,
|
|
3503
3710
|
initializeParsers,
|
|
3711
|
+
isBuildArtifact,
|
|
3504
3712
|
isFileSupported,
|
|
3505
3713
|
isSourceFile,
|
|
3714
|
+
isTestFile,
|
|
3506
3715
|
loadConfig,
|
|
3507
3716
|
loadMergedConfig,
|
|
3508
3717
|
loadScoreHistory,
|
|
@@ -3510,6 +3719,7 @@ export {
|
|
|
3510
3719
|
normalizeAnalysisResult,
|
|
3511
3720
|
normalizeIssue,
|
|
3512
3721
|
normalizeMetrics,
|
|
3722
|
+
normalizeSeverity,
|
|
3513
3723
|
normalizeSpokeOutput,
|
|
3514
3724
|
normalizeToolName,
|
|
3515
3725
|
parseFileExports,
|
|
@@ -3520,6 +3730,7 @@ export {
|
|
|
3520
3730
|
readFileContent,
|
|
3521
3731
|
resolveOutputFormat,
|
|
3522
3732
|
resolveOutputPath,
|
|
3733
|
+
runBatchAnalysis,
|
|
3523
3734
|
runStandardCliAction,
|
|
3524
3735
|
saveScoreEntry,
|
|
3525
3736
|
scanEntries,
|