@aiready/core 0.23.19 → 0.23.21

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 (96) hide show
  1. package/dist/chunk-2N7ISIKE.mjs +158 -0
  2. package/dist/chunk-ARUIZO7M.mjs +297 -0
  3. package/dist/chunk-CYC5EGEI.mjs +297 -0
  4. package/dist/{chunk-ZB3EHHAG.mjs → chunk-DBOPSRBC.mjs} +88 -26
  5. package/dist/chunk-EZ7ECLAZ.mjs +299 -0
  6. package/dist/chunk-FNPULWG7.mjs +248 -0
  7. package/dist/chunk-FZTFKZUQ.mjs +250 -0
  8. package/dist/chunk-GTS642BQ.mjs +262 -0
  9. package/dist/chunk-IXPY5J4K.mjs +248 -0
  10. package/dist/chunk-JJQLYW6Z.mjs +111 -0
  11. package/dist/chunk-L6BKANJC.mjs +130 -0
  12. package/dist/chunk-LXEO5PG3.mjs +292 -0
  13. package/dist/chunk-LZHO636W.mjs +501 -0
  14. package/dist/chunk-MTK2IIDZ.mjs +262 -0
  15. package/dist/chunk-QDCQETSI.mjs +262 -0
  16. package/dist/chunk-QZNY7B2N.mjs +248 -0
  17. package/dist/chunk-RCZSMGCX.mjs +250 -0
  18. package/dist/chunk-SWZOT67M.mjs +250 -0
  19. package/dist/chunk-U3IY2CFC.mjs +36 -0
  20. package/dist/chunk-UBCM5Y6R.mjs +275 -0
  21. package/dist/chunk-UTCRW3N7.mjs +301 -0
  22. package/dist/{chunk-RMH2TPAT.mjs → chunk-UYLH35LA.mjs} +88 -26
  23. package/dist/{chunk-TJXR2CHZ.mjs → chunk-WVNVC2PP.mjs} +266 -213
  24. package/dist/chunk-WYOW6O3P.mjs +114 -0
  25. package/dist/{chunk-CGOS2J6T.mjs → chunk-YRSSR4KN.mjs} +260 -217
  26. package/dist/client-2xbeKnrg.d.mts +1291 -0
  27. package/dist/client-2xbeKnrg.d.ts +1291 -0
  28. package/dist/client-4HLAGzFg.d.mts +1291 -0
  29. package/dist/client-4HLAGzFg.d.ts +1291 -0
  30. package/dist/client-B4TQwNa7.d.mts +1290 -0
  31. package/dist/client-B4TQwNa7.d.ts +1290 -0
  32. package/dist/client-Bdi4ty0v.d.mts +1294 -0
  33. package/dist/client-Bdi4ty0v.d.ts +1294 -0
  34. package/dist/client-BsKpUH3H.d.mts +1339 -0
  35. package/dist/client-BsKpUH3H.d.ts +1339 -0
  36. package/dist/client-Bv1zOaWF.d.mts +1291 -0
  37. package/dist/client-Bv1zOaWF.d.ts +1291 -0
  38. package/dist/client-Bz9YJMIX.d.mts +1290 -0
  39. package/dist/client-Bz9YJMIX.d.ts +1290 -0
  40. package/dist/client-CBpzm34X.d.mts +1291 -0
  41. package/dist/client-CBpzm34X.d.ts +1291 -0
  42. package/dist/client-CNu_tCZZ.d.mts +1305 -0
  43. package/dist/client-CNu_tCZZ.d.ts +1305 -0
  44. package/dist/client-CmEvxxQu.d.mts +1339 -0
  45. package/dist/client-CmEvxxQu.d.ts +1339 -0
  46. package/dist/client-DGMAxkZc.d.mts +1339 -0
  47. package/dist/client-DGMAxkZc.d.ts +1339 -0
  48. package/dist/client-DZq-CqcD.d.mts +1292 -0
  49. package/dist/client-DZq-CqcD.d.ts +1292 -0
  50. package/dist/{client-WVCAIWdJ.d.mts → client-DcqGfDTt.d.mts} +318 -226
  51. package/dist/{client-WVCAIWdJ.d.ts → client-DcqGfDTt.d.ts} +318 -226
  52. package/dist/{client-DLvFR2qA.d.mts → client-O8RvSRm0.d.mts} +89 -25
  53. package/dist/{client-DLvFR2qA.d.ts → client-O8RvSRm0.d.ts} +89 -25
  54. package/dist/client.d.mts +1 -1
  55. package/dist/client.d.ts +1 -1
  56. package/dist/client.js +53 -27
  57. package/dist/client.mjs +6 -6
  58. package/dist/csharp-parser-4ZKCSX5B.mjs +9 -0
  59. package/dist/csharp-parser-5HKICCRR.mjs +9 -0
  60. package/dist/csharp-parser-JCKXIAJW.mjs +9 -0
  61. package/dist/go-parser-J4KIH4RG.mjs +9 -0
  62. package/dist/go-parser-TKXL3DVH.mjs +9 -0
  63. package/dist/go-parser-XOM232XZ.mjs +9 -0
  64. package/dist/index.d.mts +332 -54
  65. package/dist/index.d.ts +332 -54
  66. package/dist/index.js +3930 -3064
  67. package/dist/index.mjs +933 -2036
  68. package/dist/java-parser-3KHXOXRQ.mjs +9 -0
  69. package/dist/java-parser-MASGS4WB.mjs +9 -0
  70. package/dist/java-parser-T5LXD63J.mjs +9 -0
  71. package/dist/python-parser-FNFK2473.mjs +8 -0
  72. package/dist/typescript-parser-2GGNRNB5.mjs +7 -0
  73. package/dist/typescript-parser-3ENJ6C7H.mjs +7 -0
  74. package/dist/typescript-parser-4GI7DPSW.mjs +7 -0
  75. package/dist/typescript-parser-4H3HUBO4.mjs +7 -0
  76. package/dist/typescript-parser-K63IVZMF.mjs +7 -0
  77. package/dist/typescript-parser-ZJKROMQG.mjs +7 -0
  78. package/package.json +1 -1
  79. package/dist/chunk-5SHLHMH7.mjs +0 -760
  80. package/dist/chunk-Q55AMEFV.mjs +0 -760
  81. package/dist/client-BEoUYNLp.d.mts +0 -1191
  82. package/dist/client-BEoUYNLp.d.ts +0 -1191
  83. package/dist/client-BrIMPk89.d.mts +0 -1214
  84. package/dist/client-BrIMPk89.d.ts +0 -1214
  85. package/dist/client-C5BuGX4F.d.mts +0 -1205
  86. package/dist/client-C5BuGX4F.d.ts +0 -1205
  87. package/dist/client-CKcjnPXt.d.mts +0 -1214
  88. package/dist/client-CKcjnPXt.d.ts +0 -1214
  89. package/dist/client-CLulBnie.d.mts +0 -1182
  90. package/dist/client-CLulBnie.d.ts +0 -1182
  91. package/dist/client-CQwvp8ep.d.mts +0 -1182
  92. package/dist/client-CQwvp8ep.d.ts +0 -1182
  93. package/dist/client-PFPdeo-z.d.mts +0 -1186
  94. package/dist/client-PFPdeo-z.d.ts +0 -1186
  95. package/dist/client-wk2fgk1q.d.mts +0 -1184
  96. package/dist/client-wk2fgk1q.d.ts +0 -1184
