autosnippet 3.2.6 → 3.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +16 -1
  2. package/bin/cli.js +7 -0
  3. package/dashboard/dist/assets/index-D5jiDBQG.css +1 -0
  4. package/dashboard/dist/assets/{index-DfHY_3ln.js → index-e5OKj-Ni.js} +38 -38
  5. package/dashboard/dist/index.html +2 -2
  6. package/lib/cli/AiScanService.js +3 -3
  7. package/lib/core/AstAnalyzer.js +26 -4
  8. package/lib/core/analysis/CallEdgeResolver.js +402 -0
  9. package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
  10. package/lib/core/analysis/CallSiteExtractor.js +629 -0
  11. package/lib/core/analysis/DataFlowInferrer.js +57 -0
  12. package/lib/core/analysis/ImportPathResolver.js +189 -0
  13. package/lib/core/analysis/ImportRecord.js +105 -0
  14. package/lib/core/analysis/SymbolTableBuilder.js +211 -0
  15. package/lib/core/ast/ProjectGraph.js +8 -0
  16. package/lib/core/ast/lang-dart.js +352 -5
  17. package/lib/core/ast/lang-go.js +212 -10
  18. package/lib/core/ast/lang-java.js +205 -1
  19. package/lib/core/ast/lang-kotlin.js +330 -1
  20. package/lib/core/ast/lang-python.js +31 -2
  21. package/lib/core/ast/lang-rust.js +284 -3
  22. package/lib/core/ast/lang-swift.js +180 -1
  23. package/lib/core/ast/lang-typescript.js +290 -1
  24. package/lib/external/mcp/McpServer.js +1 -0
  25. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +21 -0
  26. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +5 -4
  27. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
  28. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +70 -4
  29. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +95 -1
  30. package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
  31. package/lib/external/mcp/handlers/bootstrap-internal.js +17 -6
  32. package/lib/external/mcp/handlers/consolidated.js +9 -0
  33. package/lib/external/mcp/handlers/guard.js +3 -3
  34. package/lib/external/mcp/handlers/structure.js +62 -0
  35. package/lib/external/mcp/handlers/task.js +182 -10
  36. package/lib/external/mcp/handlers/wiki-external.js +66 -3
  37. package/lib/external/mcp/tools.js +36 -1
  38. package/lib/http/HttpServer.js +4 -0
  39. package/lib/http/routes/remote.js +1138 -0
  40. package/lib/http/routes/task.js +1 -0
  41. package/lib/infrastructure/database/migrations/003_add_remote_commands.js +27 -0
  42. package/lib/injection/ServiceContainer.js +6 -11
  43. package/lib/platform/ios/index.js +2 -2
  44. package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
  45. package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
  46. package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
  47. package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
  48. package/lib/service/chat/ChatAgent.js +1 -1
  49. package/lib/service/chat/ChatAgentPrompts.js +13 -1
  50. package/lib/service/chat/ExplorationTracker.js +52 -8
  51. package/lib/service/chat/HandoffProtocol.js +19 -1
  52. package/lib/service/chat/WorkingMemory.js +3 -1
  53. package/lib/service/chat/memory/ActiveContext.js +3 -1
  54. package/lib/service/chat/memory/SessionStore.js +4 -3
  55. package/lib/service/chat/tools/ast-graph.js +229 -32
  56. package/lib/service/chat/tools/index.js +6 -1
  57. package/lib/service/chat/tools/infrastructure.js +5 -0
  58. package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
  59. package/lib/service/knowledge/CodeEntityGraph.js +327 -2
  60. package/lib/service/knowledge/KnowledgeService.js +5 -1
  61. package/lib/service/module/ModuleService.js +9 -0
  62. package/lib/service/wiki/WikiGenerator.js +1 -1
  63. package/lib/shared/PathGuard.js +1 -1
  64. package/package.json +12 -1
  65. package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
