autosnippet 3.0.0 → 3.0.2

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 (290) hide show
  1. package/README.md +230 -324
  2. package/bin/api-server.js +1 -1
  3. package/bin/cli.js +204 -244
  4. package/bin/mcp-server.js +5 -3
  5. package/config/knowledge-base.config.js +132 -132
  6. package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
  7. package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
  8. package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
  9. package/dashboard/dist/index.html +3 -3
  10. package/lib/bootstrap.js +8 -8
  11. package/lib/cli/AiScanService.js +86 -40
  12. package/lib/cli/KnowledgeSyncService.js +113 -74
  13. package/lib/cli/SetupService.js +439 -277
  14. package/lib/cli/UpgradeService.js +63 -100
  15. package/lib/core/AstAnalyzer.js +276 -597
  16. package/lib/core/ast/ProjectGraph.js +101 -40
  17. package/lib/core/ast/ensure-grammars.js +232 -0
  18. package/lib/core/ast/index.js +115 -0
  19. package/lib/core/ast/lang-dart.js +661 -0
  20. package/lib/core/ast/lang-go.js +530 -0
  21. package/lib/core/ast/lang-java.js +435 -0
  22. package/lib/core/ast/lang-javascript.js +272 -0
  23. package/lib/core/ast/lang-kotlin.js +423 -0
  24. package/lib/core/ast/lang-objc.js +388 -0
  25. package/lib/core/ast/lang-python.js +371 -0
  26. package/lib/core/ast/lang-swift.js +337 -0
  27. package/lib/core/ast/lang-typescript.js +503 -0
  28. package/lib/core/capability/CapabilityProbe.js +18 -9
  29. package/lib/core/constitution/Constitution.js +2 -3
  30. package/lib/core/constitution/ConstitutionValidator.js +65 -24
  31. package/lib/core/discovery/DartDiscoverer.js +534 -0
  32. package/lib/core/discovery/DiscovererRegistry.js +83 -0
  33. package/lib/core/discovery/GenericDiscoverer.js +225 -0
  34. package/lib/core/discovery/GoDiscoverer.js +541 -0
  35. package/lib/core/discovery/JvmDiscoverer.js +506 -0
  36. package/lib/core/discovery/NodeDiscoverer.js +466 -0
  37. package/lib/core/discovery/ProjectDiscoverer.js +93 -0
  38. package/lib/core/discovery/PythonDiscoverer.js +338 -0
  39. package/lib/core/discovery/SpmDiscoverer.js +5 -0
  40. package/lib/core/discovery/index.js +53 -0
  41. package/lib/core/enhancement/EnhancementPack.js +71 -0
  42. package/lib/core/enhancement/EnhancementRegistry.js +47 -0
  43. package/lib/core/enhancement/android-enhancement.js +102 -0
  44. package/lib/core/enhancement/django-enhancement.js +70 -0
  45. package/lib/core/enhancement/fastapi-enhancement.js +63 -0
  46. package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
  47. package/lib/core/enhancement/go-web-enhancement.js +201 -0
  48. package/lib/core/enhancement/index.js +65 -0
  49. package/lib/core/enhancement/node-server-enhancement.js +88 -0
  50. package/lib/core/enhancement/react-enhancement.js +86 -0
  51. package/lib/core/enhancement/spring-enhancement.js +112 -0
  52. package/lib/core/enhancement/vue-enhancement.js +96 -0
  53. package/lib/core/gateway/Gateway.js +8 -9
  54. package/lib/core/gateway/GatewayActionRegistry.js +1 -1
  55. package/lib/core/permission/PermissionManager.js +12 -8
  56. package/lib/domain/index.js +13 -9
  57. package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
  58. package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
  59. package/lib/domain/knowledge/Lifecycle.js +22 -22
  60. package/lib/domain/knowledge/index.js +9 -12
  61. package/lib/domain/knowledge/values/Constraints.js +31 -21
  62. package/lib/domain/knowledge/values/Content.js +21 -13
  63. package/lib/domain/knowledge/values/Quality.js +31 -18
  64. package/lib/domain/knowledge/values/Reasoning.js +20 -12
  65. package/lib/domain/knowledge/values/Relations.js +37 -25
  66. package/lib/domain/knowledge/values/Stats.js +18 -12
  67. package/lib/domain/knowledge/values/index.js +4 -3
  68. package/lib/domain/snippet/Snippet.js +35 -10
  69. package/lib/external/ai/AiFactory.js +48 -16
  70. package/lib/external/ai/AiProvider.js +184 -90
  71. package/lib/external/ai/providers/ClaudeProvider.js +25 -12
  72. package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
  73. package/lib/external/ai/providers/MockProvider.js +9 -3
  74. package/lib/external/ai/providers/OpenAiProvider.js +51 -29
  75. package/lib/external/mcp/McpServer.js +66 -36
  76. package/lib/external/mcp/errorHandler.js +23 -11
  77. package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
  78. package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
  79. package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
  80. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
  81. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
  82. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
  83. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
  84. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
  85. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
  86. package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
  87. package/lib/external/mcp/handlers/bootstrap.js +691 -168
  88. package/lib/external/mcp/handlers/browse.js +66 -22
  89. package/lib/external/mcp/handlers/candidate.js +118 -35
  90. package/lib/external/mcp/handlers/consolidated.js +49 -17
  91. package/lib/external/mcp/handlers/guard.js +104 -39
  92. package/lib/external/mcp/handlers/knowledge.js +60 -36
  93. package/lib/external/mcp/handlers/search.js +43 -14
  94. package/lib/external/mcp/handlers/skill.js +120 -45
  95. package/lib/external/mcp/handlers/structure.js +240 -86
  96. package/lib/external/mcp/handlers/system.js +42 -12
  97. package/lib/external/mcp/handlers/wiki.js +58 -33
  98. package/lib/external/mcp/tools.js +306 -123
  99. package/lib/http/HttpServer.js +72 -47
  100. package/lib/http/middleware/RateLimiter.js +5 -3
  101. package/lib/http/middleware/errorHandler.js +6 -1
  102. package/lib/http/middleware/requestLogger.js +14 -3
  103. package/lib/http/middleware/roleResolver.js +30 -23
  104. package/lib/http/routes/ai.js +387 -265
  105. package/lib/http/routes/auth.js +81 -61
  106. package/lib/http/routes/candidates.js +430 -320
  107. package/lib/http/routes/commands.js +289 -189
  108. package/lib/http/routes/extract.js +158 -125
  109. package/lib/http/routes/guardRules.js +309 -217
  110. package/lib/http/routes/knowledge.js +213 -154
  111. package/lib/http/routes/modules.js +578 -0
  112. package/lib/http/routes/monitoring.js +6 -6
  113. package/lib/http/routes/recipes.js +104 -93
  114. package/lib/http/routes/search.js +361 -305
  115. package/lib/http/routes/skills.js +145 -98
  116. package/lib/http/routes/snippets.js +42 -30
  117. package/lib/http/routes/spm.js +3 -405
  118. package/lib/http/routes/violations.js +113 -93
  119. package/lib/http/routes/wiki.js +211 -170
  120. package/lib/http/utils/routeHelpers.js +3 -1
  121. package/lib/http/utils/sse-sessions.js +16 -6
  122. package/lib/http/utils/sse.js +15 -5
  123. package/lib/infrastructure/audit/AuditLogger.js +5 -2
  124. package/lib/infrastructure/audit/AuditStore.js +10 -7
  125. package/lib/infrastructure/cache/CacheService.js +3 -1
  126. package/lib/infrastructure/cache/GraphCache.js +8 -4
  127. package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
  128. package/lib/infrastructure/config/ConfigLoader.js +9 -5
  129. package/lib/infrastructure/config/Defaults.js +30 -10
  130. package/lib/infrastructure/config/Paths.js +28 -8
  131. package/lib/infrastructure/config/TriggerSymbol.js +22 -10
  132. package/lib/infrastructure/database/DatabaseConnection.js +15 -10
  133. package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
  134. package/lib/infrastructure/external/ClipboardManager.js +6 -2
  135. package/lib/infrastructure/external/NativeUi.js +50 -43
  136. package/lib/infrastructure/external/OpenBrowser.js +14 -17
  137. package/lib/infrastructure/external/XcodeAutomation.js +14 -258
  138. package/lib/infrastructure/logging/Logger.js +46 -30
  139. package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
  140. package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
  141. package/lib/infrastructure/paths/HeaderResolver.js +25 -9
  142. package/lib/infrastructure/paths/PathFinder.js +34 -12
  143. package/lib/infrastructure/plugin/PluginManager.js +26 -8
  144. package/lib/infrastructure/realtime/RealtimeService.js +2 -2
  145. package/lib/infrastructure/vector/Chunker.js +22 -7
  146. package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
  147. package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
  148. package/lib/infrastructure/vector/VectorStore.js +28 -10
  149. package/lib/injection/ServiceContainer.js +247 -93
  150. package/lib/platform/ios/index.js +63 -0
  151. package/lib/platform/ios/routes/spm.js +437 -0
  152. package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
  153. package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
  154. package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
  155. package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
  156. package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
  157. package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
  158. package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
  159. package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
  160. package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
  161. package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
  162. package/lib/repository/base/BaseRepository.js +7 -9
  163. package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
  164. package/lib/repository/token/TokenUsageStore.js +4 -2
  165. package/lib/service/automation/ActionPipeline.js +1 -1
  166. package/lib/service/automation/AutomationOrchestrator.js +8 -4
  167. package/lib/service/automation/ContextCollector.js +7 -5
  168. package/lib/service/automation/DirectiveDetector.js +23 -16
  169. package/lib/service/automation/FileWatcher.js +112 -56
  170. package/lib/service/automation/TriggerResolver.js +6 -4
  171. package/lib/service/automation/handlers/AlinkHandler.js +24 -12
  172. package/lib/service/automation/handlers/CreateHandler.js +19 -20
  173. package/lib/service/automation/handlers/DraftHandler.js +14 -8
  174. package/lib/service/automation/handlers/GuardHandler.js +93 -63
  175. package/lib/service/automation/handlers/HeaderHandler.js +1 -6
  176. package/lib/service/automation/handlers/SearchHandler.js +155 -88
  177. package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
  178. package/lib/service/candidate/SimilarityService.js +25 -9
  179. package/lib/service/chat/AnalystAgent.js +50 -24
  180. package/lib/service/chat/CandidateGuardrail.js +143 -17
  181. package/lib/service/chat/ChatAgent.js +759 -243
  182. package/lib/service/chat/ContextWindow.js +116 -71
  183. package/lib/service/chat/ConversationStore.js +77 -36
  184. package/lib/service/chat/EpisodicConsolidator.js +47 -23
  185. package/lib/service/chat/HandoffProtocol.js +98 -22
  186. package/lib/service/chat/Memory.js +34 -14
  187. package/lib/service/chat/ProducerAgent.js +40 -20
  188. package/lib/service/chat/ProjectSemanticMemory.js +109 -78
  189. package/lib/service/chat/ReasoningLayer.js +148 -70
  190. package/lib/service/chat/ReasoningTrace.js +44 -32
  191. package/lib/service/chat/TaskPipeline.js +39 -19
  192. package/lib/service/chat/ToolRegistry.js +48 -29
  193. package/lib/service/chat/WorkingMemory.js +44 -18
  194. package/lib/service/chat/tools.js +1096 -494
  195. package/lib/service/context/RecipeExtractor.js +132 -51
  196. package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
  197. package/lib/service/cursor/KnowledgeCompressor.js +25 -22
  198. package/lib/service/cursor/RulesGenerator.js +13 -7
  199. package/lib/service/cursor/SkillsSyncer.js +77 -27
  200. package/lib/service/cursor/TokenBudget.js +2 -2
  201. package/lib/service/cursor/TopicClassifier.js +54 -20
  202. package/lib/service/guard/ComplianceReporter.js +55 -43
  203. package/lib/service/guard/ExclusionManager.js +67 -29
  204. package/lib/service/guard/GuardCheckEngine.js +381 -86
  205. package/lib/service/guard/GuardFeedbackLoop.js +22 -10
  206. package/lib/service/guard/GuardService.js +29 -19
  207. package/lib/service/guard/RuleLearner.js +55 -23
  208. package/lib/service/guard/SourceFileCollector.js +27 -20
  209. package/lib/service/guard/ViolationsStore.js +43 -38
  210. package/lib/service/knowledge/CodeEntityGraph.js +147 -82
  211. package/lib/service/knowledge/ConfidenceRouter.js +12 -10
  212. package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
  213. package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
  214. package/lib/service/knowledge/KnowledgeService.js +222 -112
  215. package/lib/service/module/ModuleService.js +969 -0
  216. package/lib/service/quality/FeedbackCollector.js +27 -15
  217. package/lib/service/quality/QualityScorer.js +78 -24
  218. package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
  219. package/lib/service/recipe/RecipeParser.js +78 -45
  220. package/lib/service/search/CoarseRanker.js +43 -28
  221. package/lib/service/search/CrossEncoderReranker.js +32 -21
  222. package/lib/service/search/InvertedIndex.js +21 -7
  223. package/lib/service/search/MultiSignalRanker.js +90 -28
  224. package/lib/service/search/RetrievalFunnel.js +45 -24
  225. package/lib/service/search/SearchEngine.js +255 -103
  226. package/lib/service/skills/EventAggregator.js +32 -15
  227. package/lib/service/skills/SignalCollector.js +140 -64
  228. package/lib/service/skills/SkillAdvisor.js +79 -42
  229. package/lib/service/skills/SkillHooks.js +16 -14
  230. package/lib/service/snippet/PlaceholderConverter.js +5 -0
  231. package/lib/service/snippet/SnippetFactory.js +116 -99
  232. package/lib/service/snippet/SnippetInstaller.js +234 -62
  233. package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
  234. package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
  235. package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
  236. package/lib/service/wiki/WikiGenerator.js +637 -263
  237. package/lib/shared/DimensionCopyRegistry.js +472 -0
  238. package/lib/shared/LanguageService.js +399 -0
  239. package/lib/shared/PathGuard.js +45 -28
  240. package/lib/shared/RecipeReadinessChecker.js +72 -12
  241. package/lib/shared/constants.js +41 -41
  242. package/lib/shared/errors/BaseError.js +2 -2
  243. package/lib/shared/errors/index.js +4 -4
  244. package/lib/shared/similarity.js +25 -8
  245. package/lib/shared/token-utils.js +6 -2
  246. package/lib/shared/utils/common.js +12 -4
  247. package/package.json +49 -13
  248. package/scripts/bench-real-projects.mjs +256 -0
  249. package/scripts/build-native-ui.js +30 -30
  250. package/scripts/clear-old-vector-index.js +5 -35
  251. package/scripts/clear-vector-cache.js +7 -37
  252. package/scripts/collect-test-project-stats.mjs +160 -0
  253. package/scripts/diagnose-mcp.js +41 -32
  254. package/scripts/ensure-parse-package.js +6 -9
  255. package/scripts/generate-recipe-drafts.js +116 -77
  256. package/scripts/init-db.js +3 -20
  257. package/scripts/init-snippets.js +305 -0
  258. package/scripts/init-vector-db.js +173 -170
  259. package/scripts/install-cursor-skill.js +148 -104
  260. package/scripts/install-full.js +8 -21
  261. package/scripts/install-vscode-copilot.js +146 -145
  262. package/scripts/migrate-md-to-knowledge.mjs +139 -151
  263. package/scripts/postinstall-safe.js +5 -17
  264. package/scripts/recipe-audit.js +106 -82
  265. package/scripts/release.js +283 -323
  266. package/scripts/setup-mcp-config.js +60 -52
  267. package/scripts/verify-context-api.js +20 -20
  268. package/skills/autosnippet-analysis/SKILL.md +10 -6
  269. package/skills/autosnippet-candidates/SKILL.md +27 -26
  270. package/skills/autosnippet-coldstart/SKILL.md +555 -38
  271. package/skills/autosnippet-concepts/SKILL.md +349 -337
  272. package/skills/autosnippet-create/SKILL.md +5 -5
  273. package/skills/autosnippet-reference-dart/SKILL.md +543 -0
  274. package/skills/autosnippet-reference-go/SKILL.md +539 -0
  275. package/skills/autosnippet-reference-java/SKILL.md +534 -0
  276. package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
  277. package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
  278. package/skills/autosnippet-reference-objc/SKILL.md +29 -6
  279. package/skills/autosnippet-reference-python/SKILL.md +800 -0
  280. package/skills/autosnippet-reference-swift/SKILL.md +70 -14
  281. package/skills/autosnippet-structure/SKILL.md +4 -4
  282. package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
  283. package/templates/recipes-setup/README.md +2 -2
  284. package/templates/recipes-setup/_template.md +1 -1
  285. package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
  286. package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
  287. package/resources/asd-entry/main.swift +0 -159
  288. package/scripts/build-asd-entry.js +0 -51
  289. package/scripts/init-xcode-snippets.js +0 -311
  290. package/template.json +0 -39
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @module AstAnalyzer
3
- * @description 基于 Tree-sitter 的多语言 AST 分析器
3
+ * @description 基于 Tree-sitter 的多语言 AST 分析器(插件注册制)
4
4
  *
