@aiready/core 0.23.20 → 0.23.22

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 (116) 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-REU6OUBT.mjs → chunk-DBOPSRBC.mjs} +47 -22
  5. package/dist/chunk-E55RNGGK.mjs +852 -0
  6. package/dist/chunk-EZ7ECLAZ.mjs +299 -0
  7. package/dist/chunk-FNPULWG7.mjs +248 -0
  8. package/dist/chunk-FZTFKZUQ.mjs +250 -0
  9. package/dist/chunk-GTS642BQ.mjs +262 -0
  10. package/dist/chunk-IXPY5J4K.mjs +248 -0
  11. package/dist/chunk-JJQLYW6Z.mjs +111 -0
  12. package/dist/chunk-L6BKANJC.mjs +130 -0
  13. package/dist/chunk-LXEO5PG3.mjs +292 -0
  14. package/dist/chunk-LZHO636W.mjs +501 -0
  15. package/dist/chunk-MTK2IIDZ.mjs +262 -0
  16. package/dist/chunk-QDCQETSI.mjs +262 -0
  17. package/dist/chunk-QZNY7B2N.mjs +248 -0
  18. package/dist/chunk-RCZSMGCX.mjs +250 -0
  19. package/dist/chunk-SWZOT67M.mjs +250 -0
  20. package/dist/chunk-U3IY2CFC.mjs +36 -0
  21. package/dist/chunk-UBCM5Y6R.mjs +275 -0
  22. package/dist/chunk-UTCRW3N7.mjs +301 -0
  23. package/dist/{chunk-SQHS6PFL.mjs → chunk-UYLH35LA.mjs} +47 -22
  24. package/dist/{chunk-ZB3EHHAG.mjs → chunk-WVNVC2PP.mjs} +90 -60
  25. package/dist/chunk-WYOW6O3P.mjs +114 -0
  26. package/dist/{chunk-RMH2TPAT.mjs → chunk-YRSSR4KN.mjs} +87 -59
  27. package/dist/client/index.d.mts +2 -0
  28. package/dist/client/index.d.ts +2 -0
  29. package/dist/client/index.js +922 -0
  30. package/dist/client/index.mjs +104 -0
  31. package/dist/client-2xbeKnrg.d.mts +1291 -0
  32. package/dist/client-2xbeKnrg.d.ts +1291 -0
  33. package/dist/client-4HLAGzFg.d.mts +1291 -0
  34. package/dist/client-4HLAGzFg.d.ts +1291 -0
  35. package/dist/client-B4TQwNa7.d.mts +1290 -0
  36. package/dist/client-B4TQwNa7.d.ts +1290 -0
  37. package/dist/client-Bdi4ty0v.d.mts +1294 -0
  38. package/dist/client-Bdi4ty0v.d.ts +1294 -0
  39. package/dist/client-BsKpUH3H.d.mts +1339 -0
  40. package/dist/client-BsKpUH3H.d.ts +1339 -0
  41. package/dist/client-Bv1zOaWF.d.mts +1291 -0
  42. package/dist/client-Bv1zOaWF.d.ts +1291 -0
  43. package/dist/client-Bz9YJMIX.d.mts +1290 -0
  44. package/dist/client-Bz9YJMIX.d.ts +1290 -0
  45. package/dist/client-CBpzm34X.d.mts +1291 -0
  46. package/dist/client-CBpzm34X.d.ts +1291 -0
  47. package/dist/client-CNu_tCZZ.d.mts +1305 -0
  48. package/dist/client-CNu_tCZZ.d.ts +1305 -0
  49. package/dist/client-CmEvxxQu.d.mts +1339 -0
  50. package/dist/client-CmEvxxQu.d.ts +1339 -0
  51. package/dist/client-Ctl_0z6F.d.mts +1294 -0
  52. package/dist/client-Ctl_0z6F.d.ts +1294 -0
  53. package/dist/client-DGMAxkZc.d.mts +1339 -0
  54. package/dist/client-DGMAxkZc.d.ts +1339 -0
  55. package/dist/client-DZq-CqcD.d.mts +1292 -0
  56. package/dist/client-DZq-CqcD.d.ts +1292 -0
  57. package/dist/{client-CYz0qxGB.d.mts → client-DcqGfDTt.d.mts} +90 -23
  58. package/dist/{client-CYz0qxGB.d.ts → client-DcqGfDTt.d.ts} +90 -23
  59. package/dist/{client-jGuH6TAG.d.mts → client-O8RvSRm0.d.mts} +18 -1
  60. package/dist/{client-jGuH6TAG.d.ts → client-O8RvSRm0.d.ts} +18 -1
  61. package/dist/client.d.mts +1 -1
  62. package/dist/client.d.ts +1 -1
  63. package/dist/client.js +47 -13
  64. package/dist/client.mjs +6 -4
  65. package/dist/csharp-parser-4ZKCSX5B.mjs +9 -0
  66. package/dist/csharp-parser-5HKICCRR.mjs +9 -0
  67. package/dist/csharp-parser-JCKXIAJW.mjs +9 -0
  68. package/dist/go-parser-J4KIH4RG.mjs +9 -0
  69. package/dist/go-parser-TKXL3DVH.mjs +9 -0
  70. package/dist/go-parser-XOM232XZ.mjs +9 -0
  71. package/dist/index-Ctl_0z6F.d.mts +1294 -0
  72. package/dist/index-Ctl_0z6F.d.ts +1294 -0
  73. package/dist/index.d.mts +372 -165
  74. package/dist/index.d.ts +372 -165
  75. package/dist/index.js +3825 -3123
  76. package/dist/index.mjs +901 -2128
  77. package/dist/java-parser-3KHXOXRQ.mjs +9 -0
  78. package/dist/java-parser-MASGS4WB.mjs +9 -0
  79. package/dist/java-parser-T5LXD63J.mjs +9 -0
  80. package/dist/python-parser-FNFK2473.mjs +8 -0
  81. package/dist/typescript-parser-2GGNRNB5.mjs +7 -0
  82. package/dist/typescript-parser-3ENJ6C7H.mjs +7 -0
  83. package/dist/typescript-parser-4GI7DPSW.mjs +7 -0
  84. package/dist/typescript-parser-4H3HUBO4.mjs +7 -0
  85. package/dist/typescript-parser-K63IVZMF.mjs +7 -0
  86. package/dist/typescript-parser-ZJKROMQG.mjs +7 -0
  87. package/package.json +6 -6
  88. package/dist/chunk-2Y6WZCES.mjs +0 -859
  89. package/dist/chunk-5SHLHMH7.mjs +0 -760
  90. package/dist/chunk-CGOS2J6T.mjs +0 -807
  91. package/dist/chunk-FMNCV4CC.mjs +0 -859
  92. package/dist/chunk-Q55AMEFV.mjs +0 -760
  93. package/dist/chunk-ST75O5C5.mjs +0 -859
  94. package/dist/chunk-TJXR2CHZ.mjs +0 -799
  95. package/dist/client-BEoUYNLp.d.mts +0 -1191
  96. package/dist/client-BEoUYNLp.d.ts +0 -1191
  97. package/dist/client-BrIMPk89.d.mts +0 -1214
  98. package/dist/client-BrIMPk89.d.ts +0 -1214
  99. package/dist/client-C5BuGX4F.d.mts +0 -1205
  100. package/dist/client-C5BuGX4F.d.ts +0 -1205
  101. package/dist/client-CKcjnPXt.d.mts +0 -1214
  102. package/dist/client-CKcjnPXt.d.ts +0 -1214
  103. package/dist/client-CLulBnie.d.mts +0 -1182
  104. package/dist/client-CLulBnie.d.ts +0 -1182
  105. package/dist/client-CQwvp8ep.d.mts +0 -1182
  106. package/dist/client-CQwvp8ep.d.ts +0 -1182
  107. package/dist/client-DLvFR2qA.d.mts +0 -1197
  108. package/dist/client-DLvFR2qA.d.ts +0 -1197
  109. package/dist/client-PFPdeo-z.d.mts +0 -1186
  110. package/dist/client-PFPdeo-z.d.ts +0 -1186
  111. package/dist/client-WVCAIWdJ.d.mts +0 -1192
  112. package/dist/client-WVCAIWdJ.d.ts +0 -1192
  113. package/dist/client-pYldIAg2.d.mts +0 -1209
  114. package/dist/client-pYldIAg2.d.ts +0 -1209
  115. package/dist/client-wk2fgk1q.d.mts +0 -1184
  116. 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,
@@ -47,6 +44,7 @@ import {
47
44
  getRatingDisplay,
48
45
  getRatingEmoji,
49
46
  getRatingLabel,
47
+ getRatingMetadata,
50
48
  getRatingSlug,
51
49
  getRatingWithContext,
52
50
  getRecommendedThreshold,
@@ -54,7 +52,33 @@ import {
54
52
  getToolWeight,
55
53
  normalizeToolName,
56
54
  parseWeightString
57
- } from "./chunk-SQHS6PFL.mjs";
55
+ } from "./chunk-E55RNGGK.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";
58
82
 
59
83
  // src/utils/normalization.ts
60
84
  function normalizeIssue(raw) {
@@ -508,6 +532,30 @@ import {
508
532
  } from "fs";
509
533
  import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
510
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
+ }
511
559
  function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
512
560
  let outputPath;
513
561
  if (userPath) {
@@ -523,10 +571,7 @@ function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()
523
571
  const aireadyDir = join2(baseDir, ".aiready");
524
572
  outputPath = join2(aireadyDir, defaultFilename);
525
573
  }
526
- const parentDir = dirname2(outputPath);
527
- if (!existsSync2(parentDir)) {
528
- mkdirSync(parentDir, { recursive: true });
529
- }
574
+ ensureDir(outputPath);
530
575
  return outputPath;
531
576
  }