@@ -6,10 +6,14 @@
6
6
  * 模式: Flutter Widget (Stateless/Stateful/Consumer), Factory, Singleton,
7
7
  * Builder, BLoC/Cubit, Provider/Riverpod, Freezed
8
8
  *
9
+ * Phase 5: 新增 ImportRecord 结构化导入 + extractCallSites 调用点提取
10
+ *
9
11
  * 注意: tree-sitter-dart 目前尚无兼容 tree-sitter ≥0.25 的稳定版。
10
12
  * 已迁移至 web-tree-sitter (WASM),无原生编译依赖。
11
13
  */
12
14
 
15
+ import { ImportRecord } from '../analysis/ImportRecord.js';
16
+
13
17
  function walkDart(root, ctx) {
14
18
  _walkNode(root, ctx, null);
15
19
  }
@@ -22,11 +26,30 @@ function _walkNode(node, ctx, parentClassName) {
22
26
  case 'import_or_export': // tree-sitter-dart import 节点
23
27
  case 'import_specification':
24
28
  case 'library_import': {
25
- const uri = child.namedChildren.find(
26
- (c) => c.type === 'uri' || c.type === 'string_literal' || c.type === 'configurable_uri'
27
- );
28
- if (uri) {
29
- ctx.imports.push(uri.text.replace(/['"]/g, ''));
29
+ // tree-sitter-dart AST 嵌套: import_or_export > library_import > import_specification
30
+ // URI 节点埋在深层,直接从文本中用正则提取更可靠
31
+ const text = child.text;
32
+ const pathMatch = text.match(/import\s+(['"])(.+?)\1/);
33
+ if (pathMatch) {
34
+ const importPath = pathMatch[2];
35
+ // Dart: import 'pkg' as alias
36
+ const asMatch = text.match(/\bas\s+(\w+)/);
37
+ const alias = asMatch ? asMatch[1] : null;
38
+ // Dart: import 'pkg' show A, B
39
+ const showClause = text.match(/\bshow\s+([\w\s,]+)/);
40
+ // Dart: import 'pkg' hide A, B (暂不使用,记录备查)
41
+ // const hideClause = text.match(/\bhide\s+([\w\s,]+)/);
42
+
43
+ let symbols = ['*'];
44
+ let kind = 'namespace';
45
+ if (showClause) {
46
+ symbols = showClause[1].split(',').map((s) => s.trim()).filter(Boolean);
47
+ kind = 'named';
48
+ }
49
+
50
+ ctx.imports.push(
51
+ new ImportRecord(importPath, { symbols, alias, kind })
52
+ );
30
53
  }
31
54
  break;
32
55
  }
@@ -645,6 +668,329 @@ function _maxNesting(node, depth) {
645
668
  return max;
646
669
  }
647
670
 
671
+ // ── Dart Call Site 提取 (Phase 5) ────────────────────────────
672
+
673
+ /**
674
+ * 从 Dart AST root 提取所有调用点
675
+ * 遍历 function_definition / method 中的 body → 各种 invocation 节点
676
+ *
677
+ * @param {TreeSitterNode} root
678
+ * @param {object} ctx
679
+ * @param {string} _lang
680
+ */
681
+ function extractCallSitesDart(root, ctx, _lang) {
682
+ const scopes = _collectDartScopes(root);
683
+ for (const scope of scopes) {
684
+ _extractDartCallSitesFromBody(scope.body, scope.className, scope.methodName, ctx);
685
+ }
686
+ }
687
+
688
+ /**
689
+ * 递归收集 Dart 中所有函数/方法体作用域
690
+ */
691
+ function _collectDartScopes(root) {
692
+ const scopes = [];
693
+
694
+ function visit(node, className) {
695
+ for (let i = 0; i < node.namedChildCount; i++) {
696
+ const child = node.namedChild(i);
697
+
698
+ if (child.type === 'class_definition') {
699
+ const name = child.namedChildren.find(
700
+ (c) => c.type === 'identifier' || c.type === 'type_identifier'
701
+ )?.text;
702
+ const body = child.namedChildren.find((c) => c.type === 'class_body');
703
+ if (body) {
704
+ visit(body, name || className);
705
+ }
706
+ } else if (child.type === 'mixin_declaration') {
707
+ const name = child.namedChildren.find(
708
+ (c) => c.type === 'identifier' || c.type === 'type_identifier'
709
+ )?.text;
710
+ const body = child.namedChildren.find((c) => c.type === 'class_body');
711
+ if (body) {
712
+ visit(body, name || className);
713
+ }
714
+ } else if (child.type === 'extension_declaration') {
715
+ const name = child.namedChildren.find(
716
+ (c) => c.type === 'identifier' || c.type === 'type_identifier'
717
+ )?.text;
718
+ const body = child.namedChildren.find(
719
+ (c) => c.type === 'class_body' || c.type === 'extension_body'
720
+ );
721
+ if (body) {
722
+ visit(body, name || className);
723
+ }
724
+ } else if (
725
+ child.type === 'function_definition' ||
726
+ child.type === 'method_signature' ||
727
+ child.type === 'function_signature'
728
+ ) {
729
+ // 提取方法/函数名
730
+ let name;
731
+ if (child.type === 'method_signature' || child.type === 'function_signature') {
732
+ // method_signature 可能包含嵌套 function_signature
733
+ const funcSig = child.namedChildren.find((c) => c.type === 'function_signature') || child;
734
+ name = funcSig.namedChildren.find(
735
+ (c) => c.type === 'identifier' || c.type === 'function_name'
736
+ )?.text;
737
+ } else {
738
+ name = child.namedChildren.find(
739
+ (c) => c.type === 'identifier' || c.type === 'function_name'
740
+ )?.text;
741
+ }
742
+
743
+ // tree-sitter-dart: function_body 可能是子节点或下一个兄弟节点
744
+ let body = child.namedChildren.find(
745
+ (c) => c.type === 'function_body' || c.type === 'block'
746
+ );
747
+ // 如果 body 不在子节点中,检查下一个兄弟节点 (tree-sitter-dart 的 sibling 结构)
748
+ if (!body && i + 1 < node.namedChildCount) {
749
+ const nextSibling = node.namedChild(i + 1);
750
+ if (nextSibling?.type === 'function_body' || nextSibling?.type === 'block') {
751
+ body = nextSibling;
752
+ i++; // 跳过已消费的 body 节点
753
+ }
754
+ }
755
+ if (name && body) {
756
+ scopes.push({ body, className, methodName: name });
757
+ }
758
+ } else if (child.type === 'getter_signature' || child.type === 'setter_signature') {
759
+ const name = child.namedChildren.find((c) => c.type === 'identifier')?.text;
760
+ let body = child.namedChildren.find(
761
+ (c) => c.type === 'function_body' || c.type === 'block'
762
+ );
763
+ if (!body && i + 1 < node.namedChildCount) {
764
+ const nextSibling = node.namedChild(i + 1);
765
+ if (nextSibling?.type === 'function_body' || nextSibling?.type === 'block') {
766
+ body = nextSibling;
767
+ i++;
768
+ }
769
+ }
770
+ if (name && body) {
771
+ scopes.push({ body, className, methodName: `get_${name}` });
772
+ }
773
+ }
774
+ }
775
+ }
776
+
777
+ visit(root, null);
778
+ return scopes;
779
+ }
780
+
781
+ /**
782
+ * 从 Dart function body 中递归提取调用点
783
+ *
784
+ * tree-sitter-dart 的调用表达式由 **兄弟节点序列** 构成(而非单个 call_expression 节点):
785
+ * Pattern A: identifier + selector("(args)") → 直接调用: func(args)
786
+ * Pattern B: (identifier|this|super) + selector(".method") → 方法调用: obj.method(args)
787
+ * + selector("(args)")
788
+ * 因此需要 sibling-aware scanning,避免逐个 walk 子节点时丢失上下文。
789
+ */
790
+ function _extractDartCallSitesFromBody(bodyNode, className, methodName, ctx) {
791
+ if (!bodyNode) return;
792
+
793
+ const DART_NOISE = new Set([
794
+ 'print', 'debugPrint', 'log',
795
+ 'setState', 'notifyListeners',
796
+ 'List', 'Map', 'Set', 'Future', 'Stream',
797
+ ]);
798
+
799
+ /** 在 selectorNode 的子树中查找 arguments / argument_part */
800
+ function findArgs(selectorNode) {
801
+ return selectorNode.namedChildren.find(
802
+ (c) => c.type === 'arguments' || c.type === 'argument_part',
803
+ );
804
+ }
805
+
806
+ /**
807
+ * 尝试从 parent.namedChild(idx) 开始消费一条调用链。
808
+ * 成功 → 返回消费到的最后一个子节点索引;失败 → 返回 null。
809
+ */
810
+ function tryConsumeCall(parent, idx, startNode, isAwaited) {
811
+ const sib1 = parent.namedChild(idx + 1);
812
+ if (!sib1) return null;
813
+
814
+ // ── Pattern A: identifier + selector("(args)") → 直接调用 ────────
815
+ if (
816
+ (startNode.type === 'identifier' || startNode.type === 'type_identifier') &&
817
+ sib1.type === 'selector' &&
818
+ /^\s*\(/.test(sib1.text)
819
+ ) {
820
+ const callee = startNode.text;
821
+ if (!DART_NOISE.has(callee)) {
822
+ const callType = /^[A-Z]/.test(callee) ? 'constructor' : 'function';
823
+ const args = findArgs(sib1);
824
+ ctx.callSites.push({
825
+ callee,
826
+ callerMethod: methodName,
827
+ callerClass: className,
828
+ callType,
829
+ receiver: null,
830
+ receiverType: callType === 'constructor' ? callee : null,
831
+ argCount: args ? args.namedChildCount : 0,
832
+ line: startNode.startPosition.row + 1,
833
+ isAwait: isAwaited,
834
+ });
835
+ }
836
+ // 递归扫描 selector 内部(处理嵌套调用,如 MyApp() 内的参数调用)
837
+ scanChildren(sib1, false);
838
+ return idx + 1;
839
+ }
840
+
841
+ // ── Pattern B: receiver + methodSelector + argsSelector → 方法调用 ──
842
+ const sib2 = parent.namedChild(idx + 2);
843
+ const isMethodSel =
844
+ sib1.type === 'selector' || sib1.type === 'unconditional_assignable_selector';
845
+ const isArgsSel = sib2?.type === 'selector' && sib2.text.includes('(');
846
+
847
+ if (isMethodSel && isArgsSel) {
848
+ const methodMatch = sib1.text.match(/\.(\w+)/);
849
+ if (methodMatch) {
850
+ const callee = methodMatch[1];
851
+ const receiverText = startNode.text;
852
+ let receiver = receiverText;
853
+ let receiverType = null;
854
+ let callType;
855
+
856
+ if (startNode.type === 'this' || receiver === 'this' || receiver === 'self') {
857
+ receiverType = className;
858
+ callType = 'method';
859
+ } else if (startNode.type === 'super' || receiver === 'super') {
860
+ receiverType = className;
861
+ callType = 'super';
862
+ } else if (/^[A-Z]/.test(receiver)) {
863
+ receiverType = receiver;
864
+ callType = 'static';
865
+ } else {
866
+ callType = 'method';
867
+ }
868
+
869
+ if (!DART_NOISE.has(callee)) {
870
+ const args = findArgs(sib2);
871
+ ctx.callSites.push({
872
+ callee,
873
+ callerMethod: methodName,
874
+ callerClass: className,
875
+ callType,
876
+ receiver,
877
+ receiverType,
878
+ argCount: args ? args.namedChildCount : 0,
879
+ line: startNode.startPosition.row + 1,
880
+ isAwait: isAwaited,
881
+ });
882
+ }
883
+ // 递归扫描 argsSelector 内部
884
+ scanChildren(sib2, false);
885
+ return idx + 2;
886
+ }
887
+ }
888
+
889
+ return null; // 未匹配任何模式
890
+ }
891
+
892
+ /**
893
+ * 以 sibling-aware 方式扫描 node 的 namedChildren。
894
+ * 当发现调用起始节点(identifier / this / super)时,尝试消费完整调用链并跳过已消费兄弟。
895
+ */
896
+ function scanChildren(node, isAwaited) {
897
+ for (let i = 0; i < node.namedChildCount; i++) {
898
+ const child = node.namedChild(i);
899
+
900
+ // await → 标记子树为 awaited
901
+ if (child.type === 'await_expression') {
902
+ scanChildren(child, true);
903
+ continue;
904
+ }
905
+
906
+ // function_expression_invocation / method_invocation (部分 grammar 变体)
907
+ if (child.type === 'function_expression_invocation' || child.type === 'method_invocation') {
908
+ _processDartCall(child, className, methodName, ctx, isAwaited, DART_NOISE);
909
+ const args = child.namedChildren.find(
910
+ (c) => c.type === 'arguments' || c.type === 'argument_part',
911
+ );
912
+ if (args) scanChildren(args, false);
913
+ continue;
914
+ }
915
+
916
+ // 尝试从当前位置消费调用模式
917
+ const isCallStarter =
918
+ child.type === 'identifier' ||
919
+ child.type === 'type_identifier' ||
920
+ child.type === 'this' ||
921
+ child.type === 'super';
922
+
923
+ if (isCallStarter) {
924
+ const consumed = tryConsumeCall(node, i, child, isAwaited);
925
+ if (consumed !== null) {
926
+ i = consumed; // 跳过已消费的兄弟
927
+ continue;
928
+ }
929
+ }
930
+
931
+ // Dart cascade: obj..method1()..method2()
932
+ if (child.type === 'cascade_section') {
933
+ _processDartCall(child, className, methodName, ctx, isAwaited, DART_NOISE);
934
+ scanChildren(child, false);
935
+ continue;
936
+ }
937
+
938
+ // 未匹配 → 递归进入子节点
939
+ scanChildren(child, isAwaited);
940
+ }
941
+ }
942
+
943
+ scanChildren(bodyNode, false);
944
+ }
945
+
946
+ /**
947
+ * 处理 Dart 函数/方法调用节点
948
+ */
949
+ function _processDartCall(node, className, methodName, ctx, isAwaited, DART_NOISE) {
950
+ const text = node.text || '';
951
+ const callMatch = text.match(/^(?:(\w[\w.]*?)\.)?(\w+)\s*\(/);
952
+ if (!callMatch) return;
953
+
954
+ const receiverText = callMatch[1] || null;
955
+ const callee = callMatch[2];
956
+
957
+ if (DART_NOISE.has(callee)) return;
958
+
959
+ let receiver = receiverText;
960
+ let receiverType = null;
961
+ let callType;
962
+
963
+ if (receiver === 'this' || receiver === 'super') {
964
+ receiverType = className;
965
+ callType = receiver === 'super' ? 'super' : 'method';
966
+ } else if (receiver && /^[A-Z]/.test(receiver)) {
967
+ receiverType = receiver;
968
+ callType = 'static';
969
+ } else if (receiver) {
970
+ callType = 'method';
971
+ } else {
972
+ callType = /^[A-Z]/.test(callee) ? 'constructor' : 'function';
973
+ if (callType === 'constructor') receiverType = callee;
974
+ }
975
+
976
+ const args = node.namedChildren.find(
977
+ (c) => c.type === 'arguments' || c.type === 'argument_part'
978
+ );
979
+ const argCount = args ? args.namedChildCount : 0;
980
+
981
+ ctx.callSites.push({
982
+ callee,
983
+ callerMethod: methodName,
984
+ callerClass: className,
985
+ callType,
986
+ receiver,
987
+ receiverType,
988
+ argCount,
989
+ line: node.startPosition.row + 1,
990
+ isAwait: isAwaited,
991
+ });
992
+ }
993
+
648
994
  // ── Plugin Export ────────────────────────────────────────────
649
995
 
650
996
  let _grammar = null;
@@ -659,5 +1005,6 @@ export const plugin = {
659
1005
  getGrammar,
660
1006
  walk: walkDart,
661
1007
  detectPatterns: detectDartPatterns,
1008
+ extractCallSites: extractCallSitesDart,
662
1009
  extensions: ['.dart'],
663
1010
  };
@@ -5,8 +5,12 @@
5
5
  * 提取: struct, interface, method (with receiver), function, field, import
6
6
  * 模式: Singleton (sync.Once), Factory (New*), Constructor (New*),
7
7
  * Goroutine, Channel, Middleware (http.Handler chain)
8
+ *
9
+ * Phase 5: 新增 ImportRecord 结构化导入 + extractCallSites 调用点提取
8
10
  */
9
11
 
12
+ import { ImportRecord } from '../analysis/ImportRecord.js';
13
+
10
14
  function walkGo(root, ctx) {
11
15
  for (let i = 0; i < root.namedChildCount; i++) {
12
16
  const child = root.namedChild(i);
@@ -27,22 +31,16 @@ function walkGo(root, ctx) {
27
31
  for (let j = 0; j < specList.namedChildCount; j++) {
28
32
  const spec = specList.namedChild(j);
29
33
  if (spec.type === 'import_spec') {
30
- const strLit = spec.namedChildren.find(
31
- (c) => c.type === 'interpreted_string_literal'
32
- );
33
- if (strLit) {
34
- ctx.imports.push(strLit.text.replace(/"/g, ''));
35
- }
34
+ const rec = _parseGoImportSpec(spec);
35
+ if (rec) ctx.imports.push(rec);
36
36
  }
37
37
  }
38
38
  } else {
39
39
  // 单行 import
40
40
  const spec = child.namedChildren.find((c) => c.type === 'import_spec');
41
41
  if (spec) {
42
- const strLit = spec.namedChildren.find((c) => c.type === 'interpreted_string_literal');
43
- if (strLit) {
44
- ctx.imports.push(strLit.text.replace(/"/g, ''));
45
- }
42
+ const rec = _parseGoImportSpec(spec);
43
+ if (rec) ctx.imports.push(rec);
46
44
  }
47
45
  }
48
46
  break;
@@ -494,6 +492,209 @@ function _maxNesting(node, depth) {
494
492
  return max;
495
493
  }
496
494
 
495
+ // ── Go Import 解析 ──────────────────────────────────────────
496
+
497
+ /**
498
+ * 解析 Go import_spec 节点为 ImportRecord
499
+ *
500
+ * Go import 语法:
501
+ * import "fmt" → namespace, alias='fmt'
502
+ * import alias "pkg/path" → namespace, alias=alias
503
+ * import . "pkg/path" → named (dot import, 类似全部导入)
504
+ * import _ "pkg/path" → side-effect
505
+ *
506
+ * @param {TreeSitterNode} spec — import_spec 节点
507
+ * @returns {ImportRecord|null}
508
+ */
509
+ function _parseGoImportSpec(spec) {
510
+ const strLit = spec.namedChildren.find(
511
+ (c) => c.type === 'interpreted_string_literal'
512
+ );
513
+ if (!strLit) return null;
514
+
515
+ const importPath = strLit.text.replace(/"/g, '');
516
+ const aliasNode = spec.namedChildren.find(
517
+ (c) => c.type === 'package_identifier' || c.type === 'dot' || c.type === 'blank_identifier'
518
+ || (c.type === 'identifier' && (c.text === '.' || c.text === '_'))
519
+ );
520
+
521
+ if (aliasNode) {
522
+ if (aliasNode.text === '.' || aliasNode.type === 'dot') {
523
+ // dot import: import . "pkg" → all exports available
524
+ return new ImportRecord(importPath, { symbols: ['*'], kind: 'named' });
525
+ }
526
+ if (aliasNode.text === '_' || aliasNode.type === 'blank_identifier') {
527
+ // blank import: side-effect only
528
+ return new ImportRecord(importPath, { symbols: [], kind: 'side-effect' });
529
+ }
530
+ // explicit alias: import alias "pkg/path"
531
+ return new ImportRecord(importPath, {
532
+ symbols: ['*'],
533
+ alias: aliasNode.text,
534
+ kind: 'namespace',
535
+ });
536
+ }
537
+
538
+ // default: import "pkg/path" → alias is last segment of path
539
+ const parts = importPath.split('/');
540
+ const defaultAlias = parts[parts.length - 1];
541
+ return new ImportRecord(importPath, {
542
+ symbols: ['*'],
543
+ alias: defaultAlias,
544
+ kind: 'namespace',
545
+ });
546
+ }
547
+
548
+ // ── Go Call Site 提取 (Phase 5) ─────────────────────────────
549
+
550
+ /**
551
+ * 从 Go AST root 提取所有调用点
552
+ * 遍历 function_declaration / method_declaration 中的 block → call_expression
553
+ *
554
+ * @param {TreeSitterNode} root
555
+ * @param {object} ctx
556
+ * @param {string} _lang
557
+ */
558
+ function extractCallSitesGo(root, ctx, _lang) {
559
+ const scopes = _collectGoScopes(root);
560
+ for (const scope of scopes) {
561
+ _extractGoCallSitesFromBody(scope.body, scope.className, scope.methodName, ctx);
562
+ }
563
+ }
564
+
565
+ /**
566
+ * 收集 Go 中所有函数/方法作用域
567
+ */
568
+ function _collectGoScopes(root) {
569
+ const scopes = [];
570
+ for (let i = 0; i < root.namedChildCount; i++) {
571
+ const child = root.namedChild(i);
572
+
573
+ if (child.type === 'function_declaration') {
574
+ const name = child.namedChildren.find((c) => c.type === 'identifier')?.text;
575
+ const body = child.namedChildren.find((c) => c.type === 'block');
576
+ if (name && body) {
577
+ scopes.push({ body, className: null, methodName: name });
578
+ }
579
+ } else if (child.type === 'method_declaration') {
580
+ const name = child.namedChildren.find((c) => c.type === 'field_identifier')?.text;
581
+ const body = child.namedChildren.find((c) => c.type === 'block');
582
+ // 提取 receiver type
583
+ const paramLists = child.namedChildren.filter((c) => c.type === 'parameter_list');
584
+ let receiverType = null;
585
+ if (paramLists[0]) {
586
+ const paramDecl = paramLists[0].namedChildren.find((c) => c.type === 'parameter_declaration');
587
+ if (paramDecl) {
588
+ const pointer = paramDecl.namedChildren.find((c) => c.type === 'pointer_type');
589
+ if (pointer) {
590
+ receiverType = pointer.namedChildren.find((c) => c.type === 'type_identifier')?.text;
591
+ } else {
592
+ receiverType = paramDecl.namedChildren.find((c) => c.type === 'type_identifier')?.text;
593
+ }
594
+ }
595
+ }
596
+ if (name && body) {
597
+ scopes.push({ body, className: receiverType, methodName: name });
598
+ }
599
+ }
600
+ }
601
+ return scopes;
602
+ }
603
+
604
+ /**
605
+ * 从 Go block 中递归提取调用点
606
+ */
607
+ function _extractGoCallSitesFromBody(bodyNode, className, methodName, ctx) {
608
+ if (!bodyNode) return;
609
+
610
+ const GO_NOISE = new Set([
611
+ 'fmt', 'log', 'errors', 'strings', 'strconv', 'math', 'sort',
612
+ 'time', 'sync', 'context', 'reflect', 'unsafe', 'os', 'io',
613
+ 'bytes', 'bufio', 'regexp', 'path', 'filepath', 'encoding',
614
+ ]);
615
+
616
+ function walk(node) {
617
+ if (!node || node.type === 'ERROR' || node.isMissing) return;
618
+
619
+ if (node.type === 'call_expression') {
620
+ const func = node.namedChildren[0];
621
+ if (!func) { walkChildren(node); return; }
622
+
623
+ let callee, receiver = null, receiverType = null, callType;
624
+
625
+ if (func.type === 'selector_expression') {
626
+ // pkg.Func() or obj.Method()
627
+ const parts = func.text.split('.');
628
+ if (parts.length >= 2) {
629
+ receiver = parts.slice(0, -1).join('.');
630
+ callee = parts[parts.length - 1];
631
+ callType = 'method';
632
+ // Go: uppercase receiver might be package name → static
633
+ if (receiver && /^[a-z]/.test(receiver) && !GO_NOISE.has(receiver)) {
634
+ receiverType = null; // instance method
635
+ } else if (GO_NOISE.has(receiver)) {
636
+ walkChildren(node); return; // skip noise
637
+ } else {
638
+ receiverType = receiver;
639
+ callType = 'static';
640
+ }
641
+ } else {
642
+ callee = func.text;
643
+ callType = 'function';
644
+ }
645
+ } else if (func.type === 'identifier') {
646
+ callee = func.text;
647
+ // Go: uppercase = exported, New* = constructor pattern
648
+ if (/^New[A-Z]/.test(callee)) {
649
+ callType = 'constructor';
650
+ receiverType = callee.slice(3); // NewUserService → UserService
651
+ } else {
652
+ callType = 'function';
653
+ }
654
+ } else {
655
+ callee = func.text?.slice(0, 80) || 'unknown';
656
+ callType = 'function';
657
+ }
658
+
659
+ // 计算参数数量
660
+ const args = node.namedChildren.find((c) => c.type === 'argument_list');
661
+ const argCount = args ? args.namedChildCount : 0;
662
+
663
+ ctx.callSites.push({
664
+ callee,
665
+ callerMethod: methodName,
666
+ callerClass: className,
667
+ callType,
668
+ receiver,
669
+ receiverType,
670
+ argCount,
671
+ line: node.startPosition.row + 1,
672
+ isAwait: false, // Go 不使用 await
673
+ });
674
+
675
+ // 遍历参数中的嵌套调用
676
+ if (args) { walkChildren(args); }
677
+ return;
678
+ }
679
+
680
+ // go goroutine: go func() — 异步调用
681
+ if (node.type === 'go_statement') {
682
+ walkChildren(node);
683
+ return;
684
+ }
685
+
686
+ walkChildren(node);
687
+ }
688
+
689
+ function walkChildren(node) {
690
+ for (let i = 0; i < node.namedChildCount; i++) {
691
+ walk(node.namedChild(i));
692
+ }
693
+ }
694
+
695
+ walk(bodyNode);
696
+ }
697
+
497
698
  // ── Plugin Export ────────────────────────────────────────────
498
699
 
499
700
  let _grammar = null;
@@ -508,5 +709,6 @@ export const plugin = {
508
709
  getGrammar,
509
710
  walk: walkGo,
510
711
  detectPatterns: detectGoPatterns,
712
+ extractCallSites: extractCallSitesGo,
511
713
  extensions: ['.go'],
512
714
  };