autosnippet 3.0.1 → 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 +655 -260
  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,123 +1,167 @@
1
1
  /**
2
2
  * Commands API 路由
3
- * 执行 Install (同步 Xcode)、SPM Map 刷新、Embed (重建索引) 等命令
3
+ * 执行 Install (同步 Snippet 到 IDE)、SPM Map 刷新、Embed (重建索引) 等命令
4
4
  */
5
5
 
6
6
  import express from 'express';
7
- import { asyncHandler } from '../middleware/errorHandler.js';
8
- import { getServiceContainer } from '../../injection/ServiceContainer.js';
9
7
  import Logger from '../../infrastructure/logging/Logger.js';
8
+ import { getServiceContainer } from '../../injection/ServiceContainer.js';
9
+ import { asyncHandler } from '../middleware/errorHandler.js';
10
10
 
11
11
  const router = express.Router();
12
12
  const logger = Logger.getInstance();
13
13
 
14
14
  /**
15
15
  * POST /api/v1/commands/install
16
- * 从 Recipe 生成并同步 Snippet 到 Xcode
16
+ * 从 Recipe 生成并同步 Snippet 到 IDE
17
+ * Body: { target?: 'xcode' | 'vscode' | 'all' }
17
18
  */
18
- router.post('/install', asyncHandler(async (req, res) => {
19
- const container = getServiceContainer();
20
- const snippetFactory = container.get('snippetFactory');
21
- const snippetInstaller = container.get('snippetInstaller');
22
- const recipeRepository = container.get('recipeRepository');
23
-
24
- // 获取所有活跃 Recipe
25
- const result = await recipeRepository.findWithPagination(
26
- { status: 'active' },
27
- { page: 1, pageSize: 9999 },
28
- );
29
- const recipes = (result?.data || result?.items || [])
30
- .map(r => ({
31
- id: r.id,
32
- title: r.title,
33
- trigger: r.trigger,
34
- code: r.content?.pattern || '',
35
- description: r.description || r.summaryCn || '',
36
- language: r.language || 'swift',
37
- }))
38
- .filter(r => r.code.trim().length > 0); // 跳过没有代码内容的 Recipe
39
-
40
- const installResult = snippetInstaller.installFromRecipes(recipes);
41
-
42
- logger.info('Xcode snippets installed via dashboard', { result: installResult });
43
- res.json({
44
- success: true,
45
- data: installResult,
46
- });
47
- }));
19
+ router.post(
20
+ '/install',
21
+ asyncHandler(async (req, res) => {
22
+ const container = getServiceContainer();
23
+ const knowledgeRepository = container.get('knowledgeRepository');
24
+ const target = req.body?.target || 'all';
25
+
26
+ // 获取所有活跃 Recipe(V3: lifecycle='active' Recipe)
27
+ const result = await knowledgeRepository.findWithPagination(
28
+ { lifecycle: 'active' },
29
+ { page: 1, pageSize: 9999 }
30
+ );
31
+ const recipes = (result?.data || result?.items || [])
32
+ .map((r) => ({
33
+ id: r.id,
34
+ title: r.title,
35
+ trigger: r.trigger,
36
+ code: r.content?.pattern || '',
37
+ description: r.description || r.summaryCn || '',
38
+ language: r.language || 'unknown',
39
+ }))
40
+ .filter((r) => r.code.trim().length > 0);
41
+
42
+ const installResults = {};
43
+
44
+ // Xcode
45
+ if ((target === 'all' || target === 'xcode') && process.platform === 'darwin') {
46
+ try {
47
+ const xcodeInstaller = container.get('snippetInstaller');
48
+ installResults.xcode = xcodeInstaller.installFromRecipes(recipes);
49
+ } catch (e) {
50
+ installResults.xcode = { success: false, error: e.message };
51
+ }
52
+ }
53
+
54
+ // VSCode
55
+ if (target === 'all' || target === 'vscode') {
56
+ try {
57
+ const vscodeInstaller = container.get('vscodeSnippetInstaller');
58
+ installResults.vscode = vscodeInstaller.installFromRecipes(recipes);
59
+ } catch (e) {
60
+ installResults.vscode = { success: false, error: e.message };
61
+ }
62
+ }
63
+
64
+ logger.info('Snippets installed via dashboard', { target, results: installResults });
65
+ res.json({
66
+ success: true,
67
+ data: installResults,
68
+ });
69
+ })
70
+ );
48
71
 
