@aiready/core 0.23.1 → 0.23.3

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.
Files changed (79) hide show
  1. package/dist/__tests__/parser-factory.test.d.ts +1 -1
  2. package/dist/__tests__/parser-factory.test.js +62 -50
  3. package/dist/__tests__/python-parser.test.d.ts +1 -1
  4. package/dist/__tests__/python-parser.test.js +111 -109
  5. package/dist/__tests__/scoring.test.d.ts +1 -1
  6. package/dist/__tests__/scoring.test.js +193 -176
  7. package/dist/chunk-3YI4IS3D.mjs +191 -173
  8. package/dist/chunk-5HIXDC3X.mjs +273 -251
  9. package/dist/chunk-5V3L53AE.mjs +805 -0
  10. package/dist/chunk-CKVKHN3G.mjs +228 -211
  11. package/dist/chunk-COHIBX3Q.mjs +213 -195
  12. package/dist/chunk-CWRCDSKZ.mjs +91 -82
  13. package/dist/chunk-D3D3NCRR.mjs +147 -129
  14. package/dist/chunk-HCFYP7UD.mjs +805 -0
  15. package/dist/chunk-HFLFBA6F.mjs +79 -72
  16. package/dist/chunk-HKSARRCD.mjs +66 -58
  17. package/dist/chunk-JJ5JL5FX.mjs +91 -82
  18. package/dist/chunk-KDSTXVLQ.mjs +724 -0
  19. package/dist/chunk-KI7XORTN.mjs +91 -82
  20. package/dist/chunk-LTMHFNFK.mjs +690 -0
  21. package/dist/chunk-LTNXTXRI.mjs +228 -211
  22. package/dist/chunk-M22BXHBR.mjs +805 -0
  23. package/dist/chunk-MH3A3LX6.mjs +200 -182
  24. package/dist/chunk-NGHT7JOG.mjs +697 -0
  25. package/dist/chunk-OQ6IGDXG.mjs +147 -129
  26. package/dist/chunk-QAFB3HXQ.mjs +181 -165
  27. package/dist/chunk-QQBKXHLU.mjs +678 -0
  28. package/dist/chunk-RDHYGES7.mjs +678 -0
  29. package/dist/chunk-SWTDBVYJ.mjs +228 -213
  30. package/dist/chunk-UIWL5JQB.mjs +79 -72
  31. package/dist/chunk-UQGI67WR.mjs +79 -72
  32. package/dist/chunk-UTZOO4XO.mjs +147 -131
  33. package/dist/chunk-X4F46I5L.mjs +213 -195
  34. package/dist/chunk-XKK7YHPX.mjs +204 -186
  35. package/dist/chunk-YCA4FTEK.mjs +190 -172
  36. package/dist/chunk-ZSZRRTJM.mjs +719 -0
  37. package/dist/client-BgmiMoil.d.mts +1344 -0
  38. package/dist/client-BgmiMoil.d.ts +1344 -0
  39. package/dist/client-BxGrPuuN.d.mts +1191 -0
  40. package/dist/client-BxGrPuuN.d.ts +1191 -0
  41. package/dist/client-D-cn9ydj.d.mts +1136 -0
  42. package/dist/client-D-cn9ydj.d.ts +1136 -0
  43. package/dist/client-D9seCH4K.d.mts +1334 -0
  44. package/dist/client-D9seCH4K.d.ts +1334 -0
  45. package/dist/client-DIXIh7rw.d.mts +1193 -0
  46. package/dist/client-DIXIh7rw.d.ts +1193 -0
  47. package/dist/client-DVHXWOHw.d.mts +1245 -0
  48. package/dist/client-DVHXWOHw.d.ts +1245 -0
  49. package/dist/client.d.mts +2 -1094
  50. package/dist/client.d.ts +2 -1094
  51. package/dist/client.js +23 -43
  52. package/dist/client.mjs +3 -25
  53. package/dist/index.d.mts +380 -108
  54. package/dist/index.d.ts +380 -108
  55. package/dist/index.js +609 -445
  56. package/dist/index.mjs +587 -429
  57. package/dist/parsers/parser-factory.d.ts +45 -45
  58. package/dist/parsers/parser-factory.js +86 -84
  59. package/dist/parsers/python-parser.d.ts +33 -28
  60. package/dist/parsers/python-parser.js +224 -222
  61. package/dist/parsers/typescript-parser.d.ts +15 -10
  62. package/dist/parsers/typescript-parser.js +223 -197
  63. package/dist/scoring.d.ts +59 -49
  64. package/dist/scoring.js +129 -127
  65. package/dist/types/language.d.ts +104 -93
  66. package/dist/types/language.js +23 -23
  67. package/dist/types.d.ts +105 -87
  68. package/dist/types.js +1 -1
  69. package/dist/utils/ast-parser.d.ts +42 -33
  70. package/dist/utils/ast-parser.js +159 -162
  71. package/dist/utils/cli-helpers.d.ts +27 -10
  72. package/dist/utils/cli-helpers.js +45 -43
  73. package/dist/utils/config.d.ts +8 -3
  74. package/dist/utils/config.js +67 -69
  75. package/dist/utils/file-scanner.d.ts +1 -1
  76. package/dist/utils/file-scanner.js +80 -76
  77. package/dist/utils/metrics.d.ts +1 -1
  78. package/dist/utils/metrics.js +2 -2
  79. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -38,12 +38,13 @@ import {
38
38
  getProjectSizeTier,
39
39
  getRating,
40
40
  getRatingDisplay,
41
+ getRatingSlug,
41
42
  getRatingWithContext,
42
43
  getRecommendedThreshold,
43
44
  getToolWeight,
44
45
  normalizeToolName,
45
46
  parseWeightString
46
- } from "./chunk-5HIXDC3X.mjs";
47
+ } from "./chunk-NGHT7JOG.mjs";
47
48
 
48
49
  // src/types/contract.ts