532
577
  async function loadMergedConfig(directory, defaults, cliOptions) {
@@ -541,16 +586,24 @@ async function loadMergedConfig(directory, defaults, cliOptions) {
541
586
  }
542
587
  function handleJSONOutput(data, outputFile, successMessage) {
543
588
  if (outputFile) {
544
- const dir = dirname2(outputFile);
545
- if (!existsSync2(dir)) {
546
- mkdirSync(dir, { recursive: true });
547
- }
589
+ ensureDir(outputFile);
548
590
  writeFileSync(outputFile, JSON.stringify(data, null, 2));
549
591
  console.log(successMessage || `\u2705 Results saved to ${outputFile}`);
550
592
  } else {
551
593
  console.log(JSON.stringify(data, null, 2));
552
594
  }
553
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
+ }
554
607
  function handleCLIError(error, commandName) {
555
608
  console.error(`\u274C ${commandName} failed:`, error);
556
609
  process.exit(1);
@@ -583,33 +636,30 @@ function emitProgress(processed, total, toolId, message, onProgress, throttleCou
583
636
  }
584
637
  }
585
638
  function getSeverityColor(severity, chalkInstance = chalk) {
586
- switch (severity.toLowerCase()) {
587
- case "critical":
588
- case "high-risk":
589
- case "blind-risk":
639
+ const normalized = normalizeSeverity(severity);
640
+ switch (normalized) {
641
+ case "critical" /* Critical */:
590
642
  return chalkInstance.red;
591
- case "major":
592
- case "moderate-risk":
643
+ case "major" /* Major */:
593
644
  return chalkInstance.yellow;
594
- case "minor":
595
- case "safe":
645
+ case "minor" /* Minor */:
596
646
  return chalkInstance.green;
597
- case "info":
647
+ case "info" /* Info */:
598
648
  return chalkInstance.blue;
599
649
  default:
600
650
  return chalkInstance.white;
601
651
  }
602
652
  }
603
653
  function getSeverityValue(s) {
604
- if (!s) return 0;
605
- switch (s.toLowerCase()) {
606
- case "critical":
654
+ const normalized = normalizeSeverity(s);
655
+ switch (normalized) {
656
+ case "critical" /* Critical */:
607
657
  return 4;
608
- case "major":
658
+ case "major" /* Major */:
609
659
  return 3;
610
- case "minor":
660
+ case "minor" /* Minor */:
611
661
  return 2;
612
- case "info":
662
+ case "info" /* Info */:
613
663
  return 1;
614
664
  default:
615
665
  return 0;
@@ -644,24 +694,15 @@ function getSeverityEnum(s) {
644
694
  return "major";
645
695
  case 2:
646
696
  return "minor";
647
- case 1:
648
- return "info";
649
697
  default:
650
698
  return "info";
651
699
  }
652
700
  }
653
701
  function findLatestReport(dirPath) {
654
702
  const aireadyDir = resolvePath(dirPath, ".aiready");
655
- if (!existsSync2(aireadyDir)) {
656
- return null;
657
- }
658
- let files = readdirSync(aireadyDir).filter(
659
- (f) => f.startsWith("aiready-report-") && f.endsWith(".json")
660
- );
703
+ let files = getFilesByPattern(aireadyDir, /^aiready-report-.*\.json$/);
661
704
  if (files.length === 0) {
662
- files = readdirSync(aireadyDir).filter(
663
- (f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
664
- );
705
+ files = getFilesByPattern(aireadyDir, /^aiready-scan-.*\.json$/);
665
706
  }
666
707
  if (files.length === 0) {
667
708
  return null;
@@ -675,14 +716,8 @@ function findLatestReport(dirPath) {
675
716
  }
676
717
  function findLatestScanReport(scanReportsDir, reportFilePrefix) {
677
718
  try {
678
- let reportFiles = [];
679
- if (existsSync2(scanReportsDir)) {
680
- const files = readdirSync(scanReportsDir);
681
- if (files.length > 0) {
682
- const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
683
- reportFiles = files.filter((file) => prefixRegex.test(file));
684
- }
685
- }
719
+ const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
720
+ const reportFiles = getFilesByPattern(scanReportsDir, prefixRegex);
686
721
  if (reportFiles.length === 0) return null;
687
722
  reportFiles.sort((a, b) => {
688
723
  const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
@@ -696,6 +731,53 @@ function findLatestScanReport(scanReportsDir, reportFilePrefix) {
696
731
  }
697
732
  }
698
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
+
699
781
  // src/utils/provider-utils.ts
700
782
  function groupIssuesByFile(issues) {
701
783
  const fileIssuesMap = /* @__PURE__ */ new Map();
@@ -769,1828 +851,129 @@ function calculateImportSimilarity(export1, export2) {
769
851
  }
770
852
 
771
853
  // src/utils/dependency-analyzer.ts
772
- import { parse as parse2 } from "@typescript-eslint/typescript-estree";
773
-
774
- // src/parsers/typescript-parser.ts
775
854
  import { parse } from "@typescript-eslint/typescript-estree";
776
- var TypeScriptParser = class {
855
+
856
+ // src/parsers/parser-factory.ts
857
+ var ParserFactory = class _ParserFactory {
858
+ /**
859
+ * Create a new ParserFactory instance
860
+ */
777
861
  constructor() {
778
- this.language = "typescript" /* TypeScript */;
779
- this.extensions = [".ts", ".tsx", ".js", ".jsx"];
780
- }
781
- async initialize() {
782
- }
783
- canHandle(filePath) {
784
- return this.extensions.some((ext) => filePath.endsWith(ext));
785
- }
786
- async getAST(code, filePath) {
787
- try {
788
- return parse(code, {
789
- filePath,
790
- loc: true,
791
- range: true,
792
- tokens: true,
793
- comment: true,
794
- jsx: filePath.endsWith("x")
795
- });
796
- } catch (error) {
797
- throw new ParseError(error.message, filePath, {
798
- line: error.lineNumber || 1,
799
- column: error.column || 0
800
- });
801
- }
802
- }
803
- parse(code, filePath) {
804
- try {
805
- const ast = parse(code, {
806
- filePath,
807
- loc: true,
808
- range: true,
809
- tokens: true,
810
- comment: true,
811
- jsx: filePath.endsWith("x")
812
- });
813
- const imports = this.extractImports(ast);
814
- const exports = this.extractExports(ast, code);
815
- return {
816
- exports,
817
- imports,
818
- language: this.language
819
- };
820
- } catch (error) {
821
- throw new ParseError(error.message, filePath, {
822
- line: error.lineNumber || 1,
823
- column: error.column || 0
824
- });
825
- }
826
- }
827
- getNamingConventions() {
828
- return {
829
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
830
- functionPattern: /^[a-z][a-zA-Z0-9]*$/,
831
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
832
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
833
- typePattern: /^[A-Z][a-zA-Z0-9]*$/,
834
- interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
835
- };
836
- }
837
- analyzeMetadata(node, code) {
838
- if (!code) return {};
839
- return {
840
- isPure: this.isLikelyPure(node),
841
- hasSideEffects: !this.isLikelyPure(node)
842
- };
843
- }
844
- extractImports(ast) {
845
- const imports = [];
846
- for (const node of ast.body) {
847
- if (node.type === "ImportDeclaration") {
848
- const specifiers = [];
849
- let isTypeOnly = false;
850
- if (node.importKind === "type") {
851
- isTypeOnly = true;
852
- }
853
- for (const spec of node.specifiers) {
854
- if (spec.type === "ImportSpecifier") {
855
- const imported = spec.imported;
856
- const name = imported.type === "Identifier" ? imported.name : imported.value;
857
- specifiers.push(name);
858
- } else if (spec.type === "ImportDefaultSpecifier") {
859
- specifiers.push("default");
860
- } else if (spec.type === "ImportNamespaceSpecifier") {
861
- specifiers.push("*");
862
- }
863
- }
864
- imports.push({
865
- source: node.source.value,
866
- specifiers,
867
- isTypeOnly,
868
- loc: node.loc ? {
869
- start: {
870
- line: node.loc.start.line,
871
- column: node.loc.start.column
872
- },
873
- end: { line: node.loc.end.line, column: node.loc.end.column }
874
- } : void 0
875
- });
876
- }
877
- }
878
- return imports;
879
- }
880
- extractExports(ast, code) {
881
- const exports = [];
882
- for (const node of ast.body) {
883
- if (node.type === "ExportNamedDeclaration") {
884
- if (node.declaration) {
885
- const declaration = node.declaration;
886
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
887
- exports.push(
888
- this.createExport(
889
- declaration.id.name,
890
- "function",
891
- node,
892
- // Pass the outer ExportNamedDeclaration
893
- code
894
- )
895
- );
896
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
897
- exports.push(
898
- this.createExport(
899
- declaration.id.name,
900
- "class",
901
- node,
902
- // Pass the outer ExportNamedDeclaration
903
- code
904
- )
905
- );
906
- } else if (declaration.type === "TSTypeAliasDeclaration") {
907
- exports.push(
908
- this.createExport(
909
- declaration.id.name,
910
- "type",
911
- node,
912
- // Pass the outer ExportNamedDeclaration
913
- code
914
- )
915
- );
916
- } else if (declaration.type === "TSInterfaceDeclaration") {
917
- exports.push(
918
- this.createExport(
919
- declaration.id.name,
920
- "interface",
921
- node,
922
- // Pass the outer ExportNamedDeclaration
923
- code
924
- )
925
- );
926
- } else if (declaration.type === "VariableDeclaration") {
927
- for (const decl of declaration.declarations) {
928
- if (decl.id.type === "Identifier") {
929
- exports.push(
930
- this.createExport(
931
- decl.id.name,
932
- "const",
933
- node,
934
- // Pass the outer ExportNamedDeclaration
935
- code,
936
- decl.init
937
- )
938
- );
939
- }
940
- }
941
- }
942
- }
943
- } else if (node.type === "ExportDefaultDeclaration") {
944
- exports.push(this.createExport("default", "default", node, code));
945
- }
946
- }
947
- return exports;
948
- }
949
- createExport(name, type, node, code, initializer) {
950
- const documentation = this.extractDocumentation(node, code);
951
- let methodCount;
952
- let propertyCount;
953
- let parameters;
954
- let isPrimitive = false;
955
- if (initializer) {
956
- if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
957
- isPrimitive = true;
958
- }
959
- }
960
- const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
961
- if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
962
- const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
963
- methodCount = body.filter(
964
- (m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
965
- ).length;
966
- propertyCount = body.filter(
967
- (m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
968
- ).length;
969
- if (structNode.type === "ClassDeclaration") {
970
- const constructor = body.find(
971
- (m) => m.type === "MethodDefinition" && m.kind === "constructor"
972
- );
973
- if (constructor && constructor.value && constructor.value.params) {
974
- parameters = constructor.value.params.map((p) => {
975
- if (p.type === "Identifier") return p.name;
976
- if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
977
- return p.parameter.name;
978
- }
979
- return void 0;
980
- }).filter(Boolean);
981
- }
982
- }
983
- }
984
- if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
985
- const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
986
- if (funcNode && funcNode.params) {
987
- parameters = funcNode.params.map((p) => {
988
- if (p.type === "Identifier") return p.name;
989
- return void 0;
990
- }).filter(Boolean);
991
- }
992
- }
993
- return {
994
- name,
995
- type,
996
- isPrimitive,
997
- loc: node.loc ? {
998
- start: { line: node.loc.start.line, column: node.loc.start.column },
999
- end: { line: node.loc.end.line, column: node.loc.end.column }
1000
- } : void 0,
1001
- documentation,
1002
- methodCount,
1003
- propertyCount,
1004
- parameters,
1005
- isPure: this.isLikelyPure(node),
1006
- hasSideEffects: !this.isLikelyPure(node)
1007
- };
1008
- }
1009
- extractDocumentation(node, code) {
1010
- if (node.range) {
1011
- const start = node.range[0];
1012
- const precedingCode = code.substring(0, start);
1013
- const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1014
- if (jsdocMatch) {
1015
- return {
1016
- content: jsdocMatch[1].trim(),
1017
- type: "jsdoc"
1018
- };
1019
- }
1020
- }
1021
- 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
+ });
1022
891
  }
1023
- isLikelyPure(node) {
1024
- const structNode = node.type === "ExportNamedDeclaration" ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
1025
- if (structNode.type === "VariableDeclaration" && structNode.kind === "const")
1026
- return true;
1027
- if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition" && structNode.value) {
1028
- const body = structNode.type === "MethodDefinition" ? structNode.value.body : structNode.body;
1029
- if (body && body.type === "BlockStatement") {
1030
- const bodyContent = JSON.stringify(body);
1031
- if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"type":"AssignmentExpression"')) {
1032
- return false;
1033
- }
1034
- return true;
1035
- }
1036
- return true;
1037
- }
1038
- return false;
892
+ /**
893
+ * Register a lazy-loaded parser
894
+ */
895
+ registerLazyParser(language, loader) {
896
+ this.registeredParsers.set(language, loader);
1039
897
  }
1040
- };
1041
-
1042
- // src/parsers/metadata-utils.ts
1043
- function analyzeNodeMetadata(node, code, options) {
1044
- const metadata = {
1045
- isPure: true,
1046
- hasSideEffects: false
1047
- };
1048
- try {
1049
- let prev = node.previousSibling || null;
1050
- while (prev && /comment/i.test(prev.type)) {
1051
- const text = prev.text || "";
1052
- const loc = {
1053
- start: {
1054
- line: prev.startPosition.row + 1,
1055
- column: prev.startPosition.column
1056
- },
1057
- end: {
1058
- line: prev.endPosition.row + 1,
1059
- column: prev.endPosition.column
1060
- }
1061
- };
1062
- if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
1063
- metadata.documentation = {
1064
- content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1065
- type: "comment",
1066
- loc
1067
- };
1068
- break;
1069
- }
1070
- if (text.trim().startsWith("///")) {
1071
- metadata.documentation = {
1072
- content: text.replace(/^\/\/\//, "").trim(),
1073
- type: "xml-doc",
1074
- loc
1075
- };
1076
- break;
1077
- }
1078
- if (text.trim().startsWith("//")) {
1079
- metadata.documentation = {
1080
- content: text.replace(/^\/\//, "").trim(),
1081
- type: "comment",
1082
- loc
1083
- };
1084
- break;
1085
- }
1086
- prev = prev.previousSibling;
1087
- }
1088
- if (node.type === "function_definition" || node.type === "class_definition") {
1089
- const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
1090
- if (body2 && body2.children.length > 0) {
1091
- const firstStmt = body2.children[0];
1092
- if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
1093
- metadata.documentation = {
1094
- content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
1095
- type: "docstring",
1096
- loc: {
1097
- start: {
1098
- line: firstStmt.startPosition.row + 1,
1099
- column: firstStmt.startPosition.column
1100
- },
1101
- end: {
1102
- line: firstStmt.endPosition.row + 1,
1103
- column: firstStmt.endPosition.column
1104
- }
1105
- }
1106
- };
1107
- }
1108
- }
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();
1109
906
  }
1110
- } catch {
907
+ return _ParserFactory.instance;
1111
908
  }
1112
- const defaultSignatures = [
1113
- "console.",
1114
- "fmt.",
1115
- "panic(",
1116
- "os.Exit",
1117
- "log.",
1118
- "Console.Write",
1119
- "File.Write",
1120
- "System.out",
1121
- "System.err",
1122
- "Files.write",
1123
- "process.exit",
1124
- "exit("
1125
- ];
1126
- const signatures = Array.from(
1127
- /* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
1128
- );
1129
- const walk = (n) => {
1130
- try {
1131
- const t = n.type || "";
1132
- if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
1133
- t
1134
- )) {
1135
- metadata.isPure = false;
1136
- metadata.hasSideEffects = true;
1137
- }
1138
- const text = n.text || "";
1139
- for (const s of signatures) {
1140
- if (text.includes(s)) {
1141
- metadata.isPure = false;
1142
- metadata.hasSideEffects = true;
1143
- break;
1144
- }
1145
- }
1146
- for (let i = 0; i < n.childCount; i++) {
1147
- const c = n.child(i);
1148
- if (c) walk(c);
1149
- }
1150
- } catch {
1151
- }
1152
- };
1153
- const body = node.childForFieldName?.("body") || node.children.find(
1154
- (c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
1155
- );
1156
- if (body) walk(body);
1157
- return metadata;
1158
- }
1159
-
1160
- // src/parsers/tree-sitter-utils.ts
1161
- import * as Parser from "web-tree-sitter";
1162
- import * as path from "path";
1163
- import * as fs from "fs";
1164
- var isTreeSitterInitialized = false;
1165
- async function initTreeSitter() {
1166
- if (isTreeSitterInitialized) return;
1167
- try {
1168
- const wasmPath = getWasmPath("web-tree-sitter");
1169
- await Parser.Parser.init({
1170
- locateFile() {
1171
- return wasmPath || "web-tree-sitter.wasm";
1172
- }
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);
1173
918
  });
1174
- isTreeSitterInitialized = true;
1175
- } catch (error) {
1176
- console.error("Failed to initialize web-tree-sitter:", error);
1177
- isTreeSitterInitialized = true;
1178
- }
1179
- }
1180
- function findInPnpmStore(startDir, fileName, depth = 0) {
1181
- if (depth > 8) return null;
1182
- const pnpmDir = path.join(startDir, "node_modules", ".pnpm");
1183
- if (fs.existsSync(pnpmDir)) {
1184
- return findFileRecursively(pnpmDir, fileName, 0);
1185
919
  }
1186
- const parent = path.dirname(startDir);
1187
- if (parent === startDir) return null;
1188
- return findInPnpmStore(parent, fileName, depth + 1);
1189
- }
1190
- function findFileRecursively(dir, fileName, depth) {
1191
- if (depth > 6) return null;
1192
- try {
1193
- const entries = fs.readdirSync(dir, { withFileTypes: true });
1194
- for (const entry of entries) {
1195
- if (entry.isFile() && entry.name === fileName) {
1196
- return path.join(dir, entry.name);
1197
- }
1198
- }
1199
- for (const entry of entries) {
1200
- if (entry.isDirectory()) {
1201
- const found = findFileRecursively(
1202
- path.join(dir, entry.name),
1203
- fileName,
1204
- depth + 1
1205
- );
1206
- if (found) return found;
1207
- }
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;
1208
931
  }
1209
- } catch {
1210
- }
1211
- return null;
1212
- }
1213
- function getWasmPath(language) {
1214
- const wasmFileName = language === "web-tree-sitter" ? "web-tree-sitter.wasm" : `tree-sitter-${language}.wasm`;
1215
- const immediatePaths = [
1216
- path.join(process.cwd(), wasmFileName),
1217
- path.join(__dirname, wasmFileName),
1218
- path.join(__dirname, "assets", wasmFileName)
1219
- ];
1220
- for (const p of immediatePaths) {
1221
- if (fs.existsSync(p)) return p;
1222
- }
1223
- const pnpmPath = findInPnpmStore(__dirname, wasmFileName);
1224
- if (pnpmPath) return pnpmPath;
1225
- const pnpmPathCwd = findInPnpmStore(process.cwd(), wasmFileName);
1226
- if (pnpmPathCwd) return pnpmPathCwd;
1227
- console.warn(
1228
- `[Parser] WASM file for ${language} not found. CWD: ${process.cwd()}, DIR: ${__dirname}`
1229
- );
1230
- return null;
1231
- }
1232
- async function setupParser(language) {
1233
- await initTreeSitter();
1234
- const wasmPath = getWasmPath(language);
1235
- if (!wasmPath) {
1236
- return null;
1237
- }
1238
- try {
1239
- const parser = new Parser.Parser();
1240
- const Lang = await Parser.Language.load(wasmPath);
1241
- parser.setLanguage(Lang);
1242
- return parser;
1243
- } catch {
1244
932
  return null;
1245
933
  }
1246
- }
1247
-
1248
- // src/parsers/base-parser.ts
1249
- var BaseLanguageParser = class {
1250
- constructor() {
1251
- this.parser = null;
1252
- this.initialized = false;
1253
- }
1254
934
  /**
1255
- * Initialize the tree-sitter parser
935
+ * Get parser for a file based on its extension
1256
936
  */
1257
- async initialize() {
1258
- if (this.initialized) return;
1259
- try {
1260
- this.parser = await setupParser(this.getParserName());
1261
- this.initialized = true;
1262
- } catch (error) {
1263
- console.warn(`Failed to initialize ${this.language} parser:`, error);
1264
- }
1265
- }
1266
- async getAST(code, _filePath) {
1267
- void _filePath;
1268
- if (!this.initialized) await this.initialize();
1269
- if (!this.parser) return null;
1270
- return this.parser.parse(code);
1271
- }
1272
- parse(code, filePath) {
1273
- if (!this.initialized || !this.parser) {
1274
- return this.parseRegex(code, filePath);
1275
- }
1276
- try {
1277
- const tree = this.parser.parse(code);
1278
- if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
1279
- return this.parseRegex(code, filePath);
1280
- }
1281
- const imports = this.extractImportsAST(tree.rootNode);
1282
- const exports = this.extractExportsAST(tree.rootNode, code);
1283
- return {
1284
- exports,
1285
- imports,
1286
- language: this.language,
1287
- warnings: []
1288
- };
1289
- } catch (error) {
1290
- console.warn(
1291
- `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
1292
- );
1293
- 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;
1294
942
  }
943
+ return this.getParserForLanguage(language);
1295
944
  }
1296
- canHandle(filePath) {
1297
- const lowerPath = filePath.toLowerCase();
1298
- 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);
1299
951
  }
1300
- };
1301
-
1302
- // src/parsers/python-parser.ts
1303
- var PythonParser = class extends BaseLanguageParser {
1304
- constructor() {
1305
- super(...arguments);
1306
- this.language = "python" /* Python */;
1307
- 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);
1308
961
  }
1309
- getParserName() {
1310
- return "python";
962
+ /**
963
+ * Get all supported file extensions
964
+ */
965
+ getSupportedExtensions() {
966
+ return Array.from(this.extensionMap.keys());
1311
967
  }
1312
968
  /**
1313
- * Analyze metadata for a Python node (purity, side effects).
1314
- *
1315
- * @param node - Tree-sitter node to analyze.
1316
- * @param code - Source code for context.
1317
- * @returns Partial ExportInfo containing discovered metadata.
969
+ * Get language for a file
1318
970
  */
1319
- analyzeMetadata(node, code) {
1320
- return analyzeNodeMetadata(node, code, {
1321
- sideEffectSignatures: ["print(", "input(", "open("]
1322
- });
971
+ getLanguageForFile(filePath) {
972
+ const ext = this.getFileExtension(filePath);
973
+ return this.extensionMap.get(ext) || null;
1323
974
  }
1324
975
  /**
1325
- * Extract import information using AST walk.
1326
- *
1327
- * @param rootNode - Root node of the Python AST.
1328
- * @returns Array of discovered FileImport objects.
1329
- */
1330
- extractImportsAST(rootNode) {
1331
- const imports = [];
1332
- const processImportNode = (node) => {
1333
- if (node.type === "import_statement") {
1334
- for (const child of node.children) {
1335
- if (child.type === "dotted_name") {
1336
- const source = child.text;
1337
- imports.push({
1338
- source,
1339
- specifiers: [source],
1340
- loc: {
1341
- start: {
1342
- line: child.startPosition.row + 1,
1343
- column: child.startPosition.column
1344
- },
1345
- end: {
1346
- line: child.endPosition.row + 1,
1347
- column: child.endPosition.column
1348
- }
1349
- }
1350
- });
1351
- } else if (child.type === "aliased_import") {
1352
- const nameNode = child.childForFieldName("name");
1353
- if (nameNode) {
1354
- const source = nameNode.text;
1355
- imports.push({
1356
- source,
1357
- specifiers: [source],
1358
- loc: {
1359
- start: {
1360
- line: child.startPosition.row + 1,
1361
- column: child.startPosition.column
1362
- },
1363
- end: {
1364
- line: child.endPosition.row + 1,
1365
- column: child.endPosition.column
1366
- }
1367
- }
1368
- });
1369
- }
1370
- }
1371
- }
1372
- } else if (node.type === "import_from_statement") {
1373
- const moduleNameNode = node.childForFieldName("module_name");
1374
- if (moduleNameNode) {
1375
- const source = moduleNameNode.text;
1376
- const specifiers = [];
1377
- for (const child of node.children) {
1378
- if (child.type === "dotted_name" && child !== moduleNameNode) {
1379
- specifiers.push(child.text);
1380
- } else if (child.type === "aliased_import") {
1381
- const nameNode = child.childForFieldName("name");
1382
- if (nameNode) specifiers.push(nameNode.text);
1383
- } else if (child.type === "wildcard_import") {
1384
- specifiers.push("*");
1385
- }
1386
- }
1387
- if (specifiers.length > 0) {
1388
- imports.push({
1389
- source,
1390
- specifiers,
1391
- loc: {
1392
- start: {
1393
- line: node.startPosition.row + 1,
1394
- column: node.startPosition.column
1395
- },
1396
- end: {
1397
- line: node.endPosition.row + 1,
1398
- column: node.endPosition.column
1399
- }
1400
- }
1401
- });
1402
- }
1403
- }
1404
- }
1405
- };
1406
- for (const node of rootNode.children) {
1407
- processImportNode(node);
1408
- }
1409
- return imports;
1410
- }
1411
- /**
1412
- * Extract export information using AST walk.
1413
- *
1414
- * @param rootNode - Root node of the Python AST.
1415
- * @param code - Source code for documentation extraction.
1416
- * @returns Array of discovered ExportInfo objects.
1417
- */
1418
- extractExportsAST(rootNode, code) {
1419
- const exports = [];
1420
- for (const node of rootNode.children) {
1421
- if (node.type === "function_definition") {
1422
- const nameNode = node.childForFieldName("name");
1423
- if (nameNode) {
1424
- const name = nameNode.text;
1425
- const isPrivate = name.startsWith("_") && !name.startsWith("__");
1426
- if (!isPrivate) {
1427
- const metadata = this.analyzeMetadata(node, code);
1428
- exports.push({
1429
- name,
1430
- type: "function",
1431
- loc: {
1432
- start: {
1433
- line: node.startPosition.row + 1,
1434
- column: node.startPosition.column
1435
- },
1436
- end: {
1437
- line: node.endPosition.row + 1,
1438
- column: node.endPosition.column
1439
- }
1440
- },
1441
- parameters: this.extractParameters(node),
1442
- ...metadata
1443
- });
1444
- }
1445
- }
1446
- } else if (node.type === "class_definition") {
1447
- const nameNode = node.childForFieldName("name");
1448
- if (nameNode) {
1449
- const metadata = this.analyzeMetadata(node, code);
1450
- exports.push({
1451
- name: nameNode.text,
1452
- type: "class",
1453
- loc: {
1454
- start: {
1455
- line: node.startPosition.row + 1,
1456
- column: node.startPosition.column
1457
- },
1458
- end: {
1459
- line: node.endPosition.row + 1,
1460
- column: node.endPosition.column
1461
- }
1462
- },
1463
- ...metadata
1464
- });
1465
- }
1466
- } else if (node.type === "expression_statement") {
1467
- const assignment = node.firstChild;
1468
- if (assignment && assignment.type === "assignment") {
1469
- const left = assignment.childForFieldName("left");
1470
- if (left && left.type === "identifier") {
1471
- const name = left.text;
1472
- const isInternal = name === "__all__" || name === "__version__" || name === "__author__";
1473
- const isPrivate = name.startsWith("_") && !name.startsWith("__");
1474
- if (!isInternal && !isPrivate) {
1475
- exports.push({
1476
- name,
1477
- type: name === name.toUpperCase() ? "const" : "variable",
1478
- loc: {
1479
- start: {
1480
- line: node.startPosition.row + 1,
1481
- column: node.startPosition.column
1482
- },
1483
- end: {
1484
- line: node.endPosition.row + 1,
1485
- column: node.endPosition.column
1486
- }
1487
- }
1488
- });
1489
- }
1490
- }
1491
- }
1492
- }
1493
- }
1494
- return exports;
1495
- }
1496
- /**
1497
- * Extract parameter names from a function definition node.
1498
- *
1499
- * @param node - Function definition node.
1500
- * @returns Array of parameter name strings.
1501
- */
1502
- extractParameters(node) {
1503
- const paramsNode = node.childForFieldName("parameters");
1504
- if (!paramsNode) return [];
1505
- return paramsNode.children.filter(
1506
- (c) => c.type === "identifier" || c.type === "typed_parameter" || c.type === "default_parameter"
1507
- ).map((c) => {
1508
- if (c.type === "identifier") return c.text;
1509
- if (c.type === "typed_parameter" || c.type === "default_parameter") {
1510
- return c.firstChild?.text || "unknown";
1511
- }
1512
- return "unknown";
1513
- });
1514
- }
1515
- /**
1516
- * Fallback regex-based parsing when tree-sitter is unavailable.
1517
- *
1518
- * @param code - Source code content.
1519
- * @param filePath - Path to the file being parsed.
1520
- * @returns Consolidated ParseResult.
1521
- */
1522
- parseRegex(code, filePath) {
1523
- try {
1524
- const imports = this.extractImportsRegex(code, filePath);
1525
- const exports = this.extractExportsRegex(code, filePath);
1526
- return {
1527
- exports,
1528
- imports,
1529
- language: "python" /* Python */,
1530
- warnings: [
1531
- "Python parsing is currently using regex-based extraction as tree-sitter wasm was not available."
1532
- ]
1533
- };
1534
- } catch (error) {
1535
- const wrapper = new Error(
1536
- `Failed to parse Python file ${filePath}: ${error.message}`
1537
- );
1538
- wrapper.cause = error;
1539
- throw wrapper;
1540
- }
1541
- }
1542
- getNamingConventions() {
1543
- return {
1544
- variablePattern: /^[a-z_][a-z0-9_]*$/,
1545
- functionPattern: /^[a-z_][a-z0-9_]*$/,
1546
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1547
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
1548
- exceptions: [
1549
- "__init__",
1550
- "__str__",
1551
- "__repr__",
1552
- "__name__",
1553
- "__main__",
1554
- "__file__",
1555
- "__doc__",
1556
- "__all__",
1557
- "__version__",
1558
- "__author__",
1559
- "__dict__",
1560
- "__class__",
1561
- "__module__",
1562
- "__bases__"
1563
- ]
1564
- };
1565
- }
1566
- canHandle(filePath) {
1567
- return filePath.toLowerCase().endsWith(".py");
1568
- }
1569
- extractImportsRegex(code, _filePath) {
1570
- void _filePath;
1571
- const imports = [];
1572
- const lines = code.split("\n");
1573
- const importRegex = /^\s*import\s+([a-zA-Z0-9_., ]+)/;
1574
- const fromImportRegex = /^\s*from\s+([a-zA-Z0-9_.]+)\s+import\s+(.+)/;
1575
- lines.forEach((line, idx) => {
1576
- if (line.trim().startsWith("#")) return;
1577
- const importMatch = line.match(importRegex);
1578
- if (importMatch) {
1579
- const modules = importMatch[1].split(",").map((m) => m.trim().split(" as ")[0]);
1580
- modules.forEach((module) => {
1581
- imports.push({
1582
- source: module,
1583
- specifiers: [module],
1584
- loc: {
1585
- start: { line: idx + 1, column: 0 },
1586
- end: { line: idx + 1, column: line.length }
1587
- }
1588
- });
1589
- });
1590
- return;
1591
- }
1592
- const fromMatch = line.match(fromImportRegex);
1593
- if (fromMatch) {
1594
- const module = fromMatch[1];
1595
- const imports_str = fromMatch[2];
1596
- if (imports_str.trim() === "*") {
1597
- imports.push({
1598
- source: module,
1599
- specifiers: ["*"],
1600
- loc: {
1601
- start: { line: idx + 1, column: 0 },
1602
- end: { line: idx + 1, column: line.length }
1603
- }
1604
- });
1605
- return;
1606
- }
1607
- const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
1608
- imports.push({
1609
- source: module,
1610
- specifiers,
1611
- loc: {
1612
- start: { line: idx + 1, column: 0 },
1613
- end: { line: idx + 1, column: line.length }
1614
- }
1615
- });
1616
- }
1617
- });
1618
- return imports;
1619
- }
1620
- extractExportsRegex(code, _filePath) {
1621
- void _filePath;
1622
- const exports = [];
1623
- const lines = code.split("\n");
1624
- const funcRegex = /^def\s+([a-zA-Z0-9_]+)\s*\(/;
1625
- const classRegex = /^class\s+([a-zA-Z0-9_]+)/;
1626
- lines.forEach((line, idx) => {
1627
- const indent = line.search(/\S/);
1628
- if (indent !== 0) return;
1629
- const classMatch = line.match(classRegex);
1630
- if (classMatch) {
1631
- exports.push({
1632
- name: classMatch[1],
1633
- type: "class",
1634
- visibility: "public",
1635
- isPure: true,
1636
- hasSideEffects: false,
1637
- loc: {
1638
- start: { line: idx + 1, column: 0 },
1639
- end: { line: idx + 1, column: line.length }
1640
- }
1641
- });
1642
- return;
1643
- }
1644
- const funcMatch = line.match(funcRegex);
1645
- if (funcMatch) {
1646
- const name = funcMatch[1];
1647
- if (name.startsWith("_") && !name.startsWith("__")) return;
1648
- let docContent;
1649
- const nextLines = lines.slice(idx + 1, idx + 4);
1650
- for (const nextLine of nextLines) {
1651
- const docMatch = nextLine.match(/^\s*"""([\s\S]*?)"""/) || nextLine.match(/^\s*'''([\s\S]*?)'''/);
1652
- if (docMatch) {
1653
- docContent = docMatch[1].trim();
1654
- break;
1655
- }
1656
- if (nextLine.trim() && !nextLine.trim().startsWith('"""') && !nextLine.trim().startsWith("'''"))
1657
- break;
1658
- }
1659
- const isImpure = name.toLowerCase().includes("impure") || line.includes("print(") || idx + 1 < lines.length && lines[idx + 1].includes("print(");
1660
- exports.push({
1661
- name,
1662
- type: "function",
1663
- visibility: "public",
1664
- isPure: !isImpure,
1665
- hasSideEffects: isImpure,
1666
- documentation: docContent ? { content: docContent, type: "docstring" } : void 0,
1667
- loc: {
1668
- start: { line: idx + 1, column: 0 },
1669
- end: { line: idx + 1, column: line.length }
1670
- }
1671
- });
1672
- }
1673
- });
1674
- return exports;
1675
- }
1676
- };
1677
-
1678
- // src/parsers/shared-parser-utils.ts
1679
- var SIDE_EFFECT_KEYWORDS = [
1680
- "print(",
1681
- "console.",
1682
- "System.out",
1683
- "System.err",
1684
- "fmt.",
1685
- "File.Write",
1686
- "Files.write",
1687
- "os.Exit",
1688
- "panic(",
1689
- "throw ",
1690
- "Logging.",
1691
- "log."
1692
- ];
1693
- function analyzeGeneralMetadata(node, code, options = {}) {
1694
- const metadata = {
1695
- isPure: true,
1696
- hasSideEffects: false
1697
- };
1698
- try {
1699
- let prev = node.previousSibling || null;
1700
- while (prev && /comment/i.test(prev.type)) {
1701
- const text = prev.text || "";
1702
- if (text.trim().startsWith("/**")) {
1703
- metadata.documentation = {
1704
- content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
1705
- type: "jsdoc"
1706
- };
1707
- break;
1708
- }
1709
- if (text.trim().startsWith("///")) {
1710
- metadata.documentation = {
1711
- content: text.replace(/^\/\/\//, "").trim(),
1712
- type: "xml-doc"
1713
- };
1714
- break;
1715
- }
1716
- if (text.trim().startsWith("//")) {
1717
- metadata.documentation = {
1718
- content: text.replace(/^\/\//, "").trim(),
1719
- type: "comment"
1720
- };
1721
- break;
1722
- }
1723
- prev = prev.previousSibling;
1724
- }
1725
- } catch {
1726
- }
1727
- const signatures = [
1728
- ...SIDE_EFFECT_KEYWORDS,
1729
- ...options.sideEffectSignatures || []
1730
- ];
1731
- const walk = (n) => {
1732
- if (/assign|assignment|assignment_statement|assignment_expression/i.test(
1733
- n.type
1734
- )) {
1735
- metadata.isPure = false;
1736
- metadata.hasSideEffects = true;
1737
- }
1738
- const text = n.text;
1739
- for (const sig of signatures) {
1740
- if (text.includes(sig)) {
1741
- metadata.isPure = false;
1742
- metadata.hasSideEffects = true;
1743
- break;
1744
- }
1745
- }
1746
- if (!metadata.hasSideEffects) {
1747
- for (let i = 0; i < n.childCount; i++) {
1748
- const child = n.child(i);
1749
- if (child) walk(child);
1750
- }
1751
- }
1752
- };
1753
- walk(node);
1754
- return metadata;
1755
- }
1756
- function extractParameterNames(node) {
1757
- const params = [];
1758
- const candidates = [
1759
- // common field name
1760
- node.childForFieldName ? node.childForFieldName("parameters") : null,
1761
- node.childForFieldName ? node.childForFieldName("parameter_list") : null,
1762
- node.children.find((c) => c.type === "parameter_list") || null,
1763
- node.children.find((c) => c.type === "parameters") || null,
1764
- node.children.find((c) => c.type === "formal_parameters") || null,
1765
- node.children.find((c) => c.type === "formal_parameter") || null
1766
- ];
1767
- const list = candidates.find(Boolean);
1768
- if (!list) return params;
1769
- for (const child of list.children) {
1770
- if (!child) continue;
1771
- const id = child.childForFieldName?.("name") || child.children.find(
1772
- (c) => [
1773
- "identifier",
1774
- "variable_name",
1775
- "name",
1776
- "parameter",
1777
- "formal_parameter"
1778
- ].includes(c.type)
1779
- ) || (child.type === "identifier" ? child : void 0);
1780
- if (id && typeof id.text === "string") params.push(id.text);
1781
- }
1782
- return params;
1783
- }
1784
-
1785
- // src/parsers/java-parser.ts
1786
- var JavaParser = class extends BaseLanguageParser {
1787
- constructor() {
1788
- super(...arguments);
1789
- this.language = "java" /* Java */;
1790
- this.extensions = [".java"];
1791
- }
1792
- getParserName() {
1793
- return "java";
1794
- }
1795
- /**
1796
- * Analyze metadata for a Java node (purity, side effects).
1797
- *
1798
- * @param node - Tree-sitter node to analyze.
1799
- * @param code - Source code for context.
1800
- * @returns Partial ExportInfo containing discovered metadata.
1801
- */
1802
- analyzeMetadata(node, code) {
1803
- return analyzeGeneralMetadata(node, code, {
1804
- sideEffectSignatures: [
1805
- "System.out",
1806
- "System.err",
1807
- "Files.write",
1808
- "Logging."
1809
- ]
1810
- });
1811
- }
1812
- parseRegex(code) {
1813
- const lines = code.split("\n");
1814
- const exports = [];
1815
- const imports = [];
1816
- const importRegex = /^import\s+([a-zA-Z0-9_.]+)/;
1817
- const classRegex = /^\s*(?:public\s+)?(?:class|interface|enum)\s+([a-zA-Z0-9_]+)/;
1818
- const methodRegex = /^\s*public\s+(?:static\s+)?[a-zA-Z0-9_<>[\]]+\s+([a-zA-Z0-9_]+)\s*\(/;
1819
- let currentClassName = "";
1820
- lines.forEach((line, idx) => {
1821
- const importMatch = line.match(importRegex);
1822
- if (importMatch) {
1823
- const source = importMatch[1];
1824
- imports.push({
1825
- source,
1826
- specifiers: [source.split(".").pop() || source],
1827
- loc: {
1828
- start: { line: idx + 1, column: 0 },
1829
- end: { line: idx + 1, column: line.length }
1830
- }
1831
- });
1832
- }
1833
- const classMatch = line.match(classRegex);
1834
- if (classMatch) {
1835
- currentClassName = classMatch[1];
1836
- exports.push({
1837
- name: currentClassName,
1838
- type: line.includes("interface") ? "interface" : "class",
1839
- visibility: "public",
1840
- isPure: true,
1841
- hasSideEffects: false,
1842
- loc: {
1843
- start: { line: idx + 1, column: 0 },
1844
- end: { line: idx + 1, column: line.length }
1845
- }
1846
- });
1847
- }
1848
- const methodMatch = line.match(methodRegex);
1849
- if (methodMatch && currentClassName) {
1850
- const name = methodMatch[1];
1851
- let docContent;
1852
- const prevLines = lines.slice(Math.max(0, idx - 5), idx);
1853
- const prevText = prevLines.join("\n");
1854
- const javadocMatch = prevText.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
1855
- if (javadocMatch) {
1856
- docContent = javadocMatch[1].replace(/^\s*\*+/gm, "").trim();
1857
- }
1858
- const isImpure = name.toLowerCase().includes("impure") || line.includes("System.out");
1859
- exports.push({
1860
- name,
1861
- type: "function",
1862
- parentClass: currentClassName,
1863
- visibility: "public",
1864
- isPure: !isImpure,
1865
- hasSideEffects: isImpure,
1866
- documentation: docContent ? { content: docContent, type: "jsdoc" } : void 0,
1867
- loc: {
1868
- start: { line: idx + 1, column: 0 },
1869
- end: { line: idx + 1, column: line.length }
1870
- }
1871
- });
1872
- }
1873
- });
1874
- return {
1875
- exports,
1876
- imports,
1877
- language: "java" /* Java */,
1878
- warnings: ["Parser falling back to regex-based analysis"]
1879
- };
1880
- }
1881
- /**
1882
- * Extract import information using AST walk.
1883
- *
1884
- * @param rootNode - Root node of the Java AST.
1885
- * @returns Array of discovered FileImport objects.
1886
- */
1887
- extractImportsAST(rootNode) {
1888
- const imports = [];
1889
- for (const node of rootNode.children) {
1890
- if (node.type === "import_declaration") {
1891
- const sourceArr = [];
1892
- let isWildcard = false;
1893
- for (const child of node.children) {
1894
- if (child.type === "scoped_identifier" || child.type === "identifier") {
1895
- sourceArr.push(child.text);
1896
- }
1897
- if (child.type === "asterisk") isWildcard = true;
1898
- }
1899
- const source = sourceArr.join(".");
1900
- if (source) {
1901
- imports.push({
1902
- source: isWildcard ? `${source}.*` : source,
1903
- specifiers: isWildcard ? ["*"] : [source.split(".").pop() || source],
1904
- loc: {
1905
- start: {
1906
- line: node.startPosition.row + 1,
1907
- column: node.startPosition.column
1908
- },
1909
- end: {
1910
- line: node.endPosition.row + 1,
1911
- column: node.endPosition.column
1912
- }
1913
- }
1914
- });
1915
- }
1916
- }
1917
- }
1918
- return imports;
1919
- }
1920
- /**
1921
- * Extract export information (classes, interfaces, methods) using AST walk.
1922
- *
1923
- * @param rootNode - Root node of the Java AST.
1924
- * @param code - Source code for documentation extraction.
1925
- * @returns Array of discovered ExportInfo objects.
1926
- */
1927
- extractExportsAST(rootNode, code) {
1928
- const exports = [];
1929
- for (const node of rootNode.children) {
1930
- if (node.type === "class_declaration" || node.type === "interface_declaration" || node.type === "enum_declaration") {
1931
- const nameNode = node.children.find((c) => c.type === "identifier");
1932
- if (nameNode) {
1933
- const modifiers = this.getModifiers(node);
1934
- const metadata = this.analyzeMetadata(node, code);
1935
- exports.push({
1936
- name: nameNode.text,
1937
- type: node.type === "class_declaration" ? "class" : "interface",
1938
- loc: {
1939
- start: {
1940
- line: node.startPosition.row + 1,
1941
- column: node.startPosition.column
1942
- },
1943
- end: {
1944
- line: node.endPosition.row + 1,
1945
- column: node.endPosition.column
1946
- }
1947
- },
1948
- visibility: modifiers.includes("public") ? "public" : "private",
1949
- ...metadata
1950
- });
1951
- this.extractSubExports(node, nameNode.text, exports, code);
1952
- }
1953
- }
1954
- }
1955
- return exports;
1956
- }
1957
- /**
1958
- * Extract modifiers (visibility, static, etc.) from a node.
1959
- *
1960
- * @param node - AST node to extract modifiers from.
1961
- * @returns Array of modifier strings.
1962
- */
1963
- getModifiers(node) {
1964
- const modifiersNode = node.children.find((c) => c.type === "modifiers");
1965
- if (!modifiersNode) return [];
1966
- return modifiersNode.children.map((c) => c.text);
1967
- }
1968
- /**
1969
- * Extract methods and nested exports from a class or interface body.
1970
- *
1971
- * @param parentNode - Class or interface declaration node.
1972
- * @param parentName - Name of the parent class/interface.
1973
- * @param exports - Array to collect discovered exports into.
1974
- * @param code - Source code for context.
1975
- */
1976
- extractSubExports(parentNode, parentName, exports, code) {
1977
- const bodyNode = parentNode.children.find((c) => c.type === "class_body");
1978
- if (!bodyNode) return;
1979
- for (const node of bodyNode.children) {
1980
- if (node.type === "method_declaration") {
1981
- const nameNode = node.children.find((c) => c.type === "identifier");
1982
- const modifiers = this.getModifiers(node);
1983
- if (nameNode && modifiers.includes("public")) {
1984
- const metadata = this.analyzeMetadata(node, code);
1985
- exports.push({
1986
- name: nameNode.text,
1987
- type: "function",
1988
- parentClass: parentName,
1989
- visibility: "public",
1990
- loc: {
1991
- start: {
1992
- line: node.startPosition.row + 1,
1993
- column: node.startPosition.column
1994
- },
1995
- end: {
1996
- line: node.endPosition.row + 1,
1997
- column: node.endPosition.column
1998
- }
1999
- },
2000
- parameters: this.extractParameters(node),
2001
- ...metadata
2002
- });
2003
- }
2004
- }
2005
- }
2006
- }
2007
- extractParameters(node) {
2008
- return extractParameterNames(node);
2009
- }
2010
- getNamingConventions() {
2011
- return {
2012
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
2013
- functionPattern: /^[a-z][a-zA-Z0-9]*$/,
2014
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
2015
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
2016
- exceptions: ["main", "serialVersionUID"]
2017
- };
2018
- }
2019
- canHandle(filePath) {
2020
- return filePath.toLowerCase().endsWith(".java");
2021
- }
2022
- };
2023
-
2024
- // src/parsers/csharp-parser.ts
2025
- var CSharpParser = class extends BaseLanguageParser {
2026
- constructor() {
2027
- super(...arguments);
2028
- this.language = "csharp" /* CSharp */;
2029
- this.extensions = [".cs"];
2030
- }
2031
- getParserName() {
2032
- return "c_sharp";
2033
- }
2034
- /**
2035
- * Analyze metadata for a C# node (purity, side effects).
2036
- *
2037
- * @param node - Tree-sitter node to analyze.
2038
- * @param code - Source code for context.
2039
- * @returns Partial ExportInfo containing discovered metadata.
2040
- */
2041
- analyzeMetadata(node, code) {
2042
- return analyzeGeneralMetadata(node, code, {
2043
- sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
2044
- });
2045
- }
2046
- /**
2047
- * Fallback regex-based parsing when tree-sitter is unavailable.
2048
- *
2049
- * @param code - Source code content.
2050
- * @returns Consolidated ParseResult.
2051
- */
2052
- parseRegex(code) {
2053
- const lines = code.split("\n");
2054
- const exports = [];
2055
- const imports = [];
2056
- const usingRegex = /^using\s+([a-zA-Z0-9_.]+);/;
2057
- const classRegex = /^\s*(?:public\s+)?class\s+([a-zA-Z0-9_]+)/;
2058
- const methodRegex = /^\s*(?:public|protected)\s+(?:static\s+)?[a-zA-Z0-9_.]+\s+([a-zA-Z0-9_]+)\s*\(/;
2059
- let currentClassName = "";
2060
- lines.forEach((line, idx) => {
2061
- const usingMatch = line.match(usingRegex);
2062
- if (usingMatch) {
2063
- const source = usingMatch[1];
2064
- imports.push({
2065
- source,
2066
- specifiers: [source.split(".").pop() || source],
2067
- loc: {
2068
- start: { line: idx + 1, column: 0 },
2069
- end: { line: idx + 1, column: line.length }
2070
- }
2071
- });
2072
- }
2073
- const classMatch = line.match(classRegex);
2074
- if (classMatch) {
2075
- currentClassName = classMatch[1];
2076
- exports.push({
2077
- name: currentClassName,
2078
- type: "class",
2079
- visibility: "public",
2080
- isPure: true,
2081
- hasSideEffects: false,
2082
- loc: {
2083
- start: { line: idx + 1, column: 0 },
2084
- end: { line: idx + 1, column: line.length }
2085
- }
2086
- });
2087
- }
2088
- const methodMatch = line.match(methodRegex);
2089
- if (methodMatch && currentClassName) {
2090
- const name = methodMatch[1];
2091
- const isImpure = name.toLowerCase().includes("impure") || line.includes("Console.WriteLine");
2092
- exports.push({
2093
- name,
2094
- type: "function",
2095
- parentClass: currentClassName,
2096
- visibility: "public",
2097
- isPure: !isImpure,
2098
- hasSideEffects: isImpure,
2099
- loc: {
2100
- start: { line: idx + 1, column: 0 },
2101
- end: { line: idx + 1, column: line.length }
2102
- }
2103
- });
2104
- }
2105
- });
2106
- return {
2107
- exports,
2108
- imports,
2109
- language: "csharp" /* CSharp */,
2110
- warnings: ["Parser falling back to regex-based analysis"]
2111
- };
2112
- }
2113
- /**
2114
- * Extract import information (usings) using AST walk.
2115
- *
2116
- * @param rootNode - Root node of the C# AST.
2117
- * @returns Array of discovered FileImport objects.
2118
- */
2119
- extractImportsAST(rootNode) {
2120
- const imports = [];
2121
- const findUsings = (node) => {
2122
- if (node.type === "using_directive") {
2123
- const nameNode = node.childForFieldName("name") || node.children.find(
2124
- (c) => c.type === "qualified_name" || c.type === "identifier"
2125
- );
2126
- if (nameNode) {
2127
- const aliasNode = node.childForFieldName("alias");
2128
- imports.push({
2129
- source: nameNode.text,
2130
- specifiers: aliasNode ? [aliasNode.text] : [nameNode.text.split(".").pop() || nameNode.text],
2131
- loc: {
2132
- start: {
2133
- line: node.startPosition.row + 1,
2134
- column: node.startPosition.column
2135
- },
2136
- end: {
2137
- line: node.endPosition.row + 1,
2138
- column: node.endPosition.column
2139
- }
2140
- }
2141
- });
2142
- }
2143
- }
2144
- for (let i = 0; i < node.childCount; i++) {
2145
- const child = node.child(i);
2146
- if (child) findUsings(child);
2147
- }
2148
- };
2149
- findUsings(rootNode);
2150
- return imports;
2151
- }
2152
- /**
2153
- * Extract export information (classes, methods, properties) using AST walk.
2154
- * Handles nested namespaces and classes.
2155
- *
2156
- * @param rootNode - Root node of the C# AST.
2157
- * @param code - Source code for documentation extraction.
2158
- * @returns Array of discovered ExportInfo objects.
2159
- */
2160
- extractExportsAST(rootNode, code) {
2161
- const exports = [];
2162
- const traverse = (node, currentNamespace, currentClass) => {
2163
- let nextNamespace = currentNamespace;
2164
- let nextClass = currentClass;
2165
- if (node.type === "namespace_declaration" || node.type === "file_scoped_namespace_declaration") {
2166
- const nameNode = node.childForFieldName("name") || node.children.find(
2167
- (c) => c.type === "identifier" || c.type === "qualified_name"
2168
- );
2169
- if (nameNode) {
2170
- nextNamespace = currentNamespace ? `${currentNamespace}.${nameNode.text}` : nameNode.text;
2171
- }
2172
- } else if (node.type === "class_declaration" || node.type === "interface_declaration" || node.type === "enum_declaration" || node.type === "struct_declaration" || node.type === "record_declaration") {
2173
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2174
- if (nameNode) {
2175
- const modifiers = this.getModifiers(node);
2176
- const isPublic = modifiers.includes("public") || modifiers.includes("protected");
2177
- if (isPublic) {
2178
- const metadata = this.analyzeMetadata(node, code);
2179
- const type = node.type.replace("_declaration", "");
2180
- const fullName = nextClass ? `${nextClass}.${nameNode.text}` : nextNamespace ? `${nextNamespace}.${nameNode.text}` : nameNode.text;
2181
- exports.push({
2182
- name: fullName,
2183
- type: type === "record" ? "class" : type,
2184
- loc: {
2185
- start: {
2186
- line: node.startPosition.row + 1,
2187
- column: node.startPosition.column
2188
- },
2189
- end: {
2190
- line: node.endPosition.row + 1,
2191
- column: node.endPosition.column
2192
- }
2193
- },
2194
- visibility: modifiers.includes("public") ? "public" : "protected",
2195
- ...metadata
2196
- });
2197
- nextClass = fullName;
2198
- }
2199
- }
2200
- } else if (node.type === "method_declaration" || node.type === "property_declaration") {
2201
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2202
- if (nameNode) {
2203
- const modifiers = this.getModifiers(node);
2204
- const isPublic = modifiers.includes("public") || modifiers.includes("protected");
2205
- if (isPublic) {
2206
- const metadata = this.analyzeMetadata(node, code);
2207
- exports.push({
2208
- name: nameNode.text,
2209
- type: node.type === "method_declaration" ? "function" : "property",
2210
- parentClass: currentClass,
2211
- loc: {
2212
- start: {
2213
- line: node.startPosition.row + 1,
2214
- column: node.startPosition.column
2215
- },
2216
- end: {
2217
- line: node.endPosition.row + 1,
2218
- column: node.endPosition.column
2219
- }
2220
- },
2221
- visibility: modifiers.includes("public") ? "public" : "protected",
2222
- parameters: node.type === "method_declaration" ? this.extractParameters(node) : void 0,
2223
- ...metadata
2224
- });
2225
- }
2226
- }
2227
- }
2228
- for (let i = 0; i < node.childCount; i++) {
2229
- const child = node.child(i);
2230
- if (child) traverse(child, nextNamespace, nextClass);
2231
- }
2232
- };
2233
- traverse(rootNode);
2234
- return exports;
2235
- }
2236
- getModifiers(node) {
2237
- const modifiers = [];
2238
- for (const child of node.children) {
2239
- if (child.type === "modifier") {
2240
- modifiers.push(child.text);
2241
- }
2242
- }
2243
- return modifiers;
2244
- }
2245
- extractParameters(node) {
2246
- return extractParameterNames(node);
2247
- }
2248
- getNamingConventions() {
2249
- return {
2250
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
2251
- functionPattern: /^[A-Z][a-zA-Z0-9]*$/,
2252
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
2253
- constantPattern: /^[A-Z][a-zA-Z0-9_]*$/
2254
- };
2255
- }
2256
- canHandle(filePath) {
2257
- return filePath.toLowerCase().endsWith(".cs");
2258
- }
2259
- };
2260
-
2261
- // src/parsers/go-parser.ts
2262
- var GoParser = class extends BaseLanguageParser {
2263
- constructor() {
2264
- super(...arguments);
2265
- this.language = "go" /* Go */;
2266
- this.extensions = [".go"];
2267
- }
2268
- getParserName() {
2269
- return "go";
2270
- }
2271
- /**
2272
- * Analyze metadata for a Go node (purity, side effects).
2273
- *
2274
- * @param node - Tree-sitter node to analyze.
2275
- * @param code - Source code for context.
2276
- * @returns Partial ExportInfo containing discovered metadata.
2277
- */
2278
- analyzeMetadata(node, code) {
2279
- return analyzeGeneralMetadata(node, code, {
2280
- sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
2281
- });
2282
- }
2283
- /**
2284
- * Fallback regex-based parsing when tree-sitter is unavailable.
2285
- *
2286
- * @param code - Source code content.
2287
- * @returns Consolidated ParseResult.
2288
- */
2289
- parseRegex(code) {
2290
- const lines = code.split("\n");
2291
- const exports = [];
2292
- const imports = [];
2293
- const importRegex = /^import\s+"([^"]+)"/;
2294
- const funcRegex = /^func\s+([A-Z][a-zA-Z0-9_]*)\s*\(/;
2295
- const typeRegex = /^type\s+([A-Z][a-zA-Z0-9_]*)\s+(struct|interface)/;
2296
- lines.forEach((line, idx) => {
2297
- const importMatch = line.match(importRegex);
2298
- if (importMatch) {
2299
- const source = importMatch[1];
2300
- imports.push({
2301
- source,
2302
- specifiers: [source.split("/").pop() || source],
2303
- loc: {
2304
- start: { line: idx + 1, column: 0 },
2305
- end: { line: idx + 1, column: line.length }
2306
- }
2307
- });
2308
- }
2309
- const funcMatch = line.match(funcRegex);
2310
- if (funcMatch) {
2311
- const name = funcMatch[1];
2312
- const isPublic = /^[A-Z]/.test(name);
2313
- let docContent;
2314
- const prevLines = lines.slice(Math.max(0, idx - 3), idx);
2315
- for (let i = prevLines.length - 1; i >= 0; i--) {
2316
- const prevLine = prevLines[i].trim();
2317
- if (prevLine.startsWith("//")) {
2318
- const content = prevLine.slice(2).trim();
2319
- docContent = docContent ? content + "\n" + docContent : content;
2320
- } else if (prevLine.endsWith("*/")) {
2321
- const blockMatch = prevLine.match(/\/\*([\s\S]*)\*\//);
2322
- if (blockMatch) docContent = blockMatch[1].trim();
2323
- break;
2324
- } else if (!prevLine) {
2325
- if (docContent) break;
2326
- } else {
2327
- break;
2328
- }
2329
- }
2330
- const isImpure = name.toLowerCase().includes("impure") || line.includes("fmt.Print");
2331
- exports.push({
2332
- name,
2333
- type: "function",
2334
- visibility: isPublic ? "public" : "private",
2335
- isPure: !isImpure,
2336
- hasSideEffects: isImpure,
2337
- documentation: docContent ? { content: docContent, type: "comment" } : void 0,
2338
- loc: {
2339
- start: { line: idx + 1, column: 0 },
2340
- end: { line: idx + 1, column: line.length }
2341
- }
2342
- });
2343
- }
2344
- const typeMatch = line.match(typeRegex);
2345
- if (typeMatch) {
2346
- exports.push({
2347
- name: typeMatch[1],
2348
- type: typeMatch[2] === "struct" ? "class" : "interface",
2349
- visibility: "public",
2350
- isPure: true,
2351
- hasSideEffects: false,
2352
- loc: {
2353
- start: { line: idx + 1, column: 0 },
2354
- end: { line: idx + 1, column: line.length }
2355
- }
2356
- });
2357
- }
2358
- });
2359
- return {
2360
- exports,
2361
- imports,
2362
- language: "go" /* Go */,
2363
- warnings: ["Parser falling back to regex-based analysis"]
2364
- };
2365
- }
2366
- /**
2367
- * Extract import information using AST walk.
2368
- *
2369
- * @param rootNode - Root node of the Go AST.
2370
- * @returns Array of discovered FileImport objects.
2371
- */
2372
- extractImportsAST(rootNode) {
2373
- const imports = [];
2374
- const findImports = (node) => {
2375
- if (node.type === "import_spec") {
2376
- const pathNode = node.children.find(
2377
- (c) => c.type === "interpreted_string_literal"
2378
- );
2379
- if (pathNode) {
2380
- const source = pathNode.text.replace(/"/g, "");
2381
- imports.push({
2382
- source,
2383
- specifiers: [source.split("/").pop() || source],
2384
- loc: {
2385
- start: {
2386
- line: node.startPosition.row + 1,
2387
- column: node.startPosition.column
2388
- },
2389
- end: {
2390
- line: node.endPosition.row + 1,
2391
- column: node.endPosition.column
2392
- }
2393
- }
2394
- });
2395
- }
2396
- }
2397
- for (let i = 0; i < node.childCount; i++) {
2398
- const child = node.child(i);
2399
- if (child) findImports(child);
2400
- }
2401
- };
2402
- findImports(rootNode);
2403
- return imports;
2404
- }
2405
- /**
2406
- * Extract export information (functions, types, vars) using AST walk.
2407
- *
2408
- * @param rootNode - Root node of the Go AST.
2409
- * @param code - Source code for documentation extraction.
2410
- * @returns Array of discovered ExportInfo objects.
2411
- */
2412
- extractExportsAST(rootNode, code) {
2413
- const exports = [];
2414
- const isExported = (name) => {
2415
- return /^[A-Z]/.test(name);
2416
- };
2417
- const traverse = (node) => {
2418
- if (node.type === "function_declaration" || node.type === "method_declaration") {
2419
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "identifier");
2420
- if (nameNode && isExported(nameNode.text)) {
2421
- const metadata = this.analyzeMetadata(node, code);
2422
- exports.push({
2423
- name: nameNode.text,
2424
- type: "function",
2425
- loc: {
2426
- start: {
2427
- line: node.startPosition.row + 1,
2428
- column: node.startPosition.column
2429
- },
2430
- end: {
2431
- line: node.endPosition.row + 1,
2432
- column: node.endPosition.column
2433
- }
2434
- },
2435
- visibility: "public",
2436
- parameters: this.extractParameters(node),
2437
- ...metadata
2438
- });
2439
- }
2440
- } else if (node.type === "type_spec") {
2441
- const nameNode = node.childForFieldName("name") || node.children.find((c) => c.type === "type_identifier");
2442
- if (nameNode && isExported(nameNode.text)) {
2443
- const metadata = this.analyzeMetadata(node.parent || node, code);
2444
- const type = node.children.some((c) => c.type === "struct_type") ? "class" : "interface";
2445
- exports.push({
2446
- name: nameNode.text,
2447
- type,
2448
- loc: {
2449
- start: {
2450
- line: node.startPosition.row + 1,
2451
- column: node.startPosition.column
2452
- },
2453
- end: {
2454
- line: node.endPosition.row + 1,
2455
- column: node.endPosition.column
2456
- }
2457
- },
2458
- visibility: "public",
2459
- ...metadata
2460
- });
2461
- }
2462
- } else if (node.type === "var_spec" || node.type === "const_spec") {
2463
- const identifiers = node.children.filter(
2464
- (c) => c.type === "identifier"
2465
- );
2466
- for (const idNode of identifiers) {
2467
- if (isExported(idNode.text)) {
2468
- const metadata = this.analyzeMetadata(node, code);
2469
- exports.push({
2470
- name: idNode.text,
2471
- type: "variable",
2472
- loc: {
2473
- start: {
2474
- line: idNode.startPosition.row + 1,
2475
- column: idNode.startPosition.column
2476
- },
2477
- end: {
2478
- line: idNode.endPosition.row + 1,
2479
- column: idNode.endPosition.column
2480
- }
2481
- },
2482
- visibility: "public",
2483
- ...metadata
2484
- });
2485
- }
2486
- }
2487
- }
2488
- for (let i = 0; i < node.childCount; i++) {
2489
- const child = node.child(i);
2490
- if (child) traverse(child);
2491
- }
2492
- };
2493
- traverse(rootNode);
2494
- return exports;
2495
- }
2496
- extractParameters(node) {
2497
- return extractParameterNames(node);
2498
- }
2499
- getNamingConventions() {
2500
- return {
2501
- variablePattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2502
- functionPattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2503
- classPattern: /^[a-zA-Z][a-zA-Z0-9]*$/,
2504
- constantPattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/
2505
- };
2506
- }
2507
- canHandle(filePath) {
2508
- return filePath.toLowerCase().endsWith(".go");
2509
- }
2510
- };
2511
-
2512
- // src/parsers/parser-factory.ts
2513
- var ParserFactory = class _ParserFactory {
2514
- /**
2515
- * Create a new ParserFactory instance
2516
- */
2517
- constructor() {
2518
- this.parsers = /* @__PURE__ */ new Map();
2519
- this.extensionMap = new Map(
2520
- Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
2521
- );
2522
- this.registerParser(new TypeScriptParser());
2523
- this.registerParser(new PythonParser());
2524
- this.registerParser(new JavaParser());
2525
- this.registerParser(new CSharpParser());
2526
- this.registerParser(new GoParser());
2527
- }
2528
- /**
2529
- * Get the global singleton instance
2530
- *
2531
- * @returns The singleton ParserFactory instance
2532
- */
2533
- static getInstance() {
2534
- if (!_ParserFactory.instance) {
2535
- _ParserFactory.instance = new _ParserFactory();
2536
- }
2537
- return _ParserFactory.instance;
2538
- }
2539
- /**
2540
- * Register a language parser
2541
- */
2542
- registerParser(parser) {
2543
- this.parsers.set(parser.language, parser);
2544
- parser.extensions.forEach((ext) => {
2545
- const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
2546
- this.extensionMap.set(ext, lang);
2547
- this.parsers.set(lang, parser);
2548
- });
2549
- }
2550
- /**
2551
- * Get parser for a specific language
2552
- */
2553
- getParserForLanguage(language) {
2554
- return this.parsers.get(language) || null;
2555
- }
2556
- /**
2557
- * Get parser for a file based on its extension
2558
- */
2559
- getParserForFile(filePath) {
2560
- const ext = this.getFileExtension(filePath);
2561
- const language = this.extensionMap.get(ext);
2562
- if (!language) {
2563
- return null;
2564
- }
2565
- return this.parsers.get(language) || null;
2566
- }
2567
- /**
2568
- * Check if a file is supported
2569
- */
2570
- isSupported(filePath) {
2571
- return this.getParserForFile(filePath) !== null;
2572
- }
2573
- /**
2574
- * Get all registered languages
2575
- */
2576
- getSupportedLanguages() {
2577
- return Array.from(this.parsers.keys());
2578
- }
2579
- /**
2580
- * Get all supported file extensions
2581
- */
2582
- getSupportedExtensions() {
2583
- return Array.from(this.extensionMap.keys());
2584
- }
2585
- /**
2586
- * Get language for a file
2587
- */
2588
- getLanguageForFile(filePath) {
2589
- const ext = this.getFileExtension(filePath);
2590
- return this.extensionMap.get(ext) || null;
2591
- }
2592
- /**
2593
- * Extract file extension (with dot)
976
+ * Extract file extension (with dot)
2594
977
  */
2595
978
  getFileExtension(filePath) {
2596
979
  const match = filePath.match(/\.[^.]+$/);
@@ -2612,7 +995,7 @@ var ParserFactory = class _ParserFactory {
2612
995
  await Promise.all(promises);
2613
996
  }
2614
997
  };
2615
- function getParser(filePath) {
998
+ async function getParser(filePath) {
2616
999
  return ParserFactory.getInstance().getParserForFile(filePath);
2617
1000
  }
2618
1001
  async function initializeParsers() {
@@ -2799,10 +1182,11 @@ function extractTypeReferences(node) {
2799
1182
  }
2800
1183
 
2801
1184
  // src/utils/dependency-analyzer.ts
2802
- function parseFileExports(code, filePath) {
2803
- const parser = getParser(filePath);
1185
+ async function parseFileExports(code, filePath) {
1186
+ const parser = await getParser(filePath);
2804
1187
  if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
2805
1188
  try {
1189
+ await parser.initialize();
2806
1190
  const result = parser.parse(code, filePath);
2807
1191
  return {
2808
1192
  exports: result.exports.map((e) => ({
@@ -2827,7 +1211,7 @@ function parseFileExports(code, filePath) {
2827
1211
  }
2828
1212
  }
2829
1213
  try {
2830
- const ast = parse2(code, {
1214
+ const ast = parse(code, {
2831
1215
  loc: true,
2832
1216
  range: true,
2833
1217
  jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
@@ -2847,8 +1231,8 @@ function estimateTokens(text) {
2847
1231
  }
2848
1232
 
2849
1233
  // src/utils/config.ts
2850
- import { readFileSync, existsSync as existsSync4 } from "fs";
2851
- 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";
2852
1236
  import { pathToFileURL } from "url";
2853
1237
  var CONFIG_FILES = [
2854
1238
  "aiready.json",
@@ -2863,7 +1247,7 @@ async function loadConfig(rootDir) {
2863
1247
  while (true) {
2864
1248
  const foundConfigs = [];
2865
1249
  for (const configFile of CONFIG_FILES) {
2866
- if (existsSync4(join4(currentDir, configFile))) {
1250
+ if (existsSync3(join3(currentDir, configFile))) {
2867
1251
  foundConfigs.push(configFile);
2868
1252
  }
2869
1253
  }
@@ -2877,7 +1261,7 @@ async function loadConfig(rootDir) {
2877
1261
  } else {
2878
1262
  }
2879
1263
  const configFile = foundConfigs[0];
2880
- const configPath = join4(currentDir, configFile);
1264
+ const configPath = join3(currentDir, configFile);
2881
1265
  try {
2882
1266
  let config;
2883
1267
  if (configFile.endsWith(".js")) {
@@ -2919,7 +1303,7 @@ async function loadConfig(rootDir) {
2919
1303
  throw configError;
2920
1304
  }
2921
1305
  }
2922
- const parent = dirname4(currentDir);
1306
+ const parent = dirname3(currentDir);
2923
1307
  if (parent === currentDir) {
2924
1308
  break;
2925
1309
  }
@@ -2951,7 +1335,7 @@ function mergeConfigWithDefaults(userConfig, defaults) {
2951
1335
  return mergedConfig;
2952
1336
  }
2953
1337
 
2954
- // src/utils/html-report-builder.ts
1338
+ // src/utils/report-formatters.ts
2955
1339
  function generateReportHead(title) {
2956
1340
  return `<!DOCTYPE html>
2957
1341
  <html lang="en">
@@ -2967,6 +1351,9 @@ function generateReportHead(title) {
2967
1351
  .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
2968
1352
  .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
2969
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; }
2970
1357
  table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
2971
1358
  th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
2972
1359
  th { background-color: #f8fafc; font-weight: 600; color: #475569; }
@@ -2974,11 +1361,23 @@ function generateReportHead(title) {
2974
1361
  .critical { color: #dc2626; font-weight: bold; }
2975
1362
  .major { color: #ea580c; font-weight: bold; }
2976
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; }
2977
1367
  code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
2978
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; }
2979
1371
  </style>
2980
1372
  </head>`;
2981
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
+ }
2982
1381
  function generateStatCards(cards) {
2983
1382
  const cardsHtml = cards.map(
2984
1383
  (card) => `
@@ -2989,6 +1388,12 @@ function generateStatCards(cards) {
2989
1388
  ).join("");
2990
1389
  return `<div class="stats">${cardsHtml}</div>`;
2991
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
+ }
2992
1397
  function generateTable(config) {
2993
1398
  const headersHtml = config.headers.map((h) => `<th>${h}</th>`).join("");
2994
1399
  const rowsHtml = config.rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
@@ -2997,6 +1402,18 @@ function generateTable(config) {
2997
1402
  <tbody>${rowsHtml}</tbody>
2998
1403
  </table>`;
2999
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
+ }
3000
1417
  function generateReportFooter(options) {
3001
1418
  const versionText = options.version ? ` v${options.version}` : "";
3002
1419
  const links = [];
@@ -3006,26 +1423,360 @@ function generateReportFooter(options) {
3006
1423
  if (options.bugUrl) {
3007
1424
  links.push(`<a href="${options.bugUrl}">Report it here</a>`);
3008
1425
  }
3009
- const linksHtml = links.length ? links.map((l) => `<p>Like AIReady? ${l}</p>`).join("\n ") : "";
3010
- return `<div class="footer">
3011
- <p>Generated by <strong>@aiready/${options.packageName}</strong>${versionText}</p>
3012
- ${linksHtml}
3013
- </div>`;
3014
- }
3015
- function wrapInCard(content, title) {
3016
- const titleHtml = title ? `<h2>${title}</h2>` : "";
3017
- return `<div class="card">
3018
- ${titleHtml}
3019
- ${content}
3020
- </div>`;
3021
- }
3022
- function generateCompleteReport(options, bodyContent) {
3023
- return `${generateReportHead(options.title)}
3024
- <body>
3025
- ${bodyContent}
3026
- ${generateReportFooter(options)}
3027
- </body>
3028
- </html>`;
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;
3029
1780
  }
3030
1781
 
3031
1782
  // src/business/pricing-models.ts
@@ -3275,6 +2026,99 @@ function predictAcceptanceRate(toolOutputs) {
3275
2026
  return { rate: Math.round(rate * 100) / 100, confidence, factors };
3276
2027
  }
3277
2028
 
2029
+ // src/business-metrics.ts
2030
+ function calculateBusinessROI(params) {
2031
+ const model = getModelPreset(params.modelId || "claude-4.6");
2032
+ const devCount = params.developerCount || 5;
2033
+ const budget = calculateTokenBudget({
2034
+ totalContextTokens: params.tokenWaste * 2.5,
2035
+ wastedTokens: {
2036
+ duplication: params.tokenWaste * 0.7,
2037
+ fragmentation: params.tokenWaste * 0.3,
2038
+ chattiness: 0
2039
+ }
2040
+ });
2041
+ const cost = estimateCostFromBudget(budget, model, {
2042
+ developerCount: devCount
2043
+ });
2044
+ const productivity = calculateProductivityImpact(params.issues);
2045
+ const monthlySavings = cost.total;
2046
+ const productivityGainHours = productivity.totalHours;
2047
+ const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
2048
+ return {
2049
+ monthlySavings: Math.round(monthlySavings),
2050
+ productivityGainHours: Math.round(productivityGainHours),
2051
+ annualValue: Math.round(annualValue)
2052
+ };
2053
+ }
2054
+ function formatCost(cost) {
2055
+ if (cost < 1) {
2056
+ return `$${cost.toFixed(2)}`;
2057
+ } else if (cost < 1e3) {
2058
+ return `$${cost.toFixed(0)}`;
2059
+ } else {
2060
+ return `$${(cost / 1e3).toFixed(1)}k`;
2061
+ }
2062
+ }
2063
+ function formatHours(hours) {
2064
+ if (hours < 1) {
2065
+ return `${Math.round(hours * 60)}min`;
2066
+ } else if (hours < 8) {
2067
+ return `${hours.toFixed(1)}h`;
2068
+ } else if (hours < 40) {
2069
+ return `${Math.round(hours)}h`;
2070
+ } else {
2071
+ return `${(hours / 40).toFixed(1)} weeks`;
2072
+ }
2073
+ }
2074
+ function formatAcceptanceRate(rate) {
2075
+ return `${Math.round(rate * 100)}%`;
2076
+ }
2077
+ function generateValueChain(params) {
2078
+ const { issueType, count, severity } = params;
2079
+ const impacts = {
2080
+ "duplicate-pattern": {
2081
+ ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
2082
+ dev: "Developers must manually resolve conflicts between suggested variants.",
2083
+ risk: "high"
2084
+ },
2085
+ "context-fragmentation": {
2086
+ ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
2087
+ dev: "Slower AI responses and increased need for manual context pinning.",
2088
+ risk: "critical"
2089
+ },
2090
+ "naming-inconsistency": {
2091
+ ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
2092
+ dev: "Increased cognitive load for new devs during onboarding.",
2093
+ risk: "moderate"
2094
+ }
2095
+ };
2096
+ const impact = impacts[issueType] || {
2097
+ ai: "Reduced suggestion quality.",
2098
+ dev: "Slowed development velocity.",
2099
+ risk: "moderate"
2100
+ };
2101
+ const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
2102
+ return {
2103
+ issueType,
2104
+ technicalMetric: "Issue Count",
2105
+ technicalValue: count,
2106
+ aiImpact: {
2107
+ description: impact.ai,
2108
+ scoreImpact: severity === "critical" ? -15 : -5
2109
+ },
2110
+ developerImpact: {
2111
+ description: impact.dev,
2112
+ productivityLoss
2113
+ },
2114
+ businessOutcome: {
2115
+ directCost: count * 12,
2116
+ opportunityCost: productivityLoss * 15e3,
2117
+ riskLevel: impact.risk
2118
+ }
2119
+ };
2120
+ }
2121
+
3278
2122
  // src/business/risk-metrics.ts
3279
2123
  function calculateKnowledgeConcentration(params) {
3280
2124
  const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
@@ -3369,99 +2213,6 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
3369
2213
  };
3370
2214
  }
3371
2215
 
3372
- // src/business-metrics.ts
3373
- function calculateBusinessROI(params) {
3374
- const model = getModelPreset(params.modelId || "claude-4.6");
3375
- const devCount = params.developerCount || 5;
3376
- const budget = calculateTokenBudget({
3377
- totalContextTokens: params.tokenWaste * 2.5,
3378
- wastedTokens: {
3379
- duplication: params.tokenWaste * 0.7,
3380
- fragmentation: params.tokenWaste * 0.3,
3381
- chattiness: 0
3382
- }
3383
- });
3384
- const cost = estimateCostFromBudget(budget, model, {
3385
- developerCount: devCount
3386
- });
3387
- const productivity = calculateProductivityImpact(params.issues);
3388
- const monthlySavings = cost.total;
3389
- const productivityGainHours = productivity.totalHours;
3390
- const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
3391
- return {
3392
- monthlySavings: Math.round(monthlySavings),
3393
- productivityGainHours: Math.round(productivityGainHours),
3394
- annualValue: Math.round(annualValue)
3395
- };
3396
- }
3397
- function formatCost(cost) {
3398
- if (cost < 1) {
3399
- return `$${cost.toFixed(2)}`;
3400
- } else if (cost < 1e3) {
3401
- return `$${cost.toFixed(0)}`;
3402
- } else {
3403
- return `$${(cost / 1e3).toFixed(1)}k`;
3404
- }
3405
- }
3406
- function formatHours(hours) {
3407
- if (hours < 1) {
3408
- return `${Math.round(hours * 60)}min`;
3409
- } else if (hours < 8) {
3410
- return `${hours.toFixed(1)}h`;
3411
- } else if (hours < 40) {
3412
- return `${Math.round(hours)}h`;
3413
- } else {
3414
- return `${(hours / 40).toFixed(1)} weeks`;
3415
- }
3416
- }
3417
- function formatAcceptanceRate(rate) {
3418
- return `${Math.round(rate * 100)}%`;
3419
- }
3420
- function generateValueChain(params) {
3421
- const { issueType, count, severity } = params;
3422
- const impacts = {
3423
- "duplicate-pattern": {
3424
- ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
3425
- dev: "Developers must manually resolve conflicts between suggested variants.",
3426
- risk: "high"
3427
- },
3428
- "context-fragmentation": {
3429
- ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
3430
- dev: "Slower AI responses and increased need for manual context pinning.",
3431
- risk: "critical"
3432
- },
3433
- "naming-inconsistency": {
3434
- ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
3435
- dev: "Increased cognitive load for new devs during onboarding.",
3436
- risk: "moderate"
3437
- }
3438
- };
3439
- const impact = impacts[issueType] || {
3440
- ai: "Reduced suggestion quality.",
3441
- dev: "Slowed development velocity.",
3442
- risk: "moderate"
3443
- };
3444
- const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
3445
- return {
3446
- issueType,
3447
- technicalMetric: "Issue Count",
3448
- technicalValue: count,
3449
- aiImpact: {
3450
- description: impact.ai,
3451
- scoreImpact: severity === "critical" ? -15 : -5
3452
- },
3453
- developerImpact: {
3454
- description: impact.dev,
3455
- productivityLoss
3456
- },
3457
- businessOutcome: {
3458
- directCost: count * 12,
3459
- opportunityCost: productivityLoss * 15e3,
3460
- riskLevel: impact.risk
3461
- }
3462
- };
3463
- }
3464
-
3465
2216
  // src/metrics/remediation-utils.ts
3466
2217
  function collectFutureProofRecommendations(params) {
3467
2218
  const recommendations = [];
@@ -3499,33 +2250,163 @@ function collectFutureProofRecommendations(params) {
3499
2250
  }
3500
2251
  }
3501
2252
  if (params.dependencyHealth) {
3502
- for (const rec of params.dependencyHealth.recommendations) {
3503
- recommendations.push({
3504
- action: rec,
3505
- estimatedImpact: 7,
3506
- priority: "medium"
3507
- });
3508
- }
2253
+ for (const rec of params.dependencyHealth.recommendations) {
2254
+ recommendations.push({
2255
+ action: rec,
2256
+ estimatedImpact: 7,
2257
+ priority: "medium"
2258
+ });
2259
+ }
2260
+ }
2261
+ return recommendations;
2262
+ }
2263
+ function collectBaseFutureProofRecommendations(params) {
2264
+ const recommendations = [];
2265
+ for (const rec of params.patternEntropy.recommendations) {
2266
+ recommendations.push({
2267
+ action: rec,
2268
+ estimatedImpact: 5,
2269
+ priority: "medium"
2270
+ });
2271
+ }
2272
+ if (params.conceptCohesion.rating === "poor") {
2273
+ recommendations.push({
2274
+ action: "Improve concept cohesion by grouping related exports",
2275
+ estimatedImpact: 8,
2276
+ priority: "high"
2277
+ });
2278
+ }
2279
+ return recommendations;
2280
+ }
2281
+
2282
+ // src/future-proof-metrics.ts
2283
+ function calculateFutureProofScore(params) {
2284
+ const loadScore = 100 - params.cognitiveLoad.score;
2285
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2286
+ const cohesionScore = params.conceptCohesion.score * 100;
2287
+ const overall = Math.round(
2288
+ loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
2289
+ );
2290
+ const factors = [
2291
+ {
2292
+ name: "Cognitive Load",
2293
+ impact: Math.round(loadScore - 50),
2294
+ description: params.cognitiveLoad.rating
2295
+ },
2296
+ {
2297
+ name: "Pattern Entropy",
2298
+ impact: Math.round(entropyScore - 50),
2299
+ description: params.patternEntropy.rating
2300
+ },
2301
+ {
2302
+ name: "Concept Cohesion",
2303
+ impact: Math.round(cohesionScore - 50),
2304
+ description: params.conceptCohesion.rating
2305
+ }
2306
+ ];
2307
+ const recommendations = collectBaseFutureProofRecommendations({
2308
+ patternEntropy: params.patternEntropy,
2309
+ conceptCohesion: params.conceptCohesion
2310
+ });
2311
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2312
+ return {
2313
+ toolName: "future-proof",
2314
+ score: overall,
2315
+ rawMetrics: {
2316
+ cognitiveLoadScore: params.cognitiveLoad.score,
2317
+ entropyScore: params.patternEntropy.entropy,
2318
+ cohesionScore: params.conceptCohesion.score,
2319
+ semanticDistanceAvg
2320
+ },
2321
+ factors,
2322
+ recommendations
2323
+ };
2324
+ }
2325
+ function calculateExtendedFutureProofScore(params) {
2326
+ const loadScore = 100 - params.cognitiveLoad.score;
2327
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2328
+ const cohesionScore = params.conceptCohesion.score * 100;
2329
+ const aiSignalClarityScore = 100 - params.aiSignalClarity.score;
2330
+ const groundingScore = params.agentGrounding.score;
2331
+ const testabilityScore = params.testability.score;
2332
+ const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2333
+ const depsHealthScore = params.dependencyHealth?.score ?? 100;
2334
+ let totalWeight = 0.8;
2335
+ let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2336
+ if (params.docDrift) {
2337
+ overall += docDriftScore * 0.1;
2338
+ totalWeight += 0.1;
2339
+ }
2340
+ if (params.dependencyHealth) {
2341
+ overall += depsHealthScore * 0.1;
2342
+ totalWeight += 0.1;
3509
2343
  }
3510
- return recommendations;
3511
- }
3512
- function collectBaseFutureProofRecommendations(params) {
3513
- const recommendations = [];
3514
- for (const rec of params.patternEntropy.recommendations) {
3515
- recommendations.push({
3516
- action: rec,
3517
- estimatedImpact: 5,
3518
- priority: "medium"
2344
+ overall = Math.round(overall / totalWeight);
2345
+ const factors = [
2346
+ {
2347
+ name: "Cognitive Load",
2348
+ impact: Math.round(loadScore - 50),
2349
+ description: params.cognitiveLoad.rating
2350
+ },
2351
+ {
2352
+ name: "Pattern Entropy",
2353
+ impact: Math.round(entropyScore - 50),
2354
+ description: params.patternEntropy.rating
2355
+ },
2356
+ {
2357
+ name: "Concept Cohesion",
2358
+ impact: Math.round(cohesionScore - 50),
2359
+ description: params.conceptCohesion.rating
2360
+ },
2361
+ {
2362
+ name: "AI Signal Clarity",
2363
+ impact: Math.round(aiSignalClarityScore - 50),
2364
+ description: `${params.aiSignalClarity.rating} risk`
2365
+ },
2366
+ {
2367
+ name: "Agent Grounding",
2368
+ impact: Math.round(groundingScore - 50),
2369
+ description: params.agentGrounding.rating
2370
+ },
2371
+ {
2372
+ name: "Testability",
2373
+ impact: Math.round(testabilityScore - 50),
2374
+ description: params.testability.rating
2375
+ }
2376
+ ];
2377
+ if (params.docDrift) {
2378
+ factors.push({
2379
+ name: "Documentation Drift",
2380
+ impact: Math.round(docDriftScore - 50),
2381
+ description: params.docDrift.rating
3519
2382
  });
3520
2383
  }
3521
- if (params.conceptCohesion.rating === "poor") {
3522
- recommendations.push({
3523
- action: "Improve concept cohesion by grouping related exports",
3524
- estimatedImpact: 8,
3525
- priority: "high"
2384
+ if (params.dependencyHealth) {
2385
+ factors.push({
2386
+ name: "Dependency Health",
2387
+ impact: Math.round(depsHealthScore - 50),
2388
+ description: params.dependencyHealth.rating
3526
2389
  });
3527
2390
  }
3528
- return recommendations;
2391
+ const recommendations = collectFutureProofRecommendations(params);
2392
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2393
+ return {
2394
+ toolName: "future-proof",
2395
+ score: overall,
2396
+ rawMetrics: {
2397
+ cognitiveLoadScore: params.cognitiveLoad.score,
2398
+ entropyScore: params.patternEntropy.entropy,
2399
+ cohesionScore: params.conceptCohesion.score,
2400
+ aiSignalClarityScore: params.aiSignalClarity.score,
2401
+ agentGroundingScore: params.agentGrounding.score,
2402
+ testabilityScore: params.testability.score,
2403
+ docDriftScore: params.docDrift?.score,
2404
+ dependencyHealthScore: params.dependencyHealth?.score,
2405
+ semanticDistanceAvg
2406
+ },
2407
+ factors,
2408
+ recommendations
2409
+ };
3529
2410
  }
3530
2411
 
3531
2412
  // src/metrics/cognitive-load.ts
@@ -4286,145 +3167,15 @@ function calculateChangeAmplification(params) {
4286
3167
  };
4287
3168
  }
4288
3169
 
4289
- // src/future-proof-metrics.ts
4290
- function calculateFutureProofScore(params) {
4291
- const loadScore = 100 - params.cognitiveLoad.score;
4292
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
4293
- const cohesionScore = params.conceptCohesion.score * 100;
4294
- const overall = Math.round(
4295
- loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
4296
- );
4297
- const factors = [
4298
- {
4299
- name: "Cognitive Load",
4300
- impact: Math.round(loadScore - 50),
4301
- description: params.cognitiveLoad.rating
4302
- },
4303
- {
4304
- name: "Pattern Entropy",
4305
- impact: Math.round(entropyScore - 50),
4306
- description: params.patternEntropy.rating
4307
- },
4308
- {
4309
- name: "Concept Cohesion",
4310
- impact: Math.round(cohesionScore - 50),
4311
- description: params.conceptCohesion.rating
4312
- }
4313
- ];
4314
- const recommendations = collectBaseFutureProofRecommendations({
4315
- patternEntropy: params.patternEntropy,
4316
- conceptCohesion: params.conceptCohesion
4317
- });
4318
- const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
4319
- return {
4320
- toolName: "future-proof",
4321
- score: overall,
4322
- rawMetrics: {
4323
- cognitiveLoadScore: params.cognitiveLoad.score,
4324
- entropyScore: params.patternEntropy.entropy,
4325
- cohesionScore: params.conceptCohesion.score,
4326
- semanticDistanceAvg
4327
- },
4328
- factors,
4329
- recommendations
4330
- };
4331
- }
4332
- function calculateExtendedFutureProofScore(params) {
4333
- const loadScore = 100 - params.cognitiveLoad.score;
4334
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
4335
- const cohesionScore = params.conceptCohesion.score * 100;
4336
- const aiSignalClarityScore = 100 - params.aiSignalClarity.score;
4337
- const groundingScore = params.agentGrounding.score;
4338
- const testabilityScore = params.testability.score;
4339
- const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
4340
- const depsHealthScore = params.dependencyHealth?.score ?? 100;
4341
- let totalWeight = 0.8;
4342
- let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
4343
- if (params.docDrift) {
4344
- overall += docDriftScore * 0.1;
4345
- totalWeight += 0.1;
4346
- }
4347
- if (params.dependencyHealth) {
4348
- overall += depsHealthScore * 0.1;
4349
- totalWeight += 0.1;
4350
- }
4351
- overall = Math.round(overall / totalWeight);
4352
- const factors = [
4353
- {
4354
- name: "Cognitive Load",
4355
- impact: Math.round(loadScore - 50),
4356
- description: params.cognitiveLoad.rating
4357
- },
4358
- {
4359
- name: "Pattern Entropy",
4360
- impact: Math.round(entropyScore - 50),
4361
- description: params.patternEntropy.rating
4362
- },
4363
- {
4364
- name: "Concept Cohesion",
4365
- impact: Math.round(cohesionScore - 50),
4366
- description: params.conceptCohesion.rating
4367
- },
4368
- {
4369
- name: "AI Signal Clarity",
4370
- impact: Math.round(aiSignalClarityScore - 50),
4371
- description: `${params.aiSignalClarity.rating} risk`
4372
- },
4373
- {
4374
- name: "Agent Grounding",
4375
- impact: Math.round(groundingScore - 50),
4376
- description: params.agentGrounding.rating
4377
- },
4378
- {
4379
- name: "Testability",
4380
- impact: Math.round(testabilityScore - 50),
4381
- description: params.testability.rating
4382
- }
4383
- ];
4384
- if (params.docDrift) {
4385
- factors.push({
4386
- name: "Documentation Drift",
4387
- impact: Math.round(docDriftScore - 50),
4388
- description: params.docDrift.rating
4389
- });
4390
- }
4391
- if (params.dependencyHealth) {
4392
- factors.push({
4393
- name: "Dependency Health",
4394
- impact: Math.round(depsHealthScore - 50),
4395
- description: params.dependencyHealth.rating
4396
- });
4397
- }
4398
- const recommendations = collectFutureProofRecommendations(params);
4399
- const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
4400
- return {
4401
- toolName: "future-proof",
4402
- score: overall,
4403
- rawMetrics: {
4404
- cognitiveLoadScore: params.cognitiveLoad.score,
4405
- entropyScore: params.patternEntropy.entropy,
4406
- cohesionScore: params.conceptCohesion.score,
4407
- aiSignalClarityScore: params.aiSignalClarity.score,
4408
- agentGroundingScore: params.agentGrounding.score,
4409
- testabilityScore: params.testability.score,
4410
- docDriftScore: params.docDrift?.score,
4411
- dependencyHealthScore: params.dependencyHealth?.score,
4412
- semanticDistanceAvg
4413
- },
4414
- factors,
4415
- recommendations
4416
- };
4417
- }
4418
-
4419
3170
  // src/utils/history.ts
4420
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
4421
- 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";
4422
3173
  function getHistoryPath(rootDir) {
4423
- return join5(rootDir, ".aiready", "history.json");
3174
+ return join4(rootDir, ".aiready", "history.json");
4424
3175
  }
4425
3176
  function loadScoreHistory(rootDir) {
4426
3177
  const historyPath = getHistoryPath(rootDir);
4427
- if (!existsSync5(historyPath)) {
3178
+ if (!existsSync4(historyPath)) {
4428
3179
  return [];
4429
3180
  }
4430
3181
  try {
@@ -4437,8 +3188,8 @@ function loadScoreHistory(rootDir) {
4437
3188
  }
4438
3189
  function saveScoreEntry(rootDir, entry) {
4439
3190
  const historyPath = getHistoryPath(rootDir);
4440
- const historyDir = dirname5(historyPath);
4441
- if (!existsSync5(historyDir)) {
3191
+ const historyDir = dirname4(historyPath);
3192
+ if (!existsSync4(historyDir)) {
4442
3193
  mkdirSync2(historyDir, { recursive: true });
4443
3194
  }
4444
3195
  const history = loadScoreHistory(rootDir);
@@ -4485,7 +3236,7 @@ function exportHistory(rootDir, format = "json") {
4485
3236
  }
4486
3237
  function clearHistory(rootDir) {
4487
3238
  const historyPath = getHistoryPath(rootDir);
4488
- if (existsSync5(historyPath)) {
3239
+ if (existsSync4(historyPath)) {
4489
3240
  writeFileSync2(historyPath, JSON.stringify([]));
4490
3241
  }
4491
3242
  }
@@ -4652,8 +3403,10 @@ export {
4652
3403
  TypeScriptParser,
4653
3404
  UnifiedReportSchema,
4654
3405
  VAGUE_FILE_NAMES,
3406
+ buildFactorsFromDimensions,
4655
3407
  buildSimpleProviderScore,
4656
3408
  buildSpokeOutput,
3409
+ buildStandardToolScore,
4657
3410
  calculateAgentGrounding,
4658
3411
  calculateAiSignalClarity,
4659
3412
  calculateBusinessROI,
@@ -4667,6 +3420,7 @@ export {
4667
3420
  calculateDocDrift,
4668
3421
  calculateExtendedFutureProofScore,
4669
3422
  calculateFutureProofScore,
3423
+ calculateHeuristicConfidence,
4670
3424
  calculateImportSimilarity,
4671
3425
  calculateKnowledgeConcentration,
4672
3426
  calculateMonthlyCost,
@@ -4674,28 +3428,37 @@ export {
4674
3428
  calculatePatternEntropy,
4675
3429
  calculateProductivityImpact,
4676
3430
  calculateSemanticDistance,
3431
+ calculateStringSimilarity,
4677
3432
  calculateTechnicalValueChain,
4678
3433
  calculateTestabilityIndex,
4679
3434
  calculateTokenBudget,
4680
3435
  clearHistory,
4681
3436
  createProvider,
3437
+ createStandardProgressCallback,
3438
+ displayStandardConsoleReport,
4682
3439
  emitAnnotation,
4683
3440
  emitIssuesAsAnnotations,
4684
3441
  emitProgress,
4685
3442
  estimateCostFromBudget,
4686
3443
  estimateTokens,
4687
3444
  exportHistory,
3445
+ extractCodeBlocks,
4688
3446
  findLatestReport,
4689
3447
  findLatestScanReport,
4690
3448
  formatAcceptanceRate,
4691
3449
  formatCost,
4692
3450
  formatHours,
4693
3451
  formatScore,
3452
+ formatStandardCliResult,
3453
+ formatStandardReport,
4694
3454
  formatToolScore,
4695
3455
  generateCompleteReport,
4696
3456
  generateHTML,
3457
+ generateIssueSummary,
4697
3458
  generateReportFooter,
4698
3459
  generateReportHead,
3460
+ generateReportHero,
3461
+ generateScoreCard,
4699
3462
  generateStatCards,
4700
3463
  generateTable,
4701
3464
  generateValueChain,
@@ -4712,24 +3475,30 @@ export {
4712
3475
  getRatingDisplay,
4713
3476
  getRatingEmoji,
4714
3477
  getRatingLabel,
3478
+ getRatingMetadata,
4715
3479
  getRatingSlug,
4716
3480
  getRatingWithContext,
4717
3481
  getRecommendedThreshold,
4718
3482
  getRepoMetadata,
3483
+ getReportTimestamp,
4719
3484
  getSafetyIcon,
4720
3485
  getScoreBar,
3486
+ getScoreColor,
4721
3487
  getSeverityBadge,
4722
3488
  getSeverityColor,
4723
3489
  getSeverityEnum,
4724
3490
  getSeverityLevel,
4725
3491
  getSeverityValue,
4726
3492
  getSupportedLanguages,
3493
+ getTerminalDivider,
4727
3494
  getToolEmoji,
4728
3495
  getToolWeight,
4729
3496
  getWasmPath,
4730
3497
  groupIssuesByFile,
4731
3498
  handleCLIError,
4732
3499
  handleJSONOutput,
3500
+ handleStandardJSONOutput,
3501
+ inferPatternType,
4733
3502
  initTreeSitter,
4734
3503
  initializeParsers,
4735
3504
  isFileSupported,
@@ -4746,8 +3515,12 @@ export {
4746
3515
  parseFileExports,
4747
3516
  parseWeightString,
4748
3517
  predictAcceptanceRate,
3518
+ prepareActionConfig,
3519
+ printTerminalHeader,
4749
3520
  readFileContent,
3521
+ resolveOutputFormat,
4750
3522
  resolveOutputPath,
3523
+ runStandardCliAction,
4751
3524
  saveScoreEntry,
4752
3525
  scanEntries,
4753
3526
  scanFiles,