49
72
  /**
50
73
  * POST /api/v1/commands/spm-map
51
- * 执行 SPM 依赖映射刷新
74
+ * 执行 SPM 依赖映射刷新(向后兼容)
52
75
  */
53
- router.post('/spm-map', asyncHandler(async (req, res) => {
54
- const container = getServiceContainer();
55
- const spmService = container.get('spmService');
56
-
57
- const result = await spmService.updateDependencyMap({
58
- aggressive: true,
59
- });
60
-
61
- logger.info('SPM map updated via dashboard', { result });
62
- res.json({
63
- success: true,
64
- data: result,
65
- });
66
- }));
76
+ router.post(
77
+ '/spm-map',
78
+ asyncHandler(async (req, res) => {
79
+ const container = getServiceContainer();
80
+
81
+ const moduleService = container.get('moduleService');
82
+ const result = await moduleService.updateModuleMap({ aggressive: true });
83
+
84
+ logger.info('Module map updated via dashboard', { result });
85
+ res.json({
86
+ success: true,
87
+ data: result,
88
+ });
89
+ })
90
+ );
67
91
 
68
92
  /**
69
93
  * POST /api/v1/commands/embed
70
94
  * 全量重建语义索引
71
95
  */
72
- router.post('/embed', asyncHandler(async (req, res) => {
73
- const container = getServiceContainer();
74
- const indexingPipeline = container.get('indexingPipeline');
75
-
76
- const result = await indexingPipeline.run({
77
- clear: req.body?.clear !== false,
78
- });
79
-
80
- logger.info('Semantic index rebuilt via dashboard', { result });
81
- res.json({
82
- success: true,
83
- data: {
84
- indexed: result.indexed || 0,
85
- skipped: result.skipped || 0,
86
- removed: result.removed || 0,
87
- },
88
- });
89
- }));
96
+ router.post(
97
+ '/embed',
98
+ asyncHandler(async (req, res) => {
99
+ const container = getServiceContainer();
100
+ const indexingPipeline = container.get('indexingPipeline');
101
+
102
+ const result = await indexingPipeline.run({
103
+ clear: req.body?.clear !== false,
104
+ });
105
+
106
+ logger.info('Semantic index rebuilt via dashboard', { result });
107
+ res.json({
108
+ success: true,
109
+ data: {
110
+ indexed: result.indexed || 0,
111
+ skipped: result.skipped || 0,
112
+ removed: result.removed || 0,
113
+ },
114
+ });
115
+ })
116
+ );
90
117
 
91
118
  /**
92
119
  * GET /api/v1/commands/status
93
- * 获取命令执行状态(Xcode 同步状态、索引状态等)
120
+ * 获取命令执行状态(Snippet 同步状态、索引状态等)
94
121
  */