49
50
  function validateSpokeOutput(toolName, output) {
@@ -439,8 +440,15 @@ function isSourceFile(filePath) {
439
440
  }
440
441
 
441
442
  // src/utils/cli-helpers.ts
442
- import { writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
443
- import { join as join2, dirname as dirname2 } from "path";
443
+ import {
444
+ writeFileSync,
445
+ mkdirSync,
446
+ existsSync as existsSync2,
447
+ readdirSync,
448
+ statSync
449
+ } from "fs";
450
+ import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
451
+ import chalk from "chalk";
444
452
  function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
445
453
  let outputPath;
446
454
  if (userPath) {
@@ -508,25 +516,180 @@ function emitProgress(processed, total, toolId, message, onProgress, throttleCou
508
516
  onProgress(processed, total, `${message} (${processed}/${total})`);
509
517
  }
510
518
  }
511
- function getSeverityColor(severity, chalk) {
519
+ function getSeverityColor(severity, chalkInstance = chalk) {
512
520
  switch (severity.toLowerCase()) {
513
521
  case "critical":
514
522
  case "high-risk":
515
523
  case "blind-risk":
516
- return chalk.red;
524
+ return chalkInstance.red;
517
525
  case "major":
518
526
  case "moderate-risk":
519
- return chalk.yellow;
527
+ return chalkInstance.yellow;
520
528
  case "minor":
521
529
  case "safe":
522
- return chalk.green;
530
+ return chalkInstance.green;
531
+ case "info":
532
+ return chalkInstance.blue;
533
+ default:
534
+ return chalkInstance.white;
535
+ }
536
+ }
537
+ function getSeverityValue(s) {
538
+ if (!s) return 0;
539
+ switch (s.toLowerCase()) {
540
+ case "critical":
541
+ return 4;
542
+ case "major":
543
+ return 3;
544
+ case "minor":
545
+ return 2;
523
546
  case "info":
524
- return chalk.blue;
547
+ return 1;
548
+ default:
549
+ return 0;
550
+ }
551
+ }
552
+ function getSeverityLevel(s) {
553
+ return getSeverityValue(s);
554
+ }
555
+ function getSeverityBadge(severity, chalkInstance = chalk) {
556
+ const val = getSeverityValue(
557
+ typeof severity === "string" ? severity : severity
558
+ );
559
+ switch (val) {
560
+ case 4:
561
+ return chalkInstance.bgRed.white.bold(" CRITICAL ");
562
+ case 3:
563
+ return chalkInstance.bgYellow.black.bold(" MAJOR ");
564
+ case 2:
565
+ return chalkInstance.bgBlue.white.bold(" MINOR ");
566
+ case 1:
567
+ return chalkInstance.bgCyan.black(" INFO ");
568
+ default:
569
+ return chalkInstance.bgCyan.black(" INFO ");
570
+ }
571
+ }
572
+ function getSeverityEnum(s) {
573
+ const level = getSeverityLevel(s);
574
+ switch (level) {
575
+ case 4:
576
+ return "critical";
577
+ case 3:
578
+ return "major";
579
+ case 2:
580
+ return "minor";
581
+ case 1:
582
+ return "info";
525
583
  default:
526
- return chalk.white;
584
+ return "info";
585
+ }
586
+ }
587
+ function findLatestReport(dirPath) {
588
+ const aireadyDir = resolvePath(dirPath, ".aiready");
589
+ if (!existsSync2(aireadyDir)) {
590
+ return null;
591
+ }
592
+ let files = readdirSync(aireadyDir).filter(
593
+ (f) => f.startsWith("aiready-report-") && f.endsWith(".json")
594
+ );
595
+ if (files.length === 0) {
596
+ files = readdirSync(aireadyDir).filter(
597
+ (f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
598
+ );
599
+ }
600
+ if (files.length === 0) {
601
+ return null;
602
+ }
603
+ const sortedFiles = files.map((f) => ({
604
+ name: f,
605
+ path: resolvePath(aireadyDir, f),
606
+ mtime: statSync(resolvePath(aireadyDir, f)).mtime
607
+ })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
608
+ return sortedFiles[0].path;
609
+ }
610
+ function findLatestScanReport(scanReportsDir, reportFilePrefix) {
611
+ try {
612
+ let reportFiles = [];
613
+ if (existsSync2(scanReportsDir)) {
614
+ const files = readdirSync(scanReportsDir);
615
+ if (files.length > 0) {
616
+ const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
617
+ reportFiles = files.filter((file) => prefixRegex.test(file));
618
+ }
619
+ }
620
+ if (reportFiles.length === 0) return null;
621
+ reportFiles.sort((a, b) => {
622
+ const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
623
+ const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
624
+ return idB - idA;
625
+ });
626
+ return join2(scanReportsDir, reportFiles[0]);
627
+ } catch (e) {
628
+ console.error("Error while finding latest scan report:", e);
629
+ return null;
527
630
  }
528
631
  }
529
632
 
633
+ // src/utils/provider-utils.ts
634
+ function groupIssuesByFile(issues) {
635
+ const fileIssuesMap = /* @__PURE__ */ new Map();
636
+ for (const issue of issues) {
637
+ const file = issue.location?.file ?? "unknown";
638
+ if (!fileIssuesMap.has(file)) fileIssuesMap.set(file, []);
639
+ fileIssuesMap.get(file).push(issue);
640
+ }
641
+ return Array.from(fileIssuesMap.entries()).map(([fileName, issueList]) => ({
642
+ fileName,
643
+ issues: issueList,
644
+ metrics: {}
645
+ }));
646
+ }
647
+ function buildSimpleProviderScore(toolName, summary, rawData = {}) {
648
+ return {
649
+ toolName,
650
+ score: summary.score ?? 0,
651
+ rawMetrics: { ...summary, ...rawData },
652
+ factors: [],
653
+ recommendations: (summary.recommendations ?? []).map((action) => ({
654
+ action,
655
+ estimatedImpact: 5,
656
+ priority: "medium"
657
+ }))
658
+ };
659
+ }
660
+ function buildSpokeOutput(toolName, version, summary, results, metadata = {}) {
661
+ return SpokeOutputSchema.parse({
662
+ results,
663
+ summary,
664
+ metadata: {
665
+ toolName,
666
+ version,
667
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
668
+ ...metadata
669
+ }
670
+ });
671
+ }
672
+ function createProvider(config) {
673
+ return {
674
+ id: config.id,
675
+ alias: config.alias,
676
+ defaultWeight: config.defaultWeight,
677
+ async analyze(options) {
678
+ const report = await config.analyzeReport(options);
679
+ return buildSpokeOutput(
680
+ config.id,
681
+ config.version,
682
+ config.getSummary(report),
683
+ config.getResults(report),
684
+ config.getMetadata?.(report) ?? {}
685
+ );
686
+ },
687
+ score(output, options) {
688
+ return config.score(output, options);
689
+ }
690
+ };
691
+ }
692
+
530
693
  // src/utils/ast-parser.ts
531
694
  import { parse as parse2 } from "@typescript-eslint/typescript-estree";
532
695
 
@@ -653,8 +816,10 @@ var TypeScriptParser = class {
653
816
  // camelCase for variables and functions
654
817
  variablePattern: /^[a-z][a-zA-Z0-9]*$/,
655
818
  functionPattern: /^[a-z][a-zA-Z0-9]*$/,
656
- // PascalCase for classes
819
+ // PascalCase for classes, types and interfaces
657
820
  classPattern: /^[A-Z][a-zA-Z0-9]*$/,
821
+ typePattern: /^[A-Z][a-zA-Z0-9]*$/,
822
+ interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
658
823
  // UPPER_CASE for constants
659
824
  constantPattern: /^[A-Z][A-Z0-9_]*$/,
660
825
  // Common exceptions (React hooks, etc.)
@@ -865,6 +1030,101 @@ var TypeScriptParser = class {
865
1030
  }
866
1031
  };
867
1032
 
1033
+ // src/parsers/metadata-utils.ts
1034
+ function analyzeNodeMetadata(node, code, options) {
1035
+ const metadata = {
1036
+ isPure: true,
1037
+ hasSideEffects: false
1038
+ };
1039
+ try {
1040
+ let prev = node.previousSibling || null;
1041
+ while (prev && /comment/i.test(prev.type)) {
1042
+ const text = prev.text || "";
1043
+ if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
1044
+ metadata.documentation = {
1045
+ content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1046
+ type: "comment"
1047
+ };
1048
+ break;
1049
+ }
1050
+ if (text.trim().startsWith("///")) {
1051
+ metadata.documentation = {
1052
+ content: text.replace(/^\/\/\//, "").trim(),
1053
+ type: "xml-doc"
1054
+ };
1055
+ break;
1056
+ }
1057
+ if (text.trim().startsWith("//")) {
1058
+ metadata.documentation = {
1059
+ content: text.replace(/^\/\//, "").trim(),
1060
+ type: "comment"
1061
+ };
1062
+ break;
1063
+ }
1064
+ prev = prev.previousSibling;
1065
+ }
1066
+ if (node.type === "function_definition") {
1067
+ const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
1068
+ if (body2 && body2.children.length > 0) {
1069
+ const firstStmt = body2.children[0];
1070
+ if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
1071
+ metadata.documentation = {
1072
+ content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
1073
+ type: "docstring"
1074
+ };
1075
+ }
1076
+ }
1077
+ }
1078
+ } catch {
1079
+ }
1080
+ const defaultSignatures = [
1081
+ "console.",
1082
+ "fmt.",
1083
+ "panic(",
1084
+ "os.Exit",
1085
+ "log.",
1086
+ "Console.Write",
1087
+ "File.Write",
1088
+ "System.out",
1089
+ "System.err",
1090
+ "Files.write",
1091
+ "process.exit",
1092
+ "exit("
1093
+ ];
1094
+ const signatures = Array.from(
1095
+ /* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
1096
+ );
1097
+ const walk = (n) => {
1098
+ try {
1099
+ const t = n.type || "";
1100
+ if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
1101
+ t
1102
+ )) {
1103
+ metadata.isPure = false;
1104
+ metadata.hasSideEffects = true;
1105
+ }
1106
+ const text = n.text || "";
1107
+ for (const s of signatures) {
1108
+ if (text.includes(s)) {
1109
+ metadata.isPure = false;
1110
+ metadata.hasSideEffects = true;
1111
+ break;
1112
+ }
1113
+ }
1114
+ for (let i = 0; i < n.childCount; i++) {
1115
+ const c = n.child(i);
1116
+ if (c) walk(c);
1117
+ }
1118
+ } catch {
1119
+ }
1120
+ };
1121
+ const body = node.childForFieldName?.("body") || node.children.find(
1122
+ (c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
1123
+ );
1124
+ if (body) walk(body);
1125
+ return metadata;
1126
+ }
1127
+
868
1128
  // src/parsers/tree-sitter-utils.ts
869
1129
  import * as Parser from "web-tree-sitter";
870
1130
  import * as path from "path";
@@ -953,11 +1213,9 @@ async function setupParser(language) {
953
1213
  }
954
1214
  }
955
1215
 
956
- // src/parsers/python-parser.ts
957
- var PythonParser = class {
1216
+ // src/parsers/base-parser.ts
1217
+ var BaseLanguageParser = class {
958
1218
  constructor() {
959
- this.language = "python" /* Python */;
960
- this.extensions = [".py"];
961
1219
  this.parser = null;
962
1220
  this.initialized = false;
963
1221
  }
@@ -966,48 +1224,18 @@ var PythonParser = class {
966
1224
  */
967
1225
  async initialize() {
968
1226
  if (this.initialized) return;
969
- this.parser = await setupParser("python");
970
- this.initialized = true;
1227
+ try {
1228
+ this.parser = await setupParser(this.getParserName());
1229
+ this.initialized = true;
1230
+ } catch (error) {
1231
+ console.warn(`Failed to initialize ${this.language} parser:`, error);
1232
+ }
971
1233
  }
972
1234
  async getAST(code, filePath) {
973
1235
  if (!this.initialized) await this.initialize();
974
1236
  if (!this.parser) return null;
975
1237
  return this.parser.parse(code);
976
1238
  }
977
- analyzeMetadata(node, code) {
978
- const metadata = {
979
- isPure: true,
980
- hasSideEffects: false
981
- };
982
- const body = node.childForFieldName("body");
983
- if (body && body.children.length > 0) {
984
- const firstStmt = body.children[0];
985
- if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
986
- metadata.documentation = {
987
- content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
988
- type: "docstring"
989
- };
990
- }
991
- }
992
- const walk = (n) => {
993
- if (n.type === "global_statement" || n.type === "nonlocal_statement") {
994
- metadata.isPure = false;
995
- metadata.hasSideEffects = true;
996
- }
997
- if (n.type === "call") {
998
- const functionNode = n.childForFieldName("function");
999
- if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
1000
- metadata.isPure = false;
1001
- metadata.hasSideEffects = true;
1002
- }
1003
- }
1004
- for (const child of n.children) {
1005
- walk(child);
1006
- }
1007
- };
1008
- if (body) walk(body);
1009
- return metadata;
1010
- }
1011
1239
  parse(code, filePath) {
1012
1240
  if (!this.initialized || !this.parser) {
1013
1241
  return this.parseRegex(code, filePath);
@@ -1017,19 +1245,42 @@ var PythonParser = class {
1017
1245
  if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
1018
1246
  return this.parseRegex(code, filePath);
1019
1247
  }
1020
- const rootNode = tree.rootNode;
1021
- const imports = this.extractImportsAST(rootNode);
1022
- const exports = this.extractExportsAST(rootNode, code);
1248
+ const imports = this.extractImportsAST(tree.rootNode);
1249
+ const exports = this.extractExportsAST(tree.rootNode, code);
1023
1250
  return {
1024
1251
  exports,
1025
1252
  imports,
1026
- language: "python" /* Python */,
1253
+ language: this.language,
1027
1254
  warnings: []
1028
1255
  };
1029
1256
  } catch (error) {
1257
+ console.warn(
1258
+ `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
1259
+ );
1030
1260
  return this.parseRegex(code, filePath);
1031
1261
  }
1032
1262
  }
1263
+ canHandle(filePath) {
1264
+ const lowerPath = filePath.toLowerCase();
1265
+ return this.extensions.some((ext) => lowerPath.endsWith(ext));
1266
+ }
1267
+ };
1268
+
1269
+ // src/parsers/python-parser.ts
1270
+ var PythonParser = class extends BaseLanguageParser {
1271
+ constructor() {
1272
+ super(...arguments);
1273
+ this.language = "python" /* Python */;
1274
+ this.extensions = [".py"];
1275
+ }
1276
+ getParserName() {
1277
+ return "python";
1278
+ }
1279
+ analyzeMetadata(node, code) {
1280
+ return analyzeNodeMetadata(node, code, {
1281
+ sideEffectSignatures: ["print(", "input(", "open("]
1282
+ });
1283
+ }
1033
1284
  extractImportsAST(rootNode) {
1034
1285
  const imports = [];
1035
1286
  const processImportNode = (node) => {
@@ -1355,94 +1606,132 @@ var PythonParser = class {
1355
1606
  }
1356
1607
  };
1357
1608
 
1358
- // src/parsers/java-parser.ts
1359
- var JavaParser = class {
1360
- constructor() {
1361
- this.language = "java" /* Java */;
1362
- this.extensions = [".java"];
1363
- this.parser = null;
1364
- this.initialized = false;
1365
- }
1366
- /**
1367
- * Initialize the tree-sitter parser
1368
- */
1369
- async initialize() {
1370
- if (this.initialized) return;
1371
- this.parser = await setupParser("java");
1372
- this.initialized = true;
1373
- }
1374
- async getAST(code, filePath) {
1375
- if (!this.initialized) await this.initialize();
1376
- if (!this.parser) return null;
1377
- return this.parser.parse(code);
1378
- }
1379
- analyzeMetadata(node, code) {
1380
- const metadata = {
1381
- isPure: true,
1382
- hasSideEffects: false
1383
- };
1384
- let prev = node.previousSibling;
1385
- while (prev && (prev.type === "comment" || prev.type === "line_comment")) {
1386
- if (prev.text.startsWith("/**")) {
1609
+ // src/parsers/shared-parser-utils.ts
1610
+ var SIDE_EFFECT_KEYWORDS = [
1611
+ "print(",
1612
+ "console.",
1613
+ "System.out",
1614
+ "System.err",
1615
+ "fmt.",
1616
+ "File.Write",
1617
+ "Files.write",
1618
+ "os.Exit",
1619
+ "panic(",
1620
+ "throw ",
1621
+ "Logging.",
1622
+ "log."
1623
+ ];
1624
+ function analyzeGeneralMetadata(node, code, options = {}) {
1625
+ const metadata = {
1626
+ isPure: true,
1627
+ hasSideEffects: false
1628
+ };
1629
+ try {
1630
+ let prev = node.previousSibling || null;
1631
+ while (prev && /comment/i.test(prev.type)) {
1632
+ const text = prev.text || "";
1633
+ if (text.trim().startsWith("/**")) {
1634
+ metadata.documentation = {
1635
+ content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1636
+ type: "jsdoc"
1637
+ };
1638
+ break;
1639
+ }
1640
+ if (text.trim().startsWith("///")) {
1387
1641
  metadata.documentation = {
1388
- content: prev.text.replace(/[/*]/g, "").trim(),
1642
+ content: text.replace(/^\/\/\//, "").trim(),
1389
1643
  type: "xml-doc"
1390
- // Using xml-doc as a catch-all for structured or we can add 'javadoc'
1644
+ };
1645
+ break;
1646
+ }
1647
+ if (text.trim().startsWith("//")) {
1648
+ metadata.documentation = {
1649
+ content: text.replace(/^\/\//, "").trim(),
1650
+ type: "comment"
1391
1651
  };
1392
1652
  break;
1393
1653
  }
1394
1654
  prev = prev.previousSibling;
1395
1655
  }
1396
- const walk = (n) => {
1397
- if (n.type === "assignment_expression") {
1398
- metadata.isPure = false;
1399
- metadata.hasSideEffects = true;
1400
- }
1401
- if (n.type === "method_invocation") {
1402
- const text = n.text;
1403
- if (text.includes("System.out.print") || text.includes("System.err.print") || text.includes("Files.write")) {
1404
- metadata.isPure = false;
1405
- metadata.hasSideEffects = true;
1406
- }
1407
- }
1408
- if (n.type === "throw_statement") {
1656
+ } catch {
1657
+ }
1658
+ const signatures = [
1659
+ ...SIDE_EFFECT_KEYWORDS,
1660
+ ...options.sideEffectSignatures || []
1661
+ ];
1662
+ const walk = (n) => {
1663
+ if (/assign|assignment|assignment_statement|assignment_expression/i.test(
1664
+ n.type
1665
+ )) {
1666
+ metadata.isPure = false;
1667
+ metadata.hasSideEffects = true;
1668
+ }
1669
+ const text = n.text;
1670
+ for (const sig of signatures) {
1671
+ if (text.includes(sig)) {
1409
1672
  metadata.isPure = false;
1410
1673
  metadata.hasSideEffects = true;
1674
+ break;
1411
1675
  }
1412
- for (const child of n.children) {
1413
- walk(child);
1414
- }
1415
- };
1416
- const body = node.children.find(
1417
- (c) => c.type === "block" || c.type === "class_body"
1418
- );
1419
- if (body) walk(body);
1420
- return metadata;
1421
- }
1422
- parse(code, filePath) {
1423
- if (!this.initialized || !this.parser) {
1424
- return this.parseRegex(code, filePath);
1425
1676
  }
1426
- try {
1427
- const tree = this.parser.parse(code);
1428
- if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
1429
- return this.parseRegex(code, filePath);
1677
+ if (!metadata.hasSideEffects) {
1678
+ for (let i = 0; i < n.childCount; i++) {
1679
+ const child = n.child(i);
1680
+ if (child) walk(child);
1430
1681
  }
1431
- const rootNode = tree.rootNode;
1432
- const imports = this.extractImportsAST(rootNode);
1433
- const exports = this.extractExportsAST(rootNode, code);
1434
- return {
1435
- exports,
1436
- imports,
1437
- language: "java" /* Java */,
1438
- warnings: []
1439
- };
1440
- } catch (error) {
1441
- console.warn(
1442
- `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
1443
- );
1444
- return this.parseRegex(code, filePath);
1445
1682
  }
1683
+ };
1684
+ walk(node);
1685
+ return metadata;
1686
+ }
1687
+ function extractParameterNames(node) {
1688
+ const params = [];
1689
+ const candidates = [
1690
+ // common field name
1691
+ node.childForFieldName ? node.childForFieldName("parameters") : null,
1692
+ node.childForFieldName ? node.childForFieldName("parameter_list") : null,
1693
+ node.children.find((c) => c.type === "parameter_list") || null,
1694
+ node.children.find((c) => c.type === "parameters") || null,
1695
+ node.children.find((c) => c.type === "formal_parameters") || null,
1696
+ node.children.find((c) => c.type === "formal_parameter") || null
1697
+ ];
1698
+ const list = candidates.find(Boolean);
1699
+ if (!list) return params;
1700
+ for (const child of list.children) {
1701
+ if (!child) continue;
1702
+ const id = child.childForFieldName?.("name") || child.children.find(
1703
+ (c) => [
1704
+ "identifier",
1705
+ "variable_name",
1706
+ "name",
1707
+ "parameter",
1708
+ "formal_parameter"
1709
+ ].includes(c.type)
1710
+ ) || (child.type === "identifier" ? child : void 0);
1711
+ if (id && typeof id.text === "string") params.push(id.text);
1712
+ }
1713
+ return params;
1714
+ }
1715
+
1716
+ // src/parsers/java-parser.ts
1717
+ var JavaParser = class extends BaseLanguageParser {
1718
+ constructor() {
1719
+ super(...arguments);
1720
+ this.language = "java" /* Java */;
1721
+ this.extensions = [".java"];
1722
+ }
1723
+ getParserName() {
1724
+ return "java";
1725
+ }
1726
+ analyzeMetadata(node, code) {
1727
+ return analyzeGeneralMetadata(node, code, {
1728
+ sideEffectSignatures: [
1729
+ "System.out",
1730
+ "System.err",
1731
+ "Files.write",
1732
+ "Logging."
1733
+ ]
1734
+ });
1446
1735
  }
1447
1736
  parseRegex(code, filePath) {
1448
1737
  const lines = code.split("\n");
@@ -1517,7 +1806,7 @@ var JavaParser = class {
1517
1806
  const imports = [];
1518
1807
  for (const node of rootNode.children) {
1519
1808
  if (node.type === "import_declaration") {
1520
- let sourceArr = [];
1809
+ const sourceArr = [];
1521
1810
  let isStatic = false;
1522
1811
  let isWildcard = false;
1523
1812
  for (const child of node.children) {
@@ -1615,14 +1904,7 @@ var JavaParser = class {
1615
1904
  }
1616
1905
  }
1617
1906
  extractParameters(node) {
1618
- const paramsNode = node.children.find(
1619
- (c) => c.type === "formal_parameters"
1620
- );
1621
- if (!paramsNode) return [];
1622
- return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
1623
- const idNode = c.children.find((child) => child.type === "identifier");
1624
- return idNode ? idNode.text : "unknown";
1625
- });
1907
+ return extractParameterNames(node);
1626
1908
  }
1627
1909
  getNamingConventions() {
1628
1910
  return {
@@ -1639,91 +1921,19 @@ var JavaParser = class {
1639
1921
  };
1640
1922
 
1641
1923
  // src/parsers/csharp-parser.ts
1642
- var CSharpParser = class {
1924
+ var CSharpParser = class extends BaseLanguageParser {
1643
1925
  constructor() {
1926
+ super(...arguments);
1644
1927
  this.language = "csharp" /* CSharp */;
1645
1928
  this.extensions = [".cs"];
1646
- this.parser = null;
1647
- this.initialized = false;
1648
1929
  }
1649
- /**
1650
- * Initialize the tree-sitter parser
1651
- */
1652
- async initialize() {
1653
- if (this.initialized) return;
1654
- this.parser = await setupParser("c_sharp");
1655
- this.initialized = true;
1656
- }
1657
- async getAST(code, filePath) {
1658
- if (!this.initialized) await this.initialize();
1659
- if (!this.parser) return null;
1660
- return this.parser.parse(code);
1930
+ getParserName() {
1931
+ return "c_sharp";
1661
1932
  }
1662
1933
  analyzeMetadata(node, code) {
1663
- const metadata = {
1664
- isPure: true,
1665
- hasSideEffects: false
1666
- };
1667
- let prev = node.previousSibling;
1668
- while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
1669
- if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
1670
- metadata.documentation = {
1671
- content: prev.text.replace("///", "").trim(),
1672
- type: "xml-doc"
1673
- };
1674
- break;
1675
- }
1676
- prev = prev.previousSibling;
1677
- }
1678
- const walk = (n) => {
1679
- if (n.type === "assignment_expression") {
1680
- metadata.isPure = false;
1681
- metadata.hasSideEffects = true;
1682
- }
1683
- if (n.type === "invocation_expression") {
1684
- const text = n.text;
1685
- if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
1686
- metadata.isPure = false;
1687
- metadata.hasSideEffects = true;
1688
- }
1689
- }
1690
- if (n.type === "throw_statement") {
1691
- metadata.isPure = false;
1692
- metadata.hasSideEffects = true;
1693
- }
1694
- for (let i = 0; i < n.childCount; i++) {
1695
- const child = n.child(i);
1696
- if (child) walk(child);
1697
- }
1698
- };
1699
- const body = node.children.find(
1700
- (c) => c.type === "block" || c.type === "declaration_list"
1701
- );
1702
- if (body) walk(body);
1703
- return metadata;
1704
- }
1705
- parse(code, filePath) {
1706
- if (!this.initialized || !this.parser) {
1707
- return this.parseRegex(code, filePath);
1708
- }
1709
- try {
1710
- const tree = this.parser.parse(code);
1711
- if (!tree) throw new Error("Parser.parse(code) returned null");
1712
- const rootNode = tree.rootNode;
1713
- const imports = this.extractImportsAST(rootNode);
1714
- const exports = this.extractExportsAST(rootNode, code);
1715
- return {
1716
- exports,
1717
- imports,
1718
- language: "csharp" /* CSharp */,
1719
- warnings: []
1720
- };
1721
- } catch (error) {
1722
- console.warn(
1723
- `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
1724
- );
1725
- return this.parseRegex(code, filePath);
1726
- }
1934
+ return analyzeGeneralMetadata(node, code, {
1935
+ sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
1936
+ });
1727
1937
  }
1728
1938
  parseRegex(code, filePath) {
1729
1939
  const lines = code.split("\n");
@@ -1905,19 +2115,7 @@ var CSharpParser = class {
1905
2115
  return modifiers;
1906
2116
  }
1907
2117
  extractParameters(node) {
1908
- const params = [];
1909
- const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
1910
- if (parameterList) {
1911
- for (const param of parameterList.children) {
1912
- if (param.type === "parameter") {
1913
- const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
1914
- if (nameNode) {
1915
- params.push(nameNode.text);
1916
- }
1917
- }
1918
- }
1919
- }
1920
- return params;
2118
+ return extractParameterNames(node);
1921
2119
  }
1922
2120
  getNamingConventions() {
1923
2121
  return {
@@ -1933,86 +2131,19 @@ var CSharpParser = class {
1933
2131
  };
1934
2132
 
1935
2133
  // src/parsers/go-parser.ts
1936
- var GoParser = class {
2134
+ var GoParser = class extends BaseLanguageParser {
1937
2135
  constructor() {
2136
+ super(...arguments);
1938
2137
  this.language = "go" /* Go */;
1939
2138
  this.extensions = [".go"];
1940
- this.parser = null;
1941
- this.initialized = false;
1942
2139
  }
1943
- /**
1944
- * Initialize the tree-sitter parser
1945
- */
1946
- async initialize() {
1947
- if (this.initialized) return;
1948
- this.parser = await setupParser("go");
1949
- this.initialized = true;
1950
- }
1951
- async getAST(code, filePath) {
1952
- if (!this.initialized) await this.initialize();
1953
- if (!this.parser) return null;
1954
- return this.parser.parse(code);
2140
+ getParserName() {
2141
+ return "go";
1955
2142
  }
1956
2143
  analyzeMetadata(node, code) {
1957
- const metadata = {
1958
- isPure: true,
1959
- hasSideEffects: false
1960
- };
1961
- let prev = node.previousSibling;
1962
- while (prev && prev.type === "comment") {
1963
- metadata.documentation = {
1964
- content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
1965
- type: "comment"
1966
- };
1967
- break;
1968
- }
1969
- const walk = (n) => {
1970
- if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
1971
- metadata.isPure = false;
1972
- metadata.hasSideEffects = true;
1973
- }
1974
- if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
1975
- }
1976
- if (n.type === "call_expression") {
1977
- const text = n.text;
1978
- if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
1979
- metadata.isPure = false;
1980
- metadata.hasSideEffects = true;
1981
- }
1982
- }
1983
- for (let i = 0; i < n.childCount; i++) {
1984
- const child = n.child(i);
1985
- if (child) walk(child);
1986
- }
1987
- };
1988
- const body = node.childForFieldName("body");
1989
- if (body) walk(body);
1990
- return metadata;
1991
- }
1992
- parse(code, filePath) {
1993
- if (!this.initialized || !this.parser) {
1994
- return this.parseRegex(code, filePath);
1995
- }
1996
- try {
1997
- const tree = this.parser.parse(code);
1998
- if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
1999
- return this.parseRegex(code, filePath);
2000
- }
2001
- const rootNode = tree.rootNode;
2002
- const imports = this.extractImportsAST(rootNode);
2003
- const exports = this.extractExportsAST(rootNode, code);
2004
- return {
2005
- exports,
2006
- imports,
2007
- language: "go" /* Go */,
2008
- warnings: []
2009
- };
2010
- } catch (error) {
2011
- console.warn(
2012
- `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
2013
- );
2014
- return this.parseRegex(code, filePath);
2015
- }
2144
+ return analyzeGeneralMetadata(node, code, {
2145
+ sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
2146
+ });
2016
2147
  }
2017
2148
  parseRegex(code, filePath) {
2018
2149
  const lines = code.split("\n");
@@ -2209,17 +2340,7 @@ var GoParser = class {
2209
2340
  return exports;
2210
2341
  }
2211
2342
  extractParameters(node) {
2212
- const params = [];
2213
- const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
2214
- if (parameterList) {
2215
- for (const param of parameterList.children) {
2216
- if (param.type === "parameter_declaration") {
2217
- const names = param.children.filter((c) => c.type === "identifier");
2218
- names.forEach((n) => params.push(n.text));
2219
- }
2220
- }
2221
- }
2222
- return params;
2343
+ return extractParameterNames(node);
2223
2344
  }
2224
2345
  getNamingConventions() {
2225
2346
  return {
@@ -2350,48 +2471,7 @@ function getSupportedLanguages() {
2350
2471
  return ParserFactory.getInstance().getSupportedLanguages();
2351
2472
  }
2352
2473
 
2353
- // src/utils/ast-parser.ts
2354
- function parseFileExports(code, filePath) {
2355
- const parser = getParser(filePath);
2356
- if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
2357
- try {
2358
- const result = parser.parse(code, filePath);
2359
- return {
2360
- exports: result.exports.map((e) => ({
2361
- name: e.name,
2362
- type: e.type,
2363
- imports: e.imports || [],
2364
- dependencies: e.dependencies || [],
2365
- typeReferences: e.typeReferences || [],
2366
- loc: e.loc ? {
2367
- start: { line: e.loc.start.line, column: e.loc.start.column },
2368
- end: { line: e.loc.end.line, column: e.loc.end.column }
2369
- } : void 0
2370
- })),
2371
- imports: result.imports.map((i) => ({
2372
- source: i.source,
2373
- specifiers: i.specifiers,
2374
- isTypeOnly: i.isTypeOnly || false
2375
- }))
2376
- };
2377
- } catch (e) {
2378
- return { exports: [], imports: [] };
2379
- }
2380
- }
2381
- try {
2382
- const ast = parse2(code, {
2383
- loc: true,
2384
- range: true,
2385
- jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
2386
- filePath
2387
- });
2388
- const imports = extractFileImports(ast);
2389
- const exports = extractExportsWithDependencies(ast, imports);
2390
- return { exports, imports };
2391
- } catch (error) {
2392
- return { exports: [], imports: [] };
2393
- }
2394
- }
2474
+ // src/utils/ast-visitor.ts
2395
2475
  function extractFileImports(ast) {
2396
2476
  const imports = [];
2397
2477
  for (const node of ast.body) {
@@ -2449,22 +2529,23 @@ function extractExportsWithDependencies(ast, fileImports) {
2449
2529
  }
2450
2530
  return exports;
2451
2531
  }
2452
- function extractFromDeclaration(declaration) {
2532
+ function extractFromDeclaration(node) {
2533
+ if (!node) return [];
2453
2534
  const results = [];
2454
- if (declaration.type === "FunctionDeclaration" && "id" in declaration && declaration.id) {
2455
- results.push({ name: declaration.id.name, type: "function" });
2456
- } else if (declaration.type === "ClassDeclaration" && "id" in declaration && declaration.id) {
2457
- results.push({ name: declaration.id.name, type: "class" });
2458
- } else if (declaration.type === "VariableDeclaration") {
2459
- for (const declarator of declaration.declarations) {
2460
- if (declarator.id.type === "Identifier") {
2461
- results.push({ name: declarator.id.name, type: "const" });
2535
+ if (node.type === "FunctionDeclaration" && node.id) {
2536
+ results.push({ name: node.id.name, type: "function" });
2537
+ } else if (node.type === "ClassDeclaration" && node.id) {
2538
+ results.push({ name: node.id.name, type: "class" });
2539
+ } else if (node.type === "VariableDeclaration") {
2540
+ for (const decl of node.declarations) {
2541
+ if (decl.id.type === "Identifier") {
2542
+ results.push({ name: decl.id.name, type: "const" });
2462
2543
  }
2463
2544
  }
2464
- } else if (declaration.type === "TSTypeAliasDeclaration") {
2465
- results.push({ name: declaration.id.name, type: "type" });
2466
- } else if (declaration.type === "TSInterfaceDeclaration") {
2467
- results.push({ name: declaration.id.name, type: "interface" });
2545
+ } else if (node.type === "TSInterfaceDeclaration" && node.id) {
2546
+ results.push({ name: node.id.name, type: "interface" });
2547
+ } else if (node.type === "TSTypeAliasDeclaration" && node.id) {
2548
+ results.push({ name: node.id.name, type: "type" });
2468
2549
  }
2469
2550
  return results;
2470
2551
  }
@@ -2492,16 +2573,6 @@ function findUsedImports(node, importedNames) {
2492
2573
  visit(node);
2493
2574
  return Array.from(usedImports);
2494
2575
  }
2495
- function calculateImportSimilarity(export1, export2) {
2496
- if (export1.imports.length === 0 && export2.imports.length === 0) {
2497
- return 1;
2498
- }
2499
- const set1 = new Set(export1.imports);
2500
- const set2 = new Set(export2.imports);
2501
- const intersection = new Set([...set1].filter((x) => set2.has(x)));
2502
- const union = /* @__PURE__ */ new Set([...set1, ...set2]);
2503
- return intersection.size / union.size;
2504
- }
2505
2576
  function extractTypeReferences(node) {
2506
2577
  const types = /* @__PURE__ */ new Set();
2507
2578
  function visit(n) {
@@ -2539,6 +2610,59 @@ function extractTypeReferences(node) {
2539
2610
  visit(node);
2540
2611
  return Array.from(types);
2541
2612
  }
2613
+
2614
+ // src/utils/ast-parser.ts
2615
+ function parseFileExports(code, filePath) {
2616
+ const parser = getParser(filePath);
2617
+ if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
2618
+ try {
2619
+ const result = parser.parse(code, filePath);
2620
+ return {
2621
+ exports: result.exports.map((e) => ({
2622
+ name: e.name,
2623
+ type: e.type,
2624
+ imports: e.imports || [],
2625
+ dependencies: e.dependencies || [],
2626
+ typeReferences: e.typeReferences || [],
2627
+ loc: e.loc ? {
2628
+ start: { line: e.loc.start.line, column: e.loc.start.column },
2629
+ end: { line: e.loc.end.line, column: e.loc.end.column }
2630
+ } : void 0
2631
+ })),
2632
+ imports: result.imports.map((i) => ({
2633
+ source: i.source,
2634
+ specifiers: i.specifiers,
2635
+ isTypeOnly: i.isTypeOnly || false
2636
+ }))
2637
+ };
2638
+ } catch (e) {
2639
+ return { exports: [], imports: [] };
2640
+ }
2641
+ }
2642
+ try {
2643
+ const ast = parse2(code, {
2644
+ loc: true,
2645
+ range: true,
2646
+ jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
2647
+ filePath
2648
+ });
2649
+ const imports = extractFileImports(ast);
2650
+ const exports = extractExportsWithDependencies(ast, imports);
2651
+ return { exports, imports };
2652
+ } catch (error) {
2653
+ return { exports: [], imports: [] };
2654
+ }
2655
+ }
2656
+ function calculateImportSimilarity(export1, export2) {
2657
+ if (export1.imports.length === 0 && export2.imports.length === 0) {
2658
+ return 1;
2659
+ }
2660
+ const set1 = new Set(export1.imports);
2661
+ const set2 = new Set(export2.imports);
2662
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
2663
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
2664
+ return intersection.size / union.size;
2665
+ }
2542
2666
  function parseCode(code, language) {
2543
2667
  return null;
2544
2668
  }
@@ -2569,34 +2693,47 @@ var CONFIG_FILES = [
2569
2693
  async function loadConfig(rootDir) {
2570
2694
  let currentDir = resolve(rootDir);
2571
2695
  while (true) {
2696
+ const foundConfigs = [];
2572
2697
  for (const configFile of CONFIG_FILES) {
2698
+ if (existsSync4(join4(currentDir, configFile))) {
2699
+ foundConfigs.push(configFile);
2700
+ }
2701
+ }
2702
+ if (foundConfigs.length > 0) {
2703
+ if (foundConfigs.length > 1) {
2704
+ console.warn(
2705
+ `\u26A0\uFE0F Multiple configuration files found in ${currentDir}: ${foundConfigs.join(
2706
+ ", "
2707
+ )}. Using ${foundConfigs[0]}.`
2708
+ );
2709
+ } else {
2710
+ }
2711
+ const configFile = foundConfigs[0];
2573
2712
  const configPath = join4(currentDir, configFile);
2574
- if (existsSync4(configPath)) {
2713
+ try {
2714
+ let config;
2715
+ if (configFile.endsWith(".js")) {
2716
+ const fileUrl = pathToFileURL(configPath).href;
2717
+ const module = await import(`${fileUrl}?t=${Date.now()}`);
2718
+ config = module.default || module;
2719
+ } else {
2720
+ const content = readFileSync(configPath, "utf-8");
2721
+ config = JSON.parse(content);
2722
+ }
2723
+ if (typeof config !== "object" || config === null) {
2724
+ throw new Error("Config must be an object");
2725
+ }
2726
+ return config;
2727
+ } catch (error) {
2728
+ const errorMessage = error instanceof Error ? error.message : String(error);
2729
+ const configError = new Error(
2730
+ `Failed to load config from ${configPath}: ${errorMessage}`
2731
+ );
2575
2732
  try {
2576
- let config;
2577
- if (configFile.endsWith(".js")) {
2578
- const fileUrl = pathToFileURL(configPath).href;
2579
- const module = await import(`${fileUrl}?t=${Date.now()}`);
2580
- config = module.default || module;
2581
- } else {
2582
- const content = readFileSync(configPath, "utf-8");
2583
- config = JSON.parse(content);
2584
- }
2585
- if (typeof config !== "object" || config === null) {
2586
- throw new Error("Config must be an object");
2587
- }
2588
- return config;
2589
- } catch (error) {
2590
- const errorMessage = error instanceof Error ? error.message : String(error);
2591
- const e = new Error(
2592
- `Failed to load config from ${configPath}: ${errorMessage}`
2593
- );
2594
- try {
2595
- e.cause = error instanceof Error ? error : void 0;
2596
- } catch {
2597
- }
2598
- throw e;
2733
+ configError.cause = error instanceof Error ? error : void 0;
2734
+ } catch {
2599
2735
  }
2736
+ throw configError;
2600
2737
  }
2601
2738
  }
2602
2739
  const parent = dirname4(currentDir);
@@ -2609,28 +2746,28 @@ async function loadConfig(rootDir) {
2609
2746
  }
2610
2747
  function mergeConfigWithDefaults(userConfig, defaults) {
2611
2748
  if (!userConfig) return defaults;
2612
- const result = { ...defaults };
2749
+ const mergedConfig = { ...defaults };
2613
2750
  if (userConfig.scan) {
2614
- if (userConfig.scan.include) result.include = userConfig.scan.include;
2615
- if (userConfig.scan.exclude) result.exclude = userConfig.scan.exclude;
2751
+ if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
2752
+ if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
2616
2753
  }
2617
2754
  const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
2618
2755
  if (toolOverrides) {
2619
- if (!result.toolConfigs) result.toolConfigs = {};
2756
+ if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
2620
2757
  for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
2621
2758
  if (typeof toolConfig === "object" && toolConfig !== null) {
2622
- result[toolName] = { ...result[toolName], ...toolConfig };
2623
- result.toolConfigs[toolName] = {
2624
- ...result.toolConfigs[toolName],
2759
+ mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
2760
+ mergedConfig.toolConfigs[toolName] = {
2761
+ ...mergedConfig.toolConfigs[toolName],
2625
2762
  ...toolConfig
2626
2763
  };
2627
2764
  }
2628
2765
  }
2629
2766
  }
2630
2767
  if (userConfig.output) {
2631
- result.output = { ...result.output, ...userConfig.output };
2768
+ mergedConfig.output = { ...mergedConfig.output, ...userConfig.output };
2632
2769
  }
2633
- return result;
2770
+ return mergedConfig;
2634
2771
  }
2635
2772
 
2636
2773
  // src/business/pricing-models.ts
@@ -2843,7 +2980,10 @@ function predictAcceptanceRate(toolOutputs) {
2843
2980
  impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
2844
2981
  });
2845
2982
  }
2846
- const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
2983
+ const totalImpact = factors.reduce(
2984
+ (sum, f) => sum + f.impact / 100,
2985
+ 0
2986
+ );
2847
2987
  const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
2848
2988
  let confidence = 0.35;
2849
2989
  if (toolOutputs.size >= 4) confidence = 0.75;
@@ -3086,6 +3226,24 @@ function collectFutureProofRecommendations(params) {
3086
3226
  }
3087
3227
  return recommendations;
3088
3228
  }
3229
+ function collectBaseFutureProofRecommendations(params) {
3230
+ const recommendations = [];
3231
+ for (const rec of params.patternEntropy.recommendations) {
3232
+ recommendations.push({
3233
+ action: rec,
3234
+ estimatedImpact: 5,
3235
+ priority: "medium"
3236
+ });
3237
+ }
3238
+ if (params.conceptCohesion.rating === "poor") {
3239
+ recommendations.push({
3240
+ action: "Improve concept cohesion by grouping related exports",
3241
+ estimatedImpact: 8,
3242
+ priority: "high"
3243
+ });
3244
+ }
3245
+ return recommendations;
3246
+ }
3089
3247
 
3090
3248
  // src/metrics/cognitive-load.ts
3091
3249
  function calculateCognitiveLoad(params) {
@@ -3776,21 +3934,10 @@ function calculateFutureProofScore(params) {
3776
3934
  description: params.conceptCohesion.rating
3777
3935
  }
3778
3936
  ];
3779
- const recommendations = [];
3780
- for (const rec of params.patternEntropy.recommendations) {
3781
- recommendations.push({
3782
- action: rec,
3783
- estimatedImpact: 5,
3784
- priority: "medium"
3785
- });
3786
- }
3787
- if (params.conceptCohesion.rating === "poor") {
3788
- recommendations.push({
3789
- action: "Improve concept cohesion by grouping related exports",
3790
- estimatedImpact: 8,
3791
- priority: "high"
3792
- });
3793
- }
3937
+ const recommendations = collectBaseFutureProofRecommendations({
3938
+ patternEntropy: params.patternEntropy,
3939
+ conceptCohesion: params.conceptCohesion
3940
+ });
3794
3941
  const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
3795
3942
  return {
3796
3943
  toolName: "future-proof",
@@ -4122,6 +4269,8 @@ export {
4122
4269
  TypeScriptParser,
4123
4270
  UnifiedReportSchema,
4124
4271
  VAGUE_FILE_NAMES,
4272
+ buildSimpleProviderScore,
4273
+ buildSpokeOutput,
4125
4274
  calculateAgentGrounding,
4126
4275
  calculateAiSignalClarity,
4127
4276
  calculateBusinessROI,
@@ -4145,6 +4294,7 @@ export {
4145
4294
  calculateTestabilityIndex,
4146
4295
  calculateTokenBudget,
4147
4296
  clearHistory,
4297
+ createProvider,
4148
4298
  emitAnnotation,
4149
4299
  emitIssuesAsAnnotations,
4150
4300
  emitProgress,
@@ -4153,6 +4303,8 @@ export {
4153
4303
  exportHistory,
4154
4304
  extractFunctions,
4155
4305
  extractImports,
4306
+ findLatestReport,
4307
+ findLatestScanReport,
4156
4308
  formatAcceptanceRate,
4157
4309
  formatCost,
4158
4310
  formatHours,
@@ -4170,15 +4322,21 @@ export {
4170
4322
  getProjectSizeTier,
4171
4323
  getRating,
4172
4324
  getRatingDisplay,
4325
+ getRatingSlug,
4173
4326
  getRatingWithContext,
4174
4327
  getRecommendedThreshold,
4175
4328
  getRepoMetadata,
4176
4329
  getSafetyIcon,
4177
4330
  getScoreBar,
4331
+ getSeverityBadge,
4178
4332
  getSeverityColor,
4333
+ getSeverityEnum,
4334
+ getSeverityLevel,
4335
+ getSeverityValue,
4179
4336
  getSupportedLanguages,
4180
4337
  getToolWeight,
4181
4338
  getWasmPath,
4339
+ groupIssuesByFile,
4182
4340
  handleCLIError,
4183
4341
  handleJSONOutput,
4184
4342
  initTreeSitter,