@aiready/core 0.21.13 → 0.21.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -467,904 +467,477 @@ function getSeverityColor(severity, chalk) {
467
467
  }
468
468
 
469
469
  // src/utils/ast-parser.ts
470
+ import { parse as parse2 } from "@typescript-eslint/typescript-estree";
471
+
472
+ // src/parsers/typescript-parser.ts
470
473
  import { parse } from "@typescript-eslint/typescript-estree";
471
- function parseFileExports(code, filePath) {
472
- try {
473
- const ast = parse(code, {
474
- loc: true,
475
- range: true,
476
- jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
477
- filePath
478
- });
479
- const imports = extractFileImports(ast);
480
- const exports = extractExportsWithDependencies(ast, imports);
481
- return { exports, imports };
482
- } catch (error) {
483
- return { exports: [], imports: [] };
474
+ var TypeScriptParser = class {
475
+ constructor() {
476
+ this.language = "typescript" /* TypeScript */;
477
+ this.extensions = [".ts", ".tsx", ".js", ".jsx"];
484
478
  }
485
- }
486
- function extractFileImports(ast) {
487
- const imports = [];
488
- for (const node of ast.body) {
489
- if (node.type === "ImportDeclaration") {
490
- const source = node.source.value;
491
- const specifiers = [];
492
- const isTypeOnly = node.importKind === "type";
493
- for (const spec of node.specifiers) {
494
- if (spec.type === "ImportSpecifier") {
495
- const imported = spec.imported;
496
- const importName = imported.type === "Identifier" ? imported.name : imported.value;
497
- specifiers.push(importName);
498
- } else if (spec.type === "ImportDefaultSpecifier") {
499
- specifiers.push("default");
500
- } else if (spec.type === "ImportNamespaceSpecifier") {
501
- specifiers.push("*");
502
- }
503
- }
504
- imports.push({ source, specifiers, isTypeOnly });
505
- }
479
+ async initialize() {
480
+ return Promise.resolve();
506
481
  }
507
- return imports;
508
- }
509
- function extractExportsWithDependencies(ast, fileImports) {
510
- const exports = [];
511
- const importedNames = new Set(fileImports.flatMap((imp) => imp.specifiers));
512
- for (const node of ast.body) {
513
- if (node.type === "ExportNamedDeclaration") {
514
- if (node.declaration) {
515
- const exportNodes = extractFromDeclaration(node.declaration);
516
- for (const exp of exportNodes) {
517
- const usedImports = findUsedImports(node.declaration, importedNames);
518
- const typeReferences = extractTypeReferences(node.declaration);
519
- exports.push({
520
- ...exp,
521
- imports: usedImports,
522
- dependencies: [],
523
- typeReferences,
524
- loc: node.loc
525
- });
526
- }
527
- }
528
- } else if (node.type === "ExportDefaultDeclaration") {
529
- const usedImports = findUsedImports(node.declaration, importedNames);
530
- const typeReferences = extractTypeReferences(node.declaration);
531
- exports.push({
532
- name: "default",
533
- type: "default",
534
- imports: usedImports,
535
- dependencies: [],
536
- typeReferences,
537
- loc: node.loc
482
+ parse(code, filePath) {
483
+ try {
484
+ const isJavaScript = filePath.match(/\.jsx?$/i);
485
+ const ast = parse(code, {
486
+ loc: true,
487
+ range: true,
488
+ jsx: filePath.match(/\.[jt]sx$/i) !== null,
489
+ filePath,
490
+ sourceType: "module",
491
+ ecmaVersion: "latest"
538
492
  });
493
+ const imports = this.extractImports(ast);
494
+ const exports = this.extractExports(ast, imports);
495
+ return {
496
+ exports,
497
+ imports,
498
+ language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
499
+ warnings: []
500
+ };
501
+ } catch (error) {
502
+ const err = error;
503
+ throw new ParseError(
504
+ `Failed to parse ${filePath}: ${err.message}`,
505
+ filePath
506
+ );
539
507
  }
540
508
  }
541
- return exports;
542
- }
543
- function extractFromDeclaration(declaration) {
544
- const results = [];
545
- if (declaration.type === "FunctionDeclaration" && "id" in declaration && declaration.id) {
546
- results.push({ name: declaration.id.name, type: "function" });
547
- } else if (declaration.type === "ClassDeclaration" && "id" in declaration && declaration.id) {
548
- results.push({ name: declaration.id.name, type: "class" });
549
- } else if (declaration.type === "VariableDeclaration") {
550
- for (const declarator of declaration.declarations) {
551
- if (declarator.id.type === "Identifier") {
552
- results.push({ name: declarator.id.name, type: "const" });
553
- }
554
- }
555
- } else if (declaration.type === "TSTypeAliasDeclaration") {
556
- results.push({ name: declaration.id.name, type: "type" });
557
- } else if (declaration.type === "TSInterfaceDeclaration") {
558
- results.push({ name: declaration.id.name, type: "interface" });
559
- }
560
- return results;
561
- }
562
- function findUsedImports(node, importedNames) {
563
- const usedImports = /* @__PURE__ */ new Set();
564
- function visit(n) {
565
- if (n.type === "Identifier" && importedNames.has(n.name)) {
566
- usedImports.add(n.name);
567
- }
568
- for (const key in n) {
569
- const value = n[key];
570
- if (value && typeof value === "object") {
571
- if (Array.isArray(value)) {
572
- value.forEach((child) => {
573
- if (child && typeof child === "object" && "type" in child) {
574
- visit(child);
575
- }
576
- });
577
- } else if ("type" in value) {
578
- visit(value);
579
- }
580
- }
581
- }
509
+ getNamingConventions() {
510
+ return {
511
+ // camelCase for variables and functions
512
+ variablePattern: /^[a-z][a-zA-Z0-9]*$/,
513
+ functionPattern: /^[a-z][a-zA-Z0-9]*$/,
514
+ // PascalCase for classes
515
+ classPattern: /^[A-Z][a-zA-Z0-9]*$/,
516
+ // UPPER_CASE for constants
517
+ constantPattern: /^[A-Z][A-Z0-9_]*$/,
518
+ // Common exceptions (React hooks, etc.)
519
+ exceptions: ["__filename", "__dirname", "__esModule"]
520
+ };
582
521
  }
583
- visit(node);
584
- return Array.from(usedImports);
585
- }
586
- function calculateImportSimilarity(export1, export2) {
587
- if (export1.imports.length === 0 && export2.imports.length === 0) {
588
- return 1;
522
+ canHandle(filePath) {
523
+ return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
589
524
  }
590
- const set1 = new Set(export1.imports);
591
- const set2 = new Set(export2.imports);
592
- const intersection = new Set([...set1].filter((x) => set2.has(x)));
593
- const union = /* @__PURE__ */ new Set([...set1, ...set2]);
594
- return intersection.size / union.size;
595
- }
596
- function extractTypeReferences(node) {
597
- const types = /* @__PURE__ */ new Set();
598
- function visit(n) {
599
- if (!n || typeof n !== "object") return;
600
- if (n.type === "TSTypeReference" && n.typeName) {
601
- if (n.typeName.type === "Identifier") {
602
- types.add(n.typeName.name);
603
- } else if (n.typeName.type === "TSQualifiedName") {
604
- let current = n.typeName;
605
- while (current.type === "TSQualifiedName") {
606
- if (current.right?.type === "Identifier") {
607
- types.add(current.right.name);
608
- }
609
- current = current.left;
525
+ extractImports(ast) {
526
+ const imports = [];
527
+ for (const node of ast.body) {
528
+ if (node.type === "ImportDeclaration") {
529
+ const specifiers = [];
530
+ let isTypeOnly = false;
531
+ if (node.importKind === "type") {
532
+ isTypeOnly = true;
610
533
  }
611
- if (current.type === "Identifier") {
612
- types.add(current.name);
534
+ for (const spec of node.specifiers) {
535
+ if (spec.type === "ImportSpecifier") {
536
+ const imported = spec.imported;
537
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
538
+ specifiers.push(name);
539
+ } else if (spec.type === "ImportDefaultSpecifier") {
540
+ specifiers.push("default");
541
+ } else if (spec.type === "ImportNamespaceSpecifier") {
542
+ specifiers.push("*");
543
+ }
613
544
  }
545
+ imports.push({
546
+ source: node.source.value,
547
+ specifiers,
548
+ isTypeOnly,
549
+ loc: node.loc ? {
550
+ start: {
551
+ line: node.loc.start.line,
552
+ column: node.loc.start.column
553
+ },
554
+ end: { line: node.loc.end.line, column: node.loc.end.column }
555
+ } : void 0
556
+ });
614
557
  }
615
558
  }
616
- if (n.type === "TSInterfaceHeritage" && n.expression) {
617
- if (n.expression.type === "Identifier") {
618
- types.add(n.expression.name);
619
- }
620
- }
621
- for (const key of Object.keys(n)) {
622
- const value = n[key];
623
- if (Array.isArray(value)) {
624
- value.forEach(visit);
625
- } else if (value && typeof value === "object") {
626
- visit(value);
559
+ return imports;
560
+ }
561
+ extractExports(ast, imports) {
562
+ const exports = [];
563
+ const importedNames = new Set(
564
+ imports.flatMap(
565
+ (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
566
+ )
567
+ );
568
+ for (const node of ast.body) {
569
+ if (node.type === "ExportNamedDeclaration" && node.declaration) {
570
+ const extracted = this.extractFromDeclaration(
571
+ node.declaration,
572
+ importedNames
573
+ );
574
+ exports.push(...extracted);
575
+ } else if (node.type === "ExportDefaultDeclaration") {
576
+ let name = "default";
577
+ let type = "default";
578
+ if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
579
+ name = node.declaration.id.name;
580
+ type = "function";
581
+ } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
582
+ name = node.declaration.id.name;
583
+ type = "class";
584
+ }
585
+ exports.push({
586
+ name,
587
+ type,
588
+ loc: node.loc ? {
589
+ start: {
590
+ line: node.loc.start.line,
591
+ column: node.loc.start.column
592
+ },
593
+ end: { line: node.loc.end.line, column: node.loc.end.column }
594
+ } : void 0
595
+ });
627
596
  }
628
597
  }
598
+ return exports;
629
599
  }
630
- visit(node);
631
- return Array.from(types);
632
- }
633
- function parseCode(code, language) {
634
- return null;
635
- }
636
- function extractFunctions(ast) {
637
- return [];
638
- }
639
- function extractImports(ast) {
640
- return [];
641
- }
642
-
643
- // src/utils/metrics.ts
644
- function estimateTokens(text) {
645
- return Math.ceil(text.length / 4);
646
- }
647
-
648
- // src/utils/config.ts
649
- import { readFileSync, existsSync as existsSync3 } from "fs";
650
- import { join as join3, resolve, dirname as dirname3 } from "path";
651
- import { pathToFileURL } from "url";
652
- var CONFIG_FILES = [
653
- "aiready.json",
654
- "aiready.config.json",
655
- ".aiready.json",
656
- ".aireadyrc.json",
657
- "aiready.config.js",
658
- ".aireadyrc.js"
659
- ];
660
- async function loadConfig(rootDir) {
661
- let currentDir = resolve(rootDir);
662
- while (true) {
663
- for (const configFile of CONFIG_FILES) {
664
- const configPath = join3(currentDir, configFile);
665
- if (existsSync3(configPath)) {
666
- try {
667
- let config;
668
- if (configFile.endsWith(".js")) {
669
- const fileUrl = pathToFileURL(configPath).href;
670
- const module = await import(`${fileUrl}?t=${Date.now()}`);
671
- config = module.default || module;
672
- } else {
673
- const content = readFileSync(configPath, "utf-8");
674
- config = JSON.parse(content);
600
+ extractFromDeclaration(declaration, importedNames) {
601
+ const exports = [];
602
+ if (declaration.type === "FunctionDeclaration" && declaration.id) {
603
+ exports.push({
604
+ name: declaration.id.name,
605
+ type: "function",
606
+ parameters: declaration.params.map(
607
+ (p) => p.type === "Identifier" ? p.name : "unknown"
608
+ ),
609
+ loc: declaration.loc ? {
610
+ start: {
611
+ line: declaration.loc.start.line,
612
+ column: declaration.loc.start.column
613
+ },
614
+ end: {
615
+ line: declaration.loc.end.line,
616
+ column: declaration.loc.end.column
675
617
  }
676
- if (typeof config !== "object" || config === null) {
677
- throw new Error("Config must be an object");
618
+ } : void 0
619
+ });
620
+ } else if (declaration.type === "ClassDeclaration" && declaration.id) {
621
+ exports.push({
622
+ name: declaration.id.name,
623
+ type: "class",
624
+ loc: declaration.loc ? {
625
+ start: {
626
+ line: declaration.loc.start.line,
627
+ column: declaration.loc.start.column
628
+ },
629
+ end: {
630
+ line: declaration.loc.end.line,
631
+ column: declaration.loc.end.column
678
632
  }
679
- return config;
680
- } catch (error) {
681
- const errorMessage = error instanceof Error ? error.message : String(error);
682
- const e = new Error(
683
- `Failed to load config from ${configPath}: ${errorMessage}`
684
- );
685
- try {
686
- e.cause = error instanceof Error ? error : void 0;
687
- } catch {
633
+ } : void 0
634
+ });
635
+ } else if (declaration.type === "VariableDeclaration") {
636
+ for (const declarator of declaration.declarations) {
637
+ if (declarator.id.type === "Identifier") {
638
+ exports.push({
639
+ name: declarator.id.name,
640
+ type: "const",
641
+ loc: declarator.loc ? {
642
+ start: {
643
+ line: declarator.loc.start.line,
644
+ column: declarator.loc.start.column
645
+ },
646
+ end: {
647
+ line: declarator.loc.end.line,
648
+ column: declarator.loc.end.column
649
+ }
650
+ } : void 0
651
+ });
652
+ }
653
+ }
654
+ } else if (declaration.type === "TSTypeAliasDeclaration") {
655
+ exports.push({
656
+ name: declaration.id.name,
657
+ type: "type",
658
+ loc: declaration.loc ? {
659
+ start: {
660
+ line: declaration.loc.start.line,
661
+ column: declaration.loc.start.column
662
+ },
663
+ end: {
664
+ line: declaration.loc.end.line,
665
+ column: declaration.loc.end.column
688
666
  }
689
- throw e;
667
+ } : void 0
668
+ });
669
+ } else if (declaration.type === "TSInterfaceDeclaration") {
670
+ exports.push({
671
+ name: declaration.id.name,
672
+ type: "interface",
673
+ loc: declaration.loc ? {
674
+ start: {
675
+ line: declaration.loc.start.line,
676
+ column: declaration.loc.start.column
677
+ },
678
+ end: {
679
+ line: declaration.loc.end.line,
680
+ column: declaration.loc.end.column
681
+ }
682
+ } : void 0
683
+ });
684
+ }
685
+ return exports;
686
+ }
687
+ };
688
+
689
+ // src/parsers/python-parser.ts
690
+ import * as Parser from "web-tree-sitter";
691
+ import * as path from "path";
692
+ import * as fs from "fs";
693
+ var PythonParser = class {
694
+ constructor() {
695
+ this.language = "python" /* Python */;
696
+ this.extensions = [".py"];
697
+ this.parser = null;
698
+ this.initialized = false;
699
+ }
700
+ /**
701
+ * Initialize the tree-sitter parser
702
+ */
703
+ async initialize() {
704
+ if (this.initialized) return;
705
+ try {
706
+ await Parser.Parser.init();
707
+ this.parser = new Parser.Parser();
708
+ const possiblePaths = [
709
+ path.join(
710
+ process.cwd(),
711
+ "node_modules/tree-sitter-python/tree-sitter-python.wasm"
712
+ ),
713
+ path.join(
714
+ __dirname,
715
+ "../../node_modules/tree-sitter-python/tree-sitter-python.wasm"
716
+ ),
717
+ path.join(
718
+ __dirname,
719
+ "../../../node_modules/tree-sitter-python/tree-sitter-python.wasm"
720
+ ),
721
+ path.join(
722
+ __dirname,
723
+ "../../../../node_modules/tree-sitter-python/tree-sitter-python.wasm"
724
+ ),
725
+ path.join(__dirname, "../assets/tree-sitter-python.wasm")
726
+ ];
727
+ let wasmPath = "";
728
+ for (const p of possiblePaths) {
729
+ if (fs.existsSync(p)) {
730
+ wasmPath = p;
731
+ break;
690
732
  }
691
733
  }
734
+ if (!wasmPath) {
735
+ console.warn(
736
+ "Python WASM not found in common locations, attempting fallback regex parser"
737
+ );
738
+ return;
739
+ }
740
+ const Python = await Parser.Language.load(wasmPath);
741
+ this.parser.setLanguage(Python);
742
+ this.initialized = true;
743
+ } catch (error) {
744
+ console.error(
745
+ `Failed to initialize tree-sitter-python: ${error.message}`
746
+ );
692
747
  }
693
- const parent = dirname3(currentDir);
694
- if (parent === currentDir) {
695
- break;
748
+ }
749
+ parse(code, filePath) {
750
+ if (!this.initialized || !this.parser) {
751
+ return this.parseRegex(code, filePath);
752
+ }
753
+ try {
754
+ const tree = this.parser.parse(code);
755
+ if (!tree) throw new Error("Parser.parse(code) returned null");
756
+ const rootNode = tree.rootNode;
757
+ const imports = this.extractImportsAST(rootNode);
758
+ const exports = this.extractExportsAST(rootNode);
759
+ return {
760
+ exports,
761
+ imports,
762
+ language: "python" /* Python */,
763
+ warnings: []
764
+ };
765
+ } catch (error) {
766
+ console.warn(
767
+ `AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
768
+ );
769
+ return this.parseRegex(code, filePath);
696
770
  }
697
- currentDir = parent;
698
771
  }
699
- return null;
700
- }
701
- function mergeConfigWithDefaults(userConfig, defaults) {
702
- if (!userConfig) return defaults;
703
- const result = { ...defaults };
704
- if (userConfig.scan) {
705
- if (userConfig.scan.include) result.include = userConfig.scan.include;
706
- if (userConfig.scan.exclude) result.exclude = userConfig.scan.exclude;
772
+ extractImportsAST(rootNode) {
773
+ const imports = [];
774
+ const processImportNode = (node) => {
775
+ if (node.type === "import_statement") {
776
+ for (const child of node.children) {
777
+ if (child.type === "dotted_name") {
778
+ const source = child.text;
779
+ imports.push({
780
+ source,
781
+ specifiers: [source],
782
+ loc: {
783
+ start: {
784
+ line: child.startPosition.row + 1,
785
+ column: child.startPosition.column
786
+ },
787
+ end: {
788
+ line: child.endPosition.row + 1,
789
+ column: child.endPosition.column
790
+ }
791
+ }
792
+ });
793
+ } else if (child.type === "aliased_import") {
794
+ const nameNode = child.childForFieldName("name");
795
+ if (nameNode) {
796
+ const source = nameNode.text;
797
+ imports.push({
798
+ source,
799
+ specifiers: [source],
800
+ loc: {
801
+ start: {
802
+ line: child.startPosition.row + 1,
803
+ column: child.startPosition.column
804
+ },
805
+ end: {
806
+ line: child.endPosition.row + 1,
807
+ column: child.endPosition.column
808
+ }
809
+ }
810
+ });
811
+ }
812
+ }
813
+ }
814
+ } else if (node.type === "import_from_statement") {
815
+ const moduleNameNode = node.childForFieldName("module_name");
816
+ if (moduleNameNode) {
817
+ const source = moduleNameNode.text;
818
+ const specifiers = [];
819
+ for (const child of node.children) {
820
+ if (child.type === "dotted_name" && child !== moduleNameNode) {
821
+ specifiers.push(child.text);
822
+ } else if (child.type === "aliased_import") {
823
+ const nameNode = child.childForFieldName("name");
824
+ if (nameNode) specifiers.push(nameNode.text);
825
+ } else if (child.type === "wildcard_import") {
826
+ specifiers.push("*");
827
+ }
828
+ }
829
+ if (specifiers.length > 0) {
830
+ imports.push({
831
+ source,
832
+ specifiers,
833
+ loc: {
834
+ start: {
835
+ line: node.startPosition.row + 1,
836
+ column: node.startPosition.column
837
+ },
838
+ end: {
839
+ line: node.endPosition.row + 1,
840
+ column: node.endPosition.column
841
+ }
842
+ }
843
+ });
844
+ }
845
+ }
846
+ }
847
+ };
848
+ for (const node of rootNode.children) {
849
+ processImportNode(node);
850
+ }
851
+ return imports;
707
852
  }
708
- const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
709
- if (toolOverrides) {
710
- if (!result.toolConfigs) result.toolConfigs = {};
711
- for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
712
- if (typeof toolConfig === "object" && toolConfig !== null) {
713
- result[toolName] = { ...result[toolName], ...toolConfig };
714
- result.toolConfigs[toolName] = {
715
- ...result.toolConfigs[toolName],
716
- ...toolConfig
717
- };
853
+ extractExportsAST(rootNode) {
854
+ const exports = [];
855
+ for (const node of rootNode.children) {
856
+ if (node.type === "function_definition") {
857
+ const nameNode = node.childForFieldName("name");
858
+ if (nameNode) {
859
+ const name = nameNode.text;
860
+ const isPrivate = name.startsWith("_") && !name.startsWith("__");
861
+ if (!isPrivate) {
862
+ exports.push({
863
+ name,
864
+ type: "function",
865
+ loc: {
866
+ start: {
867
+ line: node.startPosition.row + 1,
868
+ column: node.startPosition.column
869
+ },
870
+ end: {
871
+ line: node.endPosition.row + 1,
872
+ column: node.endPosition.column
873
+ }
874
+ },
875
+ parameters: this.extractParameters(node)
876
+ });
877
+ }
878
+ }
879
+ } else if (node.type === "class_definition") {
880
+ const nameNode = node.childForFieldName("name");
881
+ if (nameNode) {
882
+ exports.push({
883
+ name: nameNode.text,
884
+ type: "class",
885
+ loc: {
886
+ start: {
887
+ line: node.startPosition.row + 1,
888
+ column: node.startPosition.column
889
+ },
890
+ end: {
891
+ line: node.endPosition.row + 1,
892
+ column: node.endPosition.column
893
+ }
894
+ }
895
+ });
896
+ }
897
+ } else if (node.type === "expression_statement") {
898
+ const assignment = node.firstChild;
899
+ if (assignment && assignment.type === "assignment") {
900
+ const left = assignment.childForFieldName("left");
901
+ if (left && left.type === "identifier") {
902
+ const name = left.text;
903
+ const isInternal = name === "__all__" || name === "__version__" || name === "__author__";
904
+ const isPrivate = name.startsWith("_") && !name.startsWith("__");
905
+ if (!isInternal && !isPrivate) {
906
+ exports.push({
907
+ name,
908
+ type: name === name.toUpperCase() ? "const" : "variable",
909
+ loc: {
910
+ start: {
911
+ line: node.startPosition.row + 1,
912
+ column: node.startPosition.column
913
+ },
914
+ end: {
915
+ line: node.endPosition.row + 1,
916
+ column: node.endPosition.column
917
+ }
918
+ }
919
+ });
920
+ }
921
+ }
922
+ }
718
923
  }
719
924
  }
925
+ return exports;
720
926
  }
721
- if (userConfig.output) {
722
- result.output = { ...result.output, ...userConfig.output };
927
+ extractParameters(node) {
928
+ const paramsNode = node.childForFieldName("parameters");
929
+ if (!paramsNode) return [];
930
+ return paramsNode.children.filter(
931
+ (c) => c.type === "identifier" || c.type === "typed_parameter" || c.type === "default_parameter"
932
+ ).map((c) => {
933
+ if (c.type === "identifier") return c.text;
934
+ if (c.type === "typed_parameter" || c.type === "default_parameter") {
935
+ return c.firstChild?.text || "unknown";
936
+ }
937
+ return "unknown";
938
+ });
723
939
  }
724
- return result;
725
- }
726
-
727
- // src/business/pricing-models.ts
728
- var MODEL_PRICING_PRESETS = {
729
- "gpt-5.3": {
730
- name: "GPT-5.3",
731
- pricePer1KInputTokens: 2e-3,
732
- pricePer1KOutputTokens: 8e-3,
733
- contextTier: "frontier",
734
- typicalQueriesPerDevPerDay: 100
735
- },
736
- "claude-4.6": {
737
- name: "Claude 4.6",
738
- pricePer1KInputTokens: 15e-4,
739
- pricePer1KOutputTokens: 75e-4,
740
- contextTier: "frontier",
741
- typicalQueriesPerDevPerDay: 100
742
- },
743
- "gemini-3.1": {
744
- name: "Gemini 3.1 Pro",
745
- pricePer1KInputTokens: 8e-4,
746
- pricePer1KOutputTokens: 3e-3,
747
- contextTier: "frontier",
748
- typicalQueriesPerDevPerDay: 120
749
- },
750
- "gpt-4o": {
751
- name: "GPT-4o (legacy)",
752
- pricePer1KInputTokens: 5e-3,
753
- pricePer1KOutputTokens: 0.015,
754
- contextTier: "extended",
755
- typicalQueriesPerDevPerDay: 60
756
- },
757
- "claude-3-5-sonnet": {
758
- name: "Claude 3.5 Sonnet (legacy)",
759
- pricePer1KInputTokens: 3e-3,
760
- pricePer1KOutputTokens: 0.015,
761
- contextTier: "extended",
762
- typicalQueriesPerDevPerDay: 80
763
- },
764
- "gemini-1-5-pro": {
765
- name: "Gemini 1.5 Pro (legacy)",
766
- pricePer1KInputTokens: 125e-5,
767
- pricePer1KOutputTokens: 5e-3,
768
- contextTier: "frontier",
769
- typicalQueriesPerDevPerDay: 80
770
- },
771
- copilot: {
772
- name: "GitHub Copilot (subscription)",
773
- pricePer1KInputTokens: 8e-5,
774
- pricePer1KOutputTokens: 8e-5,
775
- contextTier: "frontier",
776
- typicalQueriesPerDevPerDay: 150
777
- },
778
- "cursor-pro": {
779
- name: "Cursor Pro (subscription)",
780
- pricePer1KInputTokens: 8e-5,
781
- pricePer1KOutputTokens: 8e-5,
782
- contextTier: "frontier",
783
- typicalQueriesPerDevPerDay: 200
784
- }
785
- };
786
- function getModelPreset(modelId) {
787
- return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
788
- }
789
-
790
- // src/business/cost-metrics.ts
791
- var DEFAULT_COST_CONFIG = {
792
- pricePer1KTokens: 5e-3,
793
- queriesPerDevPerDay: 60,
794
- developerCount: 5,
795
- daysPerMonth: 30
796
- };
797
- function calculateMonthlyCost(tokenWaste, config = {}) {
798
- const budget = calculateTokenBudget({
799
- totalContextTokens: tokenWaste * 2.5,
800
- wastedTokens: {
801
- duplication: tokenWaste * 0.7,
802
- fragmentation: tokenWaste * 0.3,
803
- chattiness: 0
804
- }
805
- });
806
- const preset = getModelPreset("claude-4.6");
807
- return estimateCostFromBudget(budget, preset, config);
808
- }
809
- function calculateTokenBudget(params) {
810
- const { totalContextTokens, wastedTokens } = params;
811
- const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
812
- const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
813
- const efficiencyRatio = Math.max(
814
- 0,
815
- Math.min(
816
- 1,
817
- (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens)
818
- )
819
- );
820
- return {
821
- totalContextTokens: Math.round(totalContextTokens),
822
- estimatedResponseTokens: Math.round(estimatedResponseTokens),
823
- wastedTokens: {
824
- total: Math.round(totalWaste),
825
- bySource: {
826
- duplication: Math.round(wastedTokens.duplication),
827
- fragmentation: Math.round(wastedTokens.fragmentation),
828
- chattiness: Math.round(wastedTokens.chattiness)
829
- }
830
- },
831
- efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
832
- potentialRetrievableTokens: Math.round(totalWaste * 0.8)
833
- };
834
- }
835
- function estimateCostFromBudget(budget, model, config = {}) {
836
- const cfg = { ...DEFAULT_COST_CONFIG, ...config };
837
- const wastePerQuery = budget.wastedTokens.total;
838
- const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
839
- const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
840
- const totalWeight = cfg.developerCount;
841
- const price = config.pricePer1KTokens ?? model.pricePer1KInputTokens;
842
- const baseCost = tokensPerMonth / 1e3 * price * totalWeight;
843
- let confidence = 0.85;
844
- if (model.contextTier === "frontier") confidence = 0.7;
845
- const variance = 0.25;
846
- const range = [
847
- Math.round(baseCost * (1 - variance) * 100) / 100,
848
- Math.round(baseCost * (1 + variance) * 100) / 100
849
- ];
850
- return {
851
- total: Math.round(baseCost * 100) / 100,
852
- range,
853
- confidence
854
- };
855
- }
856
-
857
- // src/business/productivity-metrics.ts
858
- var SEVERITY_TIME_ESTIMATES = {
859
- ["critical" /* Critical */]: 4,
860
- ["major" /* Major */]: 2,
861
- ["minor" /* Minor */]: 0.5,
862
- ["info" /* Info */]: 0.25
863
- };
864
- var DEFAULT_HOURLY_RATE = 75;
865
- function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
866
- const counts = {
867
- ["critical" /* Critical */]: issues.filter((i) => i.severity === "critical" /* Critical */).length,
868
- ["major" /* Major */]: issues.filter((i) => i.severity === "major" /* Major */).length,
869
- ["minor" /* Minor */]: issues.filter((i) => i.severity === "minor" /* Minor */).length,
870
- ["info" /* Info */]: issues.filter((i) => i.severity === "info" /* Info */).length
871
- };
872
- const hours = {
873
- ["critical" /* Critical */]: counts["critical" /* Critical */] * SEVERITY_TIME_ESTIMATES["critical" /* Critical */],
874
- ["major" /* Major */]: counts["major" /* Major */] * SEVERITY_TIME_ESTIMATES["major" /* Major */],
875
- ["minor" /* Minor */]: counts["minor" /* Minor */] * SEVERITY_TIME_ESTIMATES["minor" /* Minor */],
876
- ["info" /* Info */]: counts["info" /* Info */] * SEVERITY_TIME_ESTIMATES["info" /* Info */]
877
- };
878
- const totalHours = hours["critical" /* Critical */] + hours["major" /* Major */] + hours["minor" /* Minor */] + hours["info" /* Info */];
879
- const totalCost = totalHours * hourlyRate;
880
- return {
881
- totalHours: Math.round(totalHours * 10) / 10,
882
- hourlyRate,
883
- totalCost: Math.round(totalCost),
884
- bySeverity: {
885
- ["critical" /* Critical */]: {
886
- hours: Math.round(hours["critical" /* Critical */] * 10) / 10,
887
- cost: Math.round(hours["critical" /* Critical */] * hourlyRate)
888
- },
889
- ["major" /* Major */]: {
890
- hours: Math.round(hours["major" /* Major */] * 10) / 10,
891
- cost: Math.round(hours["major" /* Major */] * hourlyRate)
892
- },
893
- ["minor" /* Minor */]: {
894
- hours: Math.round(hours["minor" /* Minor */] * 10) / 10,
895
- cost: Math.round(hours["minor" /* Minor */] * hourlyRate)
896
- },
897
- ["info" /* Info */]: {
898
- hours: Math.round(hours["info" /* Info */] * 10) / 10,
899
- cost: Math.round(hours["info" /* Info */] * hourlyRate)
900
- }
901
- }
902
- };
903
- }
904
- function predictAcceptanceRate(toolOutputs) {
905
- const factors = [];
906
- const baseRate = 0.3;
907
- const patterns = toolOutputs.get("pattern-detect");
908
- if (patterns) {
909
- factors.push({
910
- name: "Semantic Duplication",
911
- impact: Math.round((patterns.score - 50) * 3e-3 * 100)
912
- });
913
- }
914
- const context = toolOutputs.get("context-analyzer");
915
- if (context) {
916
- factors.push({
917
- name: "Context Efficiency",
918
- impact: Math.round((context.score - 50) * 4e-3 * 100)
919
- });
920
- }
921
- const consistency = toolOutputs.get("consistency");
922
- if (consistency) {
923
- factors.push({
924
- name: "Code Consistency",
925
- impact: Math.round((consistency.score - 50) * 2e-3 * 100)
926
- });
927
- }
928
- const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
929
- if (aiSignalClarity) {
930
- factors.push({
931
- name: "AI Signal Clarity",
932
- impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
933
- });
934
- }
935
- const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
936
- const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
937
- let confidence = 0.35;
938
- if (toolOutputs.size >= 4) confidence = 0.75;
939
- else if (toolOutputs.size >= 3) confidence = 0.65;
940
- else if (toolOutputs.size >= 2) confidence = 0.5;
941
- return { rate: Math.round(rate * 100) / 100, confidence, factors };
942
- }
943
-
944
- // src/business/risk-metrics.ts
945
- function calculateKnowledgeConcentration(params) {
946
- const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
947
- const concentrationRatio = totalFiles > 0 ? (uniqueConceptFiles + singleAuthorFiles) / (totalFiles * 2) : 0;
948
- const score = Math.round(
949
- Math.min(
950
- 100,
951
- concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
952
- )
953
- );
954
- let rating;
955
- if (score < 30) rating = "low";
956
- else if (score < 50) rating = "moderate";
957
- else if (score < 75) rating = "high";
958
- else rating = "critical";
959
- const recommendations = [];
960
- if (singleAuthorFiles > 0)
961
- recommendations.push(
962
- `Distribute knowledge for ${singleAuthorFiles} single-author files.`
963
- );
964
- if (orphanFiles > 0)
965
- recommendations.push(
966
- `Link ${orphanFiles} orphan files to the rest of the codebase.`
967
- );
968
- return {
969
- score,
970
- rating,
971
- recommendations,
972
- analysis: {
973
- uniqueConceptFiles,
974
- totalFiles,
975
- concentrationRatio,
976
- singleAuthorFiles,
977
- orphanFiles
978
- }
979
- };
980
- }
981
- function calculateDebtInterest(principal, monthlyGrowthRate) {
982
- const monthlyRate = monthlyGrowthRate;
983
- const annualRate = Math.pow(1 + monthlyRate, 12) - 1;
984
- const monthlyCost = principal * monthlyRate;
985
- return {
986
- monthlyRate,
987
- annualRate,
988
- principal,
989
- monthlyCost,
990
- projections: {
991
- months6: principal * Math.pow(1 + monthlyRate, 6),
992
- months12: principal * Math.pow(1 + monthlyRate, 12),
993
- months24: principal * Math.pow(1 + monthlyRate, 24)
994
- }
995
- };
996
- }
997
-
998
- // src/business/comprehension-metrics.ts
999
- function calculateTechnicalValueChain(params) {
1000
- const { businessLogicDensity, dataAccessComplexity, apiSurfaceArea } = params;
1001
- const score = (businessLogicDensity * 0.5 + (1 - dataAccessComplexity / 10) * 0.3 + (1 - apiSurfaceArea / 20) * 0.2) * 100;
1002
- return {
1003
- score: Math.round(Math.max(0, Math.min(100, score))),
1004
- density: businessLogicDensity,
1005
- complexity: dataAccessComplexity,
1006
- surface: apiSurfaceArea
1007
- };
1008
- }
1009
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, modelTier = "frontier") {
1010
- const tierMap = {
1011
- compact: "compact",
1012
- standard: "standard",
1013
- extended: "extended",
1014
- frontier: "frontier",
1015
- easy: "frontier",
1016
- // Map legacy 'easy' to 'frontier'
1017
- moderate: "standard",
1018
- difficult: "compact"
1019
- };
1020
- const tier = tierMap[modelTier] || "frontier";
1021
- const threshold = CONTEXT_TIER_THRESHOLDS[tier];
1022
- const budgetRatio = contextBudget / threshold.idealTokens;
1023
- const score = (budgetRatio * 0.6 + importDepth / 10 * 0.2 + fragmentation * 0.2) * 100;
1024
- const finalScore = Math.round(Math.max(0, Math.min(100, score)));
1025
- let rating;
1026
- if (finalScore < 20) rating = "trivial";
1027
- else if (finalScore < 40) rating = "easy";
1028
- else if (finalScore < 60) rating = "moderate";
1029
- else if (finalScore < 85) rating = "difficult";
1030
- else rating = "expert";
1031
- return {
1032
- score: finalScore,
1033
- rating,
1034
- factors: { budgetRatio, depthRatio: importDepth / 10, fragmentation }
1035
- };
1036
- }
1037
-
1038
- // src/business-metrics.ts
1039
- function calculateBusinessROI(params) {
1040
- const model = getModelPreset(params.modelId || "claude-4.6");
1041
- const devCount = params.developerCount || 5;
1042
- const budget = calculateTokenBudget({
1043
- totalContextTokens: params.tokenWaste * 2.5,
1044
- wastedTokens: {
1045
- duplication: params.tokenWaste * 0.7,
1046
- fragmentation: params.tokenWaste * 0.3,
1047
- chattiness: 0
1048
- }
1049
- });
1050
- const cost = estimateCostFromBudget(budget, model, {
1051
- developerCount: devCount
1052
- });
1053
- const productivity = calculateProductivityImpact(params.issues);
1054
- const monthlySavings = cost.total;
1055
- const productivityGainHours = productivity.totalHours;
1056
- const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
1057
- return {
1058
- monthlySavings: Math.round(monthlySavings),
1059
- productivityGainHours: Math.round(productivityGainHours),
1060
- annualValue: Math.round(annualValue)
1061
- };
1062
- }
1063
- function formatCost(cost) {
1064
- if (cost < 1) {
1065
- return `$${cost.toFixed(2)}`;
1066
- } else if (cost < 1e3) {
1067
- return `$${cost.toFixed(0)}`;
1068
- } else {
1069
- return `$${(cost / 1e3).toFixed(1)}k`;
1070
- }
1071
- }
1072
- function formatHours(hours) {
1073
- if (hours < 1) {
1074
- return `${Math.round(hours * 60)}min`;
1075
- } else if (hours < 8) {
1076
- return `${hours.toFixed(1)}h`;
1077
- } else if (hours < 40) {
1078
- return `${Math.round(hours)}h`;
1079
- } else {
1080
- return `${(hours / 40).toFixed(1)} weeks`;
1081
- }
1082
- }
1083
- function formatAcceptanceRate(rate) {
1084
- return `${Math.round(rate * 100)}%`;
1085
- }
1086
- function generateValueChain(params) {
1087
- const { issueType, count, severity } = params;
1088
- const impacts = {
1089
- "duplicate-pattern": {
1090
- ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
1091
- dev: "Developers must manually resolve conflicts between suggested variants.",
1092
- risk: "high"
1093
- },
1094
- "context-fragmentation": {
1095
- ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
1096
- dev: "Slower AI responses and increased need for manual context pinning.",
1097
- risk: "critical"
1098
- },
1099
- "naming-inconsistency": {
1100
- ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
1101
- dev: "Increased cognitive load for new devs during onboarding.",
1102
- risk: "moderate"
1103
- }
1104
- };
1105
- const impact = impacts[issueType] || {
1106
- ai: "Reduced suggestion quality.",
1107
- dev: "Slowed development velocity.",
1108
- risk: "moderate"
1109
- };
1110
- const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
1111
- return {
1112
- issueType,
1113
- technicalMetric: "Issue Count",
1114
- technicalValue: count,
1115
- aiImpact: {
1116
- description: impact.ai,
1117
- scoreImpact: severity === "critical" ? -15 : -5
1118
- },
1119
- developerImpact: {
1120
- description: impact.dev,
1121
- productivityLoss
1122
- },
1123
- businessOutcome: {
1124
- directCost: count * 12,
1125
- opportunityCost: productivityLoss * 15e3,
1126
- riskLevel: impact.risk
1127
- }
1128
- };
1129
- }
1130
-
1131
- // src/parsers/typescript-parser.ts
1132
- import { parse as parse2 } from "@typescript-eslint/typescript-estree";
1133
- var TypeScriptParser = class {
1134
- constructor() {
1135
- this.language = "typescript" /* TypeScript */;
1136
- this.extensions = [".ts", ".tsx", ".js", ".jsx"];
1137
- }
1138
- parse(code, filePath) {
1139
- try {
1140
- const isJavaScript = filePath.match(/\.jsx?$/i);
1141
- const ast = parse2(code, {
1142
- loc: true,
1143
- range: true,
1144
- jsx: filePath.match(/\.[jt]sx$/i) !== null,
1145
- filePath,
1146
- sourceType: "module",
1147
- ecmaVersion: "latest"
1148
- });
1149
- const imports = this.extractImports(ast);
1150
- const exports = this.extractExports(ast, imports);
1151
- return {
1152
- exports,
1153
- imports,
1154
- language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
1155
- warnings: []
1156
- };
1157
- } catch (error) {
1158
- const err = error;
1159
- throw new ParseError(
1160
- `Failed to parse ${filePath}: ${err.message}`,
1161
- filePath
1162
- );
1163
- }
1164
- }
1165
- getNamingConventions() {
1166
- return {
1167
- // camelCase for variables and functions
1168
- variablePattern: /^[a-z][a-zA-Z0-9]*$/,
1169
- functionPattern: /^[a-z][a-zA-Z0-9]*$/,
1170
- // PascalCase for classes
1171
- classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1172
- // UPPER_CASE for constants
1173
- constantPattern: /^[A-Z][A-Z0-9_]*$/,
1174
- // Common exceptions (React hooks, etc.)
1175
- exceptions: ["__filename", "__dirname", "__esModule"]
1176
- };
1177
- }
1178
- canHandle(filePath) {
1179
- return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
1180
- }
1181
- extractImports(ast) {
1182
- const imports = [];
1183
- for (const node of ast.body) {
1184
- if (node.type === "ImportDeclaration") {
1185
- const specifiers = [];
1186
- let isTypeOnly = false;
1187
- if (node.importKind === "type") {
1188
- isTypeOnly = true;
1189
- }
1190
- for (const spec of node.specifiers) {
1191
- if (spec.type === "ImportSpecifier") {
1192
- const imported = spec.imported;
1193
- const name = imported.type === "Identifier" ? imported.name : imported.value;
1194
- specifiers.push(name);
1195
- } else if (spec.type === "ImportDefaultSpecifier") {
1196
- specifiers.push("default");
1197
- } else if (spec.type === "ImportNamespaceSpecifier") {
1198
- specifiers.push("*");
1199
- }
1200
- }
1201
- imports.push({
1202
- source: node.source.value,
1203
- specifiers,
1204
- isTypeOnly,
1205
- loc: node.loc ? {
1206
- start: {
1207
- line: node.loc.start.line,
1208
- column: node.loc.start.column
1209
- },
1210
- end: { line: node.loc.end.line, column: node.loc.end.column }
1211
- } : void 0
1212
- });
1213
- }
1214
- }
1215
- return imports;
1216
- }
1217
- extractExports(ast, imports) {
1218
- const exports = [];
1219
- const importedNames = new Set(
1220
- imports.flatMap(
1221
- (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1222
- )
1223
- );
1224
- for (const node of ast.body) {
1225
- if (node.type === "ExportNamedDeclaration" && node.declaration) {
1226
- const extracted = this.extractFromDeclaration(
1227
- node.declaration,
1228
- importedNames
1229
- );
1230
- exports.push(...extracted);
1231
- } else if (node.type === "ExportDefaultDeclaration") {
1232
- let name = "default";
1233
- let type = "default";
1234
- if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
1235
- name = node.declaration.id.name;
1236
- type = "function";
1237
- } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
1238
- name = node.declaration.id.name;
1239
- type = "class";
1240
- }
1241
- exports.push({
1242
- name,
1243
- type,
1244
- loc: node.loc ? {
1245
- start: {
1246
- line: node.loc.start.line,
1247
- column: node.loc.start.column
1248
- },
1249
- end: { line: node.loc.end.line, column: node.loc.end.column }
1250
- } : void 0
1251
- });
1252
- }
1253
- }
1254
- return exports;
1255
- }
1256
- extractFromDeclaration(declaration, importedNames) {
1257
- const exports = [];
1258
- if (declaration.type === "FunctionDeclaration" && declaration.id) {
1259
- exports.push({
1260
- name: declaration.id.name,
1261
- type: "function",
1262
- parameters: declaration.params.map(
1263
- (p) => p.type === "Identifier" ? p.name : "unknown"
1264
- ),
1265
- loc: declaration.loc ? {
1266
- start: {
1267
- line: declaration.loc.start.line,
1268
- column: declaration.loc.start.column
1269
- },
1270
- end: {
1271
- line: declaration.loc.end.line,
1272
- column: declaration.loc.end.column
1273
- }
1274
- } : void 0
1275
- });
1276
- } else if (declaration.type === "ClassDeclaration" && declaration.id) {
1277
- exports.push({
1278
- name: declaration.id.name,
1279
- type: "class",
1280
- loc: declaration.loc ? {
1281
- start: {
1282
- line: declaration.loc.start.line,
1283
- column: declaration.loc.start.column
1284
- },
1285
- end: {
1286
- line: declaration.loc.end.line,
1287
- column: declaration.loc.end.column
1288
- }
1289
- } : void 0
1290
- });
1291
- } else if (declaration.type === "VariableDeclaration") {
1292
- for (const declarator of declaration.declarations) {
1293
- if (declarator.id.type === "Identifier") {
1294
- exports.push({
1295
- name: declarator.id.name,
1296
- type: "const",
1297
- loc: declarator.loc ? {
1298
- start: {
1299
- line: declarator.loc.start.line,
1300
- column: declarator.loc.start.column
1301
- },
1302
- end: {
1303
- line: declarator.loc.end.line,
1304
- column: declarator.loc.end.column
1305
- }
1306
- } : void 0
1307
- });
1308
- }
1309
- }
1310
- } else if (declaration.type === "TSTypeAliasDeclaration") {
1311
- exports.push({
1312
- name: declaration.id.name,
1313
- type: "type",
1314
- loc: declaration.loc ? {
1315
- start: {
1316
- line: declaration.loc.start.line,
1317
- column: declaration.loc.start.column
1318
- },
1319
- end: {
1320
- line: declaration.loc.end.line,
1321
- column: declaration.loc.end.column
1322
- }
1323
- } : void 0
1324
- });
1325
- } else if (declaration.type === "TSInterfaceDeclaration") {
1326
- exports.push({
1327
- name: declaration.id.name,
1328
- type: "interface",
1329
- loc: declaration.loc ? {
1330
- start: {
1331
- line: declaration.loc.start.line,
1332
- column: declaration.loc.start.column
1333
- },
1334
- end: {
1335
- line: declaration.loc.end.line,
1336
- column: declaration.loc.end.column
1337
- }
1338
- } : void 0
1339
- });
1340
- }
1341
- return exports;
1342
- }
1343
- };
1344
-
1345
- // src/parsers/python-parser.ts
1346
- var PythonParser = class {
1347
- constructor() {
1348
- this.language = "python" /* Python */;
1349
- this.extensions = [".py"];
1350
- this.parser = null;
1351
- this.initialized = false;
1352
- }
1353
- /**
1354
- * Initialize the tree-sitter parser
1355
- * This is async because tree-sitter WASM needs to be loaded
1356
- */
1357
- async initialize() {
1358
- if (this.initialized) return;
1359
- try {
1360
- this.initialized = true;
1361
- } catch (error) {
1362
- throw new Error(
1363
- `Failed to initialize Python parser: ${error.message}`
1364
- );
1365
- }
1366
- }
1367
- parse(code, filePath) {
940
+ parseRegex(code, filePath) {
1368
941
  try {
1369
942
  const imports = this.extractImportsRegex(code, filePath);
1370
943
  const exports = this.extractExportsRegex(code, filePath);
@@ -1373,7 +946,7 @@ var PythonParser = class {
1373
946
  imports,
1374
947
  language: "python" /* Python */,
1375
948
  warnings: [
1376
- "Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."
949
+ "Python parsing is currently using regex-based extraction as tree-sitter wasm was not available."
1377
950
  ]
1378
951
  };
1379
952
  } catch (error) {
@@ -1385,14 +958,10 @@ var PythonParser = class {
1385
958
  }
1386
959
  getNamingConventions() {
1387
960
  return {
1388
- // snake_case for variables and functions
1389
961
  variablePattern: /^[a-z_][a-z0-9_]*$/,
1390
962
  functionPattern: /^[a-z_][a-z0-9_]*$/,
1391
- // PascalCase for classes
1392
963
  classPattern: /^[A-Z][a-zA-Z0-9]*$/,
1393
- // UPPER_CASE for constants
1394
964
  constantPattern: /^[A-Z][A-Z0-9_]*$/,
1395
- // Python special methods and common exceptions
1396
965
  exceptions: [
1397
966
  "__init__",
1398
967
  "__str__",
@@ -1414,10 +983,7 @@ var PythonParser = class {
1414
983
  canHandle(filePath) {
1415
984
  return filePath.toLowerCase().endsWith(".py");
1416
985
  }
1417
- /**
1418
- * Regex-based import extraction (temporary implementation)
1419
- */
1420
- extractImportsRegex(code, filePath) {
986
+ extractImportsRegex(code, _filePath) {
1421
987
  const imports = [];
1422
988
  const lines = code.split("\n");
1423
989
  const importRegex = /^\s*import\s+([a-zA-Z0-9_., ]+)/;
@@ -1439,202 +1005,892 @@ var PythonParser = class {
1439
1005
  });
1440
1006
  return;
1441
1007
  }
1442
- const fromMatch = line.match(fromImportRegex);
1443
- if (fromMatch) {
1444
- const module = fromMatch[1];
1445
- const imports_str = fromMatch[2];
1446
- if (imports_str.trim() === "*") {
1447
- imports.push({
1448
- source: module,
1449
- specifiers: ["*"],
1450
- loc: {
1451
- start: { line: idx + 1, column: 0 },
1452
- end: { line: idx + 1, column: line.length }
1453
- }
1008
+ const fromMatch = line.match(fromImportRegex);
1009
+ if (fromMatch) {
1010
+ const module = fromMatch[1];
1011
+ const imports_str = fromMatch[2];
1012
+ if (imports_str.trim() === "*") {
1013
+ imports.push({
1014
+ source: module,
1015
+ specifiers: ["*"],
1016
+ loc: {
1017
+ start: { line: idx + 1, column: 0 },
1018
+ end: { line: idx + 1, column: line.length }
1019
+ }
1020
+ });
1021
+ return;
1022
+ }
1023
+ const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
1024
+ imports.push({
1025
+ source: module,
1026
+ specifiers,
1027
+ loc: {
1028
+ start: { line: idx + 1, column: 0 },
1029
+ end: { line: idx + 1, column: line.length }
1030
+ }
1031
+ });
1032
+ }
1033
+ });
1034
+ return imports;
1035
+ }
1036
+ extractExportsRegex(code, _filePath) {
1037
+ const exports = [];
1038
+ const lines = code.split("\n");
1039
+ const functionRegex = /^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/;
1040
+ const classRegex = /^class\s+([a-zA-Z_][a-zA-Z0-9_]*)/;
1041
+ const allRegex = /__all__\s*=\s*\[([^\]]+)\]/;
1042
+ let inClass = false;
1043
+ let classIndent = 0;
1044
+ lines.forEach((line, idx) => {
1045
+ const indent = line.search(/\S/);
1046
+ if (line.match(classRegex)) {
1047
+ inClass = true;
1048
+ classIndent = indent;
1049
+ } else if (inClass && indent <= classIndent && line.trim()) {
1050
+ inClass = false;
1051
+ }
1052
+ if (inClass) {
1053
+ const classMatch = line.match(classRegex);
1054
+ if (classMatch) {
1055
+ exports.push({
1056
+ name: classMatch[1],
1057
+ type: "class",
1058
+ loc: {
1059
+ start: { line: idx + 1, column: indent },
1060
+ end: { line: idx + 1, column: line.length }
1061
+ }
1062
+ });
1063
+ }
1064
+ return;
1065
+ }
1066
+ const funcMatch = line.match(functionRegex);
1067
+ if (funcMatch && indent === 0) {
1068
+ const name = funcMatch[1];
1069
+ if (!name.startsWith("_") || name.startsWith("__")) {
1070
+ exports.push({
1071
+ name,
1072
+ type: "function",
1073
+ loc: {
1074
+ start: { line: idx + 1, column: 0 },
1075
+ end: { line: idx + 1, column: line.length }
1076
+ }
1077
+ });
1078
+ }
1079
+ }
1080
+ const allMatch = line.match(allRegex);
1081
+ if (allMatch) {
1082
+ const names = allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, ""));
1083
+ names.forEach((name) => {
1084
+ if (name && !exports.find((e) => e.name === name)) {
1085
+ exports.push({
1086
+ name,
1087
+ type: "variable",
1088
+ loc: {
1089
+ start: { line: idx + 1, column: 0 },
1090
+ end: { line: idx + 1, column: line.length }
1091
+ }
1092
+ });
1093
+ }
1094
+ });
1095
+ }
1096
+ });
1097
+ return exports;
1098
+ }
1099
+ };
1100
+
1101
+ // src/parsers/parser-factory.ts
1102
+ var ParserFactory = class _ParserFactory {
1103
+ constructor() {
1104
+ this.parsers = /* @__PURE__ */ new Map();
1105
+ this.extensionMap = new Map(
1106
+ Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
1107
+ );
1108
+ this.registerParser(new TypeScriptParser());
1109
+ this.registerParser(new PythonParser());
1110
+ }
1111
+ /**
1112
+ * Get singleton instance
1113
+ */
1114
+ static getInstance() {
1115
+ if (!_ParserFactory.instance) {
1116
+ _ParserFactory.instance = new _ParserFactory();
1117
+ }
1118
+ return _ParserFactory.instance;
1119
+ }
1120
+ /**
1121
+ * Register a language parser
1122
+ */
1123
+ registerParser(parser) {
1124
+ this.parsers.set(parser.language, parser);
1125
+ parser.extensions.forEach((ext) => {
1126
+ const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
1127
+ this.extensionMap.set(ext, lang);
1128
+ this.parsers.set(lang, parser);
1129
+ });
1130
+ }
1131
+ /**
1132
+ * Get parser for a specific language
1133
+ */
1134
+ getParserForLanguage(language) {
1135
+ return this.parsers.get(language) || null;
1136
+ }
1137
+ /**
1138
+ * Get parser for a file based on its extension
1139
+ */
1140
+ getParserForFile(filePath) {
1141
+ const ext = this.getFileExtension(filePath);
1142
+ const language = this.extensionMap.get(ext);
1143
+ if (!language) {
1144
+ return null;
1145
+ }
1146
+ return this.parsers.get(language) || null;
1147
+ }
1148
+ /**
1149
+ * Check if a file is supported
1150
+ */
1151
+ isSupported(filePath) {
1152
+ return this.getParserForFile(filePath) !== null;
1153
+ }
1154
+ /**
1155
+ * Get all registered languages
1156
+ */
1157
+ getSupportedLanguages() {
1158
+ return Array.from(this.parsers.keys());
1159
+ }
1160
+ /**
1161
+ * Get all supported file extensions
1162
+ */
1163
+ getSupportedExtensions() {
1164
+ return Array.from(this.extensionMap.keys());
1165
+ }
1166
+ /**
1167
+ * Get language for a file
1168
+ */
1169
+ getLanguageForFile(filePath) {
1170
+ const ext = this.getFileExtension(filePath);
1171
+ return this.extensionMap.get(ext) || null;
1172
+ }
1173
+ /**
1174
+ * Extract file extension (with dot)
1175
+ */
1176
+ getFileExtension(filePath) {
1177
+ const match = filePath.match(/\.[^.]+$/);
1178
+ return match ? match[0].toLowerCase() : "";
1179
+ }
1180
+ /**
1181
+ * Reset factory (useful for testing)
1182
+ */
1183
+ static reset() {
1184
+ _ParserFactory.instance = new _ParserFactory();
1185
+ }
1186
+ /**
1187
+ * Initialize all registered parsers
1188
+ */
1189
+ async initializeAll() {
1190
+ const promises = Array.from(this.parsers.values()).map(
1191
+ (p) => p.initialize()
1192
+ );
1193
+ await Promise.all(promises);
1194
+ }
1195
+ };
1196
+ function getParser(filePath) {
1197
+ return ParserFactory.getInstance().getParserForFile(filePath);
1198
+ }
1199
+ async function initializeParsers() {
1200
+ await ParserFactory.getInstance().initializeAll();
1201
+ }
1202
+ function isFileSupported(filePath) {
1203
+ return ParserFactory.getInstance().isSupported(filePath);
1204
+ }
1205
+ function getSupportedLanguages() {
1206
+ return ParserFactory.getInstance().getSupportedLanguages();
1207
+ }
1208
+
1209
+ // src/utils/ast-parser.ts
1210
+ function parseFileExports(code, filePath) {
1211
+ const parser = getParser(filePath);
1212
+ if (parser && parser.language === "python" /* Python */) {
1213
+ try {
1214
+ const result = parser.parse(code, filePath);
1215
+ return {
1216
+ exports: result.exports.map((e) => ({
1217
+ name: e.name,
1218
+ type: e.type,
1219
+ imports: e.imports || [],
1220
+ dependencies: e.dependencies || [],
1221
+ typeReferences: e.typeReferences || [],
1222
+ loc: e.loc ? {
1223
+ start: { line: e.loc.start.line, column: e.loc.start.column },
1224
+ end: { line: e.loc.end.line, column: e.loc.end.column }
1225
+ } : void 0
1226
+ })),
1227
+ imports: result.imports.map((i) => ({
1228
+ source: i.source,
1229
+ specifiers: i.specifiers,
1230
+ isTypeOnly: i.isTypeOnly || false
1231
+ }))
1232
+ };
1233
+ } catch (e) {
1234
+ return { exports: [], imports: [] };
1235
+ }
1236
+ }
1237
+ try {
1238
+ const ast = parse2(code, {
1239
+ loc: true,
1240
+ range: true,
1241
+ jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
1242
+ filePath
1243
+ });
1244
+ const imports = extractFileImports(ast);
1245
+ const exports = extractExportsWithDependencies(ast, imports);
1246
+ return { exports, imports };
1247
+ } catch (error) {
1248
+ return { exports: [], imports: [] };
1249
+ }
1250
+ }
1251
+ function extractFileImports(ast) {
1252
+ const imports = [];
1253
+ for (const node of ast.body) {
1254
+ if (node.type === "ImportDeclaration") {
1255
+ const source = node.source.value;
1256
+ const specifiers = [];
1257
+ const isTypeOnly = node.importKind === "type";
1258
+ for (const spec of node.specifiers) {
1259
+ if (spec.type === "ImportSpecifier") {
1260
+ const imported = spec.imported;
1261
+ const importName = imported.type === "Identifier" ? imported.name : imported.value;
1262
+ specifiers.push(importName);
1263
+ } else if (spec.type === "ImportDefaultSpecifier") {
1264
+ specifiers.push("default");
1265
+ } else if (spec.type === "ImportNamespaceSpecifier") {
1266
+ specifiers.push("*");
1267
+ }
1268
+ }
1269
+ imports.push({ source, specifiers, isTypeOnly });
1270
+ }
1271
+ }
1272
+ return imports;
1273
+ }
1274
+ function extractExportsWithDependencies(ast, fileImports) {
1275
+ const exports = [];
1276
+ const importedNames = new Set(fileImports.flatMap((imp) => imp.specifiers));
1277
+ for (const node of ast.body) {
1278
+ if (node.type === "ExportNamedDeclaration") {
1279
+ if (node.declaration) {
1280
+ const exportNodes = extractFromDeclaration(node.declaration);
1281
+ for (const exp of exportNodes) {
1282
+ const usedImports = findUsedImports(node.declaration, importedNames);
1283
+ const typeReferences = extractTypeReferences(node.declaration);
1284
+ exports.push({
1285
+ ...exp,
1286
+ imports: usedImports,
1287
+ dependencies: [],
1288
+ typeReferences,
1289
+ loc: node.loc
1454
1290
  });
1455
- return;
1456
1291
  }
1457
- const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
1458
- imports.push({
1459
- source: module,
1460
- specifiers,
1461
- loc: {
1462
- start: { line: idx + 1, column: 0 },
1463
- end: { line: idx + 1, column: line.length }
1464
- }
1465
- });
1466
1292
  }
1467
- });
1468
- return imports;
1293
+ } else if (node.type === "ExportDefaultDeclaration") {
1294
+ const usedImports = findUsedImports(node.declaration, importedNames);
1295
+ const typeReferences = extractTypeReferences(node.declaration);
1296
+ exports.push({
1297
+ name: "default",
1298
+ type: "default",
1299
+ imports: usedImports,
1300
+ dependencies: [],
1301
+ typeReferences,
1302
+ loc: node.loc
1303
+ });
1304
+ }
1469
1305
  }
1470
- /**
1471
- * Regex-based export extraction (temporary implementation)
1472
- *
1473
- * Python doesn't have explicit exports like JavaScript.
1474
- * We extract:
1475
- * - Functions defined at module level (def)
1476
- * - Classes defined at module level (class)
1477
- * - Variables in __all__ list
1478
- */
1479
- extractExportsRegex(code, filePath) {
1480
- const exports = [];
1481
- const lines = code.split("\n");
1482
- const functionRegex = /^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/;
1483
- const classRegex = /^class\s+([a-zA-Z_][a-zA-Z0-9_]*)/;
1484
- const allRegex = /__all__\s*=\s*\[([^\]]+)\]/;
1485
- let inClass = false;
1486
- let classIndent = 0;
1487
- lines.forEach((line, idx) => {
1488
- const indent = line.search(/\S/);
1489
- if (line.match(classRegex)) {
1490
- inClass = true;
1491
- classIndent = indent;
1492
- } else if (inClass && indent <= classIndent && line.trim()) {
1493
- inClass = false;
1306
+ return exports;
1307
+ }
1308
+ function extractFromDeclaration(declaration) {
1309
+ const results = [];
1310
+ if (declaration.type === "FunctionDeclaration" && "id" in declaration && declaration.id) {
1311
+ results.push({ name: declaration.id.name, type: "function" });
1312
+ } else if (declaration.type === "ClassDeclaration" && "id" in declaration && declaration.id) {
1313
+ results.push({ name: declaration.id.name, type: "class" });
1314
+ } else if (declaration.type === "VariableDeclaration") {
1315
+ for (const declarator of declaration.declarations) {
1316
+ if (declarator.id.type === "Identifier") {
1317
+ results.push({ name: declarator.id.name, type: "const" });
1494
1318
  }
1495
- if (inClass) {
1496
- const classMatch = line.match(classRegex);
1497
- if (classMatch) {
1498
- exports.push({
1499
- name: classMatch[1],
1500
- type: "class",
1501
- loc: {
1502
- start: { line: idx + 1, column: indent },
1503
- end: { line: idx + 1, column: line.length }
1319
+ }
1320
+ } else if (declaration.type === "TSTypeAliasDeclaration") {
1321
+ results.push({ name: declaration.id.name, type: "type" });
1322
+ } else if (declaration.type === "TSInterfaceDeclaration") {
1323
+ results.push({ name: declaration.id.name, type: "interface" });
1324
+ }
1325
+ return results;
1326
+ }
1327
+ function findUsedImports(node, importedNames) {
1328
+ const usedImports = /* @__PURE__ */ new Set();
1329
+ function visit(n) {
1330
+ if (n.type === "Identifier" && importedNames.has(n.name)) {
1331
+ usedImports.add(n.name);
1332
+ }
1333
+ for (const key in n) {
1334
+ const value = n[key];
1335
+ if (value && typeof value === "object") {
1336
+ if (Array.isArray(value)) {
1337
+ value.forEach((child) => {
1338
+ if (child && typeof child === "object" && "type" in child) {
1339
+ visit(child);
1504
1340
  }
1505
1341
  });
1342
+ } else if ("type" in value) {
1343
+ visit(value);
1506
1344
  }
1507
- return;
1508
1345
  }
1509
- const funcMatch = line.match(functionRegex);
1510
- if (funcMatch && indent === 0) {
1511
- const name = funcMatch[1];
1512
- if (!name.startsWith("_") || name.startsWith("__")) {
1513
- exports.push({
1514
- name,
1515
- type: "function",
1516
- loc: {
1517
- start: { line: idx + 1, column: 0 },
1518
- end: { line: idx + 1, column: line.length }
1519
- }
1520
- });
1346
+ }
1347
+ }
1348
+ visit(node);
1349
+ return Array.from(usedImports);
1350
+ }
1351
+ function calculateImportSimilarity(export1, export2) {
1352
+ if (export1.imports.length === 0 && export2.imports.length === 0) {
1353
+ return 1;
1354
+ }
1355
+ const set1 = new Set(export1.imports);
1356
+ const set2 = new Set(export2.imports);
1357
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
1358
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
1359
+ return intersection.size / union.size;
1360
+ }
1361
+ function extractTypeReferences(node) {
1362
+ const types = /* @__PURE__ */ new Set();
1363
+ function visit(n) {
1364
+ if (!n || typeof n !== "object") return;
1365
+ if (n.type === "TSTypeReference" && n.typeName) {
1366
+ if (n.typeName.type === "Identifier") {
1367
+ types.add(n.typeName.name);
1368
+ } else if (n.typeName.type === "TSQualifiedName") {
1369
+ let current = n.typeName;
1370
+ while (current.type === "TSQualifiedName") {
1371
+ if (current.right?.type === "Identifier") {
1372
+ types.add(current.right.name);
1373
+ }
1374
+ current = current.left;
1375
+ }
1376
+ if (current.type === "Identifier") {
1377
+ types.add(current.name);
1521
1378
  }
1522
1379
  }
1523
- const allMatch = line.match(allRegex);
1524
- if (allMatch) {
1525
- const names = allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, ""));
1526
- names.forEach((name) => {
1527
- if (name && !exports.find((e) => e.name === name)) {
1528
- exports.push({
1529
- name,
1530
- type: "variable",
1531
- loc: {
1532
- start: { line: idx + 1, column: 0 },
1533
- end: { line: idx + 1, column: line.length }
1534
- }
1535
- });
1380
+ }
1381
+ if (n.type === "TSInterfaceHeritage" && n.expression) {
1382
+ if (n.expression.type === "Identifier") {
1383
+ types.add(n.expression.name);
1384
+ }
1385
+ }
1386
+ for (const key of Object.keys(n)) {
1387
+ const value = n[key];
1388
+ if (Array.isArray(value)) {
1389
+ value.forEach(visit);
1390
+ } else if (value && typeof value === "object") {
1391
+ visit(value);
1392
+ }
1393
+ }
1394
+ }
1395
+ visit(node);
1396
+ return Array.from(types);
1397
+ }
1398
+ function parseCode(code, language) {
1399
+ return null;
1400
+ }
1401
+ function extractFunctions(ast) {
1402
+ return [];
1403
+ }
1404
+ function extractImports(ast) {
1405
+ return [];
1406
+ }
1407
+
1408
+ // src/utils/metrics.ts
1409
+ function estimateTokens(text) {
1410
+ return Math.ceil(text.length / 4);
1411
+ }
1412
+
1413
+ // src/utils/config.ts
1414
+ import { readFileSync, existsSync as existsSync4 } from "fs";
1415
+ import { join as join4, resolve, dirname as dirname3 } from "path";
1416
+ import { pathToFileURL } from "url";
1417
+ var CONFIG_FILES = [
1418
+ "aiready.json",
1419
+ "aiready.config.json",
1420
+ ".aiready.json",
1421
+ ".aireadyrc.json",
1422
+ "aiready.config.js",
1423
+ ".aireadyrc.js"
1424
+ ];
1425
+ async function loadConfig(rootDir) {
1426
+ let currentDir = resolve(rootDir);
1427
+ while (true) {
1428
+ for (const configFile of CONFIG_FILES) {
1429
+ const configPath = join4(currentDir, configFile);
1430
+ if (existsSync4(configPath)) {
1431
+ try {
1432
+ let config;
1433
+ if (configFile.endsWith(".js")) {
1434
+ const fileUrl = pathToFileURL(configPath).href;
1435
+ const module = await import(`${fileUrl}?t=${Date.now()}`);
1436
+ config = module.default || module;
1437
+ } else {
1438
+ const content = readFileSync(configPath, "utf-8");
1439
+ config = JSON.parse(content);
1440
+ }
1441
+ if (typeof config !== "object" || config === null) {
1442
+ throw new Error("Config must be an object");
1443
+ }
1444
+ return config;
1445
+ } catch (error) {
1446
+ const errorMessage = error instanceof Error ? error.message : String(error);
1447
+ const e = new Error(
1448
+ `Failed to load config from ${configPath}: ${errorMessage}`
1449
+ );
1450
+ try {
1451
+ e.cause = error instanceof Error ? error : void 0;
1452
+ } catch {
1536
1453
  }
1537
- });
1454
+ throw e;
1455
+ }
1538
1456
  }
1539
- });
1540
- return exports;
1457
+ }
1458
+ const parent = dirname3(currentDir);
1459
+ if (parent === currentDir) {
1460
+ break;
1461
+ }
1462
+ currentDir = parent;
1541
1463
  }
1542
- };
1543
-
1544
- // src/parsers/parser-factory.ts
1545
- var ParserFactory = class _ParserFactory {
1546
- constructor() {
1547
- this.parsers = /* @__PURE__ */ new Map();
1548
- this.extensionMap = new Map(
1549
- Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
1550
- );
1551
- this.registerParser(new TypeScriptParser());
1552
- this.registerParser(new PythonParser());
1464
+ return null;
1465
+ }
1466
+ function mergeConfigWithDefaults(userConfig, defaults) {
1467
+ if (!userConfig) return defaults;
1468
+ const result = { ...defaults };
1469
+ if (userConfig.scan) {
1470
+ if (userConfig.scan.include) result.include = userConfig.scan.include;
1471
+ if (userConfig.scan.exclude) result.exclude = userConfig.scan.exclude;
1553
1472
  }
1554
- /**
1555
- * Get singleton instance
1556
- */
1557
- static getInstance() {
1558
- if (!_ParserFactory.instance) {
1559
- _ParserFactory.instance = new _ParserFactory();
1473
+ const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
1474
+ if (toolOverrides) {
1475
+ if (!result.toolConfigs) result.toolConfigs = {};
1476
+ for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
1477
+ if (typeof toolConfig === "object" && toolConfig !== null) {
1478
+ result[toolName] = { ...result[toolName], ...toolConfig };
1479
+ result.toolConfigs[toolName] = {
1480
+ ...result.toolConfigs[toolName],
1481
+ ...toolConfig
1482
+ };
1483
+ }
1560
1484
  }
1561
- return _ParserFactory.instance;
1562
1485
  }
1563
- /**
1564
- * Register a language parser
1565
- */
1566
- registerParser(parser) {
1567
- this.parsers.set(parser.language, parser);
1568
- parser.extensions.forEach((ext) => {
1569
- const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
1570
- this.extensionMap.set(ext, lang);
1571
- this.parsers.set(lang, parser);
1572
- });
1486
+ if (userConfig.output) {
1487
+ result.output = { ...result.output, ...userConfig.output };
1573
1488
  }
1574
- /**
1575
- * Get parser for a specific language
1576
- */
1577
- getParserForLanguage(language) {
1578
- return this.parsers.get(language) || null;
1489
+ return result;
1490
+ }
1491
+
1492
+ // src/business/pricing-models.ts
1493
+ var MODEL_PRICING_PRESETS = {
1494
+ "gpt-5.3": {
1495
+ name: "GPT-5.3",
1496
+ pricePer1KInputTokens: 2e-3,
1497
+ pricePer1KOutputTokens: 8e-3,
1498
+ contextTier: "frontier",
1499
+ typicalQueriesPerDevPerDay: 100
1500
+ },
1501
+ "claude-4.6": {
1502
+ name: "Claude 4.6",
1503
+ pricePer1KInputTokens: 15e-4,
1504
+ pricePer1KOutputTokens: 75e-4,
1505
+ contextTier: "frontier",
1506
+ typicalQueriesPerDevPerDay: 100
1507
+ },
1508
+ "gemini-3.1": {
1509
+ name: "Gemini 3.1 Pro",
1510
+ pricePer1KInputTokens: 8e-4,
1511
+ pricePer1KOutputTokens: 3e-3,
1512
+ contextTier: "frontier",
1513
+ typicalQueriesPerDevPerDay: 120
1514
+ },
1515
+ "gpt-4o": {
1516
+ name: "GPT-4o (legacy)",
1517
+ pricePer1KInputTokens: 5e-3,
1518
+ pricePer1KOutputTokens: 0.015,
1519
+ contextTier: "extended",
1520
+ typicalQueriesPerDevPerDay: 60
1521
+ },
1522
+ "claude-3-5-sonnet": {
1523
+ name: "Claude 3.5 Sonnet (legacy)",
1524
+ pricePer1KInputTokens: 3e-3,
1525
+ pricePer1KOutputTokens: 0.015,
1526
+ contextTier: "extended",
1527
+ typicalQueriesPerDevPerDay: 80
1528
+ },
1529
+ "gemini-1-5-pro": {
1530
+ name: "Gemini 1.5 Pro (legacy)",
1531
+ pricePer1KInputTokens: 125e-5,
1532
+ pricePer1KOutputTokens: 5e-3,
1533
+ contextTier: "frontier",
1534
+ typicalQueriesPerDevPerDay: 80
1535
+ },
1536
+ copilot: {
1537
+ name: "GitHub Copilot (subscription)",
1538
+ pricePer1KInputTokens: 8e-5,
1539
+ pricePer1KOutputTokens: 8e-5,
1540
+ contextTier: "frontier",
1541
+ typicalQueriesPerDevPerDay: 150
1542
+ },
1543
+ "cursor-pro": {
1544
+ name: "Cursor Pro (subscription)",
1545
+ pricePer1KInputTokens: 8e-5,
1546
+ pricePer1KOutputTokens: 8e-5,
1547
+ contextTier: "frontier",
1548
+ typicalQueriesPerDevPerDay: 200
1579
1549
  }
1580
- /**
1581
- * Get parser for a file based on its extension
1582
- */
1583
- getParserForFile(filePath) {
1584
- const ext = this.getFileExtension(filePath);
1585
- const language = this.extensionMap.get(ext);
1586
- if (!language) {
1587
- return null;
1550
+ };
1551
+ function getModelPreset(modelId) {
1552
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
1553
+ }
1554
+
1555
+ // src/business/cost-metrics.ts
1556
+ var DEFAULT_COST_CONFIG = {
1557
+ pricePer1KTokens: 5e-3,
1558
+ queriesPerDevPerDay: 60,
1559
+ developerCount: 5,
1560
+ daysPerMonth: 30
1561
+ };
1562
+ function calculateMonthlyCost(tokenWaste, config = {}) {
1563
+ const budget = calculateTokenBudget({
1564
+ totalContextTokens: tokenWaste * 2.5,
1565
+ wastedTokens: {
1566
+ duplication: tokenWaste * 0.7,
1567
+ fragmentation: tokenWaste * 0.3,
1568
+ chattiness: 0
1588
1569
  }
1589
- return this.parsers.get(language) || null;
1590
- }
1591
- /**
1592
- * Check if a file is supported
1593
- */
1594
- isSupported(filePath) {
1595
- return this.getParserForFile(filePath) !== null;
1570
+ });
1571
+ const preset = getModelPreset("claude-4.6");
1572
+ return estimateCostFromBudget(budget, preset, config);
1573
+ }
1574
+ function calculateTokenBudget(params) {
1575
+ const { totalContextTokens, wastedTokens } = params;
1576
+ const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
1577
+ const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
1578
+ const efficiencyRatio = Math.max(
1579
+ 0,
1580
+ Math.min(
1581
+ 1,
1582
+ (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens)
1583
+ )
1584
+ );
1585
+ return {
1586
+ totalContextTokens: Math.round(totalContextTokens),
1587
+ estimatedResponseTokens: Math.round(estimatedResponseTokens),
1588
+ wastedTokens: {
1589
+ total: Math.round(totalWaste),
1590
+ bySource: {
1591
+ duplication: Math.round(wastedTokens.duplication),
1592
+ fragmentation: Math.round(wastedTokens.fragmentation),
1593
+ chattiness: Math.round(wastedTokens.chattiness)
1594
+ }
1595
+ },
1596
+ efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
1597
+ potentialRetrievableTokens: Math.round(totalWaste * 0.8)
1598
+ };
1599
+ }
1600
+ function estimateCostFromBudget(budget, model, config = {}) {
1601
+ const cfg = { ...DEFAULT_COST_CONFIG, ...config };
1602
+ const wastePerQuery = budget.wastedTokens.total;
1603
+ const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
1604
+ const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
1605
+ const totalWeight = cfg.developerCount;
1606
+ const price = config.pricePer1KTokens ?? model.pricePer1KInputTokens;
1607
+ const baseCost = tokensPerMonth / 1e3 * price * totalWeight;
1608
+ let confidence = 0.85;
1609
+ if (model.contextTier === "frontier") confidence = 0.7;
1610
+ const variance = 0.25;
1611
+ const range = [
1612
+ Math.round(baseCost * (1 - variance) * 100) / 100,
1613
+ Math.round(baseCost * (1 + variance) * 100) / 100
1614
+ ];
1615
+ return {
1616
+ total: Math.round(baseCost * 100) / 100,
1617
+ range,
1618
+ confidence
1619
+ };
1620
+ }
1621
+
1622
+ // src/business/productivity-metrics.ts
1623
+ var SEVERITY_TIME_ESTIMATES = {
1624
+ ["critical" /* Critical */]: 4,
1625
+ ["major" /* Major */]: 2,
1626
+ ["minor" /* Minor */]: 0.5,
1627
+ ["info" /* Info */]: 0.25
1628
+ };
1629
+ var DEFAULT_HOURLY_RATE = 75;
1630
+ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
1631
+ const counts = {
1632
+ ["critical" /* Critical */]: issues.filter((i) => i.severity === "critical" /* Critical */).length,
1633
+ ["major" /* Major */]: issues.filter((i) => i.severity === "major" /* Major */).length,
1634
+ ["minor" /* Minor */]: issues.filter((i) => i.severity === "minor" /* Minor */).length,
1635
+ ["info" /* Info */]: issues.filter((i) => i.severity === "info" /* Info */).length
1636
+ };
1637
+ const hours = {
1638
+ ["critical" /* Critical */]: counts["critical" /* Critical */] * SEVERITY_TIME_ESTIMATES["critical" /* Critical */],
1639
+ ["major" /* Major */]: counts["major" /* Major */] * SEVERITY_TIME_ESTIMATES["major" /* Major */],
1640
+ ["minor" /* Minor */]: counts["minor" /* Minor */] * SEVERITY_TIME_ESTIMATES["minor" /* Minor */],
1641
+ ["info" /* Info */]: counts["info" /* Info */] * SEVERITY_TIME_ESTIMATES["info" /* Info */]
1642
+ };
1643
+ const totalHours = hours["critical" /* Critical */] + hours["major" /* Major */] + hours["minor" /* Minor */] + hours["info" /* Info */];
1644
+ const totalCost = totalHours * hourlyRate;
1645
+ return {
1646
+ totalHours: Math.round(totalHours * 10) / 10,
1647
+ hourlyRate,
1648
+ totalCost: Math.round(totalCost),
1649
+ bySeverity: {
1650
+ ["critical" /* Critical */]: {
1651
+ hours: Math.round(hours["critical" /* Critical */] * 10) / 10,
1652
+ cost: Math.round(hours["critical" /* Critical */] * hourlyRate)
1653
+ },
1654
+ ["major" /* Major */]: {
1655
+ hours: Math.round(hours["major" /* Major */] * 10) / 10,
1656
+ cost: Math.round(hours["major" /* Major */] * hourlyRate)
1657
+ },
1658
+ ["minor" /* Minor */]: {
1659
+ hours: Math.round(hours["minor" /* Minor */] * 10) / 10,
1660
+ cost: Math.round(hours["minor" /* Minor */] * hourlyRate)
1661
+ },
1662
+ ["info" /* Info */]: {
1663
+ hours: Math.round(hours["info" /* Info */] * 10) / 10,
1664
+ cost: Math.round(hours["info" /* Info */] * hourlyRate)
1665
+ }
1666
+ }
1667
+ };
1668
+ }
1669
+ function predictAcceptanceRate(toolOutputs) {
1670
+ const factors = [];
1671
+ const baseRate = 0.3;
1672
+ const patterns = toolOutputs.get("pattern-detect");
1673
+ if (patterns) {
1674
+ factors.push({
1675
+ name: "Semantic Duplication",
1676
+ impact: Math.round((patterns.score - 50) * 3e-3 * 100)
1677
+ });
1596
1678
  }
1597
- /**
1598
- * Get all registered languages
1599
- */
1600
- getSupportedLanguages() {
1601
- return Array.from(this.parsers.keys());
1679
+ const context = toolOutputs.get("context-analyzer");
1680
+ if (context) {
1681
+ factors.push({
1682
+ name: "Context Efficiency",
1683
+ impact: Math.round((context.score - 50) * 4e-3 * 100)
1684
+ });
1602
1685
  }
1603
- /**
1604
- * Get all supported file extensions
1605
- */
1606
- getSupportedExtensions() {
1607
- return Array.from(this.extensionMap.keys());
1686
+ const consistency = toolOutputs.get("consistency");
1687
+ if (consistency) {
1688
+ factors.push({
1689
+ name: "Code Consistency",
1690
+ impact: Math.round((consistency.score - 50) * 2e-3 * 100)
1691
+ });
1608
1692
  }
1609
- /**
1610
- * Get language for a file
1611
- */
1612
- getLanguageForFile(filePath) {
1613
- const ext = this.getFileExtension(filePath);
1614
- return this.extensionMap.get(ext) || null;
1693
+ const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
1694
+ if (aiSignalClarity) {
1695
+ factors.push({
1696
+ name: "AI Signal Clarity",
1697
+ impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
1698
+ });
1615
1699
  }
1616
- /**
1617
- * Extract file extension (with dot)
1618
- */
1619
- getFileExtension(filePath) {
1620
- const match = filePath.match(/\.[^.]+$/);
1621
- return match ? match[0].toLowerCase() : "";
1700
+ const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
1701
+ const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
1702
+ let confidence = 0.35;
1703
+ if (toolOutputs.size >= 4) confidence = 0.75;
1704
+ else if (toolOutputs.size >= 3) confidence = 0.65;
1705
+ else if (toolOutputs.size >= 2) confidence = 0.5;
1706
+ return { rate: Math.round(rate * 100) / 100, confidence, factors };
1707
+ }
1708
+
1709
+ // src/business/risk-metrics.ts
1710
+ function calculateKnowledgeConcentration(params) {
1711
+ const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
1712
+ const concentrationRatio = totalFiles > 0 ? (uniqueConceptFiles + singleAuthorFiles) / (totalFiles * 2) : 0;
1713
+ const score = Math.round(
1714
+ Math.min(
1715
+ 100,
1716
+ concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
1717
+ )
1718
+ );
1719
+ let rating;
1720
+ if (score < 30) rating = "low";
1721
+ else if (score < 50) rating = "moderate";
1722
+ else if (score < 75) rating = "high";
1723
+ else rating = "critical";
1724
+ const recommendations = [];
1725
+ if (singleAuthorFiles > 0)
1726
+ recommendations.push(
1727
+ `Distribute knowledge for ${singleAuthorFiles} single-author files.`
1728
+ );
1729
+ if (orphanFiles > 0)
1730
+ recommendations.push(
1731
+ `Link ${orphanFiles} orphan files to the rest of the codebase.`
1732
+ );
1733
+ return {
1734
+ score,
1735
+ rating,
1736
+ recommendations,
1737
+ analysis: {
1738
+ uniqueConceptFiles,
1739
+ totalFiles,
1740
+ concentrationRatio,
1741
+ singleAuthorFiles,
1742
+ orphanFiles
1743
+ }
1744
+ };
1745
+ }
1746
+ function calculateDebtInterest(principal, monthlyGrowthRate) {
1747
+ const monthlyRate = monthlyGrowthRate;
1748
+ const annualRate = Math.pow(1 + monthlyRate, 12) - 1;
1749
+ const monthlyCost = principal * monthlyRate;
1750
+ return {
1751
+ monthlyRate,
1752
+ annualRate,
1753
+ principal,
1754
+ monthlyCost,
1755
+ projections: {
1756
+ months6: principal * Math.pow(1 + monthlyRate, 6),
1757
+ months12: principal * Math.pow(1 + monthlyRate, 12),
1758
+ months24: principal * Math.pow(1 + monthlyRate, 24)
1759
+ }
1760
+ };
1761
+ }
1762
+
1763
+ // src/business/comprehension-metrics.ts
1764
+ function calculateTechnicalValueChain(params) {
1765
+ const { businessLogicDensity, dataAccessComplexity, apiSurfaceArea } = params;
1766
+ const score = (businessLogicDensity * 0.5 + (1 - dataAccessComplexity / 10) * 0.3 + (1 - apiSurfaceArea / 20) * 0.2) * 100;
1767
+ return {
1768
+ score: Math.round(Math.max(0, Math.min(100, score))),
1769
+ density: businessLogicDensity,
1770
+ complexity: dataAccessComplexity,
1771
+ surface: apiSurfaceArea
1772
+ };
1773
+ }
1774
+ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, modelTier = "frontier") {
1775
+ const tierMap = {
1776
+ compact: "compact",
1777
+ standard: "standard",
1778
+ extended: "extended",
1779
+ frontier: "frontier",
1780
+ easy: "frontier",
1781
+ // Map legacy 'easy' to 'frontier'
1782
+ moderate: "standard",
1783
+ difficult: "compact"
1784
+ };
1785
+ const tier = tierMap[modelTier] || "frontier";
1786
+ const threshold = CONTEXT_TIER_THRESHOLDS[tier];
1787
+ const budgetRatio = contextBudget / threshold.idealTokens;
1788
+ const score = (budgetRatio * 0.6 + importDepth / 10 * 0.2 + fragmentation * 0.2) * 100;
1789
+ const finalScore = Math.round(Math.max(0, Math.min(100, score)));
1790
+ let rating;
1791
+ if (finalScore < 20) rating = "trivial";
1792
+ else if (finalScore < 40) rating = "easy";
1793
+ else if (finalScore < 60) rating = "moderate";
1794
+ else if (finalScore < 85) rating = "difficult";
1795
+ else rating = "expert";
1796
+ return {
1797
+ score: finalScore,
1798
+ rating,
1799
+ factors: { budgetRatio, depthRatio: importDepth / 10, fragmentation }
1800
+ };
1801
+ }
1802
+
1803
+ // src/business-metrics.ts
1804
+ function calculateBusinessROI(params) {
1805
+ const model = getModelPreset(params.modelId || "claude-4.6");
1806
+ const devCount = params.developerCount || 5;
1807
+ const budget = calculateTokenBudget({
1808
+ totalContextTokens: params.tokenWaste * 2.5,
1809
+ wastedTokens: {
1810
+ duplication: params.tokenWaste * 0.7,
1811
+ fragmentation: params.tokenWaste * 0.3,
1812
+ chattiness: 0
1813
+ }
1814
+ });
1815
+ const cost = estimateCostFromBudget(budget, model, {
1816
+ developerCount: devCount
1817
+ });
1818
+ const productivity = calculateProductivityImpact(params.issues);
1819
+ const monthlySavings = cost.total;
1820
+ const productivityGainHours = productivity.totalHours;
1821
+ const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
1822
+ return {
1823
+ monthlySavings: Math.round(monthlySavings),
1824
+ productivityGainHours: Math.round(productivityGainHours),
1825
+ annualValue: Math.round(annualValue)
1826
+ };
1827
+ }
1828
+ function formatCost(cost) {
1829
+ if (cost < 1) {
1830
+ return `$${cost.toFixed(2)}`;
1831
+ } else if (cost < 1e3) {
1832
+ return `$${cost.toFixed(0)}`;
1833
+ } else {
1834
+ return `$${(cost / 1e3).toFixed(1)}k`;
1622
1835
  }
1623
- /**
1624
- * Reset factory (useful for testing)
1625
- */
1626
- static reset() {
1627
- _ParserFactory.instance = new _ParserFactory();
1836
+ }
1837
+ function formatHours(hours) {
1838
+ if (hours < 1) {
1839
+ return `${Math.round(hours * 60)}min`;
1840
+ } else if (hours < 8) {
1841
+ return `${hours.toFixed(1)}h`;
1842
+ } else if (hours < 40) {
1843
+ return `${Math.round(hours)}h`;
1844
+ } else {
1845
+ return `${(hours / 40).toFixed(1)} weeks`;
1628
1846
  }
1629
- };
1630
- function getParser(filePath) {
1631
- return ParserFactory.getInstance().getParserForFile(filePath);
1632
1847
  }
1633
- function isFileSupported(filePath) {
1634
- return ParserFactory.getInstance().isSupported(filePath);
1848
+ function formatAcceptanceRate(rate) {
1849
+ return `${Math.round(rate * 100)}%`;
1635
1850
  }
1636
- function getSupportedLanguages() {
1637
- return ParserFactory.getInstance().getSupportedLanguages();
1851
+ function generateValueChain(params) {
1852
+ const { issueType, count, severity } = params;
1853
+ const impacts = {
1854
+ "duplicate-pattern": {
1855
+ ai: "Ambiguous context leads to code generation variants. AI picks wrong implementation 40% of the time.",
1856
+ dev: "Developers must manually resolve conflicts between suggested variants.",
1857
+ risk: "high"
1858
+ },
1859
+ "context-fragmentation": {
1860
+ ai: "Context window overflow causes model to forget mid-file dependencies resulting in hallucinations.",
1861
+ dev: "Slower AI responses and increased need for manual context pinning.",
1862
+ risk: "critical"
1863
+ },
1864
+ "naming-inconsistency": {
1865
+ ai: "Degraded intent inference. AI misidentifies domain concepts across file boundaries.",
1866
+ dev: "Increased cognitive load for new devs during onboarding.",
1867
+ risk: "moderate"
1868
+ }
1869
+ };
1870
+ const impact = impacts[issueType] || {
1871
+ ai: "Reduced suggestion quality.",
1872
+ dev: "Slowed development velocity.",
1873
+ risk: "moderate"
1874
+ };
1875
+ const productivityLoss = severity === "critical" ? 0.25 : severity === "major" ? 0.1 : 0.05;
1876
+ return {
1877
+ issueType,
1878
+ technicalMetric: "Issue Count",
1879
+ technicalValue: count,
1880
+ aiImpact: {
1881
+ description: impact.ai,
1882
+ scoreImpact: severity === "critical" ? -15 : -5
1883
+ },
1884
+ developerImpact: {
1885
+ description: impact.dev,
1886
+ productivityLoss
1887
+ },
1888
+ businessOutcome: {
1889
+ directCost: count * 12,
1890
+ opportunityCost: productivityLoss * 15e3,
1891
+ riskLevel: impact.risk
1892
+ }
1893
+ };
1638
1894
  }
1639
1895
 
1640
1896
  // src/metrics/remediation-utils.ts
@@ -2477,14 +2733,14 @@ function calculateExtendedFutureProofScore(params) {
2477
2733
  }
2478
2734
 
2479
2735
  // src/utils/history.ts
2480
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2481
- import { join as join4, dirname as dirname4 } from "path";
2736
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2737
+ import { join as join5, dirname as dirname4 } from "path";
2482
2738
  function getHistoryPath(rootDir) {
2483
- return join4(rootDir, ".aiready", "history.json");
2739
+ return join5(rootDir, ".aiready", "history.json");
2484
2740
  }
2485
2741
  function loadScoreHistory(rootDir) {
2486
2742
  const historyPath = getHistoryPath(rootDir);
2487
- if (!existsSync4(historyPath)) {
2743
+ if (!existsSync5(historyPath)) {
2488
2744
  return [];
2489
2745
  }
2490
2746
  try {
@@ -2498,7 +2754,7 @@ function loadScoreHistory(rootDir) {
2498
2754
  function saveScoreEntry(rootDir, entry) {
2499
2755
  const historyPath = getHistoryPath(rootDir);
2500
2756
  const historyDir = dirname4(historyPath);
2501
- if (!existsSync4(historyDir)) {
2757
+ if (!existsSync5(historyDir)) {
2502
2758
  mkdirSync2(historyDir, { recursive: true });
2503
2759
  }
2504
2760
  const history = loadScoreHistory(rootDir);
@@ -2545,7 +2801,7 @@ function exportHistory(rootDir, format = "json") {
2545
2801
  }
2546
2802
  function clearHistory(rootDir) {
2547
2803
  const historyPath = getHistoryPath(rootDir);
2548
- if (existsSync4(historyPath)) {
2804
+ if (existsSync5(historyPath)) {
2549
2805
  writeFileSync2(historyPath, JSON.stringify([]));
2550
2806
  }
2551
2807
  }
@@ -2715,6 +2971,7 @@ export {
2715
2971
  getToolWeight,
2716
2972
  handleCLIError,
2717
2973
  handleJSONOutput,
2974
+ initializeParsers,
2718
2975
  isFileSupported,
2719
2976
  isSourceFile,
2720
2977
  loadConfig,