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
@@ -13,9 +13,10 @@
13
13
  * - 构建后只读
14
14
  */
15
15
 
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { LanguageService } from '../../shared/LanguageService.js';
16
19
  import { analyzeFile, isAvailable } from '../AstAnalyzer.js';
17
- import fs from 'fs';
18
- import path from 'path';
19
20
 
20
21
  // ──────────────────────────────────────────────────────────────────
21
22
  // 默认配置
@@ -23,15 +24,44 @@ import path from 'path';
23
24
 
24
25
  const DEFAULTS = {
25
26
  maxFiles: 500,
26
- maxFileSizeBytes: 500_000, // 500KB — 跳过超大文件
27
+ maxFileSizeBytes: 500_000, // 500KB — 跳过超大文件
27
28
  excludePatterns: [
28
- 'Pods/', 'Carthage/', 'node_modules/', '.build/', 'build/',
29
- 'DerivedData/', 'vendor/', '.git/', '__tests__/', 'Tests/',
29
+ // 通用
30
+ 'node_modules/',
31
+ '.build/',
32
+ 'build/',
33
+ '.git/',
34
+ 'dist/',
35
+ 'out/',
36
+ // iOS
37
+ 'Pods/',
38
+ 'Carthage/',
39
+ 'DerivedData/',
40
+ // Python
41
+ '__pycache__/',
42
+ '.venv/',
43
+ 'venv/',
44
+ '.tox/',
45
+ '*.egg-info/',
46
+ // JVM
47
+ 'target/',
48
+ '.gradle/',
49
+ '.idea/',
50
+ // Test
51
+ '__tests__/',
52
+ 'Tests/',
53
+ 'test/',
54
+ 'tests/',
55
+ // Dart / Flutter
56
+ '.dart_tool/',
57
+ '.fvm/',
58
+ // Misc
59
+ 'vendor/',
30
60
  ],
61
+ // 从 LanguageService 派生,仅覆盖 AST 解析需要区分 tsx 的场景
31
62
  extensionToLang: {
32
- '.m': 'objectivec',
33
- '.h': 'objectivec',
34
- '.swift': 'swift',
63
+ ...LanguageService.extToLangMap,
64
+ '.tsx': 'tsx', // tree-sitter 需要独立的 tsx 解析器
35
65
  },
36
66
  };
37
67
 