95
- router.get('/status', asyncHandler(async (req, res) => {
96
- const container = getServiceContainer();
97
-
98
- const status = {
99
- xcode: { synced: false },
100
- index: { ready: false },
101
- spmMap: { available: false },
102
- };
103
-
104
- try {
105
- const snippetInstaller = container.get('snippetInstaller');
106
- status.xcode.synced = await snippetInstaller.isInstalled?.() ?? false;
107
- } catch { /* ignore */ }
108
-
109
- try {
110
- const indexingPipeline = container.get('indexingPipeline');
111
- status.index.ready = indexingPipeline.isReady?.() ?? false;
112
- } catch { /* ignore */ }
122
+ router.get(
123
+ '/status',
124
+ asyncHandler(async (req, res) => {
125
+ const container = getServiceContainer();
126
+
127
+ const status = {
128
+ snippets: { xcode: { synced: false }, vscode: { synced: false } },
129
+ index: { ready: false },
130
+ spmMap: { available: false },
131
+ };
132
+
133
+ try {
134
+ const snippetInstaller = container.get('snippetInstaller');
135
+ status.snippets.xcode.synced = (await snippetInstaller.listInstalled?.())?.length > 0;
136
+ } catch {
137
+ /* ignore */
138
+ }
113
139
 
114
- try {
115
- const spmService = container.get('spmService');
116
- status.spmMap.available = spmService.hasMap?.() ?? false;
117
- } catch { /* ignore */ }
140
+ try {
141
+ const vscodeInstaller = container.get('vscodeSnippetInstaller');
142
+ status.snippets.vscode.synced = (await vscodeInstaller.listInstalled?.())?.length > 0;
143
+ } catch {
144
+ /* ignore */
145
+ }
118
146
 
119
- res.json({ success: true, data: status });
120
- }));
147
+ try {
148
+ const indexingPipeline = container.get('indexingPipeline');
149
+ status.index.ready = indexingPipeline.isReady?.() ?? false;
150
+ } catch {
151
+ /* ignore */
152
+ }
153
+
154
+ try {
155
+ const moduleService = container.get('moduleService');
156
+ await moduleService.load();
157
+ status.spmMap.available = (await moduleService.listTargets()).length > 0;
158
+ } catch {
159
+ /* ignore */
160
+ }
161
+
162
+ res.json({ success: true, data: status });
163
+ })
164
+ );
121
165
 
122
166
  // ─── File Operations (for Xcode Simulator page) ─────
123
167
 