5
5
  * 提供结构化代码分析能力:
6
6
  * - 类/协议/扩展 声明与继承关系
@@ -9,22 +9,48 @@
9
9
  * - 设计模式检测(Singleton、Delegate、Factory、Observer)
10
10
  * - 代码结构指标(圈复杂度、嵌套深度、方法行数)
11
11
  *
12
- * 支持语言:Objective-C、Swift(可扩展 JS/TS)
12
+ * 支持语言:通过插件注册 — ObjC、Swift、TypeScript、JavaScript、Python、Java、Kotlin
13
+ * 插件注册入口: lib/core/ast/index.js
13
14
  */
14
15
 
15
- import { createRequire } from 'module';
16
+ import { createRequire } from 'node:module';
17
+
16
18
  const require = createRequire(import.meta.url);
17
19
 
18
- let Parser, LangObjC, LangSwift;
20
+ let Parser;
19
21
 
20
22
  try {
21
23
  Parser = require('tree-sitter');
22
- LangObjC = require('tree-sitter-objc');
23
- LangSwift = require('tree-sitter-swift');
24
24
  } catch {
25
25
  // 在没有 tree-sitter 的环境中优雅降级
26
26
  }
27
27
 
28
+ // ──────────────────────────────────────────────────────────────────
29
+ // 插件注册表
30
+ // ──────────────────────────────────────────────────────────────────
31
+
32
+ /**
33
+ * @typedef {object} LangPlugin
34
+ * @property {Function} getGrammar — () => tree-sitter Language Module (lazy load)
35
+ * @property {Function} walk — (rootNode, ctx) => void (AST 遍历)
36
+ * @property {Function} [detectPatterns] — (root, lang, methods, properties, classes) => Pattern[]
37
+ * @property {string[]} [extensions] — 关联的文件扩展名
38
+ */
39
+
40
+ /** @type {Map<string, LangPlugin>} */
41
+ const _langPlugins = new Map();
42
+
43
+ /**
44
+ * 注册语言 AST 插件
45
+ * @param {string} langId — 语言标识 (e.g. 'objectivec', 'swift', 'typescript')
46
+ * @param {LangPlugin} plugin
47
+ */
48
+ export function registerLanguage(langId, plugin) {
49
+ _langPlugins.set(langId, plugin);
50
+ // 清除 parser cache 以便下次使用新语法
51
+ _parserCache.delete(langId);
52
+ }
53
+
28
54
  // ──────────────────────────────────────────────────────────────────
