codemap-ai 0.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,26 +1,12 @@
1
1
  import {
2
- GraphStorage
3
- } from "./chunk-FLUWKIEM.js";
2
+ FlowStorage
3
+ } from "./chunk-GX7ZMBC2.js";
4
4
 
5
- // src/types.ts
6
- var DEFAULT_CONFIG = {
7
- include: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py"],
8
- exclude: [
9
- "**/node_modules/**",
10
- "**/dist/**",
11
- "**/build/**",
12
- "**/.git/**",
13
- "**/venv/**",
14
- "**/__pycache__/**",
15
- "**/.next/**"
16
- ],
17
- languages: ["typescript", "javascript", "python"]
18
- };
19
-
20
- // src/graph/builder.ts
21
- import { readFileSync } from "fs";
5
+ // src/flow/builder.ts
6
+ import { readFileSync, statSync } from "fs";
7
+ import { createHash } from "crypto";
22
8
  import { glob } from "glob";
23
- import { relative, resolve } from "path";
9
+ import { resolve, relative, extname } from "path";
24
10
 
25
11
  // src/parsers/base.ts
26
12
  var BaseParser = class {
@@ -108,13 +94,40 @@ var TypeScriptParser = class extends BaseParser {
108
94
  }
109
95
  parse(filePath, content) {
110
96
  const analysis = this.createEmptyAnalysis(filePath);
97
+ const sourceCode = typeof content === "string" ? content : String(content);
98
+ if (sourceCode.length > 5e5) {
99
+ const lines2 = sourceCode.split("\n");
100
+ const fileNode2 = this.createNode(
101
+ "file",
102
+ filePath.split("/").pop() || filePath,
103
+ filePath,
104
+ 1,
105
+ lines2.length
106
+ );
107
+ analysis.nodes.push(fileNode2);
108
+ return analysis;
109
+ }
111
110
  if (filePath.endsWith(".tsx")) {
112
111
  this.parser.setLanguage(TypeScript.tsx);
113
112
  } else {
114
113
  this.parser.setLanguage(TypeScript.typescript);
115
114
  }
116
- const tree = this.parser.parse(content);
117
- const lines = content.split("\n");
115
+ let tree;
116
+ try {
117
+ tree = this.parser.parse(sourceCode);
118
+ } catch {
119
+ const lines2 = sourceCode.split("\n");
120
+ const fileNode2 = this.createNode(
121
+ "file",
122
+ filePath.split("/").pop() || filePath,
123
+ filePath,
124
+ 1,
125
+ lines2.length
126
+ );
127
+ analysis.nodes.push(fileNode2);
128
+ return analysis;
129
+ }
130
+ const lines = sourceCode.split("\n");
118
131
  const fileNode = this.createNode(
119
132
  "file",
120
133
  filePath.split("/").pop() || filePath,
@@ -357,7 +370,13 @@ var JavaScriptParser = class extends TypeScriptParser {
357
370
  this.parser.setLanguage(JavaScript);
358
371
  }
359
372
  parse(filePath, content) {
360
- const analysis = super.parse(filePath, content);
373
+ const sourceCode = typeof content === "string" ? content : String(content);
374
+ if (filePath.endsWith(".jsx")) {
375
+ this.parser.setLanguage(TypeScript.tsx);
376
+ } else {
377
+ this.parser.setLanguage(JavaScript);
378
+ }
379
+ const analysis = super.parse(filePath, sourceCode);
361
380
  analysis.language = "javascript";
362
381
  for (const node of analysis.nodes) {
363
382
  node.language = "javascript";
@@ -380,8 +399,41 @@ var PythonParser = class extends BaseParser {
380
399
  }
381
400
  parse(filePath, content) {
382
401
  const analysis = this.createEmptyAnalysis(filePath);
383
- const tree = this.parser.parse(content);
384
- const lines = content.split("\n");
402
+ const sourceCode = typeof content === "string" ? content : String(content);
403
+ if (sourceCode.length > 5e5) {
404
+ const lines2 = sourceCode.split("\n");
405
+ const fileNode2 = this.createNode(
406
+ "file",
407
+ filePath.split("/").pop() || filePath,
408
+ filePath,
409
+ 1,
410
+ lines2.length
411
+ );
412
+ analysis.nodes.push(fileNode2);
413
+ return analysis;
414
+ }
415
+ let tree;
416
+ try {
417
+ tree = this.parser.parse((index, position) => {
418
+ if (index >= sourceCode.length) {
419
+ return null;
420
+ }
421
+ const endIndex = Math.min(index + 1024, sourceCode.length);
422
+ return sourceCode.substring(index, endIndex);
423
+ });
424
+ } catch (error) {
425
+ const lines2 = sourceCode.split("\n");
426
+ const fileNode2 = this.createNode(
427
+ "file",
428
+ filePath.split("/").pop() || filePath,
429
+ filePath,
430
+ 1,
431
+ lines2.length
432
+ );
433
+ analysis.nodes.push(fileNode2);
434
+ return analysis;
435
+ }
436
+ const lines = sourceCode.split("\n");
385
437
  const fileNode = this.createNode(
386
438
  "file",
387
439
  filePath.split("/").pop() || filePath,
@@ -486,17 +538,66 @@ var PythonParser = class extends BaseParser {
486
538
  if (!nameNode) return;
487
539
  const name = nameNode.text;
488
540
  const decorators = [];
489
- let currentNode = node.previousSibling;
490
- while (currentNode?.type === "decorator") {
491
- const decoratorName = currentNode.children.find(
492
- (c) => c.type === "identifier" || c.type === "attribute"
493
- );
494
- if (decoratorName) {
495
- decorators.push(decoratorName.text);
541
+ const decoratorDetails = [];
542
+ if (node.parent?.type === "decorated_definition") {
543
+ for (const sibling of node.parent.children) {
544
+ if (sibling.type === "decorator") {
545
+ const decoratorCall = sibling.children.find((c) => c.type === "call");
546
+ const decoratorAttr = sibling.children.find((c) => c.type === "attribute" || c.type === "identifier");
547
+ if (decoratorCall) {
548
+ const funcName = decoratorCall.children.find((c) => c.type === "attribute" || c.type === "identifier");
549
+ if (funcName) {
550
+ const fullDecorator = funcName.text;
551
+ decorators.push(fullDecorator);
552
+ const args = [];
553
+ const kwargs = {};
554
+ const argList = decoratorCall.children.find((c) => c.type === "argument_list");
555
+ if (argList) {
556
+ for (const arg of argList.children) {
557
+ if (arg.type === "string") {
558
+ const stringContent = arg.children.find((c) => c.type === "string_content");
559
+ const argText = stringContent ? stringContent.text : arg.text.replace(/^["']|["']$/g, "");
560
+ args.push(argText);
561
+ } else if (arg.type === "keyword_argument") {
562
+ const keyNode = arg.childForFieldName("name");
563
+ const valueNode = arg.childForFieldName("value");
564
+ if (keyNode && valueNode) {
565
+ const key = keyNode.text;
566
+ let value = valueNode.text;
567
+ if (valueNode.type === "list") {
568
+ const items = [];
569
+ for (const child of valueNode.children) {
570
+ if (child.type === "string") {
571
+ const stringContent = child.children.find((c) => c.type === "string_content");
572
+ items.push(stringContent ? stringContent.text : child.text.replace(/^["']|["']$/g, ""));
573
+ }
574
+ }
575
+ value = items.length > 0 ? items.join(",") : value;
576
+ }
577
+ kwargs[key] = value;
578
+ }
579
+ } else if (arg.type === "identifier") {
580
+ args.push(arg.text);
581
+ }
582
+ }
583
+ }
584
+ decoratorDetails.push({ name: fullDecorator, args, kwargs });
585
+ }
586
+ } else if (decoratorAttr) {
587
+ const fullDecorator = decoratorAttr.text;
588
+ decorators.push(fullDecorator);
589
+ decoratorDetails.push({ name: fullDecorator, args: [] });
590
+ }
591
+ }
496
592
  }
497
- currentNode = currentNode.previousSibling;
498
593
  }
499
594
  const isAsync = node.children.some((c) => c.type === "async");
595
+ let returnType;
596
+ const returnTypeNode = node.childForFieldName("return_type");
597
+ if (returnTypeNode) {
598
+ returnType = returnTypeNode.text;
599
+ returnType = returnType.replace(/["']/g, "").split("[")[0].trim();
600
+ }
500
601
  const funcNode = this.createNode(
501
602
  "function",
502
603
  name,
@@ -506,12 +607,20 @@ var PythonParser = class extends BaseParser {
506
607
  {
507
608
  async: isAsync,
508
609
  decorators,
610
+ decoratorDetails: JSON.stringify(decoratorDetails),
509
611
  isPrivate: name.startsWith("_"),
510
- isDunder: name.startsWith("__") && name.endsWith("__")
612
+ isDunder: name.startsWith("__") && name.endsWith("__"),
613
+ returnType
511
614
  }
512
615
  );
513
616
  analysis.nodes.push(funcNode);
514
617
  analysis.edges.push(this.createEdge("contains", parentId, funcNode.id));
618
+ this.detectFastAPIRoute(decoratorDetails, funcNode, analysis);
619
+ const parameters = node.childForFieldName("parameters");
620
+ if (parameters) {
621
+ this.detectDependsPattern(parameters, funcNode, analysis);
622
+ this.extractParameterTypes(parameters, funcNode.id, filePath, analysis);
623
+ }
515
624
  const body = node.childForFieldName("body");
516
625
  if (body) {
517
626
  this.parseBodyForCalls(body, filePath, analysis, funcNode.id);
@@ -524,6 +633,95 @@ var PythonParser = class extends BaseParser {
524
633
  });
525
634
  }
526
635
  }
636
+ /**
637
+ * Detect FastAPI route decorators like @router.post("/chat")
638
+ */
639
+ detectFastAPIRoute(decoratorDetails, funcNode, analysis) {
640
+ for (const dec of decoratorDetails) {
641
+ const routeMatch = dec.name.match(/^(router|app)\.(get|post|put|patch|delete|options|head)$/);
642
+ if (routeMatch && dec.args.length > 0) {
643
+ const method = routeMatch[2].toUpperCase();
644
+ const path = dec.args[0];
645
+ if (!funcNode.metadata) funcNode.metadata = {};
646
+ funcNode.metadata.httpEndpoint = {
647
+ method,
648
+ path
649
+ };
650
+ }
651
+ }
652
+ }
653
+ /**
654
+ * Detect Depends() pattern in function parameters
655
+ * Example: user: User = Depends(authenticate_user)
656
+ */
657
+ detectDependsPattern(parameters, funcNode, analysis) {
658
+ for (const param of parameters.children) {
659
+ if (param.type === "typed_parameter" || param.type === "default_parameter") {
660
+ const defaultValue = param.children.find((c) => c.type === "call");
661
+ if (defaultValue) {
662
+ const funcCall = defaultValue.childForFieldName("function");
663
+ if (funcCall && funcCall.text === "Depends") {
664
+ const argList = defaultValue.childForFieldName("arguments");
665
+ if (argList) {
666
+ for (const arg of argList.children) {
667
+ if (arg.type === "identifier") {
668
+ const dependencyName = arg.text;
669
+ const paramName = param.children.find(
670
+ (c) => c.type === "identifier"
671
+ )?.text;
672
+ if (!funcNode.metadata) funcNode.metadata = {};
673
+ if (!funcNode.metadata.depends) funcNode.metadata.depends = [];
674
+ funcNode.metadata.depends.push({
675
+ parameterName: paramName,
676
+ dependencyName,
677
+ line: param.startPosition.row + 1
678
+ });
679
+ analysis.edges.push(
680
+ this.createEdge("depends", funcNode.id, `ref:${dependencyName}`, {
681
+ unresolvedName: dependencyName,
682
+ parameterName: paramName,
683
+ line: param.startPosition.row + 1
684
+ })
685
+ );
686
+ }
687
+ }
688
+ }
689
+ }
690
+ }
691
+ }
692
+ }
693
+ }
694
+ /**
695
+ * Extract parameter type hints and register as variable types
696
+ * Example: def process(user: User, config: Config)
697
+ */
698
+ extractParameterTypes(parameters, scopeId, filePath, analysis) {
699
+ for (const param of parameters.children) {
700
+ if (param.type === "typed_parameter") {
701
+ const nameNode = param.children.find((c) => c.type === "identifier");
702
+ if (!nameNode) continue;
703
+ const paramName = nameNode.text;
704
+ if (paramName === "self" || paramName === "cls") continue;
705
+ const typeNode = param.children.find((c) => c.type === "type");
706
+ if (!typeNode) continue;
707
+ let typeName = typeNode.text;
708
+ typeName = typeName.replace(/["']/g, "").trim();
709
+ const genericMatch = typeName.match(/(?:Optional|List|Dict|Set)\[([^\]]+)\]/);
710
+ if (genericMatch) {
711
+ typeName = genericMatch[1].trim();
712
+ }
713
+ if (!analysis.variableTypes) {
714
+ analysis.variableTypes = [];
715
+ }
716
+ analysis.variableTypes.push({
717
+ variableName: paramName,
718
+ typeName,
719
+ scopeId,
720
+ line: param.startPosition.row + 1
721
+ });
722
+ }
723
+ }
724
+ }
527
725
  handleClass(node, filePath, analysis, parentId) {
528
726
  const nameNode = node.childForFieldName("name");
529
727
  if (!nameNode) return;
@@ -562,6 +760,11 @@ var PythonParser = class extends BaseParser {
562
760
  for (const child of body.children) {
563
761
  if (child.type === "function_definition") {
564
762
  this.handleMethod(child, filePath, analysis, classNode.id);
763
+ } else if (child.type === "decorated_definition") {
764
+ const funcNode = child.children.find((c) => c.type === "function_definition");
765
+ if (funcNode) {
766
+ this.handleMethod(funcNode, filePath, analysis, classNode.id);
767
+ }
565
768
  }
566
769
  }
567
770
  }
@@ -578,15 +781,15 @@ var PythonParser = class extends BaseParser {
578
781
  if (!nameNode) return;
579
782
  const name = nameNode.text;
580
783
  const decorators = [];
581
- let currentNode = node.previousSibling;
582
- while (currentNode?.type === "decorator") {
583
- const decoratorName = currentNode.children.find(
584
- (c) => c.type === "identifier" || c.type === "attribute"
585
- );
586
- if (decoratorName) {
587
- decorators.push(decoratorName.text);
784
+ if (node.parent?.type === "decorated_definition") {
785
+ for (const sibling of node.parent.children) {
786
+ if (sibling.type === "decorator") {
787
+ const decoratorAttr = sibling.children.find((c) => c.type === "attribute" || c.type === "identifier");
788
+ if (decoratorAttr) {
789
+ decorators.push(decoratorAttr.text);
790
+ }
791
+ }
588
792
  }
589
- currentNode = currentNode.previousSibling;
590
793
  }
591
794
  const isStatic = decorators.includes("staticmethod");
592
795
  const isClassMethod = decorators.includes("classmethod");
@@ -624,19 +827,63 @@ var PythonParser = class extends BaseParser {
624
827
  } else if (funcNode.type === "attribute") {
625
828
  calledName = funcNode.text;
626
829
  }
627
- if (calledName && !calledName.startsWith("self.")) {
830
+ if (calledName) {
628
831
  analysis.edges.push(
629
832
  this.createEdge("calls", parentId, `ref:${calledName}`, {
630
833
  unresolvedName: calledName,
631
834
  line: node.startPosition.row + 1
632
835
  })
633
836
  );
837
+ this.detectDatabaseOperation(calledName, node, parentId, analysis);
838
+ }
839
+ }
840
+ /**
841
+ * Detect database operations (MongoDB, SQL, etc.)
842
+ * Examples: collection.find_one(), db.users.insert_one(), collection.update_many()
843
+ */
844
+ detectDatabaseOperation(calledName, node, parentId, analysis) {
845
+ const mongoOps = [
846
+ "find_one",
847
+ "find",
848
+ "insert_one",
849
+ "insert_many",
850
+ "update_one",
851
+ "update_many",
852
+ "delete_one",
853
+ "delete_many",
854
+ "aggregate",
855
+ "count_documents",
856
+ "replace_one"
857
+ ];
858
+ const parts = calledName.split(".");
859
+ const operation = parts[parts.length - 1];
860
+ if (mongoOps.includes(operation)) {
861
+ let collection = "unknown";
862
+ if (parts.length >= 2) {
863
+ collection = parts[parts.length - 2];
864
+ }
865
+ if (!analysis.databaseOperations) {
866
+ analysis.databaseOperations = [];
867
+ }
868
+ analysis.databaseOperations.push({
869
+ nodeId: parentId,
870
+ operation,
871
+ collection,
872
+ databaseType: "mongodb",
873
+ line: node.startPosition.row + 1
874
+ });
634
875
  }
635
876
  }
636
877
  parseBodyForCalls(body, filePath, analysis, parentId) {
637
878
  const walkForCalls = (node) => {
638
879
  if (node.type === "call") {
639
880
  this.handleCall(node, filePath, analysis, parentId);
881
+ } else if (node.type === "import_statement") {
882
+ this.handleImport(node, analysis);
883
+ } else if (node.type === "import_from_statement") {
884
+ this.handleFromImport(node, analysis);
885
+ } else if (node.type === "assignment") {
886
+ this.handleAssignment(node, filePath, analysis, parentId);
640
887
  }
641
888
  for (const child of node.children) {
642
889
  walkForCalls(child);
@@ -644,6 +891,56 @@ var PythonParser = class extends BaseParser {
644
891
  };
645
892
  walkForCalls(body);
646
893
  }
894
+ /**
895
+ * Handle variable assignments to track types
896
+ * Examples:
897
+ * user = get_user(id) # Track if get_user has type hint
898
+ * handler = UserHandler(db) # Track: handler -> UserHandler
899
+ * org_handler = OrgHandler.get_instance() # Track: org_handler -> OrgHandler
900
+ */
901
+ handleAssignment(node, filePath, analysis, scopeId) {
902
+ const leftNode = node.childForFieldName("left");
903
+ if (!leftNode || leftNode.type !== "identifier") return;
904
+ const variableName = leftNode.text;
905
+ const rightNode = node.childForFieldName("right");
906
+ if (!rightNode) return;
907
+ let typeName = null;
908
+ if (rightNode.type === "call") {
909
+ const funcNode = rightNode.childForFieldName("function");
910
+ if (funcNode) {
911
+ if (funcNode.type === "identifier") {
912
+ const funcName = funcNode.text;
913
+ if (funcName[0] === funcName[0].toUpperCase()) {
914
+ typeName = funcName;
915
+ } else {
916
+ typeName = `@call:${funcName}`;
917
+ }
918
+ } else if (funcNode.type === "attribute") {
919
+ const parts = funcNode.text.split(".");
920
+ if (parts.length >= 2) {
921
+ const className = parts[0];
922
+ const methodName = parts[1];
923
+ if (className[0] === className[0].toUpperCase()) {
924
+ typeName = className;
925
+ } else {
926
+ typeName = `@call:${funcNode.text}`;
927
+ }
928
+ }
929
+ }
930
+ }
931
+ }
932
+ if (typeName && !analysis.variableTypes) {
933
+ analysis.variableTypes = [];
934
+ }
935
+ if (typeName) {
936
+ analysis.variableTypes.push({
937
+ variableName,
938
+ typeName,
939
+ scopeId,
940
+ line: node.startPosition.row + 1
941
+ });
942
+ }
943
+ }
647
944
  };
648
945
 
649
946
  // src/parsers/index.ts
@@ -654,11 +951,14 @@ function registerAllParsers() {
654
951
  }
655
952
  registerAllParsers();
656
953
 
657
- // src/graph/builder.ts
658
- var GraphBuilder = class {
954
+ // src/flow/builder.ts
955
+ var FlowBuilder = class {
659
956
  storage;
660
957
  config;
661
958
  onProgress;
959
+ moduleMap = /* @__PURE__ */ new Map();
960
+ // file path → exports
961
+ errors = [];
662
962
  constructor(storage, config) {
663
963
  this.storage = storage;
664
964
  this.config = config;
@@ -666,498 +966,310 @@ var GraphBuilder = class {
666
966
  setProgressCallback(callback) {
667
967
  this.onProgress = callback;
668
968
  }
669
- async scan() {
969
+ async build() {
670
970
  const rootPath = resolve(this.config.rootPath);
671
- this.emitProgress({ total: 0, current: 0, currentFile: "", phase: "discovering" });
672
- const files = await this.discoverFiles(rootPath);
673
- const fileAnalyses = [];
674
- let current = 0;
675
- for (const filePath of files) {
676
- current++;
971
+ this.emitProgress({
972
+ phase: "discovering",
973
+ total: 0,
974
+ current: 0,
975
+ currentFile: ""
976
+ });
977
+ const allFiles = await this.discoverFiles(rootPath);
978
+ const filesToIndex = [];
979
+ let skipped = 0;
980
+ for (const filePath of allFiles) {
981
+ const content = readFileSync(filePath, "utf-8");
982
+ const hash = this.hashContent(content);
983
+ if (!this.config.forceReindex) {
984
+ const existingHash = this.storage.getFileHash(filePath);
985
+ if (existingHash === hash) {
986
+ skipped++;
987
+ continue;
988
+ }
989
+ }
990
+ filesToIndex.push({ path: filePath, hash });
991
+ }
992
+ let indexed = 0;
993
+ for (const file of filesToIndex) {
677
994
  this.emitProgress({
678
- total: files.length,
679
- current,
680
- currentFile: relative(rootPath, filePath),
681
- phase: "parsing"
995
+ phase: "parsing",
996
+ total: filesToIndex.length,
997
+ current: indexed + 1,
998
+ currentFile: relative(rootPath, file.path)
682
999
  });
683
1000
  try {
684
- const analysis = this.parseFile(filePath);
685
- if (analysis) {
686
- fileAnalyses.push(analysis);
687
- this.storage.insertFileAnalysis(analysis);
688
- }
1001
+ await this.parseAndStore(file.path, file.hash, rootPath);
1002
+ indexed++;
689
1003
  } catch (error) {
690
- console.error(`Error parsing ${filePath}:`, error);
1004
+ this.errors.push(`${relative(rootPath, file.path)}: ${error}`);
691
1005
  }
692
1006
  }
693
1007
  this.emitProgress({
694
- total: files.length,
695
- current: files.length,
696
- currentFile: "",
697
- phase: "resolving"
1008
+ phase: "resolving",
1009
+ total: filesToIndex.length,
1010
+ current: filesToIndex.length,
1011
+ currentFile: ""
698
1012
  });
699
- this.resolveReferences();
700
- const languages = {};
701
- for (const analysis of fileAnalyses) {
702
- languages[analysis.language] = (languages[analysis.language] || 0) + 1;
703
- }
1013
+ this.resolveAllCalls();
704
1014
  const stats = this.storage.getStats();
705
- this.storage.setMeta("rootPath", rootPath);
706
- this.storage.setMeta("analyzedAt", (/* @__PURE__ */ new Date()).toISOString());
707
- this.storage.setMeta("totalFiles", String(files.length));
708
1015
  this.emitProgress({
709
- total: files.length,
710
- current: files.length,
711
- currentFile: "",
712
- phase: "complete"
1016
+ phase: "complete",
1017
+ total: filesToIndex.length,
1018
+ current: filesToIndex.length,
1019
+ currentFile: ""
713
1020
  });
714
1021
  return {
715
- rootPath,
716
- files: fileAnalyses,
717
- totalNodes: stats.totalNodes,
718
- totalEdges: stats.totalEdges,
719
- languages,
720
- analyzedAt: (/* @__PURE__ */ new Date()).toISOString()
1022
+ indexed,
1023
+ skipped,
1024
+ resolved: stats.resolvedCalls,
1025
+ unresolved: stats.unresolvedCalls,
1026
+ errors: this.errors
721
1027
  };
722
1028
  }
723
1029
  async discoverFiles(rootPath) {
724
- const patterns = this.config.include;
725
- const ignorePatterns = this.config.exclude;
726
1030
  const allFiles = [];
727
- for (const pattern of patterns) {
1031
+ for (const pattern of this.config.include) {
728
1032
  const matches = await glob(pattern, {
729
1033
  cwd: rootPath,
730
1034
  absolute: true,
731
- ignore: ignorePatterns,
1035
+ ignore: this.config.exclude,
732
1036
  nodir: true
733
1037
  });
734
1038
  allFiles.push(...matches);
735
1039
  }
736
- const supportedExts = new Set(parserRegistry.getSupportedExtensions());
1040
+ const supportedExts = /* @__PURE__ */ new Set([".py", ".ts", ".tsx", ".js", ".jsx"]);
737
1041
  const uniqueFiles = [...new Set(allFiles)].filter((f) => {
738
- const ext = f.substring(f.lastIndexOf("."));
739
- return supportedExts.has(ext);
1042
+ const ext = extname(f);
1043
+ if (!supportedExts.has(ext)) return false;
1044
+ try {
1045
+ const stats = statSync(f);
1046
+ if (stats.size > 2e5) return false;
1047
+ } catch {
1048
+ return false;
1049
+ }
1050
+ return true;
740
1051
  });
741
1052
  return uniqueFiles.sort();
742
1053
  }
743
- parseFile(filePath) {
744
- const parser = parserRegistry.getForFile(filePath);
1054
+ hashContent(content) {
1055
+ return createHash("sha256").update(content).digest("hex").substring(0, 16);
1056
+ }
1057
+ async parseAndStore(filePath, hash, rootPath) {
1058
+ const language = this.getLanguage(filePath);
1059
+ const content = readFileSync(filePath, "utf-8");
1060
+ const parser = parserRegistry.getByLanguage(language);
745
1061
  if (!parser) {
746
- return null;
1062
+ throw new Error(`No parser found for language: ${language}`);
747
1063
  }
748
- const content = readFileSync(filePath, "utf-8");
749
- return parser.parse(filePath, content);
750
- }
751
- resolveReferences() {
752
- const allEdges = this.storage.getAllEdges();
753
- const allNodes = this.storage.getAllNodes();
754
- const nodeNameMap = /* @__PURE__ */ new Map();
755
- for (const node of allNodes) {
756
- const existing = nodeNameMap.get(node.name) || [];
757
- existing.push(node.id);
758
- nodeNameMap.set(node.name, existing);
1064
+ const analysis = parser.parse(filePath, content);
1065
+ this.storage.deleteFileData(filePath);
1066
+ const fileExports = {};
1067
+ for (const node of analysis.nodes) {
1068
+ const nodeHash = this.hashContent(`${node.name}:${node.startLine}:${node.endLine}`);
1069
+ const nodeWithHash = { ...node, hash: nodeHash };
1070
+ this.storage.insertNode(nodeWithHash);
1071
+ if (node.type === "function" || node.type === "class") {
1072
+ fileExports[node.name] = node.id;
1073
+ this.storage.registerExport(filePath, node.name, node.id);
1074
+ }
759
1075
  }
760
- for (const edge of allEdges) {
761
- if (edge.targetId.startsWith("ref:")) {
762
- const refName = edge.targetId.substring(4);
763
- const candidates = nodeNameMap.get(refName);
764
- if (candidates && candidates.length > 0) {
765
- }
1076
+ this.moduleMap.set(filePath, fileExports);
1077
+ for (const edge of analysis.edges) {
1078
+ this.storage.insertEdge(edge);
1079
+ }
1080
+ for (const imp of analysis.imports) {
1081
+ for (const spec of imp.specifiers) {
1082
+ this.storage.registerImport(filePath, spec, imp.source, imp.line);
1083
+ }
1084
+ }
1085
+ if (analysis.variableTypes) {
1086
+ for (const varType of analysis.variableTypes) {
1087
+ this.storage.registerVariableType(
1088
+ varType.variableName,
1089
+ varType.typeName,
1090
+ varType.scopeId,
1091
+ filePath,
1092
+ varType.line
1093
+ );
1094
+ }
1095
+ }
1096
+ if (analysis.databaseOperations) {
1097
+ for (const dbOp of analysis.databaseOperations) {
1098
+ this.storage.insertDatabaseOperation({
1099
+ id: `${dbOp.nodeId}:db:${dbOp.line}`,
1100
+ node_id: dbOp.nodeId,
1101
+ database_type: dbOp.databaseType,
1102
+ operation: dbOp.operation,
1103
+ collection: dbOp.collection,
1104
+ line: dbOp.line
1105
+ });
766
1106
  }
767
1107
  }
1108
+ this.storage.upsertFile(filePath, language, hash);
1109
+ this.processFrameworkPatterns(analysis.nodes, filePath);
768
1110
  }
769
- emitProgress(progress) {
770
- if (this.onProgress) {
771
- this.onProgress(progress);
1111
+ /**
1112
+ * Process framework-specific patterns (FastAPI routes, Depends, etc.)
1113
+ */
1114
+ processFrameworkPatterns(nodes, filePath) {
1115
+ for (const node of nodes) {
1116
+ if (node.type === "function" && node.metadata) {
1117
+ const httpEndpoint = node.metadata.httpEndpoint;
1118
+ if (httpEndpoint) {
1119
+ const endpointId = `${httpEndpoint.method}:${httpEndpoint.path}`;
1120
+ this.storage.insertHttpEndpoint({
1121
+ id: endpointId,
1122
+ method: httpEndpoint.method,
1123
+ path: httpEndpoint.path,
1124
+ handler_node_id: node.id,
1125
+ container_id: void 0,
1126
+ // Will be set when docker-compose is parsed
1127
+ metadata: {
1128
+ filePath,
1129
+ functionName: node.name,
1130
+ line: node.startLine
1131
+ }
1132
+ });
1133
+ }
1134
+ const depends = node.metadata.depends;
1135
+ if (depends) {
1136
+ for (const dep of depends) {
1137
+ const depId = `${node.id}:depends:${dep.parameterName}`;
1138
+ this.storage.insertFrameworkDependency({
1139
+ id: depId,
1140
+ source_node_id: node.id,
1141
+ target_node_id: void 0,
1142
+ // Will be resolved later
1143
+ framework: "fastapi",
1144
+ pattern: "depends",
1145
+ parameter_name: dep.parameterName,
1146
+ line: dep.line,
1147
+ unresolved_name: dep.dependencyName,
1148
+ metadata: {
1149
+ filePath,
1150
+ functionName: node.name
1151
+ }
1152
+ });
1153
+ }
1154
+ }
1155
+ }
772
1156
  }
773
1157
  }
774
- };
775
-
776
- // src/skills/generator.ts
777
- import { mkdirSync, writeFileSync, existsSync } from "fs";
778
- import { join as join2, dirname, basename } from "path";
779
- var SkillGenerator = class {
780
- storage;
781
- config;
782
- constructor(storage, config) {
783
- this.storage = storage;
784
- this.config = config;
1158
+ resolveAllCalls() {
1159
+ const unresolvedEdges = this.storage.db.prepare(`SELECT * FROM edges WHERE target_id LIKE 'ref:%'`).all();
1160
+ for (const edge of unresolvedEdges) {
1161
+ const refName = edge.target_id.replace("ref:", "");
1162
+ const sourceNode = this.storage.getNode(edge.source_id);
1163
+ if (!sourceNode) continue;
1164
+ const resolvedId = this.resolveReference(sourceNode.filePath, refName, edge.source_id);
1165
+ if (resolvedId) {
1166
+ this.storage.insertEdge({
1167
+ id: edge.id,
1168
+ type: edge.type,
1169
+ sourceId: edge.source_id,
1170
+ targetId: resolvedId,
1171
+ metadata: edge.metadata ? JSON.parse(edge.metadata) : void 0
1172
+ });
1173
+ }
1174
+ }
1175
+ this.resolveFrameworkDependencies();
785
1176
  }
786
1177
  /**
787
- * Generate all skills and write to .claude/skills/
1178
+ * Resolve framework dependencies (e.g., FastAPI Depends())
788
1179
  */
789
- generateAll() {
790
- const skills = [];
791
- if (this.config.includeArchitecture) {
792
- skills.push(this.generateArchitectureSkill());
1180
+ resolveFrameworkDependencies() {
1181
+ const unresolvedDeps = this.storage.getAllUnresolvedDependencies();
1182
+ for (const dep of unresolvedDeps) {
1183
+ const sourceNode = this.storage.getNode(dep.source_node_id);
1184
+ if (!sourceNode) continue;
1185
+ const resolvedId = this.resolveReference(sourceNode.filePath, dep.unresolved_name);
1186
+ if (resolvedId) {
1187
+ this.storage.updateFrameworkDependencyTarget(dep.id, resolvedId);
1188
+ this.storage.insertEdge({
1189
+ id: `${dep.id}:edge`,
1190
+ type: "calls",
1191
+ sourceId: dep.source_node_id,
1192
+ targetId: resolvedId,
1193
+ metadata: {
1194
+ framework: "fastapi",
1195
+ pattern: "depends",
1196
+ line: dep.line
1197
+ }
1198
+ });
1199
+ }
793
1200
  }
794
- if (this.config.includePatterns) {
795
- skills.push(this.generatePatternsSkill());
1201
+ }
1202
+ resolveReference(sourceFilePath, refName, scopeNodeId) {
1203
+ if (refName.startsWith("super().") && scopeNodeId) {
1204
+ const methodName = refName.replace("super().", "");
1205
+ const resolvedSuper = this.storage.resolveSuperMethod(methodName, scopeNodeId);
1206
+ if (resolvedSuper) {
1207
+ return resolvedSuper;
1208
+ }
796
1209
  }
797
- if (this.config.includeDependencies) {
798
- skills.push(this.generateDependencySkill());
1210
+ if (refName.includes(".")) {
1211
+ const parts = refName.split(".");
1212
+ if (parts.length === 2) {
1213
+ const [objectName, methodName] = parts;
1214
+ if (scopeNodeId) {
1215
+ const resolvedVarMethod = this.storage.resolveVariableMethod(objectName, methodName, scopeNodeId);
1216
+ if (resolvedVarMethod) {
1217
+ return resolvedVarMethod;
1218
+ }
1219
+ }
1220
+ const resolvedClassMethod = this.storage.resolveClassMethod(objectName, methodName, sourceFilePath);
1221
+ if (resolvedClassMethod) {
1222
+ return resolvedClassMethod;
1223
+ }
1224
+ }
799
1225
  }
800
- skills.push(this.generateImpactAnalysisSkill());
801
- skills.push(this.generateNavigationSkill());
802
- this.writeSkills(skills);
803
- return skills;
804
- }
805
- generateArchitectureSkill() {
806
- const stats = this.storage.getStats();
807
- const rootPath = this.storage.getMeta("rootPath") || "";
808
- const allNodes = this.storage.getNodesByType("file");
809
- const directories = /* @__PURE__ */ new Map();
810
- for (const node of allNodes) {
811
- const relativePath = node.filePath.replace(rootPath, "").replace(/^\//, "");
812
- const parts = relativePath.split("/");
813
- if (parts.length > 1) {
814
- const topDir = parts[0];
815
- directories.set(topDir, (directories.get(topDir) || 0) + 1);
1226
+ const nodesInFile = this.storage.getNodesByFile(sourceFilePath);
1227
+ for (const node of nodesInFile) {
1228
+ if (node.name === refName) {
1229
+ return node.id;
816
1230
  }
817
1231
  }
818
- const sortedDirs = [...directories.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
819
- const dirList = sortedDirs.map(([dir, count]) => `- \`${dir}/\` (${count} files)`).join("\n");
820
- const langList = Object.entries(stats.languages).map(([lang, count]) => `- ${lang}: ${count} files`).join("\n");
821
- const content = `---
822
- name: architecture
823
- description: Overview of ${this.config.projectName} architecture and structure
824
- user-invocable: true
825
- ---
826
-
827
- # ${this.config.projectName} Architecture
828
-
829
- ## Project Statistics
830
- - **Total files**: ${stats.totalFiles}
831
- - **Functions/Methods**: ${stats.nodesByType.function || 0} functions, ${stats.nodesByType.method || 0} methods
832
- - **Classes**: ${stats.nodesByType.class || 0}
833
- - **Import relationships**: ${stats.edgesByType.imports || 0}
834
- - **Call relationships**: ${stats.edgesByType.calls || 0}
835
-
836
- ## Languages
837
- ${langList}
838
-
839
- ## Directory Structure
840
- ${dirList}
841
-
842
- ## Key Patterns
843
- When working with this codebase:
844
- 1. Check the main directories above for feature organization
845
- 2. Use the \`/impact\` skill to understand changes
846
- 3. Use the \`/navigate\` skill to find specific code
847
-
848
- ## Getting Started
849
- To understand a specific area, ask questions like:
850
- - "What does the ${sortedDirs[0]?.[0] || "src"} directory contain?"
851
- - "How is authentication handled?"
852
- - "What are the main entry points?"
853
- `;
854
- return {
855
- name: "architecture",
856
- description: `Overview of ${this.config.projectName} architecture`,
857
- content,
858
- filePath: join2(this.config.outputDir, "architecture", "SKILL.md")
859
- };
860
- }
861
- generatePatternsSkill() {
862
- const stats = this.storage.getStats();
863
- const classes = this.storage.getNodesByType("class");
864
- const functions = this.storage.getNodesByType("function");
865
- const classDetails = classes.map((cls) => {
866
- const methods = this.storage.getEdgesFrom(cls.id).filter((e) => e.type === "contains");
867
- return { ...cls, methodCount: methods.length };
868
- }).sort((a, b) => b.methodCount - a.methodCount);
869
- const topClasses = classDetails.slice(0, 5);
870
- const classList = topClasses.map((c) => `- \`${c.name}\` (${c.methodCount} methods) - [${basename(c.filePath)}](${c.filePath}#L${c.startLine})`).join("\n");
871
- const callEdges = this.storage.getEdgesByType("calls");
872
- const callCounts = /* @__PURE__ */ new Map();
873
- for (const edge of callEdges) {
874
- const target = edge.targetId.replace("ref:", "");
875
- callCounts.set(target, (callCounts.get(target) || 0) + 1);
1232
+ const resolvedFromImport = this.storage.resolveImport(sourceFilePath, refName);
1233
+ if (resolvedFromImport) {
1234
+ return resolvedFromImport;
876
1235
  }
877
- const mostCalled = [...callCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
878
- const calledList = mostCalled.map(([name, count]) => `- \`${name}\` (called ${count} times)`).join("\n");
879
- const content = `---
880
- name: patterns
881
- description: Common patterns and conventions in ${this.config.projectName}
882
- ---
883
-
884
- # Codebase Patterns
885
-
886
- ## Key Classes
887
- These are the most substantial classes in the codebase:
888
- ${classList || "No classes found"}
889
-
890
- ## Most Used Functions
891
- These functions are called most frequently:
892
- ${calledList || "No call data available"}
893
-
894
- ## Conventions Detected
895
-
896
- ### File Organization
897
- ${stats.nodesByType.file ? `- ${stats.totalFiles} files organized across the project` : ""}
898
- ${stats.languages.typescript ? "- TypeScript is used for type safety" : ""}
899
- ${stats.languages.python ? "- Python follows standard module patterns" : ""}
900
-
901
- ### Code Structure
902
- - Functions: ${stats.nodesByType.function || 0}
903
- - Classes: ${stats.nodesByType.class || 0}
904
- - Methods: ${stats.nodesByType.method || 0}
905
-
906
- ## When Making Changes
907
- 1. Follow existing naming conventions
908
- 2. Check similar files for patterns
909
- 3. Use \`/impact\` to verify affected areas
910
- `;
911
- return {
912
- name: "patterns",
913
- description: `Common patterns in ${this.config.projectName}`,
914
- content,
915
- filePath: join2(this.config.outputDir, "patterns", "SKILL.md")
916
- };
917
- }
918
- generateDependencySkill() {
919
- const content = `---
920
- name: dependencies
921
- description: Explore dependencies and imports in ${this.config.projectName}
922
- context: fork
923
- agent: Explore
924
- ---
925
-
926
- # Dependency Explorer
927
-
928
- When analyzing dependencies for $ARGUMENTS:
929
-
930
- ## Steps
931
- 1. **Find the target file/module**
932
- - Search for files matching the query
933
- - Identify the main module or component
934
-
935
- 2. **Analyze imports**
936
- - What does this file import?
937
- - Are there circular dependencies?
938
-
939
- 3. **Analyze dependents**
940
- - What files import this module?
941
- - How deep is the dependency chain?
942
-
943
- 4. **Provide summary**
944
- - List direct dependencies
945
- - List files that depend on this
946
- - Highlight any concerns (circular deps, too many dependents)
947
-
948
- ## Query the CodeMap database
949
- If the codemap MCP server is available, use these queries:
950
- - Get imports: Query the imports table for the file
951
- - Get dependents: Find files that import this module
952
- - Get call graph: Find functions that call into this module
953
-
954
- ## Output Format
955
- \`\`\`
956
- Dependencies for [target]:
957
-
958
- IMPORTS (what this uses):
959
- - module1 (from ./path)
960
- - module2 (from package)
961
-
962
- IMPORTED BY (what uses this):
963
- - file1.ts (lines X-Y)
964
- - file2.ts (lines X-Y)
965
-
966
- DEPENDENCY DEPTH: N levels
967
- \`\`\`
968
- `;
969
- return {
970
- name: "dependencies",
971
- description: `Explore dependencies in ${this.config.projectName}`,
972
- content,
973
- filePath: join2(this.config.outputDir, "dependencies", "SKILL.md")
974
- };
975
- }
976
- generateImpactAnalysisSkill() {
977
- const content = `---
978
- name: impact
979
- description: Analyze the impact of changes to a file or function
980
- disable-model-invocation: true
981
- ---
982
-
983
- # Impact Analysis
984
-
985
- Analyze what would be affected by changes to $ARGUMENTS.
986
-
987
- ## Analysis Steps
988
-
989
- 1. **Identify the target**
990
- - Find the file, function, or class being modified
991
- - Note its current state and purpose
992
-
993
- 2. **Find direct dependents**
994
- - What files import this module?
995
- - What functions call this function?
996
- - What classes extend this class?
997
-
998
- 3. **Find indirect dependents**
999
- - Follow the chain: if A depends on B, and B is our target, A is affected
1000
- - Go up to 3 levels deep
1001
-
1002
- 4. **Categorize impact**
1003
- - **Breaking changes**: Signature changes, removed exports
1004
- - **Behavioral changes**: Logic changes that may affect callers
1005
- - **Safe changes**: Internal refactors, added functionality
1006
-
1007
- 5. **Generate checklist**
1008
-
1009
- ## Output Format
1010
-
1011
- \`\`\`markdown
1012
- # Impact Analysis: [target]
1013
-
1014
- ## Direct Impact
1015
- - [ ] file1.ts - uses [specific function/import]
1016
- - [ ] file2.ts - extends [class]
1017
-
1018
- ## Indirect Impact
1019
- - [ ] file3.ts - imports file1.ts which uses [target]
1020
-
1021
- ## Recommended Actions
1022
- 1. Update tests in [test files]
1023
- 2. Check [specific callers] for compatibility
1024
- 3. Update documentation in [docs]
1025
-
1026
- ## Risk Level: [LOW/MEDIUM/HIGH]
1027
- [Explanation of risk assessment]
1028
- \`\`\`
1029
- `;
1030
- return {
1031
- name: "impact",
1032
- description: "Analyze impact of code changes",
1033
- content,
1034
- filePath: join2(this.config.outputDir, "impact", "SKILL.md")
1035
- };
1236
+ const matches = this.storage.searchNodesByName(refName);
1237
+ if (matches.length === 1) {
1238
+ return matches[0].id;
1239
+ }
1240
+ return null;
1036
1241
  }
1037
- generateNavigationSkill() {
1038
- const stats = this.storage.getStats();
1039
- const content = `---
1040
- name: navigate
1041
- description: Navigate to specific code in ${this.config.projectName}
1042
- ---
1043
-
1044
- # Code Navigation
1045
-
1046
- Find and navigate to code matching $ARGUMENTS.
1047
-
1048
- ## Available Queries
1049
-
1050
- ### By Type
1051
- - "find class [name]" - Locate a class definition
1052
- - "find function [name]" - Locate a function
1053
- - "find file [pattern]" - Find files matching pattern
1054
- - "find imports of [module]" - Find all imports of a module
1055
-
1056
- ### By Relationship
1057
- - "callers of [function]" - Who calls this function?
1058
- - "callees of [function]" - What does this function call?
1059
- - "subclasses of [class]" - Classes that extend this
1060
- - "implementers of [interface]" - Classes implementing interface
1061
-
1062
- ### By Location
1063
- - "in [directory]" - Filter to specific directory
1064
- - "in [file]" - Show contents of file
1065
-
1066
- ## Codebase Stats
1067
- - ${stats.totalFiles} files to search
1068
- - ${stats.nodesByType.function || 0} functions
1069
- - ${stats.nodesByType.class || 0} classes
1070
- - ${stats.nodesByType.method || 0} methods
1071
-
1072
- ## Output Format
1073
- For each match, provide:
1074
- - File path with line number link
1075
- - Brief description of what it does
1076
- - Related code (callers/callees if relevant)
1077
- `;
1078
- return {
1079
- name: "navigate",
1080
- description: `Navigate ${this.config.projectName} codebase`,
1081
- content,
1082
- filePath: join2(this.config.outputDir, "navigate", "SKILL.md")
1083
- };
1242
+ getLanguage(filePath) {
1243
+ const ext = extname(filePath);
1244
+ if (ext === ".py") return "python";
1245
+ if (ext === ".ts" || ext === ".tsx") return "typescript";
1246
+ if (ext === ".js" || ext === ".jsx") return "javascript";
1247
+ return "unknown";
1084
1248
  }
1085
- writeSkills(skills) {
1086
- for (const skill of skills) {
1087
- const dir = dirname(skill.filePath);
1088
- if (!existsSync(dir)) {
1089
- mkdirSync(dir, { recursive: true });
1090
- }
1091
- writeFileSync(skill.filePath, skill.content, "utf-8");
1249
+ emitProgress(progress) {
1250
+ if (this.onProgress) {
1251
+ this.onProgress(progress);
1092
1252
  }
1093
1253
  }
1094
1254
  };
1095
1255
 
1096
- // src/index.ts
1097
- import { resolve as resolve2, join as join3 } from "path";
1098
- import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
1099
- async function scan(projectPath, options = {}) {
1100
- const rootPath = resolve2(projectPath);
1101
- const outputDir = resolve2(rootPath, ".codemap");
1102
- if (!existsSync2(outputDir)) {
1103
- mkdirSync2(outputDir, { recursive: true });
1104
- }
1105
- const dbPath = join3(outputDir, "graph.db");
1106
- const storage = new GraphStorage(dbPath);
1107
- const config = {
1108
- rootPath,
1109
- include: options.include || DEFAULT_CONFIG.include,
1110
- exclude: options.exclude || DEFAULT_CONFIG.exclude,
1111
- languages: options.languages || DEFAULT_CONFIG.languages,
1112
- dbPath,
1113
- skillsOutputDir: options.skillsOutputDir || join3(rootPath, ".claude", "skills", "codemap")
1114
- };
1115
- const builder = new GraphBuilder(storage, config);
1116
- const analysis = await builder.scan();
1117
- storage.close();
1118
- return { analysis, dbPath };
1119
- }
1120
- function generateSkills(projectPath, projectName) {
1121
- const rootPath = resolve2(projectPath);
1122
- const dbPath = join3(rootPath, ".codemap", "graph.db");
1123
- if (!existsSync2(dbPath)) {
1124
- throw new Error("No graph database found. Run scan() first.");
1125
- }
1126
- const outputDir = join3(rootPath, ".claude", "skills", "codemap");
1127
- const storage = new GraphStorage(dbPath);
1128
- const generator = new SkillGenerator(storage, {
1129
- projectName: projectName || rootPath.split("/").pop() || "project",
1130
- outputDir,
1131
- includeArchitecture: true,
1132
- includePatterns: true,
1133
- includeDependencies: true
1134
- });
1135
- const skills = generator.generateAll();
1136
- storage.close();
1137
- return skills;
1138
- }
1139
- async function scanAndGenerate(projectPath, projectName, options = {}) {
1140
- const scanResult = await scan(projectPath, options);
1141
- const skills = generateSkills(projectPath, projectName);
1142
- return {
1143
- ...scanResult,
1144
- skills
1145
- };
1146
- }
1256
+ // src/types.ts
1257
+ var DEFAULT_CONFIG = {
1258
+ include: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py"],
1259
+ exclude: [
1260
+ "**/node_modules/**",
1261
+ "**/dist/**",
1262
+ "**/build/**",
1263
+ "**/.git/**",
1264
+ "**/venv/**",
1265
+ "**/__pycache__/**",
1266
+ "**/.next/**"
1267
+ ],
1268
+ languages: ["typescript", "javascript", "python"]
1269
+ };
1147
1270
  export {
1148
- BaseParser,
1149
1271
  DEFAULT_CONFIG,
1150
- GraphBuilder,
1151
- GraphStorage,
1152
- JavaScriptParser,
1153
- ParserRegistry,
1154
- PythonParser,
1155
- SkillGenerator,
1156
- TypeScriptParser,
1157
- generateSkills,
1158
- parserRegistry,
1159
- registerAllParsers,
1160
- scan,
1161
- scanAndGenerate
1272
+ FlowBuilder,
1273
+ FlowStorage
1162
1274
  };
1163
1275
  //# sourceMappingURL=index.js.map