@@ -125,119 +169,175 @@ router.get('/status', asyncHandler(async (req, res) => {
125
169
  * GET /api/v1/commands/files/tree
126
170
  * Get project file tree – only .h / .m / .swift source files
127
171
  */
128
- router.get('/files/tree', asyncHandler(async (req, res) => {
129
- const fs = await import('node:fs');
130
- const path = await import('node:path');
131
- const container = getServiceContainer();
132
- const projectRoot = container.singletons?._projectRoot || process.cwd();
133
-
134
- const SOURCE_EXTS = new Set(['.h', '.m', '.swift']);
135
- const SKIP_DIRS = new Set(['node_modules', '.git', 'Pods', 'build', 'DerivedData', '.build', 'dist', 'vendor']);
136
-
137
- /**
138
- * Recursively scan dir, returning FileNode or null if folder has no matching files.
139
- */
140
- function scanDir(dirPath) {
141
- const dirName = path.default.basename(dirPath);
142
- if (SKIP_DIRS.has(dirName)) return null;
143
-
144
- let entries;
145
- try { entries = fs.default.readdirSync(dirPath, { withFileTypes: true }); } catch { return null; }
146
-
147
- const children = [];
148
- for (const entry of entries) {
149
- if (entry.name.startsWith('.')) continue; // skip hidden
150
- const fullPath = path.default.join(dirPath, entry.name);
151
-
152
- if (entry.isDirectory()) {
153
- const sub = scanDir(fullPath);
154
- if (sub) children.push(sub);
155
- } else if (entry.isFile()) {
156
- const ext = path.default.extname(entry.name).toLowerCase();
157
- if (SOURCE_EXTS.has(ext)) {
158
- children.push({
159
- type: 'file',
160
- name: entry.name,
161
- path: fullPath,
162
- relativePath: path.default.relative(projectRoot, fullPath),
163
- ext,
164
- });
172
+ router.get(
173
+ '/files/tree',
174
+ asyncHandler(async (req, res) => {
175
+ const fs = await import('node:fs');
176
+ const path = await import('node:path');
177
+ const container = getServiceContainer();
178
+ const projectRoot = container.singletons?._projectRoot || process.cwd();
179
+
180
+ const SOURCE_EXTS = new Set(['.h', '.m', '.swift']);
181
+ const SKIP_DIRS = new Set([
182
+ 'node_modules',
183
+ '.git',
184
+ 'Pods',
185
+ 'build',
186
+ 'DerivedData',
187
+ '.build',
188
+ 'dist',
189
+ 'vendor',
190
+ ]);
191
+
192
+ /**
193
+ * Recursively scan dir, returning FileNode or null if folder has no matching files.
194
+ */
195
+ function scanDir(dirPath) {
196
+ const dirName = path.default.basename(dirPath);
197
+ if (SKIP_DIRS.has(dirName)) {
198
+ return null;
199
+ }
200
+
201
+ let entries;
202
+ try {
203
+ entries = fs.default.readdirSync(dirPath, { withFileTypes: true });
204
+ } catch {
205
+ return null;
206
+ }
207
+
208
+ const children = [];
209
+ for (const entry of entries) {
210
+ if (entry.name.startsWith('.')) {
211
+ continue; // skip hidden
212
+ }
213
+ const fullPath = path.default.join(dirPath, entry.name);
214
+
215
+ if (entry.isDirectory()) {
216
+ const sub = scanDir(fullPath);
217
+ if (sub) {
218
+ children.push(sub);
219
+ }
220
+ } else if (entry.isFile()) {
221
+ const ext = path.default.extname(entry.name).toLowerCase();
222
+ if (SOURCE_EXTS.has(ext)) {
223
+ children.push({
224
+ type: 'file',
225
+ name: entry.name,
226
+ path: fullPath,
227
+ relativePath: path.default.relative(projectRoot, fullPath),
228
+ ext,
229
+ });
230
+ }
165
231
  }
166
232
  }
167
- }
168
233
 
169
- if (children.length === 0) return null;
234
+ if (children.length === 0) {
235
+ return null;
236
+ }
170
237
 
171
- // Sort: folders first, then alphabetical
172
- children.sort((a, b) => {
173
- if (a.type !== b.type) return a.type === 'folder' ? -1 : 1;
174
- return a.name.localeCompare(b.name);
175
- });
238
+ // Sort: folders first, then alphabetical
239
+ children.sort((a, b) => {
240
+ if (a.type !== b.type) {
241
+ return a.type === 'folder' ? -1 : 1;
242
+ }
243
+ return a.name.localeCompare(b.name);
244
+ });
245
+
246
+ return {
247
+ type: 'folder',
248
+ name: dirName,
249
+ path: dirPath,
250
+ children,
251
+ };
252
+ }
176
253
 
177
- return {
254
+ const tree = scanDir(projectRoot) || {
178
255
  type: 'folder',
179
- name: dirName,
180
- path: dirPath,
181
- children,
256
+ name: path.default.basename(projectRoot),
257
+ path: projectRoot,
258
+ children: [],
182
259
  };
183
- }
184
-
185
- const tree = scanDir(projectRoot) || { type: 'folder', name: path.default.basename(projectRoot), path: projectRoot, children: [] };
186
- res.json({ success: true, data: tree });
187
- }));
260
+ res.json({ success: true, data: tree });
261
+ })
262
+ );
188
263
 
189
264
  /**
190
265
  * GET /api/v1/commands/files/read
191
266
  * Read file content (limited to projectRoot)
192
267
  */
193
- router.get('/files/read', asyncHandler(async (req, res) => {
194
- const filePath = req.query.path;
195
- if (!filePath) return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'path is required' } });
196
-
197
- const path = await import('node:path');
198
- const container = getServiceContainer();
199
- const projectRoot = container.singletons?._projectRoot || process.cwd();
200
- const resolved = path.default.resolve(projectRoot, filePath);
201
-
202
- // 防止路径遍历:确保解析后的路径在 projectRoot 内
203
- if (!resolved.startsWith(projectRoot + path.default.sep) && resolved !== projectRoot) {
204
- return res.status(403).json({ success: false, error: { code: 'FORBIDDEN', message: 'Access denied: path outside project root' } });
205
- }
206
-
207
- const fs = await import('node:fs');
208
- try {
209
- const content = fs.default.readFileSync(resolved, 'utf8');
210
- res.json({ success: true, data: { content } });
211
- } catch {
212
- res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: 'File not found' } });
213
- }
214
- }));
268
+ router.get(
269
+ '/files/read',
270
+ asyncHandler(async (req, res) => {
271
+ const filePath = req.query.path;
272
+ if (!filePath) {
273
+ return res
274
+ .status(400)
275
+ .json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'path is required' } });
276
+ }
277
+
278
+ const path = await import('node:path');
279
+ const container = getServiceContainer();
280
+ const projectRoot = container.singletons?._projectRoot || process.cwd();
281
+ const resolved = path.default.resolve(projectRoot, filePath);
282
+
283
+ // 防止路径遍历:确保解析后的路径在 projectRoot 内
284
+ if (!resolved.startsWith(projectRoot + path.default.sep) && resolved !== projectRoot) {
285
+ return res.status(403).json({
286
+ success: false,
287
+ error: { code: 'FORBIDDEN', message: 'Access denied: path outside project root' },
288
+ });
289
+ }
290
+
291
+ const fs = await import('node:fs');
292
+ try {
293
+ const content = fs.default.readFileSync(resolved, 'utf8');
294
+ res.json({ success: true, data: { content } });
295
+ } catch {
296
+ res
297
+ .status(404)
298
+ .json({ success: false, error: { code: 'NOT_FOUND', message: 'File not found' } });
299
+ }
300
+ })
301
+ );
215
302
 