29
55
  // 公共 API
30
56
  // ──────────────────────────────────────────────────────────────────
@@ -32,49 +58,58 @@ try {
32
58
  /**
33
59
  * 分析单个源文件,返回结构化 AST 摘要
34
60
  * @param {string} source 源代码文本
35
- * @param {string} lang 语言标识 'objectivec' | 'swift'
61
+ * @param {string} lang 语言标识 'objectivec' | 'swift' | 'typescript' | 'javascript' | 'python' | 'java' | 'kotlin' | 'tsx'
36
62
  * @returns {AstSummary | null}
37
63
  */
38
64
  function analyzeFile(source, lang) {
65
+ const plugin = _langPlugins.get(lang);
66
+ if (!plugin) {
67
+ return null; // 无插件 → 优雅降级
68
+ }
69
+
39
70
  const parser = _getParser(lang);
40
- if (!parser) return null;
71
+ if (!parser) {
72
+ return null;
73
+ }
41
74
 
42
75
  const tree = parser.parse(source);
43
76
  const root = tree.rootNode;
44
77
 
45
- const classes = [];
46
- const protocols = [];
47
- const categories = [];
48
- const methods = [];
49
- const properties = [];
50
- const patterns = [];
51
- const imports = [];
78
+ const ctx = {
79
+ classes: [],
80
+ protocols: [],
81
+ categories: [],
82
+ methods: [],
83
+ properties: [],
84
+ patterns: [],
85
+ imports: [],
86
+ exports: [],
87
+ };
52
88
 
53
- if (lang === 'objectivec') {
54
- _walkObjC(root, { classes, protocols, categories, methods, properties, patterns, imports });
55
- } else if (lang === 'swift') {
56
- _walkSwift(root, { classes, protocols, categories, methods, properties, patterns, imports });
57
- }
89
+ plugin.walk(root, ctx);
58
90
 
59
91
  // 构建继承图谱
60
- const inheritanceGraph = _buildInheritanceGraph(classes, protocols, categories);
92
+ const inheritanceGraph = _buildInheritanceGraph(ctx.classes, ctx.protocols, ctx.categories);
61
93
 
62
- // 检测设计模式
63
- const detectedPatterns = _detectPatterns(root, lang, methods, properties, classes);
64
- patterns.push(...detectedPatterns);
94
+ // 检测设计模式(优先使用插件自带的检测器,否则使用通用检测器)
95
+ const detectedPatterns = plugin.detectPatterns
96
+ ? plugin.detectPatterns(root, lang, ctx.methods, ctx.properties, ctx.classes)
97
+ : _detectPatterns(root, lang, ctx.methods, ctx.properties, ctx.classes);
98
+ ctx.patterns.push(...detectedPatterns);
65
99
 
66
100
  // 结构指标
67
- const metrics = _computeMetrics(root, lang, methods);
101
+ const metrics = _computeMetrics(root, lang, ctx.methods);
68
102
 
69
103
  return {
70
104
  lang,
71
- classes,
72
- protocols,
73
- categories,
74
- methods,
75
- properties,
76
- patterns,
77
- imports,
105
+ classes: ctx.classes,
106
+ protocols: ctx.protocols,
107
+ categories: ctx.categories,
108
+ methods: ctx.methods,
109
+ properties: ctx.properties,
110
+ patterns: ctx.patterns,
111
+ imports: ctx.imports,
112
+ exports: ctx.exports,
78
113
  inheritanceGraph,
79
114
  metrics,
80
115
  };
@@ -84,9 +119,10 @@ function analyzeFile(source, lang) {
84
119
  * 批量分析多文件,返回项目级汇总
85
120
  * @param {{ name: string, relativePath: string, content: string }[]} files
86
121
  * @param {string} lang
122
+ * @param {{ preprocessFile?: (content: string, ext: string) => { content: string, lang: string } | null }} [options]
87
123
  * @returns {ProjectAstSummary}
88
124
  */
89
- function analyzeProject(files, lang) {
125
+ function analyzeProject(files, lang, options) {
90
126
  const fileSummaries = [];
91
127
  const allClasses = [];
92
128
  const allProtocols = [];
@@ -94,18 +130,34 @@ function analyzeProject(files, lang) {
94
130
  const allMethods = [];
95
131
  const allPatterns = [];
96
132
  const allImports = [];
133
+ const preprocessFile = options?.preprocessFile;
97
134
 
98
135
  for (const file of files) {
99
- const summary = analyzeFile(file.content, lang);
100
- if (!summary) continue;
136
+ let { content } = file;
137
+ let fileLang = lang;
138
+
139
+ // SFC 预处理: .vue / .svelte 等文件 → 提取 <script> 块再交给 AST
140
+ if (preprocessFile) {
141
+ const ext = file.name ? ('.' + file.name.split('.').pop()) : '';
142
+ const result = preprocessFile(content, ext);
143
+ if (result) {
144
+ content = result.content;
145
+ fileLang = result.lang || lang;
146
+ }
147
+ }
148
+
149
+ const summary = analyzeFile(content, fileLang);
150
+ if (!summary) {
151
+ continue;
152
+ }
101
153
 
102
154
  fileSummaries.push({ file: file.relativePath, ...summary });
103
- allClasses.push(...summary.classes.map(c => ({ ...c, file: file.relativePath })));
104
- allProtocols.push(...summary.protocols.map(p => ({ ...p, file: file.relativePath })));
105
- allCategories.push(...summary.categories.map(c => ({ ...c, file: file.relativePath })));
106
- allMethods.push(...summary.methods.map(m => ({ ...m, file: file.relativePath })));
107
- allPatterns.push(...summary.patterns.map(p => ({ ...p, file: file.relativePath })));
108
- allImports.push(...summary.imports.map(i => ({ path: i, file: file.relativePath })));
155
+ allClasses.push(...summary.classes.map((c) => ({ ...c, file: file.relativePath })));
156
+ allProtocols.push(...summary.protocols.map((p) => ({ ...p, file: file.relativePath })));
157
+ allCategories.push(...summary.categories.map((c) => ({ ...c, file: file.relativePath })));
158
+ allMethods.push(...summary.methods.map((m) => ({ ...m, file: file.relativePath })));
159
+ allPatterns.push(...summary.patterns.map((p) => ({ ...p, file: file.relativePath })));
160
+ allImports.push(...summary.imports.map((i) => ({ path: i, file: file.relativePath })));
109
161
  }
110
162
 
111
163
  // 项目级继承图(跨文件合并)
@@ -114,7 +166,9 @@ function analyzeProject(files, lang) {
114
166
  // 项目级模式统计
115
167
  const patternStats = {};
116
168
  for (const p of allPatterns) {
117
- if (!patternStats[p.type]) patternStats[p.type] = { count: 0, files: [], instances: [] };
169
+ if (!patternStats[p.type]) {
170
+ patternStats[p.type] = { count: 0, files: [], instances: [] };
171
+ }
118
172
  patternStats[p.type].count++;
119
173
  if (!patternStats[p.type].files.includes(p.file)) {
120
174
  patternStats[p.type].files.push(p.file);
@@ -147,7 +201,8 @@ function generateContextForAgent(projectSummary) {
147
201
  const lines = ['## 项目代码结构分析(AST)', ''];
148
202
 
149
203
  // 类型声明概览
150
- const { classes, protocols, categories, inheritanceGraph, patternStats, projectMetrics } = projectSummary;
204
+ const { classes, protocols, categories, inheritanceGraph, patternStats, projectMetrics } =
205
+ projectSummary;
151
206
 
152
207
  lines.push(`### 代码规模`);
153
208
  lines.push(`- 已分析文件: ${projectSummary.fileCount}`);
@@ -169,13 +224,15 @@ function generateContextForAgent(projectSummary) {
169
224
  }
170
225
 
171
226
  // 协议遵循
172
- const conformances = classes.filter(c => c.protocols && c.protocols.length > 0);
227
+ const conformances = classes.filter((c) => c.protocols && c.protocols.length > 0);
173
228
  if (conformances.length > 0) {
174
229
  lines.push(`### 协议遵循`);
175
230
  for (const c of conformances.slice(0, 20)) {
176
- lines.push(`- \`${c.name}\` → ${c.protocols.map(p => '`' + p + '`').join(', ')}`);
231
+ lines.push(`- \`${c.name}\` → ${c.protocols.map((p) => `\`${p}\``).join(', ')}`);
232
+ }
233
+ if (conformances.length > 20) {
234
+ lines.push(`- ... (共 ${conformances.length} 个)`);
177
235
  }
178
- if (conformances.length > 20) lines.push(`- ... (共 ${conformances.length} 个)`);
179
236
  lines.push('');
180
237
  }
181
238
 
@@ -183,10 +240,15 @@ function generateContextForAgent(projectSummary) {
183
240
  if (categories.length > 0) {
184
241
  lines.push(`### Category / Extension`);
185
242
  for (const cat of categories.slice(0, 15)) {
186
- const methodNames = (cat.methods || []).slice(0, 5).map(m => m.name).join(', ');
243
+ const methodNames = (cat.methods || [])
244
+ .slice(0, 5)
245
+ .map((m) => m.name)
246
+ .join(', ');
187
247
  lines.push(`- \`${cat.className}(${cat.categoryName})\` → ${methodNames || '(无方法)'}`);
188
248
  }
189
- if (categories.length > 15) lines.push(`- ... (共 ${categories.length} 个)`);
249
+ if (categories.length > 15) {
250
+ lines.push(`- ... (共 ${categories.length} 个)`);
251
+ }
190
252
  lines.push('');
191
253
  }
192
254
 
@@ -194,7 +256,9 @@ function generateContextForAgent(projectSummary) {
194
256
  if (Object.keys(patternStats).length > 0) {
195
257
  lines.push(`### 检测到的设计模式`);
196
258
  for (const [type, stat] of Object.entries(patternStats)) {
197
- lines.push(`- **${type}**: ${stat.count} 处 (${stat.files.slice(0, 3).join(', ')}${stat.files.length > 3 ? '...' : ''})`);
259
+ lines.push(
260
+ `- **${type}**: ${stat.count} 处 (${stat.files.slice(0, 3).join(', ')}${stat.files.length > 3 ? '...' : ''})`
261
+ );
198
262
  }
199
263
  lines.push('');
200
264
  }
@@ -204,13 +268,17 @@ function generateContextForAgent(projectSummary) {
204
268
  if (projectMetrics.complexMethods.length > 0) {
205
269
  lines.push(`- ⚠️ 高复杂度方法 (cyclomatic > 10):`);
206
270
  for (const m of projectMetrics.complexMethods.slice(0, 5)) {
207
- lines.push(` - \`${m.className || ''}${m.className ? '.' : ''}${m.name}\` (复杂度: ${m.complexity}, ${m.file}:${m.line})`);
271
+ lines.push(
272
+ ` - \`${m.className || ''}${m.className ? '.' : ''}${m.name}\` (复杂度: ${m.complexity}, ${m.file}:${m.line})`
273
+ );
208
274
  }
209
275
  }
210
276
  if (projectMetrics.longMethods.length > 0) {
211
277
  lines.push(`- ⚠️ 过长方法 (> 50 行):`);
212
278
  for (const m of projectMetrics.longMethods.slice(0, 5)) {
213
- lines.push(` - \`${m.className || ''}${m.className ? '.' : ''}${m.name}\` (${m.lines} 行, ${m.file}:${m.line})`);
279
+ lines.push(
280
+ ` - \`${m.className || ''}${m.className ? '.' : ''}${m.name}\` (${m.lines} 行, ${m.file}:${m.line})`
281
+ );
214
282
  }
215
283
  }
216
284
  lines.push('');
@@ -219,20 +287,17 @@ function generateContextForAgent(projectSummary) {
219
287
  }
220
288
 
221
289
  /**
222
- * 检查 Tree-sitter 是否可用
290
+ * 检查 Tree-sitter 是否可用(至少有一个语言插件注册)
223
291
  */
224
292
  function isAvailable() {
225
- return !!(Parser && (LangObjC || LangSwift));
293
+ return !!Parser && _langPlugins.size > 0;
226
294
  }
227
295
 
228
296
  /**
229
297
  * 获取支持的语言列表
230
298
  */
231
299
  function supportedLanguages() {
232
- const langs = [];
233
- if (LangObjC) langs.push('objectivec');
234
- if (LangSwift) langs.push('swift');
235
- return langs;
300
+ return [..._langPlugins.keys()];
236
301
  }
237
302
 
238
303
  // ──────────────────────────────────────────────────────────────────
@@ -242,509 +307,39 @@ function supportedLanguages() {
242
307
  const _parserCache = new Map();
243
308
 
244
309
  function _getParser(lang) {
245
- if (!Parser) return null;
246
- if (_parserCache.has(lang)) return _parserCache.get(lang);
247
-
248
- let langModule;
249
- if (lang === 'objectivec' && LangObjC) langModule = LangObjC;
250
- else if (lang === 'swift' && LangSwift) langModule = LangSwift;
251
- else return null;
252
-
253
- const parser = new Parser();
254
- parser.setLanguage(langModule);
255
- _parserCache.set(lang, parser);
256
- return parser;
257
- }
258
-
259
- // ──────────────────────────────────────────────────────────────────
260
- // 内部实现 — ObjC AST 遍历
261
- // ──────────────────────────────────────────────────────────────────
262
-
263
- function _walkObjC(root, ctx) {
264
- for (let i = 0; i < root.namedChildCount; i++) {
265
- const node = root.namedChild(i);
266
-
267
- switch (node.type) {
268
- case 'preproc_include': {
269
- const pathNode = node.namedChildren.find(c => c.type === 'string_literal' || c.type === 'system_lib_string');
270
- if (pathNode) ctx.imports.push(pathNode.text.replace(/^["<]|[">]$/g, ''));
271
- break;
272
- }
273
-
274
- case 'class_interface': {
275
- const classInfo = _parseObjCInterface(node);
276
- if (classInfo.isCategory) {
277
- ctx.categories.push(classInfo);
278
- } else {
279
- ctx.classes.push(classInfo);
280
- }
281
- // 提取类中的方法和属性声明
282
- for (const child of node.namedChildren) {
283
- if (child.type === 'method_declaration') {
284
- ctx.methods.push(_parseObjCMethodDecl(child, classInfo.name));
285
- } else if (child.type === 'property_declaration') {
286
- ctx.properties.push(_parseObjCProperty(child, classInfo.name));
287
- }
288
- }
289
- break;
290
- }
291
-
292
- case 'protocol_declaration': {
293
- ctx.protocols.push(_parseObjCProtocol(node));
294
- break;
295
- }
296
-
297
- case 'class_implementation': {
298
- const implName = _findIdentifier(node);
299
- // 遍历 implementation_definition 提取方法定义
300
- for (const child of node.namedChildren) {
301
- if (child.type === 'implementation_definition') {
302
- for (const implChild of child.namedChildren) {
303
- if (implChild.type === 'method_definition') {
304
- const m = _parseObjCMethodDef(implChild, implName);
305
- ctx.methods.push(m);
306
- }
307
- }
308
- }
309
- }
310
- break;
311
- }
312
-
313
- case 'category_implementation': {
314
- const catImplName = _findIdentifier(node);
315
- for (const child of node.namedChildren) {
316
- if (child.type === 'implementation_definition') {
317
- for (const implChild of child.namedChildren) {
318
- if (implChild.type === 'method_definition') {
319
- ctx.methods.push(_parseObjCMethodDef(implChild, catImplName));
320
- }
321
- }
322
- }
323
- }
324
- break;
325
- }
326
- }
327
- }
328
- }
329
-
330
- function _parseObjCInterface(node) {
331
- const identifiers = node.namedChildren.filter(c => c.type === 'identifier');
332
- const name = identifiers[0]?.text || 'Unknown';
333
-
334
- // 判断 Category: @interface ClassName (CategoryName)
335
- // tree-sitter-objc 中 category 的第二个 identifier 是 categoryName
336
- const isCategory = node.text.includes('(') && identifiers.length >= 2 &&
337
- node.text.indexOf('(') < node.text.indexOf(identifiers[1].text);
338
-
339
- // superclass: 第二个 identifier(非 category 时)
340
- let superclass = null;
341
- let categoryName = null;
342
- if (isCategory) {
343
- categoryName = identifiers[1]?.text;
344
- } else if (identifiers.length >= 2) {
345
- superclass = identifiers[1]?.text;
346
- }
347
-
348
- // protocols: parameterized_arguments 中的 type_identifier
349
- const protocols = [];
350
- const protoList = node.namedChildren.find(c => c.type === 'parameterized_arguments');
351
- if (protoList) {
352
- for (const child of protoList.namedChildren) {
353
- if (child.type === 'type_name') {
354
- const ti = child.namedChildren.find(c => c.type === 'type_identifier');
355
- if (ti) protocols.push(ti.text);
356
- }
357
- }
358
- }
359
-
360
- const methods = [];
361
- for (const child of node.namedChildren) {
362
- if (child.type === 'method_declaration') {
363
- methods.push(_parseObjCMethodDecl(child, name));
364
- }
365
- }
366
-
367
- const result = {
368
- name,
369
- superclass,
370
- protocols,
371
- isCategory,
372
- line: node.startPosition.row + 1,
373
- endLine: node.endPosition.row + 1,
374
- };
375
- if (isCategory) {
376
- result.className = name;
377
- result.categoryName = categoryName;
378
- result.methods = methods;
379
- }
380
- return result;
381
- }
382
-
383
- function _parseObjCProtocol(node) {
384
- const name = _findIdentifier(node) || 'Unknown';
385
- const inherits = [];
386
- const protoRef = node.namedChildren.find(c => c.type === 'protocol_reference_list');
387
- if (protoRef) {
388
- for (const child of protoRef.namedChildren) {
389
- if (child.type === 'identifier') inherits.push(child.text);
390
- }
391
- }
392
-
393
- const methods = [];
394
- let isOptional = false;
395
- for (const child of node.namedChildren) {
396
- if (child.type === 'qualified_protocol_interface_declaration') {
397
- isOptional = true;
398
- for (const sub of child.namedChildren) {
399
- if (sub.type === 'method_declaration') {
400
- const m = _parseObjCMethodDecl(sub, name);
401
- m.isOptional = true;
402
- methods.push(m);
403
- }
404
- }
405
- } else if (child.type === 'method_declaration') {
406
- const m = _parseObjCMethodDecl(child, name);
407
- m.isOptional = isOptional;
408
- methods.push(m);
409
- }
410
- }
411
-
412
- return {
413
- name,
414
- inherits,
415
- methods,
416
- line: node.startPosition.row + 1,
417
- };
418
- }
419
-
420
- function _parseObjCMethodDecl(node, className) {
421
- const isClassMethod = node.text.trimStart().startsWith('+');
422
- const name = _findIdentifier(node) || 'unknown';
423
-
424
- // 收集参数
425
- const params = [];
426
- for (const child of node.namedChildren) {
427
- if (child.type === 'method_parameter') {
428
- const paramName = _findIdentifier(child);
429
- params.push(paramName || '?');
430
- }
310
+ if (!Parser) {
311
+ return null;
431
312
  }
432
-
433
- // 返回类型
434
- let returnType = 'void';
435
- const methodType = node.namedChildren.find(c => c.type === 'method_type');
436
- if (methodType) {
437
- const tn = methodType.namedChildren.find(c => c.type === 'type_name');
438
- if (tn) {
439
- const ti = tn.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'primitive_type');
440
- if (ti) returnType = ti.text;
441
- }
313
+ if (_parserCache.has(lang)) {
314
+ return _parserCache.get(lang);
442
315
  }
443
316
 
444
- // 构建 ObjC selector
445
- const selector = params.length > 0 ? name + ':' + params.slice(1).map(p => p + ':').join('') : name;
446
-
447
- return {
448
- name,
449
- selector,
450
- className,
451
- isClassMethod,
452
- returnType,
453
- paramCount: params.length,
454
- line: node.startPosition.row + 1,
455
- kind: 'declaration',
456
- };
457
- }
458
-
459
- function _parseObjCMethodDef(node, className) {
460
- const isClassMethod = node.text.trimStart().startsWith('+');
461
- const name = _findIdentifier(node) || 'unknown';
462
-
463
- const params = [];
464
- for (const child of node.namedChildren) {
465
- if (child.type === 'method_parameter') {
466
- const paramName = _findIdentifier(child);
467
- params.push(paramName || '?');
468
- }
469
- }
470
-
471
- // 方法体长度
472
- const body = node.namedChildren.find(c => c.type === 'compound_statement');
473
- const bodyLines = body ? (body.endPosition.row - body.startPosition.row + 1) : 0;
474
-
475
- // 圈复杂度估算
476
- const complexity = body ? _estimateComplexity(body) : 1;
477
-
478
- // 嵌套深度
479
- const nestingDepth = body ? _maxNesting(body, 0) : 0;
480
-
481
- return {
482
- name,
483
- className,
484
- isClassMethod,
485
- paramCount: params.length,
486
- bodyLines,
487
- complexity,
488
- nestingDepth,
489
- line: node.startPosition.row + 1,
490
- kind: 'definition',
491
- };
492
- }
493
-
494
- function _parseObjCProperty(node, className) {
495
- // 属性修饰符
496
- const attrs = [];
497
- const attrDecl = node.namedChildren.find(c => c.type === 'property_attributes_declaration');
498
- if (attrDecl) {
499
- for (const attr of attrDecl.namedChildren) {
500
- if (attr.type === 'property_attribute') {
501
- const id = attr.namedChildren.find(c => c.type === 'identifier');
502
- if (id) attrs.push(id.text);
503
- }
504
- }
317
+ const plugin = _langPlugins.get(lang);
318
+ if (!plugin) {
319
+ return null;
505
320
  }
506
321
 
507
- // 属性名 — 从 struct_declaration > struct_declarator 中找 identifier
508
- let propName = 'unknown';
509
- let propType = 'id';
510
- const structDecl = node.namedChildren.find(c => c.type === 'struct_declaration');
511
- if (structDecl) {
512
- // 类型
513
- const ti = structDecl.namedChildren.find(c => c.type === 'type_identifier');
514
- if (ti) propType = ti.text;
515
-
516
- // 名字 — 在 struct_declarator 深处
517
- const sd = structDecl.namedChildren.find(c => c.type === 'struct_declarator');
518
- if (sd) {
519
- const findName = (n) => {
520
- if (n.type === 'identifier') return n.text;
521
- for (let j = 0; j < n.namedChildCount; j++) {
522
- const r = findName(n.namedChild(j));
523
- if (r) return r;
524
- }
525
- return null;
526
- };
527
- propName = findName(sd) || propName;
322
+ try {
323
+ const grammar = plugin.getGrammar();
324
+ if (!grammar) {
325
+ return null;
528
326
  }
327
+ const parser = new Parser();
328
+ parser.setLanguage(grammar);
329
+ _parserCache.set(lang, parser);
330
+ return parser;
331
+ } catch {
332
+ return null;
529
333
  }
530
-
531
- return {
532
- name: propName,
533
- type: propType,
534
- attributes: attrs,
535
- className,
536
- line: node.startPosition.row + 1,
537
- };
538
334
  }
539
335
 
540
336
  // ──────────────────────────────────────────────────────────────────
541
- // 内部实现 — Swift AST 遍历
337
+ // 内部实现 — ObjC/Swift Walker 已迁移到 ast/lang-objc.js 和 ast/lang-swift.js
338
+ // 通过 ast/index.js 自动注册到 _langPlugins
542
339
  // ──────────────────────────────────────────────────────────────────
543
340
 
544
- function _walkSwift(root, ctx) {
545
- _walkSwiftNode(root, ctx, null);
546
- }
547
-
548
- function _walkSwiftNode(node, ctx, parentClassName) {
549
- for (let i = 0; i < node.namedChildCount; i++) {
550
- const child = node.namedChild(i);
551
-
552
- switch (child.type) {
553
- case 'import_declaration': {
554
- const mod = child.namedChildren.find(c => c.type === 'identifier' || c.type === 'simple_identifier');
555
- if (mod) ctx.imports.push(mod.text);
556
- break;
557
- }
558
-
559
- case 'class_declaration':
560
- case 'struct_declaration':
561
- case 'enum_declaration': {
562
- const classInfo = _parseSwiftTypeDecl(child);
563
- ctx.classes.push(classInfo);
564
-
565
- // 递归遍历类体
566
- const body = child.namedChildren.find(c => c.type === 'class_body' || c.type === 'struct_body' || c.type === 'enum_body');
567
- if (body) _walkSwiftNode(body, ctx, classInfo.name);
568
- break;
569
- }
570
-
571
- case 'protocol_declaration': {
572
- const protoInfo = _parseSwiftProtocol(child);
573
- ctx.protocols.push(protoInfo);
574
- break;
575
- }
576
-
577
- case 'extension_declaration': {
578
- const extInfo = _parseSwiftExtension(child);
579
- ctx.categories.push(extInfo);
580
-
581
- const body = child.namedChildren.find(c => c.type === 'extension_body');
582
- if (body) _walkSwiftNode(body, ctx, extInfo.className);
583
- break;
584
- }
585
-
586
- case 'function_declaration': {
587
- const m = _parseSwiftFunction(child, parentClassName);
588
- ctx.methods.push(m);
589
- break;
590
- }
591
-
592
- case 'property_declaration': {
593
- const p = _parseSwiftProperty(child, parentClassName);
594
- if (p) ctx.properties.push(p);
595
- break;
596
- }
597
-
598
- default: {
599
- // 递归进入未识别节点
600
- if (child.namedChildCount > 0 && !['function_body', 'computed_property', 'willSet_didSet_block'].includes(child.type)) {
601
- _walkSwiftNode(child, ctx, parentClassName);
602
- }
603
- }
604
- }
605
- }
606
- }
607
-
608
- function _parseSwiftTypeDecl(node) {
609
- const name = node.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text || 'Unknown';
610
- const kind = node.type.replace('_declaration', ''); // class | struct | enum
611
-
612
- // 继承/遵循
613
- const superclass = null;
614
- const protocols = [];
615
- for (const child of node.namedChildren) {
616
- if (child.type === 'inheritance_specifier') {
617
- const typeNode = child.namedChildren.find(c => c.type === 'user_type');
618
- if (typeNode) {
619
- const typeName = typeNode.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text;
620
- if (typeName) protocols.push(typeName);
621
- }
622
- }
623
- }
624
-
625
- // Swift 无法区分 superclass 和 protocol(都是 inheritance_specifier),
626
- // 约定:第一个继承者如果首字母大写且不含 Protocol/Delegate 后缀可能是 superclass
627
- let detectedSuper = null;
628
- if (protocols.length > 0 && kind === 'class') {
629
- const first = protocols[0];
630
- if (!first.endsWith('Protocol') && !first.endsWith('Delegate') && !first.endsWith('DataSource')) {
631
- detectedSuper = first;
632
- }
633
- }
634
-
635
- return {
636
- name,
637
- kind,
638
- superclass: detectedSuper,
639
- protocols,
640
- line: node.startPosition.row + 1,
641
- endLine: node.endPosition.row + 1,
642
- };
643
- }
644
-
645
- function _parseSwiftProtocol(node) {
646
- const name = node.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text || 'Unknown';
647
- const inherits = [];
648
- for (const child of node.namedChildren) {
649
- if (child.type === 'inheritance_specifier') {
650
- const t = child.namedChildren.find(c => c.type === 'user_type');
651
- if (t) {
652
- const n = t.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'simple_identifier');
653
- if (n) inherits.push(n.text);
654
- }
655
- }
656
- }
657
- return { name, inherits, line: node.startPosition.row + 1 };
658
- }
659
-
660
- function _parseSwiftExtension(node) {
661
- const className = node.namedChildren.find(c => c.type === 'user_type' || c.type === 'type_identifier')?.text || 'Unknown';
662
- const protocols = [];
663
- for (const child of node.namedChildren) {
664
- if (child.type === 'inheritance_specifier') {
665
- const t = child.namedChildren.find(c => c.type === 'user_type');
666
- if (t) {
667
- const n = t.namedChildren.find(c => c.type === 'type_identifier' || c.type === 'simple_identifier');
668
- if (n) protocols.push(n.text);
669
- }
670
- }
671
- }
672
-
673
- const methods = [];
674
- const body = node.namedChildren.find(c => c.type === 'extension_body');
675
- if (body) {
676
- for (const child of body.namedChildren) {
677
- if (child.type === 'function_declaration') {
678
- methods.push(_parseSwiftFunction(child, className));
679
- }
680
- }
681
- }
682
-
683
- return {
684
- className,
685
- categoryName: protocols.length > 0 ? protocols.join('+') : 'ext',
686
- protocols,
687
- methods,
688
- line: node.startPosition.row + 1,
689
- };
690
- }
691
-
692
- function _parseSwiftFunction(node, className) {
693
- const name = node.namedChildren.find(c => c.type === 'simple_identifier')?.text || 'unknown';
694
-
695
- // static/class
696
- const modifiers = [];
697
- for (const child of node.namedChildren) {
698
- if (child.type === 'modifiers' || child.type === 'modifier') {
699
- modifiers.push(child.text);
700
- }
701
- }
702
- const isClassMethod = modifiers.some(m => /\b(static|class)\b/.test(m));
703
-
704
- // 方法体
705
- const body = node.namedChildren.find(c => c.type === 'function_body');
706
- const bodyLines = body ? (body.endPosition.row - body.startPosition.row + 1) : 0;
707
- const complexity = body ? _estimateComplexity(body) : 1;
708
- const nestingDepth = body ? _maxNesting(body, 0) : 0;
709
-
710
- return {
711
- name,
712
- className,
713
- isClassMethod,
714
- bodyLines,
715
- complexity,
716
- nestingDepth,
717
- line: node.startPosition.row + 1,
718
- kind: 'definition',
719
- };
720
- }
721
-
722
- function _parseSwiftProperty(node, className) {
723
- const name = node.namedChildren.find(c => c.type === 'simple_identifier' || c.type === 'pattern')?.text || null;
724
- if (!name) return null;
725
-
726
- const modifiers = [];
727
- for (const child of node.namedChildren) {
728
- if (child.type === 'modifiers' || child.type === 'modifier') {
729
- modifiers.push(child.text);
730
- }
731
- }
732
-
733
- const isStatic = modifiers.some(m => /\b(static|class)\b/.test(m));
734
- const isLet = node.text.includes(' let ');
735
-
736
- return {
737
- name,
738
- className,
739
- isStatic,
740
- isConstant: isLet,
741
- attributes: modifiers,
742
- line: node.startPosition.row + 1,
743
- };
744
- }
745
-
746
341
  // ──────────────────────────────────────────────────────────────────
747
- // 内部实现 — 设计模式检测
342
+ // 内部实现 — 设计模式检测(通用回退,插件可提供自己的 detectPatterns)
748
343
  // ──────────────────────────────────────────────────────────────────
749
344
 
750
345
  function _detectPatterns(root, lang, methods, properties, classes) {
@@ -793,8 +388,7 @@ function _detectPatterns(root, lang, methods, properties, classes) {
793
388
 
794
389
  // Observer/Notification 检测(通过方法名)
795
390
  for (const m of methods) {
796
- if (/^observe|^addObserver|^subscribe/.test(m.name) ||
797
- /^didChange|^willChange/.test(m.name)) {
391
+ if (/^observe|^addObserver|^subscribe/.test(m.name) || /^didChange|^willChange/.test(m.name)) {
798
392
  patterns.push({
799
393
  type: 'observer',
800
394
  className: m.className,
@@ -835,7 +429,11 @@ function _buildInheritanceGraph(classes, protocols, categories) {
835
429
  }
836
430
 
837
431
  for (const cat of categories) {
838
- edges.push({ from: `${cat.className}(${cat.categoryName})`, to: cat.className, type: 'extends' });
432
+ edges.push({
433
+ from: `${cat.className}(${cat.categoryName})`,
434
+ to: cat.className,
435
+ type: 'extends',
436
+ });
839
437
  if (cat.protocols) {
840
438
  for (const proto of cat.protocols) {
841
439
  edges.push({ from: cat.className, to: proto, type: 'conforms' });
@@ -848,24 +446,28 @@ function _buildInheritanceGraph(classes, protocols, categories) {
848
446
 
849
447
  function _renderInheritanceTree(edges) {
850
448
  // 找出根节点(只被继承不继承其他的)
851
- const allTargets = new Set(edges.map(e => e.to));
852
- const allSources = new Set(edges.map(e => e.from));
853
- const roots = [...allTargets].filter(t => !allSources.has(t)).slice(0, 5);
449
+ const allTargets = new Set(edges.map((e) => e.to));
450
+ const allSources = new Set(edges.map((e) => e.from));
451
+ const roots = [...allTargets].filter((t) => !allSources.has(t)).slice(0, 5);
854
452
 
855
453
  const childMap = {};
856
454
  for (const e of edges) {
857
- if (!childMap[e.to]) childMap[e.to] = [];
455
+ if (!childMap[e.to]) {
456
+ childMap[e.to] = [];
457
+ }
858
458
  const label = e.type === 'conforms' ? `${e.from} ◇` : e.from;
859
- if (!childMap[e.to].includes(label)) childMap[e.to].push(label);
459
+ if (!childMap[e.to].includes(label)) {
460
+ childMap[e.to].push(label);
461
+ }
860
462
  }
861
463
 
862
464
  const lines = [];
863
465
  function render(name, prefix, isLast) {
864
- const connector = prefix.length === 0 ? '' : (isLast ? '└─ ' : '├─ ');
466
+ const connector = prefix.length === 0 ? '' : isLast ? '└─ ' : '├─ ';
865
467
  lines.push(prefix + connector + name);
866
468
  const children = childMap[name] || [];
867
469
  for (let i = 0; i < children.length && i < 10; i++) {
868
- const childPrefix = prefix + (prefix.length === 0 ? '' : (isLast ? ' ' : '│ '));
470
+ const childPrefix = prefix + (prefix.length === 0 ? '' : isLast ? ' ' : '│ ');
869
471
  render(children[i], childPrefix, i === children.length - 1);
870
472
  }
871
473
  }
@@ -884,19 +486,32 @@ function _renderInheritanceTree(edges) {
884
486
  function _estimateComplexity(node) {
885
487
  let complexity = 1;
886
488
  const BRANCH_TYPES = new Set([
887
- 'if_statement', 'for_statement', 'for_in_statement', 'while_statement',
888
- 'switch_statement', 'case_statement', 'catch_clause', 'conditional_expression',
889
- 'ternary_expression', 'guard_statement',
489
+ 'if_statement',
490
+ 'for_statement',
491
+ 'for_in_statement',
492
+ 'while_statement',
493
+ 'switch_statement',
494
+ 'case_statement',
495
+ 'catch_clause',
496
+ 'conditional_expression',
497
+ 'ternary_expression',
498
+ 'guard_statement',
890
499
  // ObjC specific
891
500
  'for_in_expression',
892
501
  ]);
893
502
 
894
503
  function walk(n) {
895
- if (BRANCH_TYPES.has(n.type)) complexity++;
504
+ if (BRANCH_TYPES.has(n.type)) {
505
+ complexity++;
506
+ }
896
507
  // && / || 也增加复杂度
897
508
  if (n.type === 'binary_expression') {
898
- const op = n.children?.find(c => c.type === '&&' || c.type === '||' || c.text === '&&' || c.text === '||');
899
- if (op) complexity++;
509
+ const op = n.children?.find(
510
+ (c) => c.type === '&&' || c.type === '||' || c.text === '&&' || c.text === '||'
511
+ );
512
+ if (op) {
513
+ complexity++;
514
+ }
900
515
  }
901
516
  for (let i = 0; i < n.namedChildCount; i++) {
902
517
  walk(n.namedChild(i));
@@ -909,8 +524,12 @@ function _estimateComplexity(node) {
909
524
 
910
525
  function _maxNesting(node, depth) {
911
526
  const NESTING_TYPES = new Set([
912
- 'if_statement', 'for_statement', 'for_in_statement', 'while_statement',
913
- 'switch_statement', 'compound_statement',
527
+ 'if_statement',
528
+ 'for_statement',
529
+ 'for_in_statement',
530
+ 'while_statement',
531
+ 'switch_statement',
532
+ 'compound_statement',
914
533
  ]);
915
534
 
916
535
  let max = depth;
@@ -918,34 +537,38 @@ function _maxNesting(node, depth) {
918
537
 
919
538
  for (let i = 0; i < node.namedChildCount; i++) {
920
539
  const childMax = _maxNesting(node.namedChild(i), nextDepth);
921
- if (childMax > max) max = childMax;
540
+ if (childMax > max) {
541
+ max = childMax;
542
+ }
922
543
  }
923
544
 
924
545
  return max;
925
546
  }
926
547
 
927
548
  function _computeMetrics(root, lang, methods) {
928
- const defs = methods.filter(m => m.kind === 'definition');
549
+ const defs = methods.filter((m) => m.kind === 'definition');
929
550
  const totalBodyLines = defs.reduce((sum, m) => sum + (m.bodyLines || 0), 0);
930
551
 
931
552
  return {
932
553
  methodCount: defs.length,
933
554
  avgBodyLines: defs.length > 0 ? totalBodyLines / defs.length : 0,
934
- maxComplexity: defs.length > 0 ? Math.max(...defs.map(m => m.complexity || 1)) : 0,
935
- maxNestingDepth: defs.length > 0 ? Math.max(...defs.map(m => m.nestingDepth || 0)) : 0,
936
- longMethods: defs.filter(m => (m.bodyLines || 0) > 50),
937
- complexMethods: defs.filter(m => (m.complexity || 1) > 10),
555
+ maxComplexity: defs.length > 0 ? Math.max(...defs.map((m) => m.complexity || 1)) : 0,
556
+ maxNestingDepth: defs.length > 0 ? Math.max(...defs.map((m) => m.nestingDepth || 0)) : 0,
557
+ longMethods: defs.filter((m) => (m.bodyLines || 0) > 50),
558
+ complexMethods: defs.filter((m) => (m.complexity || 1) > 10),
938
559
  };
939
560
  }
940
561
 
941
562
  function _aggregateMetrics(fileSummaries) {
942
- const allMethods = fileSummaries.flatMap(f => f.methods.filter(m => m.kind === 'definition'));
943
- const allClasses = fileSummaries.flatMap(f => f.classes);
563
+ const allMethods = fileSummaries.flatMap((f) => f.methods.filter((m) => m.kind === 'definition'));
564
+ const allClasses = fileSummaries.flatMap((f) => f.classes);
944
565
 
945
566
  const methodsByClass = {};
946
567
  for (const m of allMethods) {
947
568
  if (m.className) {
948
- if (!methodsByClass[m.className]) methodsByClass[m.className] = 0;
569
+ if (!methodsByClass[m.className]) {
570
+ methodsByClass[m.className] = 0;
571
+ }
949
572
  methodsByClass[m.className]++;
950
573
  }
951
574
  }
@@ -954,16 +577,28 @@ function _aggregateMetrics(fileSummaries) {
954
577
  return {
955
578
  totalMethods: allMethods.length,
956
579
  totalClasses: allClasses.length,
957
- avgMethodsPerClass: classCounts.length > 0 ? classCounts.reduce((a, b) => a + b, 0) / classCounts.length : 0,
958
- maxNestingDepth: allMethods.length > 0 ? Math.max(...allMethods.map(m => m.nestingDepth || 0)) : 0,
959
- longMethods: allMethods.filter(m => (m.bodyLines || 0) > 50).map(m => ({
960
- name: m.name, className: m.className, lines: m.bodyLines,
961
- file: m.file, line: m.line,
962
- })),
963
- complexMethods: allMethods.filter(m => (m.complexity || 1) > 10).map(m => ({
964
- name: m.name, className: m.className, complexity: m.complexity,
965
- file: m.file, line: m.line,
966
- })),
580
+ avgMethodsPerClass:
581
+ classCounts.length > 0 ? classCounts.reduce((a, b) => a + b, 0) / classCounts.length : 0,
582
+ maxNestingDepth:
583
+ allMethods.length > 0 ? Math.max(...allMethods.map((m) => m.nestingDepth || 0)) : 0,
584
+ longMethods: allMethods
585
+ .filter((m) => (m.bodyLines || 0) > 50)
586
+ .map((m) => ({
587
+ name: m.name,
588
+ className: m.className,
589
+ lines: m.bodyLines,
590
+ file: m.file,
591
+ line: m.line,
592
+ })),
593
+ complexMethods: allMethods
594
+ .filter((m) => (m.complexity || 1) > 10)
595
+ .map((m) => ({
596
+ name: m.name,
597
+ className: m.className,
598
+ complexity: m.complexity,
599
+ file: m.file,
600
+ line: m.line,
601
+ })),
967
602
  };
968
603
  }
969
604
 
@@ -974,7 +609,11 @@ function _aggregateMetrics(fileSummaries) {
974
609
  function _findIdentifier(node) {
975
610
  for (let i = 0; i < node.namedChildCount; i++) {
976
611
  const child = node.namedChild(i);
977
- if (child.type === 'identifier' || child.type === 'simple_identifier' || child.type === 'type_identifier') {
612
+ if (
613
+ child.type === 'identifier' ||
614
+ child.type === 'simple_identifier' ||
615
+ child.type === 'type_identifier'
616
+ ) {
978
617
  return child.text;
979
618
  }
980
619
  }
@@ -994,7 +633,9 @@ function _findIdentifier(node) {
994
633
  */
995
634
  function findCallExpressions(source, lang, targetCallee) {
996
635
  const parser = _getParser(lang);
997
- if (!parser) return [];
636
+ if (!parser) {
637
+ return [];
638
+ }
998
639
 
999
640
  const tree = parser.parse(source);
1000
641
  const results = [];
@@ -1003,12 +644,23 @@ function findCallExpressions(source, lang, targetCallee) {
1003
644
  function walk(node, enclosingClass) {
1004
645
  // 更新当前所处的类
1005
646
  let currentClass = enclosingClass;
1006
- if (['class_declaration', 'struct_declaration', 'class_interface', 'class_implementation'].includes(node.type)) {
647
+ if (
648
+ [
649
+ 'class_declaration',
650
+ 'struct_declaration',
651
+ 'class_interface',
652
+ 'class_implementation',
653
+ ].includes(node.type)
654
+ ) {
1007
655
  currentClass = _findIdentifier(node) || enclosingClass;
1008
656
  }
1009
657
 
1010
658
  // 检查调用表达式
1011
- const isCallLike = ['call_expression', 'message_expression', 'function_call_expression'].includes(node.type);
659
+ const isCallLike = [
660
+ 'call_expression',
661
+ 'message_expression',
662
+ 'function_call_expression',
663
+ ].includes(node.type);
1012
664
  if (isCallLike) {
1013
665
  const nodeText = node.text || '';
1014
666
  if (nodeText.includes(targetCallee)) {
@@ -1059,7 +711,9 @@ function findCallExpressions(source, lang, targetCallee) {
1059
711
  */
1060
712
  function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1061
713
  const parser = _getParser(lang);
1062
- if (!parser) return [];
714
+ if (!parser) {
715
+ return [];
716
+ }
1063
717
 
1064
718
  const tree = parser.parse(source);
1065
719
  const results = [];
@@ -1068,7 +722,14 @@ function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1068
722
  function getEnclosingMethodName(node) {
1069
723
  let current = node.parent;
1070
724
  while (current) {
1071
- if (['method_definition', 'method_declaration', 'function_declaration', 'function_definition'].includes(current.type)) {
725
+ if (
726
+ [
727
+ 'method_definition',
728
+ 'method_declaration',
729
+ 'function_declaration',
730
+ 'function_definition',
731
+ ].includes(current.type)
732
+ ) {
1072
733
  return _findIdentifier(current) || null;
1073
734
  }
1074
735
  current = current.parent;
@@ -1079,7 +740,14 @@ function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1079
740
  function getEnclosingClassName(node) {
1080
741
  let current = node.parent;
1081
742
  while (current) {
1082
- if (['class_declaration', 'struct_declaration', 'class_interface', 'class_implementation'].includes(current.type)) {
743
+ if (
744
+ [
745
+ 'class_declaration',
746
+ 'struct_declaration',
747
+ 'class_interface',
748
+ 'class_implementation',
749
+ ].includes(current.type)
750
+ ) {
1083
751
  return _findIdentifier(current) || null;
1084
752
  }
1085
753
  current = current.parent;
@@ -1096,7 +764,10 @@ function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1096
764
 
1097
765
  if (contextFilter.forbiddenContext) {
1098
766
  // 在禁止上下文中出现 → 报告
1099
- if (methodName === contextFilter.forbiddenContext || className === contextFilter.forbiddenContext) {
767
+ if (
768
+ methodName === contextFilter.forbiddenContext ||
769
+ className === contextFilter.forbiddenContext
770
+ ) {
1100
771
  results.push({
1101
772
  line: node.startPosition.row + 1,
1102
773
  snippet: lines[node.startPosition.row]?.trim().slice(0, 120) || '',
@@ -1105,7 +776,10 @@ function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1105
776
  }
1106
777
  } else if (contextFilter.requiredContext) {
1107
778
  // 不在要求的上下文中 → 报告
1108
- if (className !== contextFilter.requiredContext && methodName !== contextFilter.requiredContext) {
779
+ if (
780
+ className !== contextFilter.requiredContext &&
781
+ methodName !== contextFilter.requiredContext
782
+ ) {
1109
783
  results.push({
1110
784
  line: node.startPosition.row + 1,
1111
785
  snippet: lines[node.startPosition.row]?.trim().slice(0, 120) || '',
@@ -1134,11 +808,15 @@ function findPatternInContext(source, lang, pattern, contextFilter = {}) {
1134
808
  */
1135
809
  function checkProtocolConformance(source, lang, className, protocolName) {
1136
810
  const summary = analyzeFile(source, lang);
1137
- if (!summary) return { conforms: false, classFound: false, classDeclLine: null };
811
+ if (!summary) {
812
+ return { conforms: false, classFound: false, classDeclLine: null };
813
+ }
1138
814
 
1139
815
  // 在 classes 中查找
1140
- const cls = summary.classes.find(c => c.name === className);
1141
- if (!cls) return { conforms: false, classFound: false, classDeclLine: null };
816
+ const cls = summary.classes.find((c) => c.name === className);
817
+ if (!cls) {
818
+ return { conforms: false, classFound: false, classDeclLine: null };
819
+ }
1142
820
 
1143
821
  // 直接遵循
1144
822
  if (cls.protocols?.includes(protocolName)) {
@@ -1147,7 +825,7 @@ function checkProtocolConformance(source, lang, className, protocolName) {
1147
825
 
1148
826
  // 通过 extension/category 遵循
1149
827
  const catConforms = summary.categories.some(
1150
- cat => cat.className === className && cat.protocols?.includes(protocolName)
828
+ (cat) => cat.className === className && cat.protocols?.includes(protocolName)
1151
829
  );
1152
830
  if (catConforms) {
1153
831
  return { conforms: true, classFound: true, classDeclLine: cls.line };
@@ -1166,6 +844,7 @@ export {
1166
844
  generateContextForAgent,
1167
845
  isAvailable,
1168
846
  supportedLanguages,
847
+ // registerLanguage 已在定义处 inline export,此处不再重复
1169
848
  // Guard AST 查询 API
1170
849
  findCallExpressions,
1171
850
  findPatternInContext,