@@ -40,7 +70,6 @@ const DEFAULTS = {
40
70
  // ──────────────────────────────────────────────────────────────────
41
71
 
42
72
  export default class ProjectGraph {
43
-
44
73
  /** @type {Map<string, ClassInfo>} */
45
74
  #classes = new Map();
46
75
 
@@ -94,9 +123,7 @@ export default class ProjectGraph {
94
123
 
95
124
  // 1. 收集文件列表
96
125
  const extToLang = opts.extensionToLang || DEFAULTS.extensionToLang;
97
- const extensions = options.extensions
98
- ? options.extensions
99
- : Object.keys(extToLang);
126
+ const extensions = options.extensions ? options.extensions : Object.keys(extToLang);
100
127
 
101
128
  const files = collectSourceFiles(projectRoot, extensions, opts);
102
129
 
@@ -106,7 +133,7 @@ export default class ProjectGraph {
106
133
  let parsed = 0;
107
134
 
108
135
  for (const filePath of files) {
109
- if (opts.timeoutMs && (Date.now() - startTime) > opts.timeoutMs) {
136
+ if (opts.timeoutMs && Date.now() - startTime > opts.timeoutMs) {
110
137
  break; // 超时 — 返回部分结果
111
138
  }
112
139
 
@@ -114,11 +141,15 @@ export default class ProjectGraph {
114
141
  const content = fs.readFileSync(filePath, 'utf-8');
115
142
  const ext = path.extname(filePath);
116
143
  const lang = extToLang[ext];
117
- if (!lang) continue;
144
+ if (!lang) {
145
+ continue;
146
+ }
118
147
 
119
148
  const relativePath = path.relative(projectRoot, filePath);
120
149
  const summary = analyzeFile(content, lang);
121
- if (!summary) continue;
150
+ if (!summary) {
151
+ continue;
152
+ }
122
153
 
123
154
  graph.#indexFileSummary(relativePath, summary);
124
155
  parsed++;
@@ -180,7 +211,9 @@ export default class ProjectGraph {
180
211
  getSubclasses(className) {
181
212
  const subs = [];
182
213
  for (const [child, parent] of this.#inheritance) {
183
- if (parent === className) subs.push(child);
214
+ if (parent === className) {
215
+ subs.push(child);
216
+ }
184
217
  }
185
218
  return subs;
186
219
  }
@@ -196,7 +229,9 @@ export default class ProjectGraph {
196
229
  const visited = new Set();
197
230
  while (queue.length > 0) {
198
231
  const current = queue.shift();
199
- if (visited.has(current)) continue;
232
+ if (visited.has(current)) {
233
+ continue;
234
+ }
200
235
  visited.add(current);
201
236
  const subs = this.getSubclasses(current);
202
237
  result.push(...subs);
@@ -226,9 +261,7 @@ export default class ProjectGraph {
226
261
 
227
262
  for (const desc of descendants) {
228
263
  const methods = this.#methodsByClass.get(desc) || [];
229
- const match = methods.find(m =>
230
- m.name === methodName || m.selector === methodName
231
- );
264
+ const match = methods.find((m) => m.name === methodName || m.selector === methodName);
232
265
  if (match) {
233
266
  overrides.push({
234
267
  className: desc,
@@ -271,7 +304,9 @@ export default class ProjectGraph {
271
304
  for (const name of this.#classes.keys()) {
272
305
  if (name.toLowerCase().includes(lower)) {
273
306
  results.push(name);
274
- if (results.length >= limit) break;
307
+ if (results.length >= limit) {
308
+ break;
309
+ }
275
310
  }
276
311
  }
277
312
  return results;
@@ -282,7 +317,9 @@ export default class ProjectGraph {
282
317
  * @returns {ProjectOverview}
283
318
  */
284
319
  getOverview() {
285
- if (this.#overview) return this.#overview;
320
+ if (this.#overview) {
321
+ return this.#overview;
322
+ }
286
323
 
287
324
  // 按模块 (顶层目录) 统计
288
325
  const classesPerModule = {};
@@ -294,7 +331,9 @@ export default class ProjectGraph {
294
331
  const module = parts.length > 1 ? parts[0] : '(root)';
295
332
  topModules.add(module);
296
333
 
297
- if (!classesPerModule[module]) classesPerModule[module] = 0;
334
+ if (!classesPerModule[module]) {
335
+ classesPerModule[module] = 0;
336
+ }
298
337
  classesPerModule[module] += symbols.classes.length;
299
338
 
300
339
  // 入口点检测
@@ -365,7 +404,7 @@ export default class ProjectGraph {
365
404
  };
366
405
 
367
406
  // 收集该类的属性
368
- for (const prop of (summary.properties || [])) {
407
+ for (const prop of summary.properties || []) {
369
408
  if (prop.className === cls.name) {
370
409
  classInfo.properties.push({
371
410
  name: prop.name,
@@ -378,7 +417,7 @@ export default class ProjectGraph {
378
417
 
379
418
  // 收集该类的方法 (声明 + 定义去重)
380
419
  const methodSet = new Set();
381
- for (const m of (summary.methods || [])) {
420
+ for (const m of summary.methods || []) {
382
421
  if (m.className === cls.name) {
383
422
  const key = `${m.isClassMethod ? '+' : '-'}${m.name}`;
384
423
  if (!methodSet.has(key)) {
@@ -429,7 +468,7 @@ export default class ProjectGraph {
429
468
  conformers: [], // 稍后在 buildReverseIndices 中填充
430
469
  };
431
470
 
432
- for (const m of (proto.methods || [])) {
471
+ for (const m of proto.methods || []) {
433
472
  const methodInfo = {
434
473
  name: m.name,
435
474
  selector: m.selector || m.name,
@@ -456,7 +495,7 @@ export default class ProjectGraph {
456
495
  categoryName: cat.categoryName || 'ext',
457
496
  filePath: relativePath,
458
497
  line: cat.line,
459
- methods: (cat.methods || []).map(m => ({
498
+ methods: (cat.methods || []).map((m) => ({
460
499
  name: m.name,
461
500
  selector: m.selector || m.name,
462
501
  line: m.line,
@@ -488,8 +527,10 @@ export default class ProjectGraph {
488
527
  }
489
528
 
490
529
  // 索引方法 (按类名分组)
491
- for (const m of (summary.methods || [])) {
492
- if (!m.className) continue;
530
+ for (const m of summary.methods || []) {
531
+ if (!m.className) {
532
+ continue;
533
+ }
493
534
  if (!this.#methodsByClass.has(m.className)) {
494
535
  this.#methodsByClass.set(m.className, []);
495
536
  }
@@ -527,7 +568,9 @@ export default class ProjectGraph {
527
568
  for (const [className, classInfo] of this.#classes) {
528
569
  const allMethods = this.#methodsByClass.get(className) || [];
529
570
  // 只补充 classInfo.methods 中没有的方法
530
- const existingNames = new Set(classInfo.methods.map(m => `${m.isClassMethod ? '+' : '-'}${m.name}`));
571
+ const existingNames = new Set(
572
+ classInfo.methods.map((m) => `${m.isClassMethod ? '+' : '-'}${m.name}`)
573
+ );
531
574
  for (const m of allMethods) {
532
575
  const key = `${m.isClassMethod ? '+' : '-'}${m.name}`;
533
576
  if (!existingNames.has(key)) {
@@ -548,7 +591,9 @@ export default class ProjectGraph {
548
591
  const mapToObj = (map) => Object.fromEntries(map);
549
592
  const mapOfSetsToObj = (map) => {
550
593
  const obj = {};
551
- for (const [k, v] of map) obj[k] = [...v];
594
+ for (const [k, v] of map) {
595
+ obj[k] = [...v];
596
+ }
552
597
  return obj;
553
598
  };
554
599
 
@@ -622,10 +667,14 @@ export default class ProjectGraph {
622
667
  */
623
668
  async incrementalUpdate(changedPaths, deletedPaths = [], options = {}) {
624
669
  const { analyzeFile, isAvailable } = await import('../AstAnalyzer.js');
625
- if (!isAvailable()) return { added: 0, updated: 0, deleted: 0 };
670
+ if (!isAvailable()) {
671
+ return { added: 0, updated: 0, deleted: 0 };
672
+ }
626
673
 
627
674
  const extToLang = options.extensionToLang || DEFAULTS.extensionToLang;
628
- let added = 0, updated = 0, deleted = 0;
675
+ let added = 0,
676
+ updated = 0,
677
+ deleted = 0;
629
678
 
630
679
  // 1. 删除已移除文件的索引
631
680
  for (const relPath of deletedPaths) {
@@ -656,7 +705,9 @@ export default class ProjectGraph {
656
705
  const content = fs.readFileSync(filePath, 'utf-8');
657
706
  const ext = path.extname(filePath);
658
707
  const lang = extToLang[ext];
659
- if (!lang) continue;
708
+ if (!lang) {
709
+ continue;
710
+ }
660
711
 
661
712
  const relativePath = path.relative(this.#projectRoot, filePath);
662
713
  const isUpdate = this.#files.has(relativePath);
@@ -680,7 +731,9 @@ export default class ProjectGraph {
680
731
  }
681
732
 
682
733
  const summary = analyzeFile(content, lang);
683
- if (!summary) continue;
734
+ if (!summary) {
735
+ continue;
736
+ }
684
737
 
685
738
  this.#indexFileSummary(relativePath, summary);
686
739
  isUpdate ? updated++ : added++;
@@ -715,7 +768,9 @@ function collectSourceFiles(dir, extensions, opts) {
715
768
  const extSet = new Set(extensions);
716
769
 
717
770
  function walk(currentDir) {
718
- if (results.length >= opts.maxFiles) return;
771
+ if (results.length >= opts.maxFiles) {
772
+ return;
773
+ }
719
774
 
720
775
  let entries;
721
776
  try {
@@ -725,13 +780,15 @@ function collectSourceFiles(dir, extensions, opts) {
725
780
  }
726
781
 
727
782
  for (const entry of entries) {
728
- if (results.length >= opts.maxFiles) return;
783
+ if (results.length >= opts.maxFiles) {
784
+ return;
785
+ }
729
786
 
730
787
  const fullPath = path.join(currentDir, entry.name);
731
788
  const relativePath = path.relative(dir, fullPath);
732
789
 
733
790
  // 排除模式检查
734
- if (opts.excludePatterns.some(p => relativePath.includes(p))) {
791
+ if (opts.excludePatterns.some((p) => relativePath.includes(p))) {
735
792
  continue;
736
793
  }
737
794
 
@@ -739,12 +796,16 @@ function collectSourceFiles(dir, extensions, opts) {
739
796
  walk(fullPath);
740
797
  } else if (entry.isFile()) {
741
798
  const ext = path.extname(entry.name);
742
- if (!extSet.has(ext)) continue;
799
+ if (!extSet.has(ext)) {
800
+ continue;
801
+ }
743
802
 
744
803
  // 跳过过大的文件
745
804
  try {
746
805
  const stat = fs.statSync(fullPath);
747
- if (stat.size > opts.maxFileSizeBytes) continue;
806
+ if (stat.size > opts.maxFileSizeBytes) {
807
+ continue;
808
+ }
748
809
  } catch {
749
810
  continue;
750
811
  }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * @module ast/ensure-grammars
3
+ * @description 按需安装缺失的 tree-sitter 语法包
4
+ *
5
+ * 在冷启动检测到项目语言后,检查对应 tree-sitter 包是否已安装,
6
+ * 未安装时自动通过 npm install 补装,然后重新加载 AST 插件。
7
+ *
8
+ * 使用方式:
9
+ * import { ensureGrammars } from '../core/ast/ensure-grammars.js';
10
+ * const result = await ensureGrammars(['typescript', 'javascript'], { logger });
11
+ */
12
+
13
+ import { execFile } from 'node:child_process';
14
+ import { createRequire } from 'node:module';
15
+ import path from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { promisify } from 'node:util';
18
+ import { LanguageService } from '../../shared/LanguageService.js';
19
+
20
+ const execFileAsync = promisify(execFile);
21
+ const require = createRequire(import.meta.url);
22
+
23
+ /**
24
+ * 语言 ID → npm 包名映射
25
+ */
26
+ const LANG_TO_PACKAGE = {
27
+ objectivec: 'tree-sitter-objc',
28
+ swift: 'tree-sitter-swift',
29
+ typescript: 'tree-sitter-typescript',
30
+ tsx: 'tree-sitter-typescript', // tsx 与 typescript 共用同一个包
31
+ javascript: 'tree-sitter-javascript',
32
+ python: 'tree-sitter-python',
33
+ java: 'tree-sitter-java',
34
+ kotlin: 'tree-sitter-kotlin',
35
+ go: 'tree-sitter-go',
36
+ dart: 'tree-sitter-dart',
37
+ };
38
+
39
+ /**
40
+ * package.json 中声明的版本范围(保持和 optionalDependencies 一致)
41
+ */
42
+ const PACKAGE_VERSIONS = {
43
+ 'tree-sitter-objc': '^3.0.2',
44
+ 'tree-sitter-swift': '^0.7.1',
45
+ 'tree-sitter-typescript': '^0.23.2',
46
+ 'tree-sitter-javascript': '^0.23.1',
47
+ 'tree-sitter-python': '^0.23.5',
48
+ 'tree-sitter-java': '^0.23.4',
49
+ 'tree-sitter-kotlin': '^0.3.8',
50
+ 'tree-sitter-go': '^0.25.0',
51
+ 'tree-sitter-dart': '^1.0.0',
52
+ };
53
+
54
+ /**
55
+ * 检测某个 npm 包是否已安装可用
56
+ */
57
+ function isPackageInstalled(pkgName) {
58
+ try {
59
+ require.resolve(pkgName);
60
+ return true;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 获取 AutoSnippet 包的安装根目录(npm install 的 cwd)
68
+ */
69
+ function getPackageRoot() {
70
+ const thisFile = fileURLToPath(import.meta.url);
71
+ // lib/core/ast/ensure-grammars.js → 向上 3 级到包根
72
+ return path.resolve(path.dirname(thisFile), '..', '..', '..');
73
+ }
74
+
75
+ /**
76
+ * 按需安装缺失的 tree-sitter 语法包
77
+ *
78
+ * @param {string[]} detectedLanguages - 检测到的语言列表 (如 ['typescript', 'javascript', 'python'])
79
+ * @param {object} [options]
80
+ * @param {object} [options.logger] - Logger 实例(可选)
81
+ * @param {number} [options.timeout=60000] - npm install 超时 (ms)
82
+ * @returns {Promise<{installed: string[], skipped: string[], failed: string[], alreadyAvailable: string[]}>}
83
+ */
84
+ export async function ensureGrammars(detectedLanguages, options = {}) {
85
+ const { logger, timeout = 60_000 } = options;
86
+
87
+ const result = {
88
+ installed: [], // 本次新安装的包
89
+ skipped: [], // 不需要安装(已有)
90
+ failed: [], // 安装失败的包
91
+ alreadyAvailable: [], // 已经可用的语言
92
+ };
93
+
94
+ if (!detectedLanguages || detectedLanguages.length === 0) {
95
+ return result;
96
+ }
97
+
98
+ // 1) 去重: 多个语言可能映射到同一个包 (如 typescript + tsx → tree-sitter-typescript)
99
+ const neededPackages = new Map(); // pkgName → [langIds]
100
+ for (const lang of detectedLanguages) {
101
+ const pkg = LANG_TO_PACKAGE[lang];
102
+ if (!pkg) {
103
+ continue;
104
+ }
105
+ if (!neededPackages.has(pkg)) {
106
+ neededPackages.set(pkg, []);
107
+ }
108
+ neededPackages.get(pkg).push(lang);
109
+ }
110
+
111
+ // 2) 检查哪些已安装
112
+ const toInstall = [];
113
+ for (const [pkg, langs] of neededPackages) {
114
+ if (isPackageInstalled(pkg)) {
115
+ result.skipped.push(pkg);
116
+ result.alreadyAvailable.push(...langs);
117
+ } else {
118
+ toInstall.push(pkg);
119
+ }
120
+ }
121
+
122
+ if (toInstall.length === 0) {
123
+ logger?.info?.('[ensure-grammars] All required grammars already installed');
124
+ return result;
125
+ }
126
+
127
+ // 3) 批量安装缺失的包
128
+ const pkgRoot = getPackageRoot();
129
+ const installArgs = toInstall.map((pkg) => {
130
+ const ver = PACKAGE_VERSIONS[pkg];
131
+ return ver ? `${pkg}@${ver}` : pkg;
132
+ });
133
+
134
+ logger?.info?.(`[ensure-grammars] Installing missing grammars: ${toInstall.join(', ')}`);
135
+
136
+ try {
137
+ const { stderr } = await execFileAsync(
138
+ 'npm',
139
+ ['install', '--no-save', '--no-audit', '--no-fund', ...installArgs],
140
+ {
141
+ cwd: pkgRoot,
142
+ timeout,
143
+ env: { ...process.env, NODE_ENV: '' }, // 避免 NODE_ENV=production 跳过 optional
144
+ }
145
+ );
146
+
147
+ if (stderr && !stderr.includes('npm warn')) {
148
+ logger?.warn?.(`[ensure-grammars] npm stderr: ${stderr.slice(0, 200)}`);
149
+ }
150
+
151
+ // 4) 逐个验证安装结果
152
+ for (const pkg of toInstall) {
153
+ // 清除 require cache 以便重新检测
154
+ try {
155
+ delete require.cache[require.resolve(pkg)];
156
+ } catch {
157
+ /* not cached */
158
+ }
159
+
160
+ if (isPackageInstalled(pkg)) {
161
+ result.installed.push(pkg);
162
+ const langs = neededPackages.get(pkg) || [];
163
+ result.alreadyAvailable.push(...langs);
164
+ logger?.info?.(`[ensure-grammars] ✓ ${pkg} installed successfully`);
165
+ } else {
166
+ result.failed.push(pkg);
167
+ logger?.warn?.(
168
+ `[ensure-grammars] ✗ ${pkg} install reported success but package not resolvable`
169
+ );
170
+ }
171
+ }
172
+ } catch (err) {
173
+ logger?.warn?.(`[ensure-grammars] npm install failed: ${err.message}`);
174
+ // 批量失败 → 逐个重试
175
+ for (const pkg of toInstall) {
176
+ try {
177
+ const ver = PACKAGE_VERSIONS[pkg];
178
+ const spec = ver ? `${pkg}@${ver}` : pkg;
179
+ await execFileAsync('npm', ['install', '--no-save', '--no-audit', '--no-fund', spec], {
180
+ cwd: pkgRoot,
181
+ timeout: timeout / 2,
182
+ });
183
+ if (isPackageInstalled(pkg)) {
184
+ result.installed.push(pkg);
185
+ const langs = neededPackages.get(pkg) || [];
186
+ result.alreadyAvailable.push(...langs);
187
+ logger?.info?.(`[ensure-grammars] ✓ ${pkg} installed (retry)`);
188
+ } else {
189
+ result.failed.push(pkg);
190
+ }
191
+ } catch {
192
+ result.failed.push(pkg);
193
+ logger?.warn?.(`[ensure-grammars] ✗ ${pkg} install failed permanently`);
194
+ }
195
+ }
196
+ }
197
+
198
+ return result;
199
+ }
200
+
201
+ /**
202
+ * 在安装新包后重新加载 AST 插件
203
+ * 由于 loadPlugins() 是幂等的(_loaded 标志),需要重置标志后重新加载
204
+ */
205
+ export async function reloadPlugins() {
206
+ // 动态 import 获取模块并重置 _loaded 状态
207
+ const astIndex = await import('./index.js');
208
+ if (typeof astIndex._resetForReload === 'function') {
209
+ astIndex._resetForReload();
210
+ }
211
+ await astIndex.loadPlugins();
212
+ }
213
+
214
+ /**
215
+ * 从文件扩展名统计推断需要的语言列表
216
+ *
217
+ * @param {Record<string, number>} langStats - { swift: 120, m: 80, ts: 200 }
218
+ * @returns {string[]} 需要的语言 ID 列表
219
+ */
220
+ export function inferLanguagesFromStats(langStats) {
221
+ // 从 LanguageService 派生,仅覆盖 tsx(tree-sitter 需要独立解析器)
222
+ const bareMap = LanguageService.bareExtToLangMap;
223
+
224
+ const langs = new Set();
225
+ for (const ext of Object.keys(langStats)) {
226
+ const lang = ext === 'tsx' ? 'tsx' : bareMap[ext];
227
+ if (lang) {
228
+ langs.add(lang);
229
+ }
230
+ }
231
+ return [...langs];
232
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @module ast/index
3
+ * @description 语言 AST 插件自动加载器
4
+ *
5
+ * 按 try/catch 逐个加载每个语言插件并注册到 AstAnalyzer。
6
+ * 缺少对应 tree-sitter 包时静默跳过(优雅降级)。
7
+ *
8
+ * 使用方式:
9
+ * import '../core/ast/index.js'; // 副作用: 注册所有可用语言插件
10
+ *
11
+ * 或按需:
12
+ * import { loadPlugins } from '../core/ast/index.js';
13
+ * await loadPlugins();
14
+ */
15
+
16
+ import { registerLanguage } from '../AstAnalyzer.js';
17
+
18
+ let _loaded = false;
19
+
20
+ /**
21
+ * 重置加载标志,允许 loadPlugins() 再次执行
22
+ * 仅由 ensure-grammars.js 在安装新包后调用
23
+ */
24
+ export function _resetForReload() {
25
+ _loaded = false;
26
+ }
27
+
28
+ /**
29
+ * 加载并注册所有可用的语言 AST 插件
30
+ * 幂等 — 多次调用只执行一次
31
+ */
32
+ export async function loadPlugins() {
33
+ if (_loaded) {
34
+ return;
35
+ }
36
+ _loaded = true;
37
+
38
+ // ObjC
39
+ try {
40
+ const { plugin } = await import('./lang-objc.js');
41
+ registerLanguage('objectivec', plugin);
42
+ } catch {
43
+ /* tree-sitter-objc not installed */
44
+ }
45
+
46
+ // Swift
47
+ try {
48
+ const { plugin } = await import('./lang-swift.js');
49
+ registerLanguage('swift', plugin);
50
+ } catch {
51
+ /* tree-sitter-swift not installed */
52
+ }
53
+
54
+ // TypeScript
55
+ try {
56
+ const { plugin: tsPlugin, tsxPlugin } = await import('./lang-typescript.js');
57
+ registerLanguage('typescript', tsPlugin);
58
+ if (tsxPlugin) {
59
+ registerLanguage('tsx', tsxPlugin);
60
+ }
61
+ } catch {
62
+ /* tree-sitter-typescript not installed */
63
+ }
64
+
65
+ // JavaScript
66
+ try {
67
+ const { plugin } = await import('./lang-javascript.js');
68
+ registerLanguage('javascript', plugin);
69
+ } catch {
70
+ /* tree-sitter-javascript not installed */
71
+ }
72
+
73
+ // Python
74
+ try {
75
+ const { plugin } = await import('./lang-python.js');
76
+ registerLanguage('python', plugin);
77
+ } catch {
78
+ /* tree-sitter-python not installed */
79
+ }
80
+
81
+ // Java
82
+ try {
83
+ const { plugin } = await import('./lang-java.js');
84
+ registerLanguage('java', plugin);
85
+ } catch {
86
+ /* tree-sitter-java not installed */
87
+ }
88
+
89
+ // Kotlin
90
+ try {
91
+ const { plugin } = await import('./lang-kotlin.js');
92
+ registerLanguage('kotlin', plugin);
93
+ } catch {
94
+ /* tree-sitter-kotlin not installed */
95
+ }
96
+
97
+ // Go
98
+ try {
99
+ const { plugin } = await import('./lang-go.js');
100
+ registerLanguage('go', plugin);
101
+ } catch {
102
+ /* tree-sitter-go not installed */
103
+ }
104
+
105
+ // Dart
106
+ try {
107
+ const { plugin } = await import('./lang-dart.js');
108
+ registerLanguage('dart', plugin);
109
+ } catch {
110
+ /* tree-sitter-dart not installed */
111
+ }
112
+ }
113
+
114
+ // 自动加载(ESM 模块顶层 await)
115
+ await loadPlugins();