216
303
  /**
217
304
  * POST /api/v1/commands/files/save
218
305
  * Save file content (limited to projectRoot)
219
306
  */
220
- router.post('/files/save', asyncHandler(async (req, res) => {
221
- const { path: filePath, content } = req.body;
222
- if (!filePath || content === undefined) return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'path and content required' } });
223
-
224
- const pathMod = await import('node:path');
225
- const container = getServiceContainer();
226
- const projectRoot = container.singletons?._projectRoot || process.cwd();
227
- const resolved = pathMod.default.resolve(projectRoot, filePath);
228
-
229
- // 防止路径遍历:确保解析后的路径在 projectRoot 内
230
- if (!resolved.startsWith(projectRoot + pathMod.default.sep) && resolved !== projectRoot) {
231
- return res.status(403).json({ success: false, error: { code: 'FORBIDDEN', message: 'Access denied: path outside project root' } });
232
- }
233
-
234
- const fs = await import('node:fs');
235
- try {
236
- fs.default.writeFileSync(resolved, content, 'utf8');
237
- res.json({ success: true });
238
- } catch (err) {
239
- res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: err.message } });
240
- }
241
- }));
307
+ router.post(
308
+ '/files/save',
309
+ asyncHandler(async (req, res) => {
310
+ const { path: filePath, content } = req.body;
311
+ if (!filePath || content === undefined) {
312
+ return res.status(400).json({
313
+ success: false,
314
+ error: { code: 'VALIDATION_ERROR', message: 'path and content required' },
315
+ });
316
+ }
317
+
318
+ const pathMod = await import('node:path');
319
+ const container = getServiceContainer();
320
+ const projectRoot = container.singletons?._projectRoot || process.cwd();
321
+ const resolved = pathMod.default.resolve(projectRoot, filePath);
322
+
323
+ // 防止路径遍历:确保解析后的路径在 projectRoot 内
324
+ if (!resolved.startsWith(projectRoot + pathMod.default.sep) && resolved !== projectRoot) {
325
+ return res.status(403).json({
326
+ success: false,
327
+ error: { code: 'FORBIDDEN', message: 'Access denied: path outside project root' },
328
+ });
329
+ }
330
+
331
+ const fs = await import('node:fs');
332
+ try {
333
+ fs.default.writeFileSync(resolved, content, 'utf8');
334
+ res.json({ success: true });
335
+ } catch (err) {
336
+ res
337
+ .status(500)
338
+ .json({ success: false, error: { code: 'INTERNAL_ERROR', message: err.message } });
339
+ }
340
+ })
341
+ );
242
342
 
243
343
  export default router;