@aiready/core 0.23.22 → 0.23.24

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/index.js CHANGED
@@ -965,8 +965,8 @@ var init_python_parser = __esm({
965
965
  const fromMatch = line.match(fromImportRegex);
966
966
  if (fromMatch) {
967
967
  const module2 = fromMatch[1];
968
- const imports_str = fromMatch[2];
969
- if (imports_str.trim() === "*") {
968
+ const importsStr = fromMatch[2];
969
+ if (importsStr.trim() === "*") {
970
970
  imports.push({
971
971
  source: module2,
972
972
  specifiers: ["*"],
@@ -977,7 +977,7 @@ var init_python_parser = __esm({
977
977
  });
978
978
  return;
979
979
  }
980
- const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
980
+ const specifiers = importsStr.split(",").map((s) => s.trim().split(" as ")[0]);
981
981
  imports.push({
982
982
  source: module2,
983
983
  specifiers,
@@ -1994,6 +1994,7 @@ __export(index_exports, {
1994
1994
  SeveritySchema: () => SeveritySchema,
1995
1995
  SpokeOutputSchema: () => SpokeOutputSchema,
1996
1996
  SpokeSummarySchema: () => SpokeSummarySchema,
1997
+ TEST_PATTERNS: () => TEST_PATTERNS,
1997
1998
  TOOL_NAME_MAP: () => TOOL_NAME_MAP,
1998
1999
  ToolName: () => ToolName,
1999
2000
  ToolNameSchema: () => ToolNameSchema,
@@ -2033,14 +2034,18 @@ __export(index_exports, {
2033
2034
  clearHistory: () => clearHistory,
2034
2035
  createProvider: () => createProvider,
2035
2036
  createStandardProgressCallback: () => createStandardProgressCallback,
2037
+ detectTestFramework: () => detectTestFramework,
2036
2038
  displayStandardConsoleReport: () => displayStandardConsoleReport,
2037
2039
  emitAnnotation: () => emitAnnotation,
2038
2040
  emitIssuesAsAnnotations: () => emitIssuesAsAnnotations,
2039
2041
  emitProgress: () => emitProgress,
2042
+ ensureDir: () => ensureDir,
2040
2043
  estimateCostFromBudget: () => estimateCostFromBudget,
2041
2044
  estimateTokens: () => estimateTokens,
2045
+ executeSpokeCli: () => executeSpokeCli,
2042
2046
  exportHistory: () => exportHistory,
2043
2047
  extractCodeBlocks: () => extractCodeBlocks,
2048
+ filterBySeverity: () => filterBySeverity,
2044
2049
  findLatestReport: () => findLatestReport,
2045
2050
  findLatestScanReport: () => findLatestScanReport,
2046
2051
  formatAcceptanceRate: () => formatAcceptanceRate,
@@ -2056,13 +2061,14 @@ __export(index_exports, {
2056
2061
  generateReportFooter: () => generateReportFooter,
2057
2062
  generateReportHead: () => generateReportHead,
2058
2063
  generateReportHero: () => generateReportHero,
2059
- generateScoreCard: () => generateScoreCard,
2064
+ generateStandardHtmlReport: () => generateStandardHtmlReport,
2060
2065
  generateStatCards: () => generateStatCards,
2061
2066
  generateTable: () => generateTable,
2062
2067
  generateValueChain: () => generateValueChain,
2063
2068
  getElapsedTime: () => getElapsedTime,
2064
2069
  getFileCommitTimestamps: () => getFileCommitTimestamps,
2065
2070
  getFileExtension: () => getFileExtension,
2071
+ getFilesByPattern: () => getFilesByPattern,
2066
2072
  getHistorySummary: () => getHistorySummary,
2067
2073
  getLineRangeLastModifiedCached: () => getLineRangeLastModifiedCached,
2068
2074
  getModelPreset: () => getModelPreset,
@@ -2085,6 +2091,7 @@ __export(index_exports, {
2085
2091
  getSeverityBadge: () => getSeverityBadge,
2086
2092
  getSeverityColor: () => getSeverityColor,
2087
2093
  getSeverityEnum: () => getSeverityEnum,
2094
+ getSeverityLabel: () => getSeverityLabel,
2088
2095
  getSeverityLevel: () => getSeverityLevel,
2089
2096
  getSeverityValue: () => getSeverityValue,
2090
2097
  getSupportedLanguages: () => getSupportedLanguages,
@@ -2099,8 +2106,10 @@ __export(index_exports, {
2099
2106
  inferPatternType: () => inferPatternType,
2100
2107
  initTreeSitter: () => initTreeSitter,
2101
2108
  initializeParsers: () => initializeParsers,
2109
+ isBuildArtifact: () => isBuildArtifact,
2102
2110
  isFileSupported: () => isFileSupported,
2103
2111
  isSourceFile: () => isSourceFile,
2112
+ isTestFile: () => isTestFile,
2104
2113
  loadConfig: () => loadConfig,
2105
2114
  loadMergedConfig: () => loadMergedConfig,
2106
2115
  loadScoreHistory: () => loadScoreHistory,
@@ -2108,6 +2117,7 @@ __export(index_exports, {
2108
2117
  normalizeAnalysisResult: () => normalizeAnalysisResult,
2109
2118
  normalizeIssue: () => normalizeIssue,
2110
2119
  normalizeMetrics: () => normalizeMetrics,
2120
+ normalizeSeverity: () => normalizeSeverity,
2111
2121
  normalizeSpokeOutput: () => normalizeSpokeOutput,
2112
2122
  normalizeToolName: () => normalizeToolName,
2113
2123
  parseFileExports: () => parseFileExports,
@@ -2118,6 +2128,7 @@ __export(index_exports, {
2118
2128
  readFileContent: () => readFileContent,
2119
2129
  resolveOutputFormat: () => resolveOutputFormat,
2120
2130
  resolveOutputPath: () => resolveOutputPath,
2131
+ runBatchAnalysis: () => runBatchAnalysis,
2121
2132
  runStandardCliAction: () => runStandardCliAction,
2122
2133
  saveScoreEntry: () => saveScoreEntry,
2123
2134
  scanEntries: () => scanEntries,
@@ -2364,12 +2375,12 @@ var UnifiedReportSchema = import_zod6.z.object({
2364
2375
  // src/types/schemas/config.ts
2365
2376
  var import_zod7 = require("zod");
2366
2377
  var AIReadyConfigSchema = import_zod7.z.object({
2367
- /** Target score threshold (0-100) */
2368
- threshold: import_zod7.z.number().optional(),
2369
- /** Files or directories to include in scan */
2370
- include: import_zod7.z.array(import_zod7.z.string()).optional(),
2371
2378
  /** Files or directories to exclude from scan */
2372
2379
  exclude: import_zod7.z.array(import_zod7.z.string()).optional(),
2380
+ /** Fail CI/CD if score below threshold (0-100) */
2381
+ threshold: import_zod7.z.number().optional(),
2382
+ /** Fail on issues: critical, major, any */
2383
+ failOn: import_zod7.z.enum(["critical", "major", "any", "none"]).optional(),
2373
2384
  /** Scan-specific configuration */
2374
2385
  scan: import_zod7.z.object({
2375
2386
  include: import_zod7.z.array(import_zod7.z.string()).optional(),
@@ -2750,14 +2761,14 @@ async function scanFiles(options) {
2750
2761
  ignoreFromFile = [];
2751
2762
  }
2752
2763
  }
2753
- const TEST_PATTERNS = [
2764
+ const TEST_PATTERNS2 = [
2754
2765
  "**/*.test.*",
2755
2766
  "**/*.spec.*",
2756
2767
  "**/__tests__/**",
2757
2768
  "**/test/**",
2758
2769
  "**/tests/**"
2759
2770
  ];
2760
- const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
2771
+ const baseExclude = options.includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS2.includes(p)) : DEFAULT_EXCLUDE;
2761
2772
  const finalExclude = [
2762
2773
  .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
2763
2774
  ];
@@ -2819,14 +2830,14 @@ async function scanEntries(options) {
2819
2830
  ignoreFromFile = [];
2820
2831
  }
2821
2832
  }
2822
- const TEST_PATTERNS = [
2833
+ const TEST_PATTERNS2 = [
2823
2834
  "**/*.test.*",
2824
2835
  "**/*.spec.*",
2825
2836
  "**/__tests__/**",
2826
2837
  "**/test/**",
2827
2838
  "**/tests/**"
2828
2839
  ];
2829
- const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS.includes(p)) : DEFAULT_EXCLUDE;
2840
+ const baseExclude = includeTests ? DEFAULT_EXCLUDE.filter((p) => !TEST_PATTERNS2.includes(p)) : DEFAULT_EXCLUDE;
2830
2841
  const finalExclude = [
2831
2842
  .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...baseExclude])
2832
2843
  ];
@@ -2881,26 +2892,15 @@ function isSourceFile(filePath) {
2881
2892
  return ["ts", "tsx", "js", "jsx", "py", "java", "go", "rs"].includes(ext);
2882
2893
  }
2883
2894
 
2884
- // src/utils/cli-helpers.ts
2895
+ // src/utils/fs-utils.ts
2885
2896
  var import_fs2 = require("fs");
2886
2897
  var import_path2 = require("path");
2887
- var import_chalk = __toESM(require("chalk"));
2888
2898
  function ensureDir(path2) {
2889
2899
  const dir = (0, import_path2.dirname)(path2);
2890
2900
  if (!(0, import_fs2.existsSync)(dir)) {
2891
2901
  (0, import_fs2.mkdirSync)(dir, { recursive: true });
2892
2902
  }
2893
2903
  }
2894
- function normalizeSeverity(s) {
2895
- if (!s) return null;
2896
- const lower = s.toLowerCase();
2897
- if (["critical", "high-risk", "blind-risk"].includes(lower))
2898
- return "critical" /* Critical */;
2899
- if (["major", "moderate-risk"].includes(lower)) return "major" /* Major */;
2900
- if (["minor", "safe"].includes(lower)) return "minor" /* Minor */;
2901
- if (lower === "info") return "info" /* Info */;
2902
- return null;
2903
- }
2904
2904
  function getFilesByPattern(dir, pattern) {
2905
2905
  if (!(0, import_fs2.existsSync)(dir)) return [];
2906
2906
  try {
@@ -2927,16 +2927,6 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
2927
2927
  ensureDir(outputPath);
2928
2928
  return outputPath;
2929
2929
  }
2930
- async function loadMergedConfig(directory, defaults, cliOptions) {
2931
- const config = await loadConfig(directory);
2932
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
2933
- const result = {
2934
- ...mergedConfig,
2935
- ...cliOptions,
2936
- rootDir: directory
2937
- };
2938
- return result;
2939
- }
2940
2930
  function handleJSONOutput(data, outputFile, successMessage) {
2941
2931
  if (outputFile) {
2942
2932
  ensureDir(outputFile);
@@ -2946,63 +2936,54 @@ function handleJSONOutput(data, outputFile, successMessage) {
2946
2936
  console.log(JSON.stringify(data, null, 2));
2947
2937
  }
2948
2938
  }
2949
- function getTerminalDivider(color = import_chalk.default.cyan, maxWidth = 60) {
2950
- const terminalWidth = process.stdout.columns || 80;
2951
- const dividerWidth = Math.min(maxWidth, terminalWidth - 2);
2952
- return color("\u2501".repeat(dividerWidth));
2953
- }
2954
- function printTerminalHeader(title, color = import_chalk.default.cyan) {
2955
- const divider = getTerminalDivider(color);
2956
- console.log(divider);
2957
- console.log(import_chalk.default.bold.white(` ${title.toUpperCase()}`));
2958
- console.log(divider + "\n");
2959
- }
2960
- function handleCLIError(error, commandName) {
2961
- console.error(`\u274C ${commandName} failed:`, error);
2962
- process.exit(1);
2963
- }
2964
- function getElapsedTime(startTime) {
2965
- return ((Date.now() - startTime) / 1e3).toFixed(2);
2966
- }
2967
- function getScoreBar(val) {
2968
- const clamped = Math.max(0, Math.min(100, val));
2969
- return "\u2588".repeat(Math.round(clamped / 10)).padEnd(10, "\u2591");
2970
- }
2971
- function getSafetyIcon(rating) {
2972
- switch (rating) {
2973
- case "safe":
2974
- return "\u2705";
2975
- case "moderate-risk":
2976
- return "\u26A0\uFE0F ";
2977
- case "high-risk":
2978
- return "\u{1F534}";
2979
- case "blind-risk":
2980
- return "\u{1F480}";
2981
- default:
2982
- return "\u2753";
2939
+ function findLatestReport(dirPath) {
2940
+ const aireadyDir = (0, import_path2.resolve)(dirPath, ".aiready");
2941
+ let files = getFilesByPattern(aireadyDir, /^aiready-report-.*\.json$/);
2942
+ if (files.length === 0) {
2943
+ files = getFilesByPattern(aireadyDir, /^aiready-scan-.*\.json$/);
2983
2944
  }
2984
- }
2985
- function emitProgress(processed, total, toolId, message, onProgress, throttleCount = 50) {
2986
- if (!onProgress) return;
2987
- if (processed % throttleCount === 0 || processed === total) {
2988
- onProgress(processed, total, `${message} (${processed}/${total})`);
2945
+ if (files.length === 0) {
2946
+ return null;
2989
2947
  }
2948
+ const sortedFiles = files.map((f) => ({
2949
+ name: f,
2950
+ path: (0, import_path2.resolve)(aireadyDir, f),
2951
+ mtime: (0, import_fs2.statSync)((0, import_path2.resolve)(aireadyDir, f)).mtime
2952
+ })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
2953
+ return sortedFiles[0].path;
2990
2954
  }
2991
- function getSeverityColor(severity, chalkInstance = import_chalk.default) {
2992
- const normalized = normalizeSeverity(severity);
2993
- switch (normalized) {
2994
- case "critical" /* Critical */:
2995
- return chalkInstance.red;
2996
- case "major" /* Major */:
2997
- return chalkInstance.yellow;
2998
- case "minor" /* Minor */:
2999
- return chalkInstance.green;
3000
- case "info" /* Info */:
3001
- return chalkInstance.blue;
3002
- default:
3003
- return chalkInstance.white;
2955
+ function findLatestScanReport(scanReportsDir, reportFilePrefix) {
2956
+ try {
2957
+ const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
2958
+ const reportFiles = getFilesByPattern(scanReportsDir, prefixRegex);
2959
+ if (reportFiles.length === 0) return null;
2960
+ reportFiles.sort((a, b) => {
2961
+ const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
2962
+ const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
2963
+ return idB - idA;
2964
+ });
2965
+ return (0, import_path2.join)(scanReportsDir, reportFiles[0]);
2966
+ } catch {
2967
+ console.error("Error while finding latest scan report");
2968
+ return null;
3004
2969
  }
3005
2970
  }
2971
+
2972
+ // src/utils/terminal-utils.ts
2973
+ var import_chalk2 = __toESM(require("chalk"));
2974
+
2975
+ // src/utils/severity-utils.ts
2976
+ var import_chalk = __toESM(require("chalk"));
2977
+ function normalizeSeverity(s) {
2978
+ if (!s) return null;
2979
+ const lower = s.toLowerCase();
2980
+ if (["critical", "high-risk", "blind-risk"].includes(lower))
2981
+ return "critical" /* Critical */;
2982
+ if (["major", "moderate-risk"].includes(lower)) return "major" /* Major */;
2983
+ if (["minor", "safe"].includes(lower)) return "minor" /* Minor */;
2984
+ if (lower === "info") return "info" /* Info */;
2985
+ return null;
2986
+ }
3006
2987
  function getSeverityValue(s) {
3007
2988
  const normalized = normalizeSeverity(s);
3008
2989
  switch (normalized) {
@@ -3021,23 +3002,6 @@ function getSeverityValue(s) {
3021
3002
  function getSeverityLevel(s) {
3022
3003
  return getSeverityValue(s);
3023
3004
  }
3024
- function getSeverityBadge(severity, chalkInstance = import_chalk.default) {
3025
- const val = getSeverityValue(
3026
- typeof severity === "string" ? severity : severity
3027
- );
3028
- switch (val) {
3029
- case 4:
3030
- return chalkInstance.bgRed.white.bold(" CRITICAL ");
3031
- case 3:
3032
- return chalkInstance.bgYellow.black.bold(" MAJOR ");
3033
- case 2:
3034
- return chalkInstance.bgBlue.white.bold(" MINOR ");
3035
- case 1:
3036
- return chalkInstance.bgCyan.black(" INFO ");
3037
- default:
3038
- return chalkInstance.bgCyan.black(" INFO ");
3039
- }
3040
- }
3041
3005
  function getSeverityEnum(s) {
3042
3006
  const level = getSeverityLevel(s);
3043
3007
  switch (level) {
@@ -3051,39 +3015,125 @@ function getSeverityEnum(s) {
3051
3015
  return "info";
3052
3016
  }
3053
3017
  }
3054
- function findLatestReport(dirPath) {
3055
- const aireadyDir = (0, import_path2.resolve)(dirPath, ".aiready");
3056
- let files = getFilesByPattern(aireadyDir, /^aiready-report-.*\.json$/);
3057
- if (files.length === 0) {
3058
- files = getFilesByPattern(aireadyDir, /^aiready-scan-.*\.json$/);
3018
+ function getSeverityColor(severity, chalkInstance = import_chalk.default) {
3019
+ const normalized = normalizeSeverity(severity);
3020
+ switch (normalized) {
3021
+ case "critical" /* Critical */:
3022
+ return chalkInstance.red;
3023
+ case "major" /* Major */:
3024
+ return chalkInstance.yellow;
3025
+ case "minor" /* Minor */:
3026
+ return chalkInstance.green;
3027
+ case "info" /* Info */:
3028
+ return chalkInstance.blue;
3029
+ default:
3030
+ return chalkInstance.white;
3059
3031
  }
3060
- if (files.length === 0) {
3061
- return null;
3032
+ }
3033
+ function getSeverityBadge(severity, chalkInstance = import_chalk.default) {
3034
+ const normalized = normalizeSeverity(severity);
3035
+ switch (normalized) {
3036
+ case "critical" /* Critical */:
3037
+ return chalkInstance.bgRed.white.bold(" CRITICAL ");
3038
+ case "major" /* Major */:
3039
+ return chalkInstance.bgYellow.black.bold(" MAJOR ");
3040
+ case "minor" /* Minor */:
3041
+ return chalkInstance.bgGreen.black.bold(" MINOR ");
3042
+ case "info" /* Info */:
3043
+ return chalkInstance.bgBlue.white.bold(" INFO ");
3044
+ default:
3045
+ return chalkInstance.bgCyan.black(" UNKNOWN ");
3062
3046
  }
3063
- const sortedFiles = files.map((f) => ({
3064
- name: f,
3065
- path: (0, import_path2.resolve)(aireadyDir, f),
3066
- mtime: (0, import_fs2.statSync)((0, import_path2.resolve)(aireadyDir, f)).mtime
3067
- })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
3068
- return sortedFiles[0].path;
3069
3047
  }
3070
- function findLatestScanReport(scanReportsDir, reportFilePrefix) {
3071
- try {
3072
- const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
3073
- const reportFiles = getFilesByPattern(scanReportsDir, prefixRegex);
3074
- if (reportFiles.length === 0) return null;
3075
- reportFiles.sort((a, b) => {
3076
- const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
3077
- const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
3078
- return idB - idA;
3079
- });
3080
- return (0, import_path2.join)(scanReportsDir, reportFiles[0]);
3081
- } catch {
3082
- console.error("Error while finding latest scan report");
3083
- return null;
3048
+ function getSeverityLabel(severity) {
3049
+ const labels = {
3050
+ ["critical" /* Critical */]: "\u{1F534} CRITICAL",
3051
+ ["major" /* Major */]: "\u{1F7E1} MAJOR",
3052
+ ["minor" /* Minor */]: "\u{1F535} MINOR",
3053
+ ["info" /* Info */]: "\u2139\uFE0F INFO"
3054
+ };
3055
+ return labels[severity];
3056
+ }
3057
+ function filterBySeverity(items, minSeverity) {
3058
+ const severityOrder = [
3059
+ "info" /* Info */,
3060
+ "minor" /* Minor */,
3061
+ "major" /* Major */,
3062
+ "critical" /* Critical */
3063
+ ];
3064
+ const minIndex = severityOrder.indexOf(minSeverity);
3065
+ if (minIndex === -1) return items;
3066
+ return items.filter((item) => {
3067
+ const itemIndex = severityOrder.indexOf(item.severity);
3068
+ return itemIndex >= minIndex;
3069
+ });
3070
+ }
3071
+
3072
+ // src/utils/terminal-utils.ts
3073
+ function getSafetyIcon(rating) {
3074
+ switch (rating) {
3075
+ case "safe":
3076
+ return "\u2705";
3077
+ case "moderate-risk":
3078
+ return "\u26A0\uFE0F ";
3079
+ case "high-risk":
3080
+ return "\u{1F534}";
3081
+ case "blind-risk":
3082
+ return "\u{1F480}";
3083
+ default:
3084
+ return "\u2753";
3085
+ }
3086
+ }
3087
+
3088
+ // src/utils/terminal-ui.ts
3089
+ var import_chalk3 = __toESM(require("chalk"));
3090
+ function printTerminalHeader(title, colorFn = import_chalk3.default.cyan.bold, width = 80) {
3091
+ const divider = "\u2501".repeat(width);
3092
+ console.log(colorFn(`
3093
+ ${divider}`));
3094
+ console.log(colorFn(` ${title.toUpperCase()}`));
3095
+ console.log(colorFn(`${divider}
3096
+ `));
3097
+ }
3098
+ function getTerminalDivider(colorFn = import_chalk3.default.gray, width = 80) {
3099
+ return colorFn("\u2501".repeat(width));
3100
+ }
3101
+ function getScoreBar(score, width = 10) {
3102
+ const normalized = Math.max(0, Math.min(100, score));
3103
+ const solid = Math.round(normalized / 100 * width);
3104
+ const empty = width - solid;
3105
+ return "\u2588".repeat(solid) + "\u2591".repeat(empty);
3106
+ }
3107
+
3108
+ // src/utils/progress-utils.ts
3109
+ function emitProgress(processed, total, toolId, message, onProgress, throttleCount = 50) {
3110
+ if (!onProgress) return;
3111
+ if (processed % throttleCount === 0 || processed === total) {
3112
+ onProgress(processed, total, `${message} (${processed}/${total})`);
3084
3113
  }
3085
3114
  }
3086
3115
 
3116
+ // src/utils/cli-utils.ts
3117
+ function getElapsedTime(startTime) {
3118
+ return ((Date.now() - startTime) / 1e3).toFixed(2);
3119
+ }
3120
+ function handleCLIError(error, commandName) {
3121
+ console.error(`\u274C ${commandName} failed:`, error);
3122
+ process.exit(1);
3123
+ }
3124
+
3125
+ // src/utils/cli-helpers.ts
3126
+ async function loadMergedConfig(directory, defaults, cliOptions) {
3127
+ const config = await loadConfig(directory);
3128
+ const mergedConfig = mergeConfigWithDefaults(config, defaults);
3129
+ const result = {
3130
+ ...mergedConfig,
3131
+ ...cliOptions,
3132
+ rootDir: directory
3133
+ };
3134
+ return result;
3135
+ }
3136
+
3087
3137
  // src/utils/cli-action-helpers.ts
3088
3138
  var import_path3 = require("path");
3089
3139
  function getReportTimestamp() {
@@ -3675,6 +3725,9 @@ function mergeConfigWithDefaults(userConfig, defaults) {
3675
3725
  if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
3676
3726
  if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
3677
3727
  }
3728
+ if (userConfig.threshold !== void 0)
3729
+ mergedConfig.threshold = userConfig.threshold;
3730
+ if (userConfig.failOn !== void 0) mergedConfig.failOn = userConfig.failOn;
3678
3731
  if (userConfig.tools) {
3679
3732
  if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
3680
3733
  for (const [toolName, toolConfig] of Object.entries(userConfig.tools)) {
@@ -3858,114 +3911,148 @@ function generateHTML(graph) {
3858
3911
  </html>`;
3859
3912
  }
3860
3913
 
3914
+ // src/utils/report-styles.ts
3915
+ var REPORT_STYLES = `
3916
+ 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; }
3917
+ h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
3918
+ .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; }
3919
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
3920
+ .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
3921
+ .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
3922
+ .stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
3923
+ .hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; }
3924
+ .hero h1 { border: none; color: white; margin: 0; }
3925
+ .hero p { margin: 10px 0 0 0; opacity: 0.9; }
3926
+ table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
3927
+ th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
3928
+ th { background-color: #f8fafc; font-weight: 600; color: #475569; }
3929
+ tr:last-child td { border-bottom: none; }
3930
+ .critical { color: #dc2626; font-weight: bold; }
3931
+ .major { color: #ea580c; font-weight: bold; }
3932
+ .minor { color: #2563eb; }
3933
+ .issue-critical { color: #dc2626; font-weight: bold; text-transform: uppercase; }
3934
+ .issue-major { color: #ea580c; font-weight: bold; text-transform: uppercase; }
3935
+ .issue-minor { color: #2563eb; font-weight: bold; text-transform: uppercase; }
3936
+ code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
3937
+ .footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
3938
+ a { color: #2563eb; text-decoration: none; }
3939
+ a:hover { text-decoration: underline; }
3940
+ `;
3941
+
3861
3942
  // src/utils/report-formatters.ts
3862
- function generateReportHead(title) {
3943
+ function tag(name, content = "", attrs = {}) {
3944
+ const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${v}"`).join("");
3945
+ return `<${name}${attrStr}>${content}</${name}>`;
3946
+ }
3947
+ function generateReportHead(title, styles = REPORT_STYLES) {
3863
3948
  return `<!DOCTYPE html>
3864
3949
  <html lang="en">
3865
3950
  <head>
3866
3951
  <meta charset="UTF-8">
3867
3952
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
3868
- <title>${title}</title>
3869
- <style>
3870
- 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; }
3871
- h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
3872
- .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; }
3873
- .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
3874
- .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
3875
- .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
3876
- .stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
3877
- .hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; }
3878
- .hero h1 { border: none; color: white; margin: 0; }
3879
- .hero p { margin: 10px 0 0 0; opacity: 0.9; }
3880
- table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
3881
- th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
3882
- th { background-color: #f8fafc; font-weight: 600; color: #475569; }
3883
- tr:last-child td { border-bottom: none; }
3884
- .critical { color: #dc2626; font-weight: bold; }
3885
- .major { color: #ea580c; font-weight: bold; }
3886
- .minor { color: #2563eb; }
3887
- .issue-critical { color: #dc2626; font-weight: bold; text-transform: uppercase; }
3888
- .issue-major { color: #ea580c; font-weight: bold; text-transform: uppercase; }
3889
- .issue-minor { color: #2563eb; font-weight: bold; text-transform: uppercase; }
3890
- code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
3891
- .footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
3892
- a { color: #2563eb; text-decoration: none; }
3893
- a:hover { text-decoration: underline; }
3894
- </style>
3953
+ ${tag("title", title)}
3954
+ ${tag("style", styles)}
3895
3955
  </head>`;
3896
3956
  }
3897
3957
  function generateReportHero(title, subtitle) {
3898
- const subtitleHtml = subtitle ? `<p>${subtitle}</p>` : "";
3899
- return `<div class="hero">
3900
- <h1>${title}</h1>
3901
- ${subtitleHtml}
3902
- </div>`;
3958
+ return tag("div", tag("h1", title) + (subtitle ? tag("p", subtitle) : ""), {
3959
+ class: "hero"
3960
+ });
3903
3961
  }
3904
3962
  function generateStatCards(cards) {
3905
3963
  const cardsHtml = cards.map(
3906
- (card) => `
3907
- <div class="stat-card">
3908
- <div class="stat-value"${card.color ? ` style="color: ${card.color}"` : ""}>${card.value}</div>
3909
- <div class="stat-label">${card.label}</div>
3910
- </div>`
3964
+ (c) => tag(
3965
+ "div",
3966
+ tag("div", String(c.value), {
3967
+ class: "stat-value",
3968
+ ...c.color ? { style: `color: ${c.color}` } : {}
3969
+ }) + tag("div", c.label, { class: "stat-label" }),
3970
+ { class: "stat-card" }
3971
+ )
3911
3972
  ).join("");
3912
- return `<div class="stats">${cardsHtml}</div>`;
3913
- }
3914
- function generateScoreCard(value, label) {
3915
- return `<div class="stat-card" style="margin-bottom: 2rem;">
3916
- <div class="stat-label">${label}</div>
3917
- <div class="stat-value">${value}</div>
3918
- </div>`;
3973
+ return tag("div", cardsHtml, { class: "stats" });
3919
3974
  }
3920
3975
  function generateTable(config) {
3921
- const headersHtml = config.headers.map((h) => `<th>${h}</th>`).join("");
3922
- const rowsHtml = config.rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
3923
- return `<table>
3924
- <thead><tr>${headersHtml}</tr></thead>
3925
- <tbody>${rowsHtml}</tbody>
3926
- </table>`;
3927
- }
3928
- function generateIssueSummary(critical, major, minor, potentialSavings) {
3929
- const savingsHtml = potentialSavings ? `<p><strong>Potential Savings:</strong> ${potentialSavings.toLocaleString()} tokens</p>` : "";
3930
- return `<div class="card" style="margin-bottom: 30px;">
3931
- <h2>\u26A0\uFE0F Issues Summary</h2>
3932
- <p>
3933
- <span class="critical">\u{1F534} Critical: ${critical}</span> &nbsp;
3934
- <span class="major">\u{1F7E1} Major: ${major}</span> &nbsp;
3935
- <span class="minor">\u{1F535} Minor: ${minor}</span>
3936
- </p>
3937
- ${savingsHtml}
3938
- </div>`;
3976
+ const head = tag(
3977
+ "thead",
3978
+ tag("tr", config.headers.map((h) => tag("th", h)).join(""))
3979
+ );
3980
+ const body = tag(
3981
+ "tbody",
3982
+ config.rows.map(
3983
+ (row) => tag("tr", row.map((cell) => tag("td", cell)).join(""))
3984
+ ).join("")
3985
+ );
3986
+ return tag("table", head + body);
3987
+ }
3988
+ function generateIssueSummary(crit, maj, min, savings) {
3989
+ const details = [
3990
+ tag("span", `\u{1F534} Critical: ${crit}`, { class: "critical" }),
3991
+ tag("span", `\u{1F7E1} Major: ${maj}`, { class: "major" }),
3992
+ tag("span", `\u{1F535} Minor: ${min}`, { class: "minor" })
3993
+ ].join(" &nbsp; ");
3994
+ const savingsHtml = savings ? tag(
3995
+ "p",
3996
+ tag("strong", "Potential Savings: ") + savings.toLocaleString() + " tokens"
3997
+ ) : "";
3998
+ return tag(
3999
+ "div",
4000
+ tag("h2", "\u26A0\uFE0F Issues Summary") + tag("p", details) + savingsHtml,
4001
+ { class: "card", style: "margin-bottom: 30px;" }
4002
+ );
3939
4003
  }
3940
4004
  function generateReportFooter(options) {
3941
- const versionText = options.version ? ` v${options.version}` : "";
4005
+ const version = options.version ? ` v${options.version}` : "";
3942
4006
  const links = [];
3943
- if (options.packageUrl) {
3944
- links.push(`<a href="${options.packageUrl}">Star us on GitHub</a>`);
3945
- }
3946
- if (options.bugUrl) {
3947
- links.push(`<a href="${options.bugUrl}">Report it here</a>`);
3948
- }
3949
- const linksHtml = links.length ? links.map((l) => `<p>Like AIReady? ${l}</p>`).join("\n ") : "";
3950
- return `<div class="footer">
3951
- <p>Generated by <strong>@aiready/${options.packageName}</strong>${versionText}</p>
3952
- ${linksHtml}
3953
- </div>`;
4007
+ if (options.packageUrl)
4008
+ links.push(
4009
+ tag(
4010
+ "p",
4011
+ `Like AIReady? ${tag("a", "Star us on GitHub", { href: options.packageUrl })}`
4012
+ )
4013
+ );
4014
+ if (options.bugUrl)
4015
+ links.push(
4016
+ tag(
4017
+ "p",
4018
+ `Like AIReady? ${tag("a", "Report it here", { href: options.bugUrl })}`
4019
+ )
4020
+ );
4021
+ return tag(
4022
+ "div",
4023
+ tag(
4024
+ "p",
4025
+ `Generated by ${tag("strong", "@aiready/" + options.packageName)}` + version
4026
+ ) + links.join(""),
4027
+ { class: "footer" }
4028
+ );
3954
4029
  }
3955
4030
  function wrapInCard(content, title) {
3956
- const titleHtml = title ? `<h2>${title}</h2>` : "";
3957
- return `<div class="card">
3958
- ${titleHtml}
3959
- ${content}
3960
- </div>`;
3961
- }
3962
- function generateCompleteReport(options, bodyContent) {
3963
- return `${generateReportHead(options.title)}
3964
- <body>
3965
- ${bodyContent}
3966
- ${generateReportFooter(options)}
3967
- </body>
3968
- </html>`;
4031
+ return tag("div", (title ? tag("h2", title) : "") + content, {
4032
+ class: "card"
4033
+ });
4034
+ }
4035
+ function generateCompleteReport(options, body) {
4036
+ return generateReportHead(options.title) + tag("body", body + generateReportFooter(options));
4037
+ }
4038
+ function generateStandardHtmlReport(options, stats, sections, score) {
4039
+ const hero = generateReportHero(
4040
+ `${options.emoji || "\u{1F50D}"} AIReady ${options.title}`,
4041
+ `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString()}`
4042
+ );
4043
+ const scoreCard = score ? tag(
4044
+ "div",
4045
+ tag("div", String(score.value), { class: "score-value" }) + tag("div", score.label, { class: "score-label" }),
4046
+ { class: "score-card" }
4047
+ ) : "";
4048
+ const statsCards = generateStatCards(stats);
4049
+ const bodyContent = `
4050
+ ${hero}
4051
+ ${scoreCard}
4052
+ ${statsCards}
4053
+ ${sections.map((s) => wrapInCard(s.content, s.title)).join("\n")}
4054
+ `;
4055
+ return generateCompleteReport(options, bodyContent);
3969
4056
  }
3970
4057
 
3971
4058
  // src/utils/rating-helpers.ts
@@ -4387,12 +4474,12 @@ function calculateHeuristicConfidence(similarity, tokens, lines) {
4387
4474
  }
4388
4475
 
4389
4476
  // src/utils/cli-factory.ts
4390
- var import_chalk2 = __toESM(require("chalk"));
4477
+ var import_chalk4 = __toESM(require("chalk"));
4391
4478
  function createStandardProgressCallback(toolName) {
4392
4479
  return (processed, total, message) => {
4393
4480
  const percent = Math.round(processed / Math.max(1, total) * 100);
4394
4481
  process.stdout.write(
4395
- `\r\x1B[K [${toolName}] ${import_chalk2.default.cyan(`${percent}%`)} ${message}`
4482
+ `\r\x1B[K [${toolName}] ${import_chalk4.default.cyan(`${percent}%`)} ${message}`
4396
4483
  );
4397
4484
  if (processed === total) {
4398
4485
  process.stdout.write("\n");
@@ -4400,12 +4487,12 @@ function createStandardProgressCallback(toolName) {
4400
4487
  };
4401
4488
  }
4402
4489
  function formatStandardCliResult(toolName, score, issuesCount) {
4403
- const scoreColor = score >= 75 ? import_chalk2.default.green : score >= 50 ? import_chalk2.default.yellow : import_chalk2.default.red;
4490
+ const scoreColor = score >= 75 ? import_chalk4.default.green : score >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
4404
4491
  console.log(`
4405
- ${import_chalk2.default.bold(toolName.toUpperCase())} Analysis Complete`);
4492
+ ${import_chalk4.default.bold(toolName.toUpperCase())} Analysis Complete`);
4406
4493
  console.log(` Overall Score: ${scoreColor(score)}/100`);
4407
4494
  console.log(
4408
- ` Issues Found: ${issuesCount > 0 ? import_chalk2.default.red(issuesCount) : import_chalk2.default.green("None")}`
4495
+ ` Issues Found: ${issuesCount > 0 ? import_chalk4.default.red(issuesCount) : import_chalk4.default.green("None")}`
4409
4496
  );
4410
4497
  }
4411
4498
  async function runStandardCliAction(toolName, action) {
@@ -4414,7 +4501,7 @@ async function runStandardCliAction(toolName, action) {
4414
4501
  formatStandardCliResult(toolName, score, issuesCount);
4415
4502
  } catch (error) {
4416
4503
  console.error(
4417
- import_chalk2.default.red(`
4504
+ import_chalk4.default.red(`
4418
4505
  \u274C [${toolName}] critical error: ${error.message}`)
4419
4506
  );
4420
4507
  process.exit(1);
@@ -4422,13 +4509,13 @@ async function runStandardCliAction(toolName, action) {
4422
4509
  }
4423
4510
 
4424
4511
  // src/utils/reporting.ts
4425
- var import_chalk3 = __toESM(require("chalk"));
4512
+ var import_chalk5 = __toESM(require("chalk"));
4426
4513
  function getScoreColor(score) {
4427
- if (score >= 85) return import_chalk3.default.green;
4428
- if (score >= 70) return import_chalk3.default.cyan;
4429
- if (score >= 50) return import_chalk3.default.yellow;
4430
- if (score >= 30) return import_chalk3.default.red;
4431
- return import_chalk3.default.bgRed.white;
4514
+ if (score >= 85) return import_chalk5.default.green;
4515
+ if (score >= 70) return import_chalk5.default.cyan;
4516
+ if (score >= 50) return import_chalk5.default.yellow;
4517
+ if (score >= 30) return import_chalk5.default.red;
4518
+ return import_chalk5.default.bgRed.white;
4432
4519
  }
4433
4520
  function displayStandardConsoleReport(data) {
4434
4521
  const {
@@ -4443,27 +4530,27 @@ function displayStandardConsoleReport(data) {
4443
4530
  noIssuesMessage = "\u2728 No issues found!",
4444
4531
  safetyRating
4445
4532
  } = data;
4446
- console.log(import_chalk3.default.bold(`
4533
+ console.log(import_chalk5.default.bold(`
4447
4534
  ${title}
4448
4535
  `));
4449
4536
  if (safetyRating) {
4450
4537
  if (safetyRating === "blind-risk" || safetyRating === "\u{1F480} blind-risk") {
4451
4538
  console.log(
4452
- import_chalk3.default.bgRed.white.bold(
4539
+ import_chalk5.default.bgRed.white.bold(
4453
4540
  " \u{1F480} BLIND RISK \u2014 NO TESTS DETECTED. AI-GENERATED CHANGES CANNOT BE VERIFIED. "
4454
4541
  )
4455
4542
  );
4456
4543
  console.log();
4457
4544
  } else if (safetyRating === "high-risk" || safetyRating === "\u{1F534} high-risk") {
4458
4545
  console.log(
4459
- import_chalk3.default.red.bold(
4546
+ import_chalk5.default.red.bold(
4460
4547
  ` \u{1F534} HIGH RISK \u2014 Insufficient test coverage. AI changes may introduce silent bugs.`
4461
4548
  )
4462
4549
  );
4463
4550
  console.log();
4464
4551
  }
4465
4552
  }
4466
- const safetyColor = safetyRating ? getSeverityColor(safetyRating, import_chalk3.default) : getScoreColor(score);
4553
+ const safetyColor = safetyRating ? getSeverityColor(safetyRating, import_chalk5.default) : getScoreColor(score);
4467
4554
  if (safetyRating) {
4468
4555
  console.log(
4469
4556
  `AI Change Safety: ${safetyColor(`${getSafetyIcon(safetyRating)} ${safetyRating.toUpperCase()}`)}`
@@ -4473,12 +4560,12 @@ ${title}
4473
4560
  `Score: ${getScoreColor(score)(score + "/100")} (${rating.toUpperCase()})`
4474
4561
  );
4475
4562
  if (stats.length > 0) {
4476
- const statsStr = stats.map((s) => `${s.label}: ${import_chalk3.default.cyan(s.value)}`).join(" ");
4563
+ const statsStr = stats.map((s) => `${s.label}: ${import_chalk5.default.cyan(s.value)}`).join(" ");
4477
4564
  console.log(statsStr);
4478
4565
  }
4479
- console.log(`Analysis Time: ${import_chalk3.default.gray(elapsedTime + "s")}
4566
+ console.log(`Analysis Time: ${import_chalk5.default.gray(elapsedTime + "s")}
4480
4567
  `);
4481
- console.log(import_chalk3.default.bold("\u{1F4D0} Dimension Scores\n"));
4568
+ console.log(import_chalk5.default.bold("\u{1F4D0} Dimension Scores\n"));
4482
4569
  for (const dim of dimensions) {
4483
4570
  const color = getScoreColor(dim.value);
4484
4571
  console.log(
@@ -4486,24 +4573,24 @@ ${title}
4486
4573
  );
4487
4574
  }
4488
4575
  if (issues.length > 0) {
4489
- console.log(import_chalk3.default.bold("\n\u26A0\uFE0F Issues\n"));
4576
+ console.log(import_chalk5.default.bold("\n\u26A0\uFE0F Issues\n"));
4490
4577
  for (const issue of issues) {
4491
- const sev = getSeverityColor(issue.severity, import_chalk3.default);
4578
+ const sev = getSeverityColor(issue.severity, import_chalk5.default);
4492
4579
  console.log(`${sev(issue.severity.toUpperCase())} ${issue.message}`);
4493
4580
  if (issue.suggestion) {
4494
4581
  console.log(
4495
- ` ${import_chalk3.default.dim("\u2192")} ${import_chalk3.default.italic(issue.suggestion)}`
4582
+ ` ${import_chalk5.default.dim("\u2192")} ${import_chalk5.default.italic(issue.suggestion)}`
4496
4583
  );
4497
4584
  }
4498
4585
  console.log();
4499
4586
  }
4500
4587
  } else {
4501
- console.log(import_chalk3.default.green(`
4588
+ console.log(import_chalk5.default.green(`
4502
4589
  ${noIssuesMessage}
4503
4590
  `));
4504
4591
  }
4505
4592
  if (recommendations.length > 0) {
4506
- console.log(import_chalk3.default.bold("\u{1F4A1} Recommendations\n"));
4593
+ console.log(import_chalk5.default.bold("\u{1F4A1} Recommendations\n"));
4507
4594
  recommendations.forEach((rec, i) => {
4508
4595
  console.log(`${i + 1}. ${rec}`);
4509
4596
  });
@@ -5492,7 +5579,17 @@ function calculateAiSignalClarity(params) {
5492
5579
  rating: "minimal",
5493
5580
  signals: [],
5494
5581
  topRisk: "No symbols to analyze",
5495
- recommendations: []
5582
+ recommendations: [],
5583
+ dimensions: {
5584
+ overloadingScore: 100,
5585
+ magicLiteralScore: 100,
5586
+ booleanTrapScore: 100,
5587
+ implicitSideEffectScore: 100,
5588
+ deepCallbackScore: 100,
5589
+ ambiguityScore: 100,
5590
+ documentationScore: 100,
5591
+ sizeScore: 100
5592
+ }
5496
5593
  };
5497
5594
  }
5498
5595
  const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
@@ -5605,7 +5702,23 @@ function calculateAiSignalClarity(params) {
5605
5702
  rating,
5606
5703
  signals: signals.filter((s) => s.count > 0),
5607
5704
  topRisk,
5608
- recommendations
5705
+ recommendations,
5706
+ dimensions: {
5707
+ overloadingScore: Math.max(0, 100 - overloadSignal.riskContribution * 5),
5708
+ magicLiteralScore: Math.max(0, 100 - magicSignal.riskContribution * 6.6),
5709
+ booleanTrapScore: Math.max(0, 100 - trapSignal.riskContribution * 6.6),
5710
+ implicitSideEffectScore: Math.max(
5711
+ 0,
5712
+ 100 - sideEffectSignal.riskContribution * 10
5713
+ ),
5714
+ deepCallbackScore: Math.max(
5715
+ 0,
5716
+ 100 - callbackSignal.riskContribution * 10
5717
+ ),
5718
+ ambiguityScore: Math.max(0, 100 - ambiguousSignal.riskContribution * 20),
5719
+ documentationScore: Math.max(0, 100 - undocSignal.riskContribution * 20),
5720
+ sizeScore: Math.max(0, 100 - largeFileSignal.riskContribution * 4)
5721
+ }
5609
5722
  };
5610
5723
  }
5611
5724
 
@@ -6208,6 +6321,104 @@ function emitIssuesAsAnnotations(issues) {
6208
6321
  });
6209
6322
  });
6210
6323
  }
6324
+
6325
+ // src/utils/analysis-orchestrator.ts
6326
+ async function runBatchAnalysis(items, label, toolId, onProgress, processFn, onResult) {
6327
+ let processed = 0;
6328
+ for (const item of items) {
6329
+ processed++;
6330
+ emitProgress(processed, items.length, toolId, label, onProgress);
6331
+ const result = await processFn(item);
6332
+ onResult(result);
6333
+ }
6334
+ }
6335
+
6336
+ // src/utils/spoke-cli-helpers.ts
6337
+ var import_chalk6 = __toESM(require("chalk"));
6338
+ async function executeSpokeCli(name, description, options, analyzeFn) {
6339
+ console.log(import_chalk6.default.cyan(`Analyzing ${description.toLowerCase()}...`));
6340
+ try {
6341
+ const report = await analyzeFn({
6342
+ rootDir: process.cwd(),
6343
+ ...options
6344
+ });
6345
+ console.log(import_chalk6.default.bold(`
6346
+ ${name} Analysis Results:`));
6347
+ console.log(
6348
+ `Rating: ${report.summary.rating.toUpperCase()} (Score: ${report.summary.score})`
6349
+ );
6350
+ if (report.issues && report.issues.length > 0) {
6351
+ console.log(import_chalk6.default.red(`
6352
+ Found ${report.issues.length} issues.`));
6353
+ } else {
6354
+ console.log(import_chalk6.default.green("\nNo issues detected."));
6355
+ }
6356
+ return report;
6357
+ } catch (err) {
6358
+ console.error(import_chalk6.default.red(`Error during ${name} analysis: ${err.message}`));
6359
+ process.exit(1);
6360
+ }
6361
+ }
6362
+
6363
+ // src/utils/test-utils.ts
6364
+ var import_fs5 = require("fs");
6365
+ var import_path6 = require("path");
6366
+ var TEST_PATTERNS = [
6367
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/,
6368
+ /_test\.go$/,
6369
+ /test_.*\.py$/,
6370
+ /.*_test\.py$/,
6371
+ /.*Test\.java$/,
6372
+ /.*Tests\.cs$/,
6373
+ /__tests__\//,
6374
+ /\/tests?\//,
6375
+ /\/e2e\//,
6376
+ /\/fixtures\//
6377
+ ];
6378
+ function isTestFile(filePath, extraPatterns) {
6379
+ if (TEST_PATTERNS.some((p) => p.test(filePath))) return true;
6380
+ if (extraPatterns) return extraPatterns.some((p) => filePath.includes(p));
6381
+ return false;
6382
+ }
6383
+ function detectTestFramework(rootDir) {
6384
+ const manifests = [
6385
+ {
6386
+ file: "package.json",
6387
+ deps: [
6388
+ "jest",
6389
+ "vitest",
6390
+ "mocha",
6391
+ "jasmine",
6392
+ "ava",
6393
+ "tap",
6394
+ "playwright",
6395
+ "cypress"
6396
+ ]
6397
+ },
6398
+ { file: "requirements.txt", deps: ["pytest", "unittest", "nose"] },
6399
+ { file: "pyproject.toml", deps: ["pytest"] },
6400
+ { file: "pom.xml", deps: ["junit", "testng"] },
6401
+ { file: "build.gradle", deps: ["junit", "testng"] },
6402
+ { file: "go.mod", deps: ["testing"] }
6403
+ // go testing is built-in
6404
+ ];
6405
+ for (const m of manifests) {
6406
+ const p = (0, import_path6.join)(rootDir, m.file);
6407
+ if ((0, import_fs5.existsSync)(p)) {
6408
+ if (m.file === "go.mod") return true;
6409
+ try {
6410
+ const content = (0, import_fs5.readFileSync)(p, "utf-8");
6411
+ if (m.deps.some((d) => content.includes(d))) return true;
6412
+ } catch {
6413
+ }
6414
+ }
6415
+ }
6416
+ return false;
6417
+ }
6418
+ function isBuildArtifact(filePath) {
6419
+ const lower = filePath.toLowerCase();
6420
+ return lower.includes("/node_modules/") || lower.includes("/dist/") || lower.includes("/build/") || lower.includes("/out/") || lower.includes("/.next/") || lower.includes("/target/") || lower.includes("/bin/");
6421
+ }
6211
6422
  // Annotate the CommonJS export names for ESM import in node:
6212
6423
  0 && (module.exports = {
6213
6424
  AIReadyConfigSchema,
@@ -6253,6 +6464,7 @@ function emitIssuesAsAnnotations(issues) {
6253
6464
  SeveritySchema,
6254
6465
  SpokeOutputSchema,
6255
6466
  SpokeSummarySchema,
6467
+ TEST_PATTERNS,
6256
6468
  TOOL_NAME_MAP,
6257
6469
  ToolName,
6258
6470
  ToolNameSchema,
@@ -6292,14 +6504,18 @@ function emitIssuesAsAnnotations(issues) {
6292
6504
  clearHistory,
6293
6505
  createProvider,
6294
6506
  createStandardProgressCallback,
6507
+ detectTestFramework,
6295
6508
  displayStandardConsoleReport,
6296
6509
  emitAnnotation,
6297
6510
  emitIssuesAsAnnotations,
6298
6511
  emitProgress,
6512
+ ensureDir,
6299
6513
  estimateCostFromBudget,
6300
6514
  estimateTokens,
6515
+ executeSpokeCli,
6301
6516
  exportHistory,
6302
6517
  extractCodeBlocks,
6518
+ filterBySeverity,
6303
6519
  findLatestReport,
6304
6520
  findLatestScanReport,
6305
6521
  formatAcceptanceRate,
@@ -6315,13 +6531,14 @@ function emitIssuesAsAnnotations(issues) {
6315
6531
  generateReportFooter,
6316
6532
  generateReportHead,
6317
6533
  generateReportHero,
6318
- generateScoreCard,
6534
+ generateStandardHtmlReport,
6319
6535
  generateStatCards,
6320
6536
  generateTable,
6321
6537
  generateValueChain,
6322
6538
  getElapsedTime,
6323
6539
  getFileCommitTimestamps,
6324
6540
  getFileExtension,
6541
+ getFilesByPattern,
6325
6542
  getHistorySummary,
6326
6543
  getLineRangeLastModifiedCached,
6327
6544
  getModelPreset,
@@ -6344,6 +6561,7 @@ function emitIssuesAsAnnotations(issues) {
6344
6561
  getSeverityBadge,
6345
6562
  getSeverityColor,
6346
6563
  getSeverityEnum,
6564
+ getSeverityLabel,
6347
6565
  getSeverityLevel,
6348
6566
  getSeverityValue,
6349
6567
  getSupportedLanguages,
@@ -6358,8 +6576,10 @@ function emitIssuesAsAnnotations(issues) {
6358
6576
  inferPatternType,
6359
6577
  initTreeSitter,
6360
6578
  initializeParsers,
6579
+ isBuildArtifact,
6361
6580
  isFileSupported,
6362
6581
  isSourceFile,
6582
+ isTestFile,
6363
6583
  loadConfig,
6364
6584
  loadMergedConfig,
6365
6585
  loadScoreHistory,
@@ -6367,6 +6587,7 @@ function emitIssuesAsAnnotations(issues) {
6367
6587
  normalizeAnalysisResult,
6368
6588
  normalizeIssue,
6369
6589
  normalizeMetrics,
6590
+ normalizeSeverity,
6370
6591
  normalizeSpokeOutput,
6371
6592
  normalizeToolName,
6372
6593
  parseFileExports,
@@ -6377,6 +6598,7 @@ function emitIssuesAsAnnotations(issues) {
6377
6598
  readFileContent,
6378
6599
  resolveOutputFormat,
6379
6600
  resolveOutputPath,
6601
+ runBatchAnalysis,
6380
6602
  runStandardCliAction,
6381
6603
  saveScoreEntry,
6382
6604
  scanEntries,