package/dist/index.mjs CHANGED
@@ -12,8 +12,6 @@ import {
12
12
  IssueSchema,
13
13
  IssueType,
14
14
  IssueTypeSchema,
15
- LANGUAGE_EXTENSIONS,
16
- Language,
17
15
  LeadSchema,
18
16
  LeadSource,
19
17
  LeadSourceSchema,
@@ -23,7 +21,6 @@ import {
23
21
  MetricsSchema,
24
22
  ModelTier,
25
23
  ModelTierSchema,
26
- ParseError,
27
24
  ReadinessRating,
28
25
  RecommendationPriority,
29
26
  SCORING_PROFILES,
@@ -41,16 +38,47 @@ import {
41
38
  formatScore,
42
39
  formatToolScore,
43
40
  generateHTML,
41
+ getPriorityIcon,
44
42
  getProjectSizeTier,
45
43
  getRating,
46
44
  getRatingDisplay,
45
+ getRatingEmoji,
46
+ getRatingLabel,
47
+ getRatingMetadata,
47
48
  getRatingSlug,
48
49
  getRatingWithContext,
49
50
  getRecommendedThreshold,
51
+ getToolEmoji,
50
52
  getToolWeight,
51
53
  normalizeToolName,
52
54
  parseWeightString
53
- } from "./chunk-RMH2TPAT.mjs";
55
+ } from "./chunk-WVNVC2PP.mjs";
56
+ import {
57
+ TypeScriptParser
58
+ } from "./chunk-UTCRW3N7.mjs";
59
+ import {
60
+ PythonParser
61
+ } from "./chunk-LZHO636W.mjs";
62
+ import {
63
+ JavaParser
64
+ } from "./chunk-SWZOT67M.mjs";
65
+ import {
66
+ CSharpParser
67
+ } from "./chunk-QZNY7B2N.mjs";
68
+ import {
69
+ GoParser
70
+ } from "./chunk-GTS642BQ.mjs";
71
+ import "./chunk-L6BKANJC.mjs";
72
+ import {
73
+ getWasmPath,
74
+ initTreeSitter,
75
+ setupParser
76
+ } from "./chunk-2N7ISIKE.mjs";
77
+ import {
78
+ LANGUAGE_EXTENSIONS,
79
+ Language,
80
+ ParseError
81
+ } from "./chunk-U3IY2CFC.mjs";
54
82
 
55
83
  // src/utils/normalization.ts
56
84
  function normalizeIssue(raw) {
@@ -504,6 +532,30 @@ import {
504
532
  } from "fs";
505
533
  import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
506
534
  import chalk from "chalk";
535
+ function ensureDir(path) {
536
+ const dir = dirname2(path);
537
+ if (!existsSync2(dir)) {
538
+ mkdirSync(dir, { recursive: true });
539
+ }
540
+ }
541
+ function normalizeSeverity(s) {
542
+ if (!s) return null;
543
+ const lower = s.toLowerCase();
544
+ if (["critical", "high-risk", "blind-risk"].includes(lower))
545
+ return "critical" /* Critical */;
546
+ if (["major", "moderate-risk"].includes(lower)) return "major" /* Major */;
547
+ if (["minor", "safe"].includes(lower)) return "minor" /* Minor */;
548
+ if (lower === "info") return "info" /* Info */;
549
+ return null;
550
+ }
551
+ function getFilesByPattern(dir, pattern) {
552
+ if (!existsSync2(dir)) return [];
553
+ try {
554
+ return readdirSync(dir).filter((f) => pattern.test(f));
555
+ } catch {
556
+ return [];
557
+ }
558
+ }
507
559
  function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
508
560
  let outputPath;
509
561
  if (userPath) {
@@ -519,10 +571,7 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
519
571
  const aireadyDir = join2(baseDir, ".aiready");
520
572
  outputPath = join2(aireadyDir, defaultFilename);
521
573
  }
522
- const parentDir = dirname2(outputPath);
523
- if (!existsSync2(parentDir)) {
524
- mkdirSync(parentDir, { recursive: true });
525
- }
574
+ ensureDir(outputPath);
526
575
  return outputPath;
527
576
  }
528
577
  async function loadMergedConfig(directory, defaults, cliOptions) {
@@ -537,16 +586,24 @@ async function loadMergedConfig(directory, defaults, cliOptions) {
537
586
  }
538
587
  function handleJSONOutput(data, outputFile, successMessage) {
539
588
  if (outputFile) {
540
- const dir = dirname2(outputFile);
541
- if (!existsSync2(dir)) {
542
- mkdirSync(dir, { recursive: true });
543
- }
589
+ ensureDir(outputFile);
544
590
  writeFileSync(outputFile, JSON.stringify(data, null, 2));
545
591
  console.log(successMessage || `\u2705 Results saved to ${outputFile}`);
546
592
  } else {
547
593
  console.log(JSON.stringify(data, null, 2));
548
594
  }
549
595
  }
596
+ function getTerminalDivider(color = chalk.cyan, maxWidth = 60) {
597
+ const terminalWidth = process.stdout.columns || 80;
598
+ const dividerWidth = Math.min(maxWidth, terminalWidth - 2);
599
+ return color("\u2501".repeat(dividerWidth));
600
+ }
601
+ function printTerminalHeader(title, color = chalk.cyan) {
602
+ const divider = getTerminalDivider(color);
603
+ console.log(divider);
604
+ console.log(chalk.bold.white(` ${title.toUpperCase()}`));
605
+ console.log(divider + "\n");
606
+ }
550
607
  function handleCLIError(error, commandName) {
551
608
  console.error(`\u274C ${commandName} failed:`, error);
552
609
  process.exit(1);
@@ -579,33 +636,30 @@ function emitProgress(processed, total, toolId, message, onProgress, throttleCou
579
636
  }
580
637
  }
581
638
  function getSeverityColor(severity, chalkInstance = chalk) {
582
- switch (severity.toLowerCase()) {
583
- case "critical":
584
- case "high-risk":
585
- case "blind-risk":
639
+ const normalized = normalizeSeverity(severity);
640
+ switch (normalized) {
641
+ case "critical" /* Critical */:
586
642
  return chalkInstance.red;
587
- case "major":
588
- case "moderate-risk":
643
+ case "major" /* Major */:
589
644
  return chalkInstance.yellow;
590
- case "minor":
591
- case "safe":
645
+ case "minor" /* Minor */:
592
646
  return chalkInstance.green;
593
- case "info":
647
+ case "info" /* Info */:
594
648
  return chalkInstance.blue;
595
649
  default:
596
650
  return chalkInstance.white;
597
651
  }
598
652
  }
599
653
  function getSeverityValue(s) {
600
- if (!s) return 0;
601
- switch (s.toLowerCase()) {
602
- case "critical":
654
+ const normalized = normalizeSeverity(s);
655
+ switch (normalized) {
656
+ case "critical" /* Critical */:
603
657
  return 4;
604
- case "major":
658
+ case "major" /* Major */:
605
659
  return 3;
606
- case "minor":
660
+ case "minor" /* Minor */:
607
661
  return 2;
608
- case "info":
662
+ case "info" /* Info */:
609
663
  return 1;
610
664
  default:
611
665
  return 0;
@@ -640,24 +694,15 @@ function getSeverityEnum(s) {
640
694
  return "major";
641
695
  case 2:
642
696
  return "minor";
643
- case 1:
644
- return "info";
645
697
  default:
646
698
  return "info";
647
699
  }
648
700
  }
649
701
  function findLatestReport(dirPath) {
650
702
  const aireadyDir = resolvePath(dirPath, ".aiready");
651
- if (!existsSync2(aireadyDir)) {
652
- return null;
653
- }
654
- let files = readdirSync(aireadyDir).filter(
655
- (f) => f.startsWith("aiready-report-") && f.endsWith(".json")
656
- );
703
+ let files = getFilesByPattern(aireadyDir, /^aiready-report-.*\.json$/);
657
704
  if (files.length === 0) {
658
- files = readdirSync(aireadyDir).filter(
659
- (f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
660
- );
705
+ files = getFilesByPattern(aireadyDir, /^aiready-scan-.*\.json$/);
661
706
  }
662
707
  if (files.length === 0) {
663
708
  return null;
@@ -671,14 +716,8 @@ function findLatestReport(dirPath) {
671
716
  }
672
717
  function findLatestScanReport(scanReportsDir, reportFilePrefix) {
673
718
  try {
674
- let reportFiles = [];
675
- if (existsSync2(scanReportsDir)) {
676
- const files = readdirSync(scanReportsDir);
677
- if (files.length > 0) {
678
- const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
679
- reportFiles = files.filter((file) => prefixRegex.test(file));
680
- }
681
- }
719
+ const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
720
+ const reportFiles = getFilesByPattern(scanReportsDir, prefixRegex);
682
721
  if (reportFiles.length === 0) return null;
683
722
  reportFiles.sort((a, b) => {
684
723
  const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
@@ -692,6 +731,53 @@ function findLatestScanReport(scanReportsDir, reportFilePrefix) {
692
731
  }
693
732
  }
694
733
 
734
+ // src/utils/cli-action-helpers.ts
735
+ import { resolve as resolvePath2 } from "path";
736
+ function getReportTimestamp() {
737
+ const now = /* @__PURE__ */ new Date();
738
+ const pad = (n) => String(n).padStart(2, "0");
739
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
740
+ }
741
+ function handleStandardJSONOutput({
742
+ outputData,
743
+ outputFile,
744
+ resolvedDir,
745
+ prefix = "aiready-report"
746
+ }) {
747
+ const outputPath = resolveOutputPath(
748
+ outputFile,
749
+ `${prefix}-${getReportTimestamp()}.json`,
750
+ resolvedDir
751
+ );
752
+ handleJSONOutput(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
753
+ }
754
+ function resolveOutputFormat(options, config) {
755
+ const format = options.output ?? config.output?.format ?? "console";
756
+ const file = options.outputFile ?? config.output?.file;
757
+ return { format, file };
758
+ }
759
+ async function prepareActionConfig(directory, defaults, cliOptions) {
760
+ const resolvedDir = resolvePath2(process.cwd(), directory ?? ".");
761
+ const finalOptions = await loadMergedConfig(
762
+ resolvedDir,
763
+ defaults,
764
+ cliOptions
765
+ );
766
+ return { resolvedDir, finalOptions };
767
+ }
768
+ function formatStandardReport(params) {
769
+ const { results, report, summary, elapsedTime, score } = params;
770
+ const baseData = report ? { ...report } : { results, summary };
771
+ return {
772
+ ...baseData,
773
+ summary: {
774
+ ...baseData.summary || summary,
775
+ executionTime: parseFloat(elapsedTime)
776
+ },
777
+ ...score && { scoring: score }
778
+ };
779
+ }
780
+
695
781
  // src/utils/provider-utils.ts
696
782
  function groupIssuesByFile(issues) {
697
783
  const fileIssuesMap = /* @__PURE__ */ new Map();
@@ -765,1828 +851,129 @@ function calculateImportSimilarity(export1, export2) {
765
851
  }
766
852
 
767
853
  // src/utils/dependency-analyzer.ts
768
- import { parse as parse2 } from "@typescript-eslint/typescript-estree";
769
-
770
- // src/parsers/typescript-parser.ts
771
854
  import { parse } from "@typescript-eslint/typescript-estree";
772
- var TypeScriptParser = class {
855
+
856
+ // src/parsers/parser-factory.ts
857
+ var ParserFactory = class _ParserFactory {
858
+ /**
859
+ * Create a new ParserFactory instance
860
+ */
773
861
  constructor() {
774
- this.language = "typescript" /* TypeScript */;
775
- this.extensions = [".ts", ".tsx", ".js", ".jsx"];
776
- }
777
- async initialize() {
778
- }
779
- canHandle(filePath) {
780
- return this.extensions.some((ext) => filePath.endsWith(ext));
781
- }
782
- async getAST(code, filePath) {
783
- try {
784
- return parse(code, {
785
- filePath,
786
- loc: true,
787
- range: true,
788
- tokens: true,
789
- comment: true,
790
- jsx: filePath.endsWith("x")
791
- });
792
- } catch (error) {
793
- throw new ParseError(error.message, filePath, {
794
- line: error.lineNumber || 1,
795
- column: error.column || 0
796
- });
797
- }
798
- }
799
- parse(code, filePath) {
800
- try {
801
- const ast = parse(code, {
802
- filePath,
803
- loc: true,
804
- range: true,
805
- tokens: true,
806
- comment: true,
807
- jsx: filePath.endsWith("x")
808
- });
809
- const imports = this.extractImports(ast);
810
- const exports = this.extractExports(ast, code);
811
- return {
812
- exports,
813
- imports,
814
- language: this.language
815
- };
816
- } catch (error) {
817
- throw new ParseError(error.message, filePath, {
818
- line: error.lineNumber || 1,
819
- column: error.column || 0
820
- });
821
- }
822
- }
823
- getNamingConventions() {
824
- return {
825
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
826
- functionPattern: /^[a-z][a-zA-Z0-9]*$/,
827
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
828
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
829
- typePattern: /^[A-Z][a-zA-Z0-9]*$/,
830
- interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
831
- };
832
- }
833
- analyzeMetadata(node, code) {
834
- if (!code) return {};
835
- return {
836
- isPure: this.isLikelyPure(node),
837
- hasSideEffects: !this.isLikelyPure(node)
838
- };
839
- }
840
- extractImports(ast) {
841
- const imports = [];
842
- for (const node of ast.body) {
843
- if (node.type === "ImportDeclaration") {
844
- const specifiers = [];
845
- let isTypeOnly = false;
846
- if (node.importKind === "type") {
847
- isTypeOnly = true;
848
- }
849
- for (const spec of node.specifiers) {
850
- if (spec.type === "ImportSpecifier") {
851
- const imported = spec.imported;
852
- const name = imported.type === "Identifier" ? imported.name : imported.value;
853
- specifiers.push(name);
854
- } else if (spec.type === "ImportDefaultSpecifier") {
855
- specifiers.push("default");
856
- } else if (spec.type === "ImportNamespaceSpecifier") {
857
- specifiers.push("*");
858
- }
859
- }
860
- imports.push({
861
- source: node.source.value,
862
- specifiers,
863
- isTypeOnly,
864
- loc: node.loc ? {
865
- start: {
866
- line: node.loc.start.line,
867
- column: node.loc.start.column
868
- },
869
- end: { line: node.loc.end.line, column: node.loc.end.column }
870
- } : void 0
871
- });
872
- }
873
- }
874
- return imports;
875
- }
876
- extractExports(ast, code) {
877
- const exports = [];
878
- for (const node of ast.body) {
879
- if (node.type === "ExportNamedDeclaration") {
880
- if (node.declaration) {
881
- const declaration = node.declaration;
882
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
883
- exports.push(
884
- this.createExport(
885
- declaration.id.name,
886
- "function",
887
- node,
888
- // Pass the outer ExportNamedDeclaration
889
- code
890
- )
891
- );
892
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
893
- exports.push(
894
- this.createExport(
895
- declaration.id.name,
896
- "class",
897
- node,
898
- // Pass the outer ExportNamedDeclaration
899
- code
900
- )
901
- );
902
- } else if (declaration.type === "TSTypeAliasDeclaration") {
903
- exports.push(
904
- this.createExport(
905
- declaration.id.name,
906
- "type",
907
- node,
908
- // Pass the outer ExportNamedDeclaration
909
- code
910
- )
911
- );
912
- } else if (declaration.type === "TSInterfaceDeclaration") {
913
- exports.push(
914
- this.createExport(
915
- declaration.id.name,
916
- "interface",
917
- node,
918
- // Pass the outer ExportNamedDeclaration
919
- code
920
- )
921
- );
922
- } else if (declaration.type === "VariableDeclaration") {
923
- for (const decl of declaration.declarations) {
924
- if (decl.id.type === "Identifier") {
925
- exports.push(
926
- this.createExport(
927
- decl.id.name,
928
- "const",
929
- node,
930
- // Pass the outer ExportNamedDeclaration
931
- code,
932
- decl.init
933
- )
934
- );
935
- }
936
- }
937
- }
938
- }
939
- } else if (node.type === "ExportDefaultDeclaration") {
940
- exports.push(this.createExport("default", "default", node, code));
941
- }
942
- }
943
- return exports;
944
- }
945
- createExport(name, type, node, code, initializer) {
946
- const documentation = this.extractDocumentation(node, code);
947
- let methodCount;
948
- let propertyCount;
949
- let parameters;
950
- let isPrimitive = false;
951
- if (initializer) {
952
- if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
953
- isPrimitive = true;
954
- }
955
- }
956
- const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
957
- if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
958
- const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
959
- methodCount = body.filter(
960
- (m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
961
- ).length;
962
- propertyCount = body.filter(
963
- (m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
964
- ).length;
965
- if (structNode.type === "ClassDeclaration") {
966
- const constructor = body.find(
967
- (m) => m.type === "MethodDefinition" && m.kind === "constructor"
968
- );
969
- if (constructor && constructor.value && constructor.value.params) {
970
- parameters = constructor.value.params.map((p) => {
971
- if (p.type === "Identifier") return p.name;
972
- if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
973
- return p.parameter.name;
974
- }
975
- return void 0;
976
- }).filter(Boolean);
977
- }
978
- }
979
- }
980
- if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
981
- const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
982
- if (funcNode && funcNode.params) {
983
- parameters = funcNode.params.map((p) => {
984
- if (p.type === "Identifier") return p.name;
985
- return void 0;
986
- }).filter(Boolean);
987
- }
988
- }
989
- return {
990
- name,
991
- type,
992
- isPrimitive,
993
- loc: node.loc ? {
994
- start: { line: node.loc.start.line, column: node.loc.start.column },
995
- end: { line: node.loc.end.line, column: node.loc.end.column }
996
- } : void 0,
997
- documentation,
998
- methodCount,
999
- propertyCount,
1000
- parameters,
1001
- isPure: this.isLikelyPure(node),
1002
- hasSideEffects: !this.isLikelyPure(node)
1003
- };
1004
- }
1005
- extractDocumentation(node, code) {
1006
- if (node.range) {
1007
- const start = node.range[0];
1008
- const precedingCode = code.substring(0, start);
1009
- const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1010
- if (jsdocMatch) {
1011
- return {
1012
- content: jsdocMatch[1].trim(),
1013
- type: "jsdoc"
1014
- };
1015
- }
1016
- }
1017
- return void 0;
862
+ this.parsers = /* @__PURE__ */ new Map();
863
+ this.registeredParsers = /* @__PURE__ */ new Map();
864
+ this.extensionMap = new Map(
865
+ Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
866
+ );
867
+ this.registerLazyParser("typescript" /* TypeScript */, async () => {
868
+ const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-ZJKROMQG.mjs");
869
+ return new TypeScriptParser2();
870
+ });
871
+ this.registerLazyParser("javascript" /* JavaScript */, async () => {
872
+ const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-ZJKROMQG.mjs");
873
+ return new TypeScriptParser2();
874
+ });
875
+ this.registerLazyParser("python" /* Python */, async () => {
876
+ const { PythonParser: PythonParser2 } = await import("./python-parser-FNFK2473.mjs");
877
+ return new PythonParser2();
878
+ });
879
+ this.registerLazyParser("java" /* Java */, async () => {
880
+ const { JavaParser: JavaParser2 } = await import("./java-parser-3KHXOXRQ.mjs");
881
+ return new JavaParser2();
882
+ });
883
+ this.registerLazyParser("csharp" /* CSharp */, async () => {
884
+ const { CSharpParser: CSharpParser2 } = await import("./csharp-parser-5HKICCRR.mjs");
885
+ return new CSharpParser2();
886
+ });
887
+ this.registerLazyParser("go" /* Go */, async () => {
888
+ const { GoParser: GoParser2 } = await import("./go-parser-XOM232XZ.mjs");
889
+ return new GoParser2();
890
+ });
1018
891
  }
1019
- isLikelyPure(node) {
1020
- const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1021
- if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
1022
- return true;
1023
- if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
1024
- const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
1025
- if (body && body.type === "BlockStatement") {
1026
- const bodyContent = JSON.stringify(body);
1027
- if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
1028
- return false;
1029
- }
1030
- return true;
1031
- }
1032
- return true;
1033
- }
1034
- return false;
892
+ /**
893
+ * Register a lazy-loaded parser
894
+ */
895
+ registerLazyParser(language, loader) {
896
+ this.registeredParsers.set(language, loader);
1035
897
  }
1036
- };
1037
-
1038
- // src/parsers/metadata-utils.ts
1039
- function analyzeNodeMetadata(node, code, options) {
1040
- const metadata = {
1041
- isPure: true,
1042
- hasSideEffects: false
1043
- };
1044
- try {
1045
- let prev = node.previousSibling || null;
1046
- while (prev && /comment/i.test(prev.type)) {
1047
- const text = prev.text || "";
1048
- const loc = {
1049
- start: {
1050
- line: prev.startPosition.row + 1,
1051
- column: prev.startPosition.column
1052
- },
1053
- end: {
1054
- line: prev.endPosition.row + 1,
1055
- column: prev.endPosition.column
1056
- }
1057
- };
1058
- if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
1059
- metadata.documentation = {
1060
- content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1061
- type: "comment",
1062
- loc
1063
- };
1064
- break;
1065
- }
1066
- if (text.trim().startsWith("///")) {
1067
- metadata.documentation = {
1068
- content: text.replace(/^\/\/\//, "").trim(),
1069
- type: "xml-doc",
1070
- loc
1071
- };
1072
- break;
1073
- }
1074
- if (text.trim().startsWith("//")) {
1075
- metadata.documentation = {
1076
- content: text.replace(/^\/\//, "").trim(),
1077
- type: "comment",
1078
- loc
1079
- };
1080
- break;
1081
- }
1082
- prev = prev.previousSibling;
1083
- }
1084
- if (node.type === "function_definition" || node.type === "class_definition") {
1085
- const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
1086
- if (body2 && body2.children.length > 0) {
1087
- const firstStmt = body2.children[0];
1088
- if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
1089
- metadata.documentation = {
1090
- content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
1091
- type: "docstring",
1092
- loc: {
1093
- start: {
1094
- line: firstStmt.startPosition.row + 1,
1095
- column: firstStmt.startPosition.column
1096
- },
1097
- end: {
1098
- line: firstStmt.endPosition.row + 1,
1099
- column: firstStmt.endPosition.column
1100
- }
1101
- }
1102
- };
1103
- }
1104
- }
898
+ /**
899
+ * Get the global singleton instance
900
+ *
901
+ * @returns The singleton ParserFactory instance
902
+ */
903
+ static getInstance() {
904
+ if (!_ParserFactory.instance) {
905
+ _ParserFactory.instance = new _ParserFactory();
1105
906
  }
1106
- } catch {
907
+ return _ParserFactory.instance;
1107
908
  }
1108
- const defaultSignatures = [
1109
- "console.",
1110
- "fmt.",
1111
- "panic(",
1112
- "os.Exit",
1113
- "log.",
1114
- "Console.Write",
1115
- "File.Write",
1116
- "System.out",
1117
- "System.err",
1118
- "Files.write",
1119
- "process.exit",
1120
- "exit("
1121
- ];
1122
- const signatures = Array.from(
1123
- /* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
1124
- );
1125
- const walk = (n) => {
1126
- try {
1127
- const t = n.type || "";
1128
- if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
1129
- t
1130
- )) {
1131
- metadata.isPure = false;
1132
- metadata.hasSideEffects = true;
1133
- }
1134
- const text = n.text || "";
1135
- for (const s of signatures) {
1136
- if (text.includes(s)) {
1137
- metadata.isPure = false;
1138
- metadata.hasSideEffects = true;
1139
- break;
1140
- }
1141
- }
1142
- for (let i = 0; i < n.childCount; i++) {
1143
- const c = n.child(i);
1144
- if (c) walk(c);
1145
- }
1146
- } catch {
1147
- }
1148
- };
1149
- const body = node.childForFieldName?.("body") || node.children.find(
1150
- (c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
1151
- );
1152
- if (body) walk(body);
1153
- return metadata;
1154
- }
1155
-
1156
- // src/parsers/tree-sitter-utils.ts
1157
- import * as Parser from "web-tree-sitter";
1158
- import * as path from "path";
1159
- import * as fs from "fs";
1160
- var isTreeSitterInitialized = false;
1161
- async function initTreeSitter() {
1162
- if (isTreeSitterInitialized) return;
1163
- try {
1164
- const wasmPath = getWasmPath("web-tree-sitter");
1165
- await Parser.Parser.init({
1166
- locateFile() {
1167
- return wasmPath || "web-tree-sitter.wasm";
1168
- }
909
+ /**
910
+ * Register a language parser
911
+ */
912
+ registerParser(parser) {
913
+ this.parsers.set(parser.language, parser);
914
+ parser.extensions.forEach((ext) => {
915
+ const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
916
+ this.extensionMap.set(ext, lang);
917
+ this.parsers.set(lang, parser);
1169
918
  });
1170
- isTreeSitterInitialized = true;
1171
- } catch (error) {
1172
- console.error("Failed to initialize web-tree-sitter:", error);
1173
- isTreeSitterInitialized = true;
1174
- }
1175
- }
1176
- function findInPnpmStore(startDir, fileName, depth = 0) {
1177
- if (depth > 8) return null;
1178
- const pnpmDir = path.join(startDir, "node_modules", ".pnpm");
1179
- if (fs.existsSync(pnpmDir)) {
1180
- return findFileRecursively(pnpmDir, fileName, 0);
1181
919
  }
1182
- const parent = path.dirname(startDir);
1183
- if (parent === startDir) return null;
1184
- return findInPnpmStore(parent, fileName, depth + 1);
1185
- }
1186
- function findFileRecursively(dir, fileName, depth) {
1187
- if (depth > 6) return null;
1188
- try {
1189
- const entries = fs.readdirSync(dir, { withFileTypes: true });
1190
- for (const entry of entries) {
1191
- if (entry.isFile() && entry.name === fileName) {
1192
- return path.join(dir, entry.name);
1193
- }
1194
- }
1195
- for (const entry of entries) {
1196
- if (entry.isDirectory()) {
1197
- const found = findFileRecursively(
1198
- path.join(dir, entry.name),
1199
- fileName,
1200
- depth + 1
1201
- );
1202
- if (found) return found;
1203
- }
920
+ /**
921
+ * Get parser for a specific language
922
+ */
923
+ async getParserForLanguage(language) {
924
+ const parser = this.parsers.get(language);
925
+ if (parser) return parser;
926
+ const loader = this.registeredParsers.get(language);
927
+ if (loader) {
928
+ const loadedParser = await loader();
929
+ this.parsers.set(language, loadedParser);
930
+ return loadedParser;
1204
931
  }
1205
- } catch {
1206
- }
1207
- return null;
1208
- }
1209
- function getWasmPath(language) {
1210
- const wasmFileName = language === "web-tree-sitter" ? "web-tree-sitter.wasm" : `tree-sitter-${language}.wasm`;
1211
- const immediatePaths = [
1212
- path.join(process.cwd(), wasmFileName),
1213
- path.join(__dirname, wasmFileName),
1214
- path.join(__dirname, "assets", wasmFileName)
1215
- ];
1216
- for (const p of immediatePaths) {
1217
- if (fs.existsSync(p)) return p;
1218
- }
1219
- const pnpmPath = findInPnpmStore(__dirname, wasmFileName);
1220
- if (pnpmPath) return pnpmPath;
1221
- const pnpmPathCwd = findInPnpmStore(process.cwd(), wasmFileName);
1222
- if (pnpmPathCwd) return pnpmPathCwd;
1223
- console.warn(
1224
- `[Parser] WASM file for ${language} not found. CWD: ${process.cwd()}, DIR: ${__dirname}`
1225
- );
1226
- return null;
1227
- }
1228
- async function setupParser(language) {
1229
- await initTreeSitter();
1230
- const wasmPath = getWasmPath(language);
1231
- if (!wasmPath) {
1232
- return null;
1233
- }
1234
- try {
1235
- const parser = new Parser.Parser();
1236
- const Lang = await Parser.Language.load(wasmPath);
1237
- parser.setLanguage(Lang);
1238
- return parser;
1239
- } catch {
1240
932
  return null;
1241
933
  }
1242
- }
1243
-
1244
- // src/parsers/base-parser.ts
1245
- var BaseLanguageParser = class {
1246
- constructor() {
1247
- this.parser = null;
1248
- this.initialized = false;
1249
- }
1250
934
  /**
1251
- * Initialize the tree-sitter parser
935
+ * Get parser for a file based on its extension
1252
936
  */
1253
- async initialize() {
1254
- if (this.initialized) return;
1255
- try {
1256
- this.parser = await setupParser(this.getParserName());
1257
- this.initialized = true;
1258
- } catch (error) {
1259
- console.warn(`Failed to initialize ${this.language} parser:`, error);
1260
- }
1261
- }
1262
- async getAST(code, _filePath) {
1263
- void _filePath;
1264
- if (!this.initialized) await this.initialize();
1265
- if (!this.parser) return null;
1266
- return this.parser.parse(code);
1267
- }
1268
- parse(code, filePath) {
1269
- if (!this.initialized || !this.parser) {
1270
- return this.parseRegex(code, filePath);
1271
- }
1272
- try {
1273
- const tree = this.parser.parse(code);
1274
- if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
1275
- return this.parseRegex(code, filePath);
1276
- }
1277
- const imports = this.extractImportsAST(tree.rootNode);
1278
- const exports = this.extractExportsAST(tree.rootNode, code);
1279
- return {
1280
- exports,
1281
- imports,
1282
- language: this.language,
1283
- warnings: []
1284
- };
1285
- } catch (error) {
1286
- console.warn(
1287
- `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
1288
- );
1289
- return this.parseRegex(code, filePath);
937
+ async getParserForFile(filePath) {
938
+ const ext = this.getFileExtension(filePath);
939
+ const language = this.extensionMap.get(ext);
940
+ if (!language) {
941
+ return null;
1290
942
  }
943
+ return this.getParserForLanguage(language);
1291
944
  }
1292
- canHandle(filePath) {
1293
- const lowerPath = filePath.toLowerCase();
1294
- return this.extensions.some((ext) => lowerPath.endsWith(ext));
945
+ /**
946
+ * Check if a file is supported (synchronous check based on extension)
947
+ */
948
+ isSupported(filePath) {
949
+ const ext = this.getFileExtension(filePath);
950
+ return this.extensionMap.has(ext);
1295
951
  }
1296
- };
1297
-
1298
- // src/parsers/python-parser.ts
1299
- var PythonParser = class extends BaseLanguageParser {
1300
- constructor() {
1301
- super(...arguments);
1302
- this.language = "python" /* Python */;
1303
- this.extensions = [".py"];
952
+ /**
953
+ * Get all registered languages
954
+ */
955
+ getSupportedLanguages() {
956
+ const languages = /* @__PURE__ */ new Set([
957
+ ...this.parsers.keys(),
958
+ ...this.registeredParsers.keys()
959
+ ]);
960
+ return Array.from(languages);
1304
961
  }
1305
- getParserName() {
1306
- return "python";
962
+ /**
963
+ * Get all supported file extensions
964
+ */
965
+ getSupportedExtensions() {
966
+ return Array.from(this.extensionMap.keys());
1307
967
  }
1308
968
  /**
1309
- * Analyze metadata for a Python node (purity, side effects).
1310
- *
1311
- * @param node - Tree-sitter node to analyze.
1312
- * @param code - Source code for context.
1313
- * @returns Partial ExportInfo containing discovered metadata.
969
+ * Get language for a file
1314
970
  */
1315
- analyzeMetadata(node, code) {
1316
- return analyzeNodeMetadata(node, code, {
1317
- sideEffectSignatures: ["print(", "input(", "open("]
1318
- });
971
+ getLanguageForFile(filePath) {
972
+ const ext = this.getFileExtension(filePath);
973
+ return this.extensionMap.get(ext) || null;
1319
974
  }
1320
975
  /**
1321
- * Extract import information using AST walk.
1322
- *
1323
- * @param rootNode - Root node of the Python AST.
1324
- * @returns Array of discovered FileImport objects.
1325
- */
1326
- extractImportsAST(rootNode) {
1327
- const imports = [];
1328
- const processImportNode = (node) => {
1329
- if (node.type === "import_statement") {
1330
- for (const child of node.children) {
1331
- if (child.type === "dotted_name") {
1332
- const source = child.text;
1333
- imports.push({
1334
- source,
1335
- specifiers: [source],
1336
- loc: {
1337
- start: {
1338
- line: child.startPosition.row + 1,
1339
- column: child.startPosition.column
1340
- },
1341
- end: {
1342
- line: child.endPosition.row + 1,
1343
- column: child.endPosition.column
1344
- }
1345
- }
1346
- });
1347
- } else if (child.type === "aliased_import") {
1348
- const nameNode = child.childForFieldName("name");
1349
- if (nameNode) {
1350
- const source = nameNode.text;
1351
- imports.push({
1352
- source,
1353
- specifiers: [source],
1354
- loc: {
1355
- start: {
1356
- line: child.startPosition.row + 1,
1357
- column: child.startPosition.column
1358
- },
1359
- end: {
1360
- line: child.endPosition.row + 1,
1361
- column: child.endPosition.column
1362
- }
1363
- }
1364
- });
1365
- }
1366
- }
1367
- }
1368
- } else if (node.type === "import_from_statement") {
1369
- const moduleNameNode = node.childForFieldName("module_name");
1370
- if (moduleNameNode) {
1371
- const source = moduleNameNode.text;
1372
- const specifiers = [];
1373
- for (const child of node.children) {
1374
- if (child.type === "dotted_name" && child !== moduleNameNode) {
1375
- specifiers.push(child.text);
1376
- } else if (child.type === "aliased_import") {
1377
- const nameNode = child.childForFieldName("name");
1378
- if (nameNode) specifiers.push(nameNode.text);
1379
- } else if (child.type === "wildcard_import") {
1380
- specifiers.push("*");
1381
- }
1382
- }
1383
- if (specifiers.length > 0) {
1384
- imports.push({
1385
- source,
1386
- specifiers,
1387
- loc: {
1388
- start: {
1389
- line: node.startPosition.row + 1,
1390
- column: node.startPosition.column
1391
- },
1392
- end: {
1393
- line: node.endPosition.row + 1,
1394
- column: node.endPosition.column
1395
- }
1396
- }
1397
- });
1398
- }
1399
- }
1400
- }
1401
- };
1402
- for (const node of rootNode.children) {
1403
- processImportNode(node);
1404
- }
1405
- return imports;
1406
- }
1407
- /**
1408
- * Extract export information using AST walk.
1409
- *
1410
- * @param rootNode - Root node of the Python AST.
1411
- * @param code - Source code for documentation extraction.
1412
- * @returns Array of discovered ExportInfo objects.
1413
- */
1414
- extractExportsAST(rootNode, code) {
1415
- const exports = [];
1416
- for (const node of rootNode.children) {
1417
- if (node.type === "function_definition") {
1418
- const nameNode = node.childForFieldName("name");
1419
- if (nameNode) {
1420
- const name = nameNode.text;
1421
- const isPrivate = name.startsWith("_") && !name.startsWith("__");
1422
- if (!isPrivate) {
1423
- const metadata = this.analyzeMetadata(node, code);
1424
- exports.push({
1425
- name,
1426
- type: "function",
1427
- loc: {
1428
- start: {
1429
- line: node.startPosition.row + 1,
1430
- column: node.startPosition.column
1431
- },
1432
- end: {
1433
- line: node.endPosition.row + 1,
1434
- column: node.endPosition.column
1435
- }
1436
- },
1437
- parameters: this.extractParameters(node),
1438
- ...metadata
1439
- });
1440
- }
1441
- }
1442
- } else if (node.type === "class_definition") {
1443
- const nameNode = node.childForFieldName("name");
1444
- if (nameNode) {
1445
- const metadata = this.analyzeMetadata(node, code);
1446
- exports.push({
1447
- name: nameNode.text,
1448
- type: "class",
1449
- loc: {
1450
- start: {
1451
- line: node.startPosition.row + 1,
1452
- column: node.startPosition.column
1453
- },
1454
- end: {
1455
- line: node.endPosition.row + 1,
1456
- column: node.endPosition.column
1457
- }
1458
- },
1459
- ...metadata
1460
- });
1461
- }
1462
- } else if (node.type === "expression_statement") {
1463
- const assignment = node.firstChild;
1464
- if (assignment && assignment.type === "assignment") {
1465
- const left = assignment.childForFieldName("left");
1466
- if (left && left.type === "identifier") {
1467
- const name = left.text;
1468
- const isInternal = name === "__all__" || name === "__version__" || name === "__author__";
1469
- const isPrivate = name.startsWith("_") && !name.startsWith("__");
1470
- if (!isInternal && !isPrivate) {
1471
- exports.push({
1472
- name,
1473
- type: name === name.toUpperCase() ? "const" : "variable",
1474
- loc: {
1475
- start: {
1476
- line: node.startPosition.row + 1,
1477
- column: node.startPosition.column
1478
- },
1479
- end: {
1480
- line: node.endPosition.row + 1,
1481
- column: node.endPosition.column
1482
- }
1483
- }
1484
- });
1485
- }
1486
- }
1487
- }
1488
- }
1489
- }
1490
- return exports;
1491
- }
1492
- /**
1493
- * Extract parameter names from a function definition node.
1494
- *
1495
- * @param node - Function definition node.
1496
- * @returns Array of parameter name strings.
1497
- */
1498
- extractParameters(node) {
1499
- const paramsNode = node.childForFieldName("parameters");
1500
- if (!paramsNode) return [];
1501
- return paramsNode.children.filter(
1502
- (c) => c.type === "identifier" || c.type === "typed_parameter" || c.type === "default_parameter"
1503
- ).map((c) => {
1504
- if (c.type === "identifier") return c.text;
1505
- if (c.type === "typed_parameter" || c.type === "default_parameter") {
1506
- return c.firstChild?.text || "unknown";
1507
- }
1508
- return "unknown";
1509
- });
1510
- }
1511
- /**
1512
- * Fallback regex-based parsing when tree-sitter is unavailable.
1513
- *
1514
- * @param code - Source code content.
1515
- * @param filePath - Path to the file being parsed.
1516
- * @returns Consolidated ParseResult.
1517
- */
1518
- parseRegex(code, filePath) {
1519
- try {
1520
- const imports = this.extractImportsRegex(code, filePath);
1521
- const exports = this.extractExportsRegex(code, filePath);
1522
- return {
1523
- exports,
1524
- imports,
1525
- language: "python" /* Python */,
1526
- warnings: [
1527
- "Python parsing is currently using regex-based extraction as tree-sitter wasm was not available."
1528
- ]
1529
- };
1530
- } catch (error) {
1531
- const wrapper = new Error(
1532
- `Failed to parse Python file ${filePath}: ${error.message}`
1533
- );
1534
- wrapper.cause = error;
1535
- throw wrapper;
1536
- }
1537
- }
1538
- getNamingConventions() {
1539
- return {
1540
- variablePattern: /^[a-z_][a-z0-9_]*$/,
1541
- functionPattern: /^[a-z_][a-z0-9_]*$/,
1542
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1543
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
1544
- exceptions: [
1545
- "__init__",
1546
- "__str__",
1547
- "__repr__",
1548
- "__name__",
1549
- "__main__",
1550
- "__file__",
1551
- "__doc__",
1552
- "__all__",
1553
- "__version__",
1554
- "__author__",
1555
- "__dict__",
1556
- "__class__",
1557
- "__module__",
1558
- "__bases__"
1559
- ]
1560
- };
1561
- }
1562
- canHandle(filePath) {
1563
- return filePath.toLowerCase().endsWith(".py");
1564
- }
1565
- extractImportsRegex(code, _filePath) {
1566
- void _filePath;
1567
- const imports = [];
1568
- const lines = code.split("\n");
1569
- const importRegex = /^\s*import\s+([a-zA-Z0-9_., ]+)/;
1570
- const fromImportRegex = /^\s*from\s+([a-zA-Z0-9_.]+)\s+import\s+(.+)/;
1571
- lines.forEach((line, idx) => {
1572
- if (line.trim().startsWith("#")) return;
1573
- const importMatch = line.match(importRegex);
1574
- if (importMatch) {
1575
- const modules = importMatch[1].split(",").map((m) => m.trim().split(" as ")[0]);
1576
- modules.forEach((module) => {
1577
- imports.push({
1578
- source: module,
1579
- specifiers: [module],
1580
- loc: {
1581
- start: { line: idx + 1, column: 0 },
1582
- end: { line: idx + 1, column: line.length }
1583
- }
1584
- });
1585
- });
1586
- return;
1587
- }
1588
- const fromMatch = line.match(fromImportRegex);
1589
- if (fromMatch) {
1590
- const module = fromMatch[1];
1591
- const imports_str = fromMatch[2];
1592
- if (imports_str.trim() === "*") {
1593
- imports.push({
1594
- source: module,
1595
- specifiers: ["*"],
1596
- loc: {
1597
- start: { line: idx + 1, column: 0 },
1598
- end: { line: idx + 1, column: line.length }
1599
- }
1600
- });
1601
- return;
1602
- }
1603
- const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
1604
- imports.push({
1605
- source: module,
1606
- specifiers,
1607
- loc: {
1608
- start: { line: idx + 1, column: 0 },
1609
- end: { line: idx + 1, column: line.length }
1610
- }
1611
- });
1612
- }
1613
- });
1614
- return imports;
1615
- }
1616
- extractExportsRegex(code, _filePath) {
1617
- void _filePath;
1618
- const exports = [];
1619
- const lines = code.split("\n");
1620
- const funcRegex = /^def\s+([a-zA-Z0-9_]+)\s*\(/;
1621
- const classRegex = /^class\s+([a-zA-Z0-9_]+)/;
1622
- lines.forEach((line, idx) => {
1623
- const indent = line.search(/\S/);
1624
- if (indent !== 0) return;
1625
- const classMatch = line.match(classRegex);
1626
- if (classMatch) {
1627
- exports.push({
1628
- name: classMatch[1],
1629
- type: "class",
1630
- visibility: "public",
1631
- isPure: true,
1632
- hasSideEffects: false,
1633
- loc: {
1634
- start: { line: idx + 1, column: 0 },
1635
- end: { line: idx + 1, column: line.length }
1636
- }
1637
- });
1638
- return;
1639
- }
1640
- const funcMatch = line.match(funcRegex);
1641
- if (funcMatch) {
1642
- const name = funcMatch[1];
1643
- if (name.startsWith("_") && !name.startsWith("__")) return;
1644
- let docContent;
1645
- const nextLines = lines.slice(idx + 1, idx + 4);
1646
- for (const nextLine of nextLines) {
1647
- const docMatch = nextLine.match(/^\s*"""([\s\S]*?)"""/) || nextLine.match(/^\s*'''([\s\S]*?)'''/);
1648
- if (docMatch) {
1649
- docContent = docMatch[1].trim();
1650
- break;
1651
- }
1652
- if (nextLine.trim() && !nextLine.trim().startsWith('"""') && !nextLine.trim().startsWith("'''"))
1653
- break;
1654
- }
1655
- const isImpure = name.toLowerCase().includes("impure") || line.includes("print(") || idx + 1 < lines.length && lines[idx + 1].includes("print(");
1656
- exports.push({
1657
- name,
1658
- type: "function",
1659
- visibility: "public",
1660
- isPure: !isImpure,
1661
- hasSideEffects: isImpure,
1662
- documentation: docContent ? { content: docContent, type: "docstring" } : void 0,
1663
- loc: {
1664
- start: { line: idx + 1, column: 0 },
1665
- end: { line: idx + 1, column: line.length }
1666
- }
1667
- });
1668
- }
1669
- });
1670
- return exports;
1671
- }
1672
- };
1673
-
1674
- // src/parsers/shared-parser-utils.ts
1675
- var SIDE_EFFECT_KEYWORDS = [
1676
- "print(",
1677
- "console.",
1678
- "System.out",
1679
- "System.err",
1680
- "fmt.",
1681
- "File.Write",
1682
- "Files.write",
1683
- "os.Exit",
1684
- "panic(",
1685
- "throw ",
1686
- "Logging.",
1687
- "log."
1688
- ];
1689
- function analyzeGeneralMetadata(node, code, options = {}) {
1690
- const metadata = {
1691
- isPure: true,
1692
- hasSideEffects: false
1693
- };
1694
- try {
1695
- let prev = node.previousSibling || null;
1696
- while (prev && /comment/i.test(prev.type)) {
1697
- const text = prev.text || "";
1698
- if (text.trim().startsWith("/**")) {
1699
- metadata.documentation = {
1700
- content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1701
- type: "jsdoc"
1702
- };
1703
- break;
1704
- }
1705
- if (text.trim().startsWith("///")) {
1706
- metadata.documentation = {
1707
- content: text.replace(/^\/\/\//, "").trim(),
1708
- type: "xml-doc"
1709
- };
1710
- break;
1711
- }
1712
- if (text.trim().startsWith("//")) {
1713
- metadata.documentation = {
1714
- content: text.replace(/^\/\//, "").trim(),
1715
- type: "comment"
1716
- };
1717
- break;
1718
- }
1719
- prev = prev.previousSibling;
1720
- }
1721
- } catch {
1722
- }
1723
- const signatures = [
1724
- ...SIDE_EFFECT_KEYWORDS,
1725
- ...options.sideEffectSignatures || []
1726
- ];
1727
- const walk = (n) => {
1728
- if (/assign|assignment|assignment_statement|assignment_expression/i.test(
1729
- n.type
1730
- )) {
1731
- metadata.isPure = false;
1732
- metadata.hasSideEffects = true;
1733
- }
1734
- const text = n.text;
1735
- for (const sig of signatures) {
1736
- if (text.includes(sig)) {
1737
- metadata.isPure = false;
1738
- metadata.hasSideEffects = true;
1739
- break;
1740
- }
1741
- }
1742
- if (!metadata.hasSideEffects) {
1743
- for (let i = 0; i < n.childCount; i++) {
1744
- const child = n.child(i);
1745
- if (child) walk(child);
1746
- }
1747
- }
1748
- };
1749
- walk(node);
1750
- return metadata;
1751
- }
1752
- function extractParameterNames(node) {
1753
- const params = [];
1754
- const candidates = [
1755
- // common field name
1756
- node.childForFieldName ? node.childForFieldName("parameters") : null,
1757
- node.childForFieldName ? node.childForFieldName("parameter_list") : null,
1758
- node.children.find((c) => c.type === "parameter_list") || null,
1759
- node.children.find((c) => c.type === "parameters") || null,
1760
- node.children.find((c) => c.type === "formal_parameters") || null,
1761
- node.children.find((c) => c.type === "formal_parameter") || null
1762
- ];
1763
- const list = candidates.find(Boolean);
1764
- if (!list) return params;
1765
- for (const child of list.children) {
1766
- if (!child) continue;
1767
- const id = child.childForFieldName?.("name") || child.children.find(
1768
- (c) => [
1769
- "identifier",
1770
- "variable_name",
1771
- "name",
1772
- "parameter",
1773
- "formal_parameter"
1774
- ].includes(c.type)
1775
- ) || (child.type === "identifier" ? child : void 0);
1776
- if (id && typeof id.text === "string") params.push(id.text);
1777
- }
1778
- return params;
1779
- }
1780
-
1781
- // src/parsers/java-parser.ts
1782
- var JavaParser = class extends BaseLanguageParser {
1783
- constructor() {
1784
- super(...arguments);
1785
- this.language = "java" /* Java */;
1786
- this.extensions = [".java"];
1787
- }
1788
- getParserName() {
1789
- return "java";
1790
- }
1791
- /**
1792
- * Analyze metadata for a Java node (purity, side effects).
1793
- *
1794
- * @param node - Tree-sitter node to analyze.
1795
- * @param code - Source code for context.
1796
- * @returns Partial ExportInfo containing discovered metadata.
1797
- */
1798
- analyzeMetadata(node, code) {
1799
- return analyzeGeneralMetadata(node, code, {
1800
- sideEffectSignatures: [
1801
- "System.out",
1802
- "System.err",
1803
- "Files.write",
1804
- "Logging."
1805
- ]
1806
- });
1807
- }
1808
- parseRegex(code) {
1809
- const lines = code.split("\n");
1810
- const exports = [];
1811
- const imports = [];
1812
- const importRegex = /^import\s+([a-zA-Z0-9_.]+)/;
1813
- const classRegex = /^\s*(?:public\s+)?(?:class|interface|enum)\s+([a-zA-Z0-9_]+)/;
1814
- const methodRegex = /^\s*public\s+(?:static\s+)?[a-zA-Z0-9_<>[\]]+\s+([a-zA-Z0-9_]+)\s*\(/;
1815
- let currentClassName = "";
1816
- lines.forEach((line, idx) => {
1817
- const importMatch = line.match(importRegex);
1818
- if (importMatch) {
1819
- const source = importMatch[1];
1820
- imports.push({
1821
- source,
1822
- specifiers: [source.split(".").pop() || source],
1823
- loc: {
1824
- start: { line: idx + 1, column: 0 },
1825
- end: { line: idx + 1, column: line.length }
1826
- }
1827
- });
1828
- }
1829
- const classMatch = line.match(classRegex);
1830
- if (classMatch) {
1831
- currentClassName = classMatch[1];
1832
- exports.push({
1833
- name: currentClassName,
1834
- type: line.includes("interface") ? "interface" : "class",
1835
- visibility: "public",
1836
- isPure: true,
1837
- hasSideEffects: false,
1838
- loc: {
1839
- start: { line: idx + 1, column: 0 },
1840
- end: { line: idx + 1, column: line.length }
1841
- }
1842
- });
1843
- }
1844
- const methodMatch = line.match(methodRegex);
1845
- if (methodMatch && currentClassName) {
1846
- const name = methodMatch[1];
1847
- let docContent;
1848
- const prevLines = lines.slice(Math.max(0, idx - 5), idx);
1849
- const prevText = prevLines.join("\n");
1850
- const javadocMatch = prevText.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1851
- if (javadocMatch) {
1852
- docContent = javadocMatch[1].replace(/^\s*\*+/gm, "").trim();
1853
- }
1854
- const isImpure = name.toLowerCase().includes("impure") || line.includes("System.out");
1855
- exports.push({
1856
- name,
1857
- type: "function",
1858
- parentClass: currentClassName,
1859
- visibility: "public",
1860
- isPure: !isImpure,
1861
- hasSideEffects: isImpure,
1862
- documentation: docContent ? { content: docContent, type: "jsdoc" } : void 0,
1863
- loc: {
1864
- start: { line: idx + 1, column: 0 },
1865
- end: { line: idx + 1, column: line.length }
1866
- }
1867
- });
1868
- }
1869
- });
1870
- return {
1871
- exports,
1872
- imports,
1873
- language: "java" /* Java */,
1874
- warnings: ["Parser falling back to regex-based analysis"]
1875
- };
1876
- }
1877
- /**
1878
- * Extract import information using AST walk.
1879
- *
1880
- * @param rootNode - Root node of the Java AST.
1881
- * @returns Array of discovered FileImport objects.
1882
- */
1883
- extractImportsAST(rootNode) {
1884
- const imports = [];
1885
- for (const node of rootNode.children) {
1886
- if (node.type === "import_declaration") {
1887
- const sourceArr = [];
1888
- let isWildcard = false;
1889
- for (const child of node.children) {
1890
- if (child.type === "scoped_identifier" || child.type === "identifier") {
1891
- sourceArr.push(child.text);
1892
- }
1893
- if (child.type === "asterisk") isWildcard = true;
1894
- }
1895
- const source = sourceArr.join(".");
1896
- if (source) {
1897
- imports.push({
1898
- source: isWildcard ? `${source}.*` : source,
1899
- specifiers: isWildcard ? ["*"] : [source.split(".").pop() || source],
1900
- loc: {
1901
- start: {
1902
- line: node.startPosition.row + 1,
1903
- column: node.startPosition.column
1904
- },
1905
- end: {
1906
- line: node.endPosition.row + 1,
1907
- column: node.endPosition.column
1908
- }
1909
- }
1910
- });
1911
- }
1912
- }
1913
- }
1914
- return imports;
1915
- }
1916
- /**
1917
- * Extract export information (classes, interfaces, methods) using AST walk.
1918
- *
1919
- * @param rootNode - Root node of the Java AST.
1920
- * @param code - Source code for documentation extraction.
1921
- * @returns Array of discovered ExportInfo objects.
1922
- */
1923
- extractExportsAST(rootNode, code) {
1924
- const exports = [];
1925
- for (const node of rootNode.children) {
1926
- if (node.type === "class_declaration" || node.type === "interface_declaration" || node.type === "enum_declaration") {
1927
- const nameNode = node.children.find((c) => c.type === "identifier");
1928
- if (nameNode) {
1929
- const modifiers = this.getModifiers(node);
1930
- const metadata = this.analyzeMetadata(node, code);
1931
- exports.push({
1932
- name: nameNode.text,
1933
- type: node.type === "class_declaration" ? "class" : "interface",
1934
- loc: {
1935
- start: {
1936
- line: node.startPosition.row + 1,
1937
- column: node.startPosition.column
1938
- },
1939
- end: {
1940
- line: node.endPosition.row + 1,
1941
- column: node.endPosition.column
1942
- }
1943
- },
1944
- visibility: modifiers.includes("public") ? "public" : "private",
1945
- ...metadata
1946
- });
1947
- this.extractSubExports(node, nameNode.text, exports, code);
1948
- }
1949
- }
1950
- }
1951
- return exports;
1952
- }
1953
- /**
1954
- * Extract modifiers (visibility, static, etc.) from a node.
1955
- *
1956
- * @param node - AST node to extract modifiers from.
1957
- * @returns Array of modifier strings.
1958
- */
1959
- getModifiers(node) {
1960
- const modifiersNode = node.children.find((c) => c.type === "modifiers");
1961
- if (!modifiersNode) return [];
1962
- return modifiersNode.children.map((c) => c.text);
1963
- }
1964
- /**
1965
- * Extract methods and nested exports from a class or interface body.
1966
- *
1967
- * @param parentNode - Class or interface declaration node.
1968
- * @param parentName - Name of the parent class/interface.
1969
- * @param exports - Array to collect discovered exports into.
1970
- * @param code - Source code for context.
1971
- */
1972
- extractSubExports(parentNode, parentName, exports, code) {
1973
- const bodyNode = parentNode.children.find((c) => c.type === "class_body");
1974
- if (!bodyNode) return;
1975
- for (const node of bodyNode.children) {
1976
- if (node.type === "method_declaration") {
1977
- const nameNode = node.children.find((c) => c.type === "identifier");
1978
- const modifiers = this.getModifiers(node);
1979
- if (nameNode && modifiers.includes("public")) {
1980
- const metadata = this.analyzeMetadata(node, code);
1981
- exports.push({
1982
- name: nameNode.text,
1983
- type: "function",
1984
- parentClass: parentName,
1985
- visibility: "public",
1986
- loc: {
1987
- start: {
1988
- line: node.startPosition.row + 1,
1989
- column: node.startPosition.column
1990
- },
1991
- end: {
1992
- line: node.endPosition.row + 1,
1993
- column: node.endPosition.column
1994
- }
1995
- },
1996
- parameters: this.extractParameters(node),
1997
- ...metadata
1998
- });
1999
- }
2000
- }
2001
- }
2002
- }
2003
- extractParameters(node) {
2004
- return extractParameterNames(node);
2005
- }
2006
- getNamingConventions() {
2007
- return {
2008
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
2009
- functionPattern: /^[a-z][a-zA-Z0-9]*$/,
2010
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
2011
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
2012
- exceptions: ["main", "serialVersionUID"]
2013
- };
2014
- }
2015
- canHandle(filePath) {
2016
- return filePath.toLowerCase().endsWith(".java");
2017
- }
2018
- };
2019
-
2020
- // src/parsers/csharp-parser.ts
2021
- var CSharpParser = class extends BaseLanguageParser {
2022
- constructor() {
2023
- super(...arguments);
2024
- this.language = "csharp" /* CSharp */;
2025
- this.extensions = [".cs"];
2026
- }
2027
- getParserName() {
2028
- return "c_sharp";
2029
- }
2030
- /**
2031
- * Analyze metadata for a C# node (purity, side effects).
2032
- *
2033
- * @param node - Tree-sitter node to analyze.
2034
- * @param code - Source code for context.
2035
- * @returns Partial ExportInfo containing discovered metadata.
2036
- */
2037
- analyzeMetadata(node, code) {
2038
- return analyzeGeneralMetadata(node, code, {
2039
- sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
2040
- });
2041
- }
2042
- /**
2043
- * Fallback regex-based parsing when tree-sitter is unavailable.
2044
- *
2045
- * @param code - Source code content.
2046
- * @returns Consolidated ParseResult.
2047
- */
2048
- parseRegex(code) {
2049
- const lines = code.split("\n");
2050
- const exports = [];
2051
- const imports = [];
2052
- const usingRegex = /^using\s+([a-zA-Z0-9_.]+);/;
2053
- const classRegex = /^\s*(?:public\s+)?class\s+([a-zA-Z0-9_]+)/;
2054
- const methodRegex = /^\s*(?:public|protected)\s+(?:static\s+)?[a-zA-Z0-9_.]+\s+([a-zA-Z0-9_]+)\s*\(/;
2055
- let currentClassName = "";
2056
- lines.forEach((line, idx) => {
2057
- const usingMatch = line.match(usingRegex);
2058
- if (usingMatch) {
2059
- const source = usingMatch[1];
2060
- imports.push({
2061
- source,
2062
- specifiers: [source.split(".").pop() || source],
2063
- loc: {
2064
- start: { line: idx + 1, column: 0 },
2065
- end: { line: idx + 1, column: line.length }
2066
- }
2067
- });
2068
- }
2069
- const classMatch = line.match(classRegex);
2070
- if (classMatch) {
2071
- currentClassName = classMatch[1];
2072
- exports.push({
2073
- name: currentClassName,
2074
- type: "class",
2075
- visibility: "public",
2076
- isPure: true,
2077
- hasSideEffects: false,
2078
- loc: {
2079
- start: { line: idx + 1, column: 0 },
2080
- end: { line: idx + 1, column: line.length }
2081
- }
2082
- });
2083
- }
2084
- const methodMatch = line.match(methodRegex);
2085
- if (methodMatch && currentClassName) {
2086
- const name = methodMatch[1];
2087
- const isImpure = name.toLowerCase().includes("impure") || line.includes("Console.WriteLine");
2088
- exports.push({
2089
- name,
2090
- type: "function",
2091
- parentClass: currentClassName,
2092
- visibility: "public",
2093
- isPure: !isImpure,
2094
- hasSideEffects: isImpure,
2095
- loc: {
2096
- start: { line: idx + 1, column: 0 },
2097
- end: { line: idx + 1, column: line.length }
2098
- }
2099
- });
2100
- }
2101
- });
2102
- return {
2103
- exports,
2104
- imports,
2105
- language: "csharp" /* CSharp */,
2106
- warnings: ["Parser falling back to regex-based analysis"]
2107
- };
2108
- }
2109
- /**
2110
- * Extract import information (usings) using AST walk.
2111
- *
2112
- * @param rootNode - Root node of the C# AST.
2113
- * @returns Array of discovered FileImport objects.
2114
- */
2115
- extractImportsAST(rootNode) {
2116
- const imports = [];
2117
- const findUsings = (node) => {
2118
- if (node.type === "using_directive") {
2119
- const nameNode = node.childForFieldName("name") || node.children.find(
2120
- (c) => c.type === "qualified_name" || c.type === "identifier"
2121
- );
2122
- if (nameNode) {
2123
- const aliasNode = node.childForFieldName("alias");
2124
- imports.push({
2125
- source: nameNode.text,
2126
- specifiers: aliasNode ? [aliasNode.text] : [nameNode.text.split(".").pop() || nameNode.text],
2127
- loc: {
2128
- start: {
2129
- line: node.startPosition.row + 1,
2130
- column: node.startPosition.column
2131
- },
2132
- end: {
2133
- line: node.endPosition.row + 1,
2134
- column: node.endPosition.column
2135
- }
2136
- }
2137
- });
2138
- }
2139
- }
2140
- for (let i = 0; i < node.childCount; i++) {
2141
- const child = node.child(i);
2142
- if (child) findUsings(child);
2143
- }
2144
- };
2145
- findUsings(rootNode);
2146
- return imports;
2147
- }
2148
- /**
2149
- * Extract export information (classes, methods, properties) using AST walk.
2150
- * Handles nested namespaces and classes.
2151
- *
2152
- * @param rootNode - Root node of the C# AST.
2153
- * @param code - Source code for documentation extraction.
2154
- * @returns Array of discovered ExportInfo objects.
2155
- */
2156
- extractExportsAST(rootNode, code) {
2157
- const exports = [];
2158
- const traverse = (node, currentNamespace, currentClass) => {
2159
- let nextNamespace = currentNamespace;
2160
- let nextClass = currentClass;
2161
- if (node.type === "namespace_declaration" || node.type === "file_scoped_namespace_declaration") {
2162
- const nameNode = node.childForFieldName("name") || node.children.find(
2163
- (c) => c.type === "identifier" || c.type === "qualified_name"
2164
- );
2165
- if (nameNode) {
2166
- nextNamespace = currentNamespace ? `${currentNamespace}.${nameNode.text}` : nameNode.text;
2167
- }
2168
- } else if (node.type === "class_declaration" || node.type === "interface_declaration" || node.type === "enum_declaration" || node.type === "struct_declaration" || node.type === "record_declaration") {
2169
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2170
- if (nameNode) {
2171
- const modifiers = this.getModifiers(node);
2172
- const isPublic = modifiers.includes("public") || modifiers.includes("protected");
2173
- if (isPublic) {
2174
- const metadata = this.analyzeMetadata(node, code);
2175
- const type = node.type.replace("_declaration", "");
2176
- const fullName = nextClass ? `${nextClass}.${nameNode.text}` : nextNamespace ? `${nextNamespace}.${nameNode.text}` : nameNode.text;
2177
- exports.push({
2178
- name: fullName,
2179
- type: type === "record" ? "class" : type,
2180
- loc: {
2181
- start: {
2182
- line: node.startPosition.row + 1,
2183
- column: node.startPosition.column
2184
- },
2185
- end: {
2186
- line: node.endPosition.row + 1,
2187
- column: node.endPosition.column
2188
- }
2189
- },
2190
- visibility: modifiers.includes("public") ? "public" : "protected",
2191
- ...metadata
2192
- });
2193
- nextClass = fullName;
2194
- }
2195
- }
2196
- } else if (node.type === "method_declaration" || node.type === "property_declaration") {
2197
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2198
- if (nameNode) {
2199
- const modifiers = this.getModifiers(node);
2200
- const isPublic = modifiers.includes("public") || modifiers.includes("protected");
2201
- if (isPublic) {
2202
- const metadata = this.analyzeMetadata(node, code);
2203
- exports.push({
2204
- name: nameNode.text,
2205
- type: node.type === "method_declaration" ? "function" : "property",
2206
- parentClass: currentClass,
2207
- loc: {
2208
- start: {
2209
- line: node.startPosition.row + 1,
2210
- column: node.startPosition.column
2211
- },
2212
- end: {
2213
- line: node.endPosition.row + 1,
2214
- column: node.endPosition.column
2215
- }
2216
- },
2217
- visibility: modifiers.includes("public") ? "public" : "protected",
2218
- parameters: node.type === "method_declaration" ? this.extractParameters(node) : void 0,
2219
- ...metadata
2220
- });
2221
- }
2222
- }
2223
- }
2224
- for (let i = 0; i < node.childCount; i++) {
2225
- const child = node.child(i);
2226
- if (child) traverse(child, nextNamespace, nextClass);
2227
- }
2228
- };
2229
- traverse(rootNode);
2230
- return exports;
2231
- }
2232
- getModifiers(node) {
2233
- const modifiers = [];
2234
- for (const child of node.children) {
2235
- if (child.type === "modifier") {
2236
- modifiers.push(child.text);
2237
- }
2238
- }
2239
- return modifiers;
2240
- }
2241
- extractParameters(node) {
2242
- return extractParameterNames(node);
2243
- }
2244
- getNamingConventions() {
2245
- return {
2246
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
2247
- functionPattern: /^[A-Z][a-zA-Z0-9]*$/,
2248
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
2249
- constantPattern: /^[A-Z][a-zA-Z0-9_]*$/
2250
- };
2251
- }
2252
- canHandle(filePath) {
2253
- return filePath.toLowerCase().endsWith(".cs");
2254
- }
2255
- };
2256
-
2257
- // src/parsers/go-parser.ts
2258
- var GoParser = class extends BaseLanguageParser {
2259
- constructor() {
2260
- super(...arguments);
2261
- this.language = "go" /* Go */;
2262
- this.extensions = [".go"];
2263
- }
2264
- getParserName() {
2265
- return "go";
2266
- }
2267
- /**
2268
- * Analyze metadata for a Go node (purity, side effects).
2269
- *
2270
- * @param node - Tree-sitter node to analyze.
2271
- * @param code - Source code for context.
2272
- * @returns Partial ExportInfo containing discovered metadata.
2273
- */
2274
- analyzeMetadata(node, code) {
2275
- return analyzeGeneralMetadata(node, code, {
2276
- sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
2277
- });
2278
- }
2279
- /**
2280
- * Fallback regex-based parsing when tree-sitter is unavailable.
2281
- *
2282
- * @param code - Source code content.
2283
- * @returns Consolidated ParseResult.
2284
- */
2285
- parseRegex(code) {
2286
- const lines = code.split("\n");
2287
- const exports = [];
2288
- const imports = [];
2289
- const importRegex = /^import\s+"([^"]+)"/;
2290
- const funcRegex = /^func\s+([A-Z][a-zA-Z0-9_]*)\s*\(/;
2291
- const typeRegex = /^type\s+([A-Z][a-zA-Z0-9_]*)\s+(struct|interface)/;
2292
- lines.forEach((line, idx) => {
2293
- const importMatch = line.match(importRegex);
2294
- if (importMatch) {
2295
- const source = importMatch[1];
2296
- imports.push({
2297
- source,
2298
- specifiers: [source.split("/").pop() || source],
2299
- loc: {
2300
- start: { line: idx + 1, column: 0 },
2301
- end: { line: idx + 1, column: line.length }
2302
- }
2303
- });
2304
- }
2305
- const funcMatch = line.match(funcRegex);
2306
- if (funcMatch) {
2307
- const name = funcMatch[1];
2308
- const isPublic = /^[A-Z]/.test(name);
2309
- let docContent;
2310
- const prevLines = lines.slice(Math.max(0, idx - 3), idx);
2311
- for (let i = prevLines.length - 1; i >= 0; i--) {
2312
- const prevLine = prevLines[i].trim();
2313
- if (prevLine.startsWith("//")) {
2314
- const content = prevLine.slice(2).trim();
2315
- docContent = docContent ? content + "\n" + docContent : content;
2316
- } else if (prevLine.endsWith("*/")) {
2317
- const blockMatch = prevLine.match(/\/\*([\s\S]*)\*\//);
2318
- if (blockMatch) docContent = blockMatch[1].trim();
2319
- break;
2320
- } else if (!prevLine) {
2321
- if (docContent) break;
2322
- } else {
2323
- break;
2324
- }
2325
- }
2326
- const isImpure = name.toLowerCase().includes("impure") || line.includes("fmt.Print");
2327
- exports.push({
2328
- name,
2329
- type: "function",
2330
- visibility: isPublic ? "public" : "private",
2331
- isPure: !isImpure,
2332
- hasSideEffects: isImpure,
2333
- documentation: docContent ? { content: docContent, type: "comment" } : void 0,
2334
- loc: {
2335
- start: { line: idx + 1, column: 0 },
2336
- end: { line: idx + 1, column: line.length }
2337
- }
2338
- });
2339
- }
2340
- const typeMatch = line.match(typeRegex);
2341
- if (typeMatch) {
2342
- exports.push({
2343
- name: typeMatch[1],
2344
- type: typeMatch[2] === "struct" ? "class" : "interface",
2345
- visibility: "public",
2346
- isPure: true,
2347
- hasSideEffects: false,
2348
- loc: {
2349
- start: { line: idx + 1, column: 0 },
2350
- end: { line: idx + 1, column: line.length }
2351
- }
2352
- });
2353
- }
2354
- });
2355
- return {
2356
- exports,
2357
- imports,
2358
- language: "go" /* Go */,
2359
- warnings: ["Parser falling back to regex-based analysis"]
2360
- };
2361
- }
2362
- /**
2363
- * Extract import information using AST walk.
2364
- *
2365
- * @param rootNode - Root node of the Go AST.
2366
- * @returns Array of discovered FileImport objects.
2367
- */
2368
- extractImportsAST(rootNode) {
2369
- const imports = [];
2370
- const findImports = (node) => {
2371
- if (node.type === "import_spec") {
2372
- const pathNode = node.children.find(
2373
- (c) => c.type === "interpreted_string_literal"
2374
- );
2375
- if (pathNode) {
2376
- const source = pathNode.text.replace(/"/g, "");
2377
- imports.push({
2378
- source,
2379
- specifiers: [source.split("/").pop() || source],
2380
- loc: {
2381
- start: {
2382
- line: node.startPosition.row + 1,
2383
- column: node.startPosition.column
2384
- },
2385
- end: {
2386
- line: node.endPosition.row + 1,
2387
- column: node.endPosition.column
2388
- }
2389
- }
2390
- });
2391
- }
2392
- }
2393
- for (let i = 0; i < node.childCount; i++) {
2394
- const child = node.child(i);
2395
- if (child) findImports(child);
2396
- }
2397
- };
2398
- findImports(rootNode);
2399
- return imports;
2400
- }
2401
- /**
2402
- * Extract export information (functions, types, vars) using AST walk.
2403
- *
2404
- * @param rootNode - Root node of the Go AST.
2405
- * @param code - Source code for documentation extraction.
2406
- * @returns Array of discovered ExportInfo objects.
2407
- */
2408
- extractExportsAST(rootNode, code) {
2409
- const exports = [];
2410
- const isExported = (name) => {
2411
- return /^[A-Z]/.test(name);
2412
- };
2413
- const traverse = (node) => {
2414
- if (node.type === "function_declaration" || node.type === "method_declaration") {
2415
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2416
- if (nameNode && isExported(nameNode.text)) {
2417
- const metadata = this.analyzeMetadata(node, code);
2418
- exports.push({
2419
- name: nameNode.text,
2420
- type: "function",
2421
- loc: {
2422
- start: {
2423
- line: node.startPosition.row + 1,
2424
- column: node.startPosition.column
2425
- },
2426
- end: {
2427
- line: node.endPosition.row + 1,
2428
- column: node.endPosition.column
2429
- }
2430
- },
2431
- visibility: "public",
2432
- parameters: this.extractParameters(node),
2433
- ...metadata
2434
- });
2435
- }
2436
- } else if (node.type === "type_spec") {
2437
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "type_identifier");
2438
- if (nameNode && isExported(nameNode.text)) {
2439
- const metadata = this.analyzeMetadata(node.parent || node, code);
2440
- const type = node.children.some((c) => c.type === "struct_type") ? "class" : "interface";
2441
- exports.push({
2442
- name: nameNode.text,
2443
- type,
2444
- loc: {
2445
- start: {
2446
- line: node.startPosition.row + 1,
2447
- column: node.startPosition.column
2448
- },
2449
- end: {
2450
- line: node.endPosition.row + 1,
2451
- column: node.endPosition.column
2452
- }
2453
- },
2454
- visibility: "public",
2455
- ...metadata
2456
- });
2457
- }
2458
- } else if (node.type === "var_spec" || node.type === "const_spec") {
2459
- const identifiers = node.children.filter(
2460
- (c) => c.type === "identifier"
2461
- );
2462
- for (const idNode of identifiers) {
2463
- if (isExported(idNode.text)) {
2464
- const metadata = this.analyzeMetadata(node, code);
2465
- exports.push({
2466
- name: idNode.text,
2467
- type: "variable",
2468
- loc: {
2469
- start: {
2470
- line: idNode.startPosition.row + 1,
2471
- column: idNode.startPosition.column
2472
- },
2473
- end: {
2474
- line: idNode.endPosition.row + 1,
2475
- column: idNode.endPosition.column
2476
- }
2477
- },
2478
- visibility: "public",
2479
- ...metadata
2480
- });
2481
- }
2482
- }
2483
- }
2484
- for (let i = 0; i < node.childCount; i++) {
2485
- const child = node.child(i);
2486
- if (child) traverse(child);
2487
- }
2488
- };
2489
- traverse(rootNode);
2490
- return exports;
2491
- }
2492
- extractParameters(node) {
2493
- return extractParameterNames(node);
2494
- }
2495
- getNamingConventions() {
2496
- return {
2497
- variablePattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2498
- functionPattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2499
- classPattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2500
- constantPattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/
2501
- };
2502
- }
2503
- canHandle(filePath) {
2504
- return filePath.toLowerCase().endsWith(".go");
2505
- }
2506
- };
2507
-
2508
- // src/parsers/parser-factory.ts
2509
- var ParserFactory = class _ParserFactory {
2510
- /**
2511
- * Create a new ParserFactory instance
2512
- */
2513
- constructor() {
2514
- this.parsers = /* @__PURE__ */ new Map();
2515
- this.extensionMap = new Map(
2516
- Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
2517
- );
2518
- this.registerParser(new TypeScriptParser());
2519
- this.registerParser(new PythonParser());
2520
- this.registerParser(new JavaParser());
2521
- this.registerParser(new CSharpParser());
2522
- this.registerParser(new GoParser());
2523
- }
2524
- /**
2525
- * Get the global singleton instance
2526
- *
2527
- * @returns The singleton ParserFactory instance
2528
- */
2529
- static getInstance() {
2530
- if (!_ParserFactory.instance) {
2531
- _ParserFactory.instance = new _ParserFactory();
2532
- }
2533
- return _ParserFactory.instance;
2534
- }
2535
- /**
2536
- * Register a language parser
2537
- */
2538
- registerParser(parser) {
2539
- this.parsers.set(parser.language, parser);
2540
- parser.extensions.forEach((ext) => {
2541
- const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
2542
- this.extensionMap.set(ext, lang);
2543
- this.parsers.set(lang, parser);
2544
- });
2545
- }
2546
- /**
2547
- * Get parser for a specific language
2548
- */
2549
- getParserForLanguage(language) {
2550
- return this.parsers.get(language) || null;
2551
- }
2552
- /**
2553
- * Get parser for a file based on its extension
2554
- */
2555
- getParserForFile(filePath) {
2556
- const ext = this.getFileExtension(filePath);
2557
- const language = this.extensionMap.get(ext);
2558
- if (!language) {
2559
- return null;
2560
- }
2561
- return this.parsers.get(language) || null;
2562
- }
2563
- /**
2564
- * Check if a file is supported
2565
- */
2566
- isSupported(filePath) {
2567
- return this.getParserForFile(filePath) !== null;
2568
- }
2569
- /**
2570
- * Get all registered languages
2571
- */
2572
- getSupportedLanguages() {
2573
- return Array.from(this.parsers.keys());
2574
- }
2575
- /**
2576
- * Get all supported file extensions
2577
- */
2578
- getSupportedExtensions() {
2579
- return Array.from(this.extensionMap.keys());
2580
- }
2581
- /**
2582
- * Get language for a file
2583
- */
2584
- getLanguageForFile(filePath) {
2585
- const ext = this.getFileExtension(filePath);
2586
- return this.extensionMap.get(ext) || null;
2587
- }
2588
- /**
2589
- * Extract file extension (with dot)
976
+ * Extract file extension (with dot)
2590
977
  */
2591
978
  getFileExtension(filePath) {
2592
979
  const match = filePath.match(/\.[^.]+$/);
@@ -2608,7 +995,7 @@ var ParserFactory = class _ParserFactory {
2608
995
  await Promise.all(promises);
2609
996
  }
2610
997
  };
2611
- function getParser(filePath) {
998
+ async function getParser(filePath) {
2612
999
  return ParserFactory.getInstance().getParserForFile(filePath);
2613
1000
  }
2614
1001
  async function initializeParsers() {
@@ -2795,10 +1182,11 @@ function extractTypeReferences(node) {
2795
1182
  }
2796
1183
 
2797
1184
  // src/utils/dependency-analyzer.ts
2798
- function parseFileExports(code, filePath) {
2799
- const parser = getParser(filePath);
1185
+ async function parseFileExports(code, filePath) {
1186
+ const parser = await getParser(filePath);
2800
1187
  if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
2801
1188
  try {
1189
+ await parser.initialize();
2802
1190
  const result = parser.parse(code, filePath);
2803
1191
  return {
2804
1192
  exports: result.exports.map((e) => ({
@@ -2823,7 +1211,7 @@ function parseFileExports(code, filePath) {
2823
1211
  }
2824
1212
  }
2825
1213
  try {
2826
- const ast = parse2(code, {
1214
+ const ast = parse(code, {
2827
1215
  loc: true,
2828
1216
  range: true,
2829
1217
  jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
@@ -2843,8 +1231,8 @@ function estimateTokens(text) {
2843
1231
  }
2844
1232
 
2845
1233
  // src/utils/config.ts
2846
- import { readFileSync, existsSync as existsSync4 } from "fs";
2847
- import { join as join4, resolve, dirname as dirname4 } from "path";
1234
+ import { readFileSync, existsSync as existsSync3 } from "fs";
1235
+ import { join as join3, resolve, dirname as dirname3 } from "path";
2848
1236
  import { pathToFileURL } from "url";
2849
1237
  var CONFIG_FILES = [
2850
1238
  "aiready.json",
@@ -2859,7 +1247,7 @@ async function loadConfig(rootDir) {
2859
1247
  while (true) {
2860
1248
  const foundConfigs = [];
2861
1249
  for (const configFile of CONFIG_FILES) {
2862
- if (existsSync4(join4(currentDir, configFile))) {
1250
+ if (existsSync3(join3(currentDir, configFile))) {
2863
1251
  foundConfigs.push(configFile);
2864
1252
  }
2865
1253
  }
@@ -2873,7 +1261,7 @@ async function loadConfig(rootDir) {
2873
1261
  } else {
2874
1262
  }
2875
1263
  const configFile = foundConfigs[0];
2876
- const configPath = join4(currentDir, configFile);
1264
+ const configPath = join3(currentDir, configFile);
2877
1265
  try {
2878
1266
  let config;
2879
1267
  if (configFile.endsWith(".js")) {
@@ -2915,7 +1303,7 @@ async function loadConfig(rootDir) {
2915
1303
  throw configError;
2916
1304
  }
2917
1305
  }
2918
- const parent = dirname4(currentDir);
1306
+ const parent = dirname3(currentDir);
2919
1307
  if (parent === currentDir) {
2920
1308
  break;
2921
1309
  }
@@ -2947,6 +1335,450 @@ function mergeConfigWithDefaults(userConfig, defaults) {
2947
1335
  return mergedConfig;
2948
1336
  }
2949
1337
 
1338
+ // src/utils/report-formatters.ts
1339
+ function generateReportHead(title) {
1340
+ return `<!DOCTYPE html>
1341
+ <html lang="en">
1342
+ <head>
1343
+ <meta charset="UTF-8">
1344
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1345
+ <title>${title}</title>
1346
+ <style>
1347
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 2rem; background-color: #f9f9f9; }
1348
+ h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
1349
+ .card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 2rem; border: 1px solid #eaeaea; }
1350
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
1351
+ .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
1352
+ .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
1353
+ .stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
1354
+ .hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 8px; margin-bottom: 30px; }
1355
+ .hero h1 { border: none; color: white; margin: 0; }
1356
+ .hero p { margin: 10px 0 0 0; opacity: 0.9; }
1357
+ table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
1358
+ th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
1359
+ th { background-color: #f8fafc; font-weight: 600; color: #475569; }
1360
+ tr:last-child td { border-bottom: none; }
1361
+ .critical { color: #dc2626; font-weight: bold; }
1362
+ .major { color: #ea580c; font-weight: bold; }
1363
+ .minor { color: #2563eb; }
1364
+ .issue-critical { color: #dc2626; font-weight: bold; text-transform: uppercase; }
1365
+ .issue-major { color: #ea580c; font-weight: bold; text-transform: uppercase; }
1366
+ .issue-minor { color: #2563eb; font-weight: bold; text-transform: uppercase; }
1367
+ code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
1368
+ .footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
1369
+ a { color: #2563eb; text-decoration: none; }
1370
+ a:hover { text-decoration: underline; }
1371
+ </style>
1372
+ </head>`;
1373
+ }
1374
+ function generateReportHero(title, subtitle) {
1375
+ const subtitleHtml = subtitle ? `<p>${subtitle}</p>` : "";
1376
+ return `<div class="hero">
1377
+ <h1>${title}</h1>
1378
+ ${subtitleHtml}
1379
+ </div>`;
1380
+ }
1381
+ function generateStatCards(cards) {
1382
+ const cardsHtml = cards.map(
1383
+ (card) => `
1384
+ <div class="stat-card">
1385
+ <div class="stat-value"${card.color ? ` style="color: ${card.color}"` : ""}>${card.value}</div>
1386
+ <div class="stat-label">${card.label}</div>
1387
+ </div>`
1388
+ ).join("");
1389
+ return `<div class="stats">${cardsHtml}</div>`;
1390
+ }
1391
+ function generateScoreCard(value, label) {
1392
+ return `<div class="stat-card" style="margin-bottom: 2rem;">
1393
+ <div class="stat-label">${label}</div>
1394
+ <div class="stat-value">${value}</div>
1395
+ </div>`;
1396
+ }
1397
+ function generateTable(config) {
1398
+ const headersHtml = config.headers.map((h) => `<th>${h}</th>`).join("");
1399
+ const rowsHtml = config.rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
1400
+ return `<table>
1401
+ <thead><tr>${headersHtml}</tr></thead>
1402
+ <tbody>${rowsHtml}</tbody>
1403
+ </table>`;
1404
+ }
1405
+ function generateIssueSummary(critical, major, minor, potentialSavings) {
1406
+ const savingsHtml = potentialSavings ? `<p><strong>Potential Savings:</strong> ${potentialSavings.toLocaleString()} tokens</p>` : "";
1407
+ return `<div class="card" style="margin-bottom: 30px;">
1408
+ <h2>\u26A0\uFE0F Issues Summary</h2>
1409
+ <p>
1410
+ <span class="critical">\u{1F534} Critical: ${critical}</span> &nbsp;
1411
+ <span class="major">\u{1F7E1} Major: ${major}</span> &nbsp;
1412
+ <span class="minor">\u{1F535} Minor: ${minor}</span>
1413
+ </p>
1414
+ ${savingsHtml}
1415
+ </div>`;
1416
+ }
1417
+ function generateReportFooter(options) {
1418
+ const versionText = options.version ? ` v${options.version}` : "";
1419
+ const links = [];
1420
+ if (options.packageUrl) {
1421
+ links.push(`<a href="${options.packageUrl}">Star us on GitHub</a>`);
1422
+ }
1423
+ if (options.bugUrl) {
1424
+ links.push(`<a href="${options.bugUrl}">Report it here</a>`);
1425
+ }
1426
+ const linksHtml = links.length ? links.map((l) => `<p>Like AIReady? ${l}</p>`).join("\n ") : "";
1427
+ return `<div class="footer">
1428
+ <p>Generated by <strong>@aiready/${options.packageName}</strong>${versionText}</p>
1429
+ ${linksHtml}
1430
+ </div>`;
1431
+ }
1432
+ function wrapInCard(content, title) {
1433
+ const titleHtml = title ? `<h2>${title}</h2>` : "";
1434
+ return `<div class="card">
1435
+ ${titleHtml}
1436
+ ${content}
1437
+ </div>`;
1438
+ }
1439
+ function generateCompleteReport(options, bodyContent) {
1440
+ return `${generateReportHead(options.title)}
1441
+ <body>
1442
+ ${bodyContent}
1443
+ ${generateReportFooter(options)}
1444
+ </body>
1445
+ </html>`;
1446
+ }
1447
+
1448
+ // src/utils/scoring-helpers.ts
1449
+ function buildFactorsFromDimensions(dimensions, dimensionNames, rawData) {
1450
+ return Object.entries(dimensionNames).map(([key, name]) => {
1451
+ const val = dimensions[key] ?? 50;
1452
+ return {
1453
+ name,
1454
+ impact: Math.round(val - 50),
1455
+ description: formatDimensionDescription(key, rawData) || `${val}/100`
1456
+ };
1457
+ });
1458
+ }
1459
+ function formatDimensionDescription(key, rawData) {
1460
+ if (key === "testCoverageRatio" && rawData.testFiles !== void 0) {
1461
+ return `${rawData.testFiles} test files / ${rawData.sourceFiles} source files`;
1462
+ }
1463
+ if (key === "purityScore" && rawData.pureFunctions !== void 0) {
1464
+ return `${rawData.pureFunctions}/${rawData.totalFunctions} functions are pure`;
1465
+ }
1466
+ if (key === "dependencyInjectionScore" && rawData.injectionPatterns !== void 0) {
1467
+ return `${rawData.injectionPatterns}/${rawData.totalClasses} classes use DI`;
1468
+ }
1469
+ if (key === "structureClarityScore" && rawData.deepDirectories !== void 0) {
1470
+ return `${rawData.deepDirectories} of ${rawData.totalDirectories} dirs exceed recommended depth`;
1471
+ }
1472
+ if (key === "apiClarityScore" && rawData.untypedExports !== void 0) {
1473
+ return `${rawData.untypedExports} of ${rawData.totalExports} exports lack type annotations`;
1474
+ }
1475
+ if (key === "graphStabilityScore") {
1476
+ return `${rawData.score}/100`;
1477
+ }
1478
+ return void 0;
1479
+ }
1480
+ function buildStandardToolScore(params) {
1481
+ const {
1482
+ toolName,
1483
+ score,
1484
+ rawData,
1485
+ dimensions,
1486
+ dimensionNames,
1487
+ recommendations,
1488
+ recommendationImpact = 8,
1489
+ rating
1490
+ } = params;
1491
+ const factors = buildFactorsFromDimensions(
1492
+ dimensions,
1493
+ dimensionNames,
1494
+ rawData
1495
+ );
1496
+ const recs = recommendations.map(
1497
+ (action) => ({
1498
+ action,
1499
+ estimatedImpact: recommendationImpact,
1500
+ priority: score < 50 || [
1501
+ "high-risk",
1502
+ "blind-risk",
1503
+ "explosive",
1504
+ "fragile",
1505
+ "critical"
1506
+ ].includes(rating || "") ? "high" /* High */ : "medium" /* Medium */
1507
+ })
1508
+ );
1509
+ return {
1510
+ toolName,
1511
+ score,
1512
+ rawMetrics: {
1513
+ ...rawData,
1514
+ rating: rating || rawData.rating
1515
+ },
1516
+ factors,
1517
+ recommendations: recs
1518
+ };
1519
+ }
1520
+
1521
+ // src/utils/similarity.ts
1522
+ function calculateStringSimilarity(a, b) {
1523
+ if (a === b) return 1;
1524
+ const tokensA = a.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 0);
1525
+ const tokensB = b.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 0);
1526
+ if (tokensA.length === 0 || tokensB.length === 0) return 0;
1527
+ const setA = new Set(tokensA);
1528
+ const setB = new Set(tokensB);
1529
+ const intersection = new Set([...setA].filter((x) => setB.has(x)));
1530
+ const union = /* @__PURE__ */ new Set([...setA, ...setB]);
1531
+ return intersection.size / union.size;
1532
+ }
1533
+ function calculateHeuristicConfidence(similarity, tokens, lines) {
1534
+ let confidence = similarity;
1535
+ if (lines > 20) confidence += 0.05;
1536
+ if (tokens > 200) confidence += 0.05;
1537
+ if (lines < 5) confidence -= 0.1;
1538
+ return Math.max(0, Math.min(1, confidence));
1539
+ }
1540
+
1541
+ // src/utils/cli-factory.ts
1542
+ import chalk2 from "chalk";
1543
+ function createStandardProgressCallback(toolName) {
1544
+ return (processed, total, message) => {
1545
+ const percent = Math.round(processed / Math.max(1, total) * 100);
1546
+ process.stdout.write(
1547
+ `\r\x1B[K [${toolName}] ${chalk2.cyan(`${percent}%`)} ${message}`
1548
+ );
1549
+ if (processed === total) {
1550
+ process.stdout.write("\n");
1551
+ }
1552
+ };
1553
+ }
1554
+ function formatStandardCliResult(toolName, score, issuesCount) {
1555
+ const scoreColor = score >= 75 ? chalk2.green : score >= 50 ? chalk2.yellow : chalk2.red;
1556
+ console.log(`
1557
+ ${chalk2.bold(toolName.toUpperCase())} Analysis Complete`);
1558
+ console.log(` Overall Score: ${scoreColor(score)}/100`);
1559
+ console.log(
1560
+ ` Issues Found: ${issuesCount > 0 ? chalk2.red(issuesCount) : chalk2.green("None")}`
1561
+ );
1562
+ }
1563
+ async function runStandardCliAction(toolName, action) {
1564
+ try {
1565
+ const { score, issuesCount } = await action();
1566
+ formatStandardCliResult(toolName, score, issuesCount);
1567
+ } catch (error) {
1568
+ console.error(
1569
+ chalk2.red(`
1570
+ \u274C [${toolName}] critical error: ${error.message}`)
1571
+ );
1572
+ process.exit(1);
1573
+ }
1574
+ }
1575
+
1576
+ // src/utils/reporting.ts
1577
+ import chalk3 from "chalk";
1578
+ function getScoreColor(score) {
1579
+ if (score >= 85) return chalk3.green;
1580
+ if (score >= 70) return chalk3.cyan;
1581
+ if (score >= 50) return chalk3.yellow;
1582
+ if (score >= 30) return chalk3.red;
1583
+ return chalk3.bgRed.white;
1584
+ }
1585
+ function displayStandardConsoleReport(data) {
1586
+ const {
1587
+ title,
1588
+ score,
1589
+ rating,
1590
+ dimensions,
1591
+ stats = [],
1592
+ issues,
1593
+ recommendations = [],
1594
+ elapsedTime,
1595
+ noIssuesMessage = "\u2728 No issues found!",
1596
+ safetyRating
1597
+ } = data;
1598
+ console.log(chalk3.bold(`
1599
+ ${title}
1600
+ `));
1601
+ if (safetyRating) {
1602
+ if (safetyRating === "blind-risk" || safetyRating === "\u{1F480} blind-risk") {
1603
+ console.log(
1604
+ chalk3.bgRed.white.bold(
1605
+ " \u{1F480} BLIND RISK \u2014 NO TESTS DETECTED. AI-GENERATED CHANGES CANNOT BE VERIFIED. "
1606
+ )
1607
+ );
1608
+ console.log();
1609
+ } else if (safetyRating === "high-risk" || safetyRating === "\u{1F534} high-risk") {
1610
+ console.log(
1611
+ chalk3.red.bold(
1612
+ ` \u{1F534} HIGH RISK \u2014 Insufficient test coverage. AI changes may introduce silent bugs.`
1613
+ )
1614
+ );
1615
+ console.log();
1616
+ }
1617
+ }
1618
+ const safetyColor = safetyRating ? getSeverityColor(safetyRating, chalk3) : getScoreColor(score);
1619
+ if (safetyRating) {
1620
+ console.log(
1621
+ `AI Change Safety: ${safetyColor(`${getSafetyIcon(safetyRating)} ${safetyRating.toUpperCase()}`)}`
1622
+ );
1623
+ }
1624
+ console.log(
1625
+ `Score: ${getScoreColor(score)(score + "/100")} (${rating.toUpperCase()})`
1626
+ );
1627
+ if (stats.length > 0) {
1628
+ const statsStr = stats.map((s) => `${s.label}: ${chalk3.cyan(s.value)}`).join(" ");
1629
+ console.log(statsStr);
1630
+ }
1631
+ console.log(`Analysis Time: ${chalk3.gray(elapsedTime + "s")}
1632
+ `);
1633
+ console.log(chalk3.bold("\u{1F4D0} Dimension Scores\n"));
1634
+ for (const dim of dimensions) {
1635
+ const color = getScoreColor(dim.value);
1636
+ console.log(
1637
+ ` ${dim.name.padEnd(22)} ${color(getScoreBar(dim.value))} ${dim.value}/100`
1638
+ );
1639
+ }
1640
+ if (issues.length > 0) {
1641
+ console.log(chalk3.bold("\n\u26A0\uFE0F Issues\n"));
1642
+ for (const issue of issues) {
1643
+ const sev = getSeverityColor(issue.severity, chalk3);
1644
+ console.log(`${sev(issue.severity.toUpperCase())} ${issue.message}`);
1645
+ if (issue.suggestion) {
1646
+ console.log(
1647
+ ` ${chalk3.dim("\u2192")} ${chalk3.italic(issue.suggestion)}`
1648
+ );
1649
+ }
1650
+ console.log();
1651
+ }
1652
+ } else {
1653
+ console.log(chalk3.green(`
1654
+ ${noIssuesMessage}
1655
+ `));
1656
+ }
1657
+ if (recommendations.length > 0) {
1658
+ console.log(chalk3.bold("\u{1F4A1} Recommendations\n"));
1659
+ recommendations.forEach((rec, i) => {
1660
+ console.log(`${i + 1}. ${rec}`);
1661
+ });
1662
+ }
1663
+ console.log();
1664
+ }
1665
+
1666
+ // src/utils/code-extractor.ts
1667
+ function inferPatternType(keyword, name) {
1668
+ const n = name.toLowerCase();
1669
+ if (keyword === "handler" || n.includes("handler") || n.includes("controller") || n.startsWith("app.")) {
1670
+ return "api-handler";
1671
+ }
1672
+ if (n.includes("validate") || n.includes("schema")) return "validator";
1673
+ if (n.includes("util") || n.includes("helper")) return "utility";
1674
+ if (keyword === "class") return "class-method";
1675
+ if (n.match(/^[A-Z]/)) return "component";
1676
+ if (keyword === "function" || keyword === "def") return "function";
1677
+ return "unknown";
1678
+ }
1679
+ function extractCodeBlocks(file, content) {
1680
+ const isPython = file.toLowerCase().endsWith(".py");
1681
+ if (isPython) {
1682
+ return extractBlocksPython(file, content);
1683
+ }
1684
+ const blocks = [];
1685
+ const lines = content.split("\n");
1686
+ const blockRegex = /^\s*(?:export\s+)?(?:async\s+)?(?:public\s+|private\s+|protected\s+|internal\s+|static\s+|readonly\s+|virtual\s+|abstract\s+|override\s+)*(function|class|interface|type|enum|record|struct|void|func|[a-zA-Z0-9_<>[]]+)\s+([a-zA-Z0-9_]+)(?:\s*\(|(?:\s+extends|\s+implements|\s+where)?\s*\{)|^\s*(?:export\s+)?const\s+([a-zA-Z0-9_]+)\s*=\s*[a-zA-Z0-9_.]+\.object\(|^\s*(app\.(?:get|post|put|delete|patch|use))\(/gm;
1687
+ let match;
1688
+ while ((match = blockRegex.exec(content)) !== null) {
1689
+ const startLine = content.substring(0, match.index).split("\n").length;
1690
+ let type;
1691
+ let name;
1692
+ if (match[1]) {
1693
+ type = match[1];
1694
+ name = match[2];
1695
+ } else if (match[3]) {
1696
+ type = "const";
1697
+ name = match[3];
1698
+ } else {
1699
+ type = "handler";
1700
+ name = match[4];
1701
+ }
1702
+ let endLine = -1;
1703
+ let openBraces = 0;
1704
+ let foundStart = false;
1705
+ const lineEnd = content.indexOf("\n", match.index);
1706
+ const lineText = content.substring(
1707
+ match.index,
1708
+ lineEnd === -1 ? content.length : lineEnd
1709
+ );
1710
+ if (lineText.includes("{") && lineText.includes("}")) {
1711
+ endLine = startLine;
1712
+ } else {
1713
+ for (let i = match.index; i < content.length; i++) {
1714
+ if (content[i] === "{") {
1715
+ openBraces++;
1716
+ foundStart = true;
1717
+ } else if (content[i] === "}") {
1718
+ openBraces--;
1719
+ }
1720
+ if (foundStart && openBraces === 0) {
1721
+ endLine = content.substring(0, i + 1).split("\n").length;
1722
+ break;
1723
+ }
1724
+ }
1725
+ }
1726
+ if (endLine === -1) {
1727
+ endLine = startLine;
1728
+ }
1729
+ endLine = Math.max(startLine, endLine);
1730
+ const blockCode = lines.slice(startLine - 1, endLine).join("\n");
1731
+ const tokens = estimateTokens(blockCode);
1732
+ blocks.push({
1733
+ file,
1734
+ startLine,
1735
+ endLine,
1736
+ code: blockCode,
1737
+ tokens,
1738
+ patternType: inferPatternType(type, name)
1739
+ });
1740
+ }
1741
+ return blocks;
1742
+ }
1743
+ function extractBlocksPython(file, content) {
1744
+ const blocks = [];
1745
+ const lines = content.split("\n");
1746
+ const blockRegex = /^\s*(?:async\s+)?(def|class)\s+([a-zA-Z0-9_]+)/gm;
1747
+ let match;
1748
+ while ((match = blockRegex.exec(content)) !== null) {
1749
+ const startLinePos = content.substring(0, match.index).split("\n").length;
1750
+ const startLineIdx = startLinePos - 1;
1751
+ const initialIndent = lines[startLineIdx].search(/\S/);
1752
+ let endLineIdx = startLineIdx;
1753
+ for (let i = startLineIdx + 1; i < lines.length; i++) {
1754
+ const line = lines[i];
1755
+ if (line.trim().length === 0) {
1756
+ endLineIdx = i;
1757
+ continue;
1758
+ }
1759
+ const currentIndent = line.search(/\S/);
1760
+ if (currentIndent <= initialIndent) {
1761
+ break;
1762
+ }
1763
+ endLineIdx = i;
1764
+ }
1765
+ while (endLineIdx > startLineIdx && lines[endLineIdx].trim().length === 0) {
1766
+ endLineIdx--;
1767
+ }
1768
+ const blockCode = lines.slice(startLineIdx, endLineIdx + 1).join("\n");
1769
+ const tokens = estimateTokens(blockCode);
1770
+ blocks.push({
1771
+ file,
1772
+ startLine: startLinePos,
1773
+ endLine: endLineIdx + 1,
1774
+ code: blockCode,
1775
+ tokens,
1776
+ patternType: inferPatternType(match[1], match[2])
1777
+ });
1778
+ }
1779
+ return blocks;
1780
+ }
1781
+
2950
1782
  // src/business/pricing-models.ts
2951
1783
  var MODEL_PRICING_PRESETS = {
2952
1784
  "gpt-5.4-mini": {
@@ -3010,20 +1842,52 @@ var DEFAULT_COST_CONFIG = {
3010
1842
  developerCount: 5,
3011
1843
  daysPerMonth: 30
3012
1844
  };
3013
- function calculateMonthlyCost(tokenWaste, config = {}) {
3014
- const multiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
1845
+ function calculateMonthlyCost(tokenWaste, config = {}, options) {
1846
+ const baseMultiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
1847
+ const contextMultiplier = options?.avgContextBudget ? options.avgContextBudget / Math.max(1, tokenWaste) : baseMultiplier;
1848
+ const fragRatio = options?.fragmentationScore ?? 0.3;
1849
+ const dupRatio = 1 - fragRatio;
3015
1850
  const budget = calculateTokenBudget({
3016
- totalContextTokens: tokenWaste * multiplier,
1851
+ totalContextTokens: tokenWaste * contextMultiplier,
3017
1852
  wastedTokens: {
3018
- duplication: tokenWaste * 0.7,
3019
- fragmentation: tokenWaste * 0.3,
1853
+ duplication: tokenWaste * dupRatio * (options?.potentialSavings ? 1.2 : 1),
1854
+ fragmentation: tokenWaste * fragRatio,
3020
1855
  chattiness: 0.1 * tokenWaste
3021
- // Added baseline chattiness
3022
1856
  }
3023
1857
  });
3024
- const preset = getModelPreset("gpt-5.4-mini");
1858
+ const preset = getModelPreset("claude-3.5-sonnet");
3025
1859
  return estimateCostFromBudget(budget, preset, config);
3026
1860
  }
1861
+ function calculateDetailedTokenROI(params) {
1862
+ const {
1863
+ totalTokens,
1864
+ avgContextBudget,
1865
+ potentialSavings,
1866
+ fragmentationScore,
1867
+ developerCount,
1868
+ queriesPerDevPerDay = 60
1869
+ } = params;
1870
+ const budget = calculateTokenBudget({
1871
+ totalContextTokens: avgContextBudget,
1872
+ wastedTokens: {
1873
+ duplication: potentialSavings * 0.8,
1874
+ // 80% of potential savings are duplication-based
1875
+ fragmentation: totalTokens * fragmentationScore * 0.5,
1876
+ // fragmentation impact
1877
+ chattiness: totalTokens * 0.1
1878
+ }
1879
+ });
1880
+ const model = getModelPreset("claude-3.5-sonnet");
1881
+ const cost = estimateCostFromBudget(budget, model, {
1882
+ developerCount,
1883
+ queriesPerDevPerDay
1884
+ });
1885
+ return {
1886
+ monthlySavings: Math.round(cost.total),
1887
+ contextTaxPerDev: Math.round(cost.total / (developerCount || 1) * 100) / 100,
1888
+ efficiencyGain: Math.round(budget.efficiencyRatio * 100) / 100
1889
+ };
1890
+ }
3027
1891
  function calculateTokenBudget(params) {
3028
1892
  const { totalContextTokens, wastedTokens } = params;
3029
1893
  const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
@@ -3579,45 +2443,175 @@ function calculatePatternEntropy(files) {
3579
2443
  recommendations
3580
2444
  };
3581
2445
  }
3582
- function calculateConceptCohesion(params) {
3583
- const { exports } = params;
3584
- if (exports.length === 0) {
3585
- return {
3586
- score: 1,
3587
- rating: "excellent",
3588
- analysis: {
3589
- uniqueDomains: 0,
3590
- domainConcentration: 0,
3591
- exportPurposeClarity: 1
3592
- }
3593
- };
2446
+ function calculateConceptCohesion(params) {
2447
+ const { exports } = params;
2448
+ if (exports.length === 0) {
2449
+ return {
2450
+ score: 1,
2451
+ rating: "excellent",
2452
+ analysis: {
2453
+ uniqueDomains: 0,
2454
+ domainConcentration: 0,
2455
+ exportPurposeClarity: 1
2456
+ }
2457
+ };
2458
+ }
2459
+ const allDomains = [];
2460
+ for (const exp of exports) {
2461
+ if (exp.inferredDomain) allDomains.push(exp.inferredDomain);
2462
+ if (exp.domains) allDomains.push(...exp.domains);
2463
+ }
2464
+ const uniqueDomains = new Set(allDomains);
2465
+ const domainCounts = /* @__PURE__ */ new Map();
2466
+ for (const d of allDomains)
2467
+ domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
2468
+ const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
2469
+ const domainConcentration = maxCount / allDomains.length;
2470
+ const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
2471
+ const score = domainConcentration * 0.5 + exportPurposeClarity * 0.5;
2472
+ let rating;
2473
+ if (score > 0.8) rating = "excellent";
2474
+ else if (score > 0.6) rating = "good";
2475
+ else if (score > 0.4) rating = "moderate";
2476
+ else rating = "poor";
2477
+ return {
2478
+ score: Math.round(score * 100) / 100,
2479
+ rating,
2480
+ analysis: {
2481
+ uniqueDomains: uniqueDomains.size,
2482
+ domainConcentration: Math.round(domainConcentration * 100) / 100,
2483
+ exportPurposeClarity: Math.round(exportPurposeClarity * 100) / 100
2484
+ }
2485
+ };
2486
+ }
2487
+
2488
+ // src/future-proof-metrics.ts
2489
+ function calculateFutureProofScore(params) {
2490
+ const loadScore = 100 - params.cognitiveLoad.score;
2491
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2492
+ const cohesionScore = params.conceptCohesion.score * 100;
2493
+ const overall = Math.round(
2494
+ loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
2495
+ );
2496
+ const factors = [
2497
+ {
2498
+ name: "Cognitive Load",
2499
+ impact: Math.round(loadScore - 50),
2500
+ description: params.cognitiveLoad.rating
2501
+ },
2502
+ {
2503
+ name: "Pattern Entropy",
2504
+ impact: Math.round(entropyScore - 50),
2505
+ description: params.patternEntropy.rating
2506
+ },
2507
+ {
2508
+ name: "Concept Cohesion",
2509
+ impact: Math.round(cohesionScore - 50),
2510
+ description: params.conceptCohesion.rating
2511
+ }
2512
+ ];
2513
+ const recommendations = collectBaseFutureProofRecommendations({
2514
+ patternEntropy: params.patternEntropy,
2515
+ conceptCohesion: params.conceptCohesion
2516
+ });
2517
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2518
+ return {
2519
+ toolName: "future-proof",
2520
+ score: overall,
2521
+ rawMetrics: {
2522
+ cognitiveLoadScore: params.cognitiveLoad.score,
2523
+ entropyScore: params.patternEntropy.entropy,
2524
+ cohesionScore: params.conceptCohesion.score,
2525
+ semanticDistanceAvg
2526
+ },
2527
+ factors,
2528
+ recommendations
2529
+ };
2530
+ }
2531
+ function calculateExtendedFutureProofScore(params) {
2532
+ const loadScore = 100 - params.cognitiveLoad.score;
2533
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2534
+ const cohesionScore = params.conceptCohesion.score * 100;
2535
+ const aiSignalClarityScore = 100 - params.aiSignalClarity.score;
2536
+ const groundingScore = params.agentGrounding.score;
2537
+ const testabilityScore = params.testability.score;
2538
+ const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2539
+ const depsHealthScore = params.dependencyHealth?.score ?? 100;
2540
+ let totalWeight = 0.8;
2541
+ let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2542
+ if (params.docDrift) {
2543
+ overall += docDriftScore * 0.1;
2544
+ totalWeight += 0.1;
3594
2545
  }
3595
- const allDomains = [];
3596
- for (const exp of exports) {
3597
- if (exp.inferredDomain) allDomains.push(exp.inferredDomain);
3598
- if (exp.domains) allDomains.push(...exp.domains);
2546
+ if (params.dependencyHealth) {
2547
+ overall += depsHealthScore * 0.1;
2548
+ totalWeight += 0.1;
3599
2549
  }
3600
- const uniqueDomains = new Set(allDomains);
3601
- const domainCounts = /* @__PURE__ */ new Map();
3602
- for (const d of allDomains)
3603
- domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
3604
- const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
3605
- const domainConcentration = maxCount / allDomains.length;
3606
- const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
3607
- const score = domainConcentration * 0.5 + exportPurposeClarity * 0.5;
3608
- let rating;
3609
- if (score > 0.8) rating = "excellent";
3610
- else if (score > 0.6) rating = "good";
3611
- else if (score > 0.4) rating = "moderate";
3612
- else rating = "poor";
3613
- return {
3614
- score: Math.round(score * 100) / 100,
3615
- rating,
3616
- analysis: {
3617
- uniqueDomains: uniqueDomains.size,
3618
- domainConcentration: Math.round(domainConcentration * 100) / 100,
3619
- exportPurposeClarity: Math.round(exportPurposeClarity * 100) / 100
2550
+ overall = Math.round(overall / totalWeight);
2551
+ const factors = [
2552
+ {
2553
+ name: "Cognitive Load",
2554
+ impact: Math.round(loadScore - 50),
2555
+ description: params.cognitiveLoad.rating
2556
+ },
2557
+ {
2558
+ name: "Pattern Entropy",
2559
+ impact: Math.round(entropyScore - 50),
2560
+ description: params.patternEntropy.rating
2561
+ },
2562
+ {
2563
+ name: "Concept Cohesion",
2564
+ impact: Math.round(cohesionScore - 50),
2565
+ description: params.conceptCohesion.rating
2566
+ },
2567
+ {
2568
+ name: "AI Signal Clarity",
2569
+ impact: Math.round(aiSignalClarityScore - 50),
2570
+ description: `${params.aiSignalClarity.rating} risk`
2571
+ },
2572
+ {
2573
+ name: "Agent Grounding",
2574
+ impact: Math.round(groundingScore - 50),
2575
+ description: params.agentGrounding.rating
2576
+ },
2577
+ {
2578
+ name: "Testability",
2579
+ impact: Math.round(testabilityScore - 50),
2580
+ description: params.testability.rating
3620
2581
  }
2582
+ ];
2583
+ if (params.docDrift) {
2584
+ factors.push({
2585
+ name: "Documentation Drift",
2586
+ impact: Math.round(docDriftScore - 50),
2587
+ description: params.docDrift.rating
2588
+ });
2589
+ }
2590
+ if (params.dependencyHealth) {
2591
+ factors.push({
2592
+ name: "Dependency Health",
2593
+ impact: Math.round(depsHealthScore - 50),
2594
+ description: params.dependencyHealth.rating
2595
+ });
2596
+ }
2597
+ const recommendations = collectFutureProofRecommendations(params);
2598
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2599
+ return {
2600
+ toolName: "future-proof",
2601
+ score: overall,
2602
+ rawMetrics: {
2603
+ cognitiveLoadScore: params.cognitiveLoad.score,
2604
+ entropyScore: params.patternEntropy.entropy,
2605
+ cohesionScore: params.conceptCohesion.score,
2606
+ aiSignalClarityScore: params.aiSignalClarity.score,
2607
+ agentGroundingScore: params.agentGrounding.score,
2608
+ testabilityScore: params.testability.score,
2609
+ docDriftScore: params.docDrift?.score,
2610
+ dependencyHealthScore: params.dependencyHealth?.score,
2611
+ semanticDistanceAvg
2612
+ },
2613
+ factors,
2614
+ recommendations
3621
2615
  };
3622
2616
  }
3623
2617
 
@@ -4173,145 +3167,15 @@ function calculateChangeAmplification(params) {
4173
3167
  };
4174
3168
  }
4175
3169
 
4176
- // src/future-proof-metrics.ts
4177
- function calculateFutureProofScore(params) {
4178
- const loadScore = 100 - params.cognitiveLoad.score;
4179
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
4180
- const cohesionScore = params.conceptCohesion.score * 100;
4181
- const overall = Math.round(
4182
- loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
4183
- );
4184
- const factors = [
4185
- {
4186
- name: "Cognitive Load",
4187
- impact: Math.round(loadScore - 50),
4188
- description: params.cognitiveLoad.rating
4189
- },
4190
- {
4191
- name: "Pattern Entropy",
4192
- impact: Math.round(entropyScore - 50),
4193
- description: params.patternEntropy.rating
4194
- },
4195
- {
4196
- name: "Concept Cohesion",
4197
- impact: Math.round(cohesionScore - 50),
4198
- description: params.conceptCohesion.rating
4199
- }
4200
- ];
4201
- const recommendations = collectBaseFutureProofRecommendations({
4202
- patternEntropy: params.patternEntropy,
4203
- conceptCohesion: params.conceptCohesion
4204
- });
4205
- const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
4206
- return {
4207
- toolName: "future-proof",
4208
- score: overall,
4209
- rawMetrics: {
4210
- cognitiveLoadScore: params.cognitiveLoad.score,
4211
- entropyScore: params.patternEntropy.entropy,
4212
- cohesionScore: params.conceptCohesion.score,
4213
- semanticDistanceAvg
4214
- },
4215
- factors,
4216
- recommendations
4217
- };
4218
- }
4219
- function calculateExtendedFutureProofScore(params) {
4220
- const loadScore = 100 - params.cognitiveLoad.score;
4221
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
4222
- const cohesionScore = params.conceptCohesion.score * 100;
4223
- const aiSignalClarityScore = 100 - params.aiSignalClarity.score;
4224
- const groundingScore = params.agentGrounding.score;
4225
- const testabilityScore = params.testability.score;
4226
- const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
4227
- const depsHealthScore = params.dependencyHealth?.score ?? 100;
4228
- let totalWeight = 0.8;
4229
- let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
4230
- if (params.docDrift) {
4231
- overall += docDriftScore * 0.1;
4232
- totalWeight += 0.1;
4233
- }
4234
- if (params.dependencyHealth) {
4235
- overall += depsHealthScore * 0.1;
4236
- totalWeight += 0.1;
4237
- }
4238
- overall = Math.round(overall / totalWeight);
4239
- const factors = [
4240
- {
4241
- name: "Cognitive Load",
4242
- impact: Math.round(loadScore - 50),
4243
- description: params.cognitiveLoad.rating
4244
- },
4245
- {
4246
- name: "Pattern Entropy",
4247
- impact: Math.round(entropyScore - 50),
4248
- description: params.patternEntropy.rating
4249
- },
4250
- {
4251
- name: "Concept Cohesion",
4252
- impact: Math.round(cohesionScore - 50),
4253
- description: params.conceptCohesion.rating
4254
- },
4255
- {
4256
- name: "AI Signal Clarity",
4257
- impact: Math.round(aiSignalClarityScore - 50),
4258
- description: `${params.aiSignalClarity.rating} risk`
4259
- },
4260
- {
4261
- name: "Agent Grounding",
4262
- impact: Math.round(groundingScore - 50),
4263
- description: params.agentGrounding.rating
4264
- },
4265
- {
4266
- name: "Testability",
4267
- impact: Math.round(testabilityScore - 50),
4268
- description: params.testability.rating
4269
- }
4270
- ];
4271
- if (params.docDrift) {
4272
- factors.push({
4273
- name: "Documentation Drift",
4274
- impact: Math.round(docDriftScore - 50),
4275
- description: params.docDrift.rating
4276
- });
4277
- }
4278
- if (params.dependencyHealth) {
4279
- factors.push({
4280
- name: "Dependency Health",
4281
- impact: Math.round(depsHealthScore - 50),
4282
- description: params.dependencyHealth.rating
4283
- });
4284
- }
4285
- const recommendations = collectFutureProofRecommendations(params);
4286
- const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
4287
- return {
4288
- toolName: "future-proof",
4289
- score: overall,
4290
- rawMetrics: {
4291
- cognitiveLoadScore: params.cognitiveLoad.score,
4292
- entropyScore: params.patternEntropy.entropy,
4293
- cohesionScore: params.conceptCohesion.score,
4294
- aiSignalClarityScore: params.aiSignalClarity.score,
4295
- agentGroundingScore: params.agentGrounding.score,
4296
- testabilityScore: params.testability.score,
4297
- docDriftScore: params.docDrift?.score,
4298
- dependencyHealthScore: params.dependencyHealth?.score,
4299
- semanticDistanceAvg
4300
- },
4301
- factors,
4302
- recommendations
4303
- };
4304
- }
4305
-
4306
3170
  // src/utils/history.ts
4307
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
4308
- import { join as join5, dirname as dirname5 } from "path";
3171
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
3172
+ import { join as join4, dirname as dirname4 } from "path";
4309
3173
  function getHistoryPath(rootDir) {
4310
- return join5(rootDir, ".aiready", "history.json");
3174
+ return join4(rootDir, ".aiready", "history.json");
4311
3175
  }
4312
3176
  function loadScoreHistory(rootDir) {
4313
3177
  const historyPath = getHistoryPath(rootDir);
4314
- if (!existsSync5(historyPath)) {
3178
+ if (!existsSync4(historyPath)) {
4315
3179
  return [];
4316
3180
  }
4317
3181
  try {
@@ -4324,8 +3188,8 @@ function loadScoreHistory(rootDir) {
4324
3188
  }
4325
3189
  function saveScoreEntry(rootDir, entry) {
4326
3190
  const historyPath = getHistoryPath(rootDir);
4327
- const historyDir = dirname5(historyPath);
4328
- if (!existsSync5(historyDir)) {
3191
+ const historyDir = dirname4(historyPath);
3192
+ if (!existsSync4(historyDir)) {
4329
3193
  mkdirSync2(historyDir, { recursive: true });
4330
3194
  }
4331
3195
  const history = loadScoreHistory(rootDir);
@@ -4372,7 +3236,7 @@ function exportHistory(rootDir, format = "json") {
4372
3236
  }
4373
3237
  function clearHistory(rootDir) {
4374
3238
  const historyPath = getHistoryPath(rootDir);
4375
- if (existsSync5(historyPath)) {
3239
+ if (existsSync4(historyPath)) {
4376
3240
  writeFileSync2(historyPath, JSON.stringify([]));
4377
3241
  }
4378
3242
  }
@@ -4539,8 +3403,10 @@ export {
4539
3403
  TypeScriptParser,
4540
3404
  UnifiedReportSchema,
4541
3405
  VAGUE_FILE_NAMES,
3406
+ buildFactorsFromDimensions,
4542
3407
  buildSimpleProviderScore,
4543
3408
  buildSpokeOutput,
3409
+ buildStandardToolScore,
4544
3410
  calculateAgentGrounding,
4545
3411
  calculateAiSignalClarity,
4546
3412
  calculateBusinessROI,
@@ -4550,9 +3416,11 @@ export {
4550
3416
  calculateConceptCohesion,
4551
3417
  calculateDebtInterest,
4552
3418
  calculateDependencyHealth,
3419
+ calculateDetailedTokenROI,
4553
3420
  calculateDocDrift,
4554
3421
  calculateExtendedFutureProofScore,
4555
3422
  calculateFutureProofScore,
3423
+ calculateHeuristicConfidence,
4556
3424
  calculateImportSimilarity,
4557
3425
  calculateKnowledgeConcentration,
4558
3426
  calculateMonthlyCost,
@@ -4560,25 +3428,39 @@ export {
4560
3428
  calculatePatternEntropy,
4561
3429
  calculateProductivityImpact,
4562
3430
  calculateSemanticDistance,
3431
+ calculateStringSimilarity,
4563
3432
  calculateTechnicalValueChain,
4564
3433
  calculateTestabilityIndex,
4565
3434
  calculateTokenBudget,
4566
3435
  clearHistory,
4567
3436
  createProvider,
3437
+ createStandardProgressCallback,
3438
+ displayStandardConsoleReport,
4568
3439
  emitAnnotation,
4569
3440
  emitIssuesAsAnnotations,
4570
3441
  emitProgress,
4571
3442
  estimateCostFromBudget,
4572
3443
  estimateTokens,
4573
3444
  exportHistory,
3445
+ extractCodeBlocks,
4574
3446
  findLatestReport,
4575
3447
  findLatestScanReport,
4576
3448
  formatAcceptanceRate,
4577
3449
  formatCost,
4578
3450
  formatHours,
4579
3451
  formatScore,
3452
+ formatStandardCliResult,
3453
+ formatStandardReport,
4580
3454
  formatToolScore,
3455
+ generateCompleteReport,
4581
3456
  generateHTML,
3457
+ generateIssueSummary,
3458
+ generateReportFooter,
3459
+ generateReportHead,
3460
+ generateReportHero,
3461
+ generateScoreCard,
3462
+ generateStatCards,
3463
+ generateTable,
4582
3464
  generateValueChain,
4583
3465
  getElapsedTime,
4584
3466
  getFileCommitTimestamps,
@@ -4587,26 +3469,36 @@ export {
4587
3469
  getLineRangeLastModifiedCached,
4588
3470
  getModelPreset,
4589
3471
  getParser,
3472
+ getPriorityIcon,
4590
3473
  getProjectSizeTier,
4591
3474
  getRating,
4592
3475
  getRatingDisplay,
3476
+ getRatingEmoji,
3477
+ getRatingLabel,
3478
+ getRatingMetadata,
4593
3479
  getRatingSlug,
4594
3480
  getRatingWithContext,
4595
3481
  getRecommendedThreshold,
4596
3482
  getRepoMetadata,
3483
+ getReportTimestamp,
4597
3484
  getSafetyIcon,
4598
3485
  getScoreBar,
3486
+ getScoreColor,
4599
3487
  getSeverityBadge,
4600
3488
  getSeverityColor,
4601
3489
  getSeverityEnum,
4602
3490
  getSeverityLevel,
4603
3491
  getSeverityValue,
4604
3492
  getSupportedLanguages,
3493
+ getTerminalDivider,
3494
+ getToolEmoji,
4605
3495
  getToolWeight,
4606
3496
  getWasmPath,
4607
3497
  groupIssuesByFile,
4608
3498
  handleCLIError,
4609
3499
  handleJSONOutput,
3500
+ handleStandardJSONOutput,
3501
+ inferPatternType,
4610
3502
  initTreeSitter,
4611
3503
  initializeParsers,
4612
3504
  isFileSupported,
@@ -4623,13 +3515,18 @@ export {
4623
3515
  parseFileExports,
4624
3516
  parseWeightString,
4625
3517
  predictAcceptanceRate,
3518
+ prepareActionConfig,
3519
+ printTerminalHeader,
4626
3520
  readFileContent,
3521
+ resolveOutputFormat,
4627
3522
  resolveOutputPath,
3523
+ runStandardCliAction,
4628
3524
  saveScoreEntry,
4629
3525
  scanEntries,
4630
3526
  scanFiles,
4631
3527
  setupParser,
4632
3528
  severityToAnnotationLevel,
4633
3529
  validateSpokeOutput,
4634
- validateWithSchema
3530
+ validateWithSchema,
3531
+ wrapInCard
4635
3532
  };