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
@@ -0,0 +1,578 @@
1
+ /**
2
+ * Modules API 路由 — 统一多语言模块扫描
3
+ * 替代 spm.js,提供语言无关的模块管理、依赖图、AI 扫描
4
+ *
5
+ * 所有端点通过 container.get('moduleService') 获取 ModuleService 实例
6
+ */
7
+
8
+ import express from 'express';
9
+ import Logger from '../../infrastructure/logging/Logger.js';
10
+ import { getServiceContainer } from '../../injection/ServiceContainer.js';
11
+ import { ValidationError } from '../../shared/errors/index.js';
12
+ import { asyncHandler } from '../middleware/errorHandler.js';
13
+ import { createStreamSession, getStreamSession } from '../utils/sse-sessions.js';
14
+
15
+ const router = express.Router();
16
+ const logger = Logger.getInstance();
17
+
18
+ /**
19
+ * GET /api/v1/modules/targets
20
+ * 获取所有模块 Target 列表(多语言合并)
21
+ */
22
+ router.get(
23
+ '/targets',
24
+ asyncHandler(async (req, res) => {
25
+ const container = getServiceContainer();
26
+ const moduleService = container.get('moduleService');
27
+
28
+ await moduleService.load();
29
+ const targets = await moduleService.listTargets();
30
+
31
+ res.json({
32
+ success: true,
33
+ data: {
34
+ targets,
35
+ total: targets.length,
36
+ projectInfo: moduleService.getProjectInfo(),
37
+ },
38
+ });
39
+ })
40
+ );
41
+
42
+ /**
43
+ * GET /api/v1/modules/dep-graph
44
+ * 获取模块依赖关系图
45
+ */
46
+ router.get(
47
+ '/dep-graph',
48
+ asyncHandler(async (req, res) => {
49
+ const container = getServiceContainer();
50
+ const moduleService = container.get('moduleService');
51
+
52
+ await moduleService.load();
53
+ const level = req.query.level || 'package'; // 'package' | 'target' | 'module'
54
+ const graph = await moduleService.getDependencyGraph({ level });
55
+
56
+ if (!graph || (!graph.nodes && !graph.packages)) {
57
+ return res.json({
58
+ success: true,
59
+ data: { nodes: [], edges: [], projectRoot: null },
60
+ });
61
+ }
62
+
63
+ // 标准化为 { nodes, edges } 格式
64
+ let nodes = [];
65
+ let edges = [];
66
+
67
+ if (graph.nodes && graph.edges) {
68
+ nodes = graph.nodes;
69
+ edges = graph.edges;
70
+ } else if (graph.packages) {
71
+ // SPM 格式兼容:从 packages 构建图
72
+ if (level === 'target') {
73
+ for (const [pkgName, pkgInfo] of Object.entries(graph.packages)) {
74
+ const targetsInfo = pkgInfo?.targetsInfo || {};
75
+ for (const [targetName, info] of Object.entries(targetsInfo)) {
76
+ const id = `${pkgName}::${targetName}`;
77
+ nodes.push({
78
+ id,
79
+ label: targetName,
80
+ type: 'target',
81
+ packageName: pkgName,
82
+ });
83
+ for (const d of info?.dependencies || []) {
84
+ if (!d?.name) continue;
85
+ const depPkg = d?.package || pkgName;
86
+ edges.push({ from: id, to: `${depPkg}::${d.name}`, source: 'base' });
87
+ }
88
+ }
89
+ }
90
+ } else {
91
+ nodes = Object.keys(graph.packages).map((id) => ({
92
+ id,
93
+ label: id,
94
+ type: 'package',
95
+ packageDir: graph.packages[id]?.packageDir,
96
+ targets: graph.packages[id]?.targets,
97
+ }));
98
+ for (const [from, tos] of Object.entries(graph.edges || {})) {
99
+ for (const to of tos || []) {
100
+ edges.push({ from, to, source: 'base' });
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ res.json({
107
+ success: true,
108
+ data: {
109
+ nodes,
110
+ edges,
111
+ projectRoot: graph.projectRoot || null,
112
+ generatedAt: graph.generatedAt || null,
113
+ },
114
+ });
115
+ })
116
+ );
117
+
118
+ /**
119
+ * GET /api/v1/modules/browse-dirs
120
+ * 浏览项目目录结构 — 供前端选择要扫描的文件夹
121
+ */
122
+ router.get(
123
+ '/browse-dirs',
124
+ asyncHandler(async (req, res) => {
125
+ const container = getServiceContainer();
126
+ const moduleService = container.get('moduleService');
127
+
128
+ await moduleService.load();
129
+
130
+ const basePath = req.query.path || '';
131
+ const maxDepth = Math.min(Number.parseInt(req.query.depth || '3', 10), 5);
132
+
133
+ const dirs = await moduleService.browseDirectories(basePath, maxDepth);
134
+
135
+ res.json({
136
+ success: true,
137
+ data: {
138
+ directories: dirs,
139
+ total: dirs.length,
140
+ basePath: basePath || '.',
141
+ projectRoot: moduleService.getProjectInfo().projectRoot,
142
+ },
143
+ });
144
+ })
145
+ );
146
+
147
+ /**
148
+ * POST /api/v1/modules/scan-folder
149
+ * 扫描任意目录 — 直接走 AI 管线(无需 Discoverer 检测)
150
+ */
151
+ router.post(
152
+ '/scan-folder',
153
+ asyncHandler(async (req, res) => {
154
+ const { path: folderPath, options = {} } = req.body;
155
+
156
+ if (!folderPath) {
157
+ throw new ValidationError('path (relative folder path) is required');
158
+ }
159
+
160
+ const container = getServiceContainer();
161
+ const moduleService = container.get('moduleService');
162
+
163
+ await moduleService.load();
164
+
165
+ const result = await moduleService.scanFolder(folderPath, options);
166
+
167
+ res.json({
168
+ success: true,
169
+ data: result,
170
+ });
171
+ })
172
+ );
173
+
174
+ /**
175
+ * POST /api/v1/modules/scan-folder/stream
176
+ * 流式扫描任意目录 — SSE Session 架构
177
+ */
178
+ router.post(
179
+ '/scan-folder/stream',
180
+ asyncHandler(async (req, res) => {
181
+ const { path: folderPath, options = {} } = req.body;
182
+
183
+ if (!folderPath) {
184
+ throw new ValidationError('path (relative folder path) is required');
185
+ }
186
+
187
+ const container = getServiceContainer();
188
+ const moduleService = container.get('moduleService');
189
+
190
+ await moduleService.load();
191
+
192
+ const sessionId = createStreamSession();
193
+ const session = getStreamSession(sessionId);
194
+
195
+ res.json({ sessionId });
196
+
197
+ // 异步执行扫描,事件推送到 session
198
+ setImmediate(async () => {
199
+ try {
200
+ const result = await moduleService.scanFolder(folderPath, {
201
+ ...options,
202
+ onProgress: (evt) => {
203
+ if (session) session.push(evt);
204
+ },
205
+ });
206
+
207
+ if (session) {
208
+ session.push({
209
+ type: 'scan:result',
210
+ recipes: result.recipes || [],
211
+ scannedFiles: result.scannedFiles || [],
212
+ message: result.message || '',
213
+ noAi: !!result.noAi,
214
+ });
215
+ session.push({ type: 'scan:done' });
216
+ }
217
+ } catch (err) {
218
+ logger.error(`[modules] scan-folder/stream error: ${err.message}`);
219
+ if (session) {
220
+ session.push({ type: 'scan:error', message: err.message });
221
+ session.push({ type: 'scan:done' });
222
+ }
223
+ }
224
+ });
225
+ })
226
+ );
227
+
228
+ /**
229
+ * POST /api/v1/modules/target-files
230
+ * 获取模块的文件列表
231
+ */
232
+ router.post(
233
+ '/target-files',
234
+ asyncHandler(async (req, res) => {
235
+ const { target, targetName } = req.body;
236
+
237
+ if (!target && !targetName) {
238
+ throw new ValidationError('target object or targetName is required');
239
+ }
240
+
241
+ const container = getServiceContainer();
242
+ const moduleService = container.get('moduleService');
243
+
244
+ await moduleService.load();
245
+
246
+ let resolvedTarget = target;
247
+ if (!resolvedTarget && targetName) {
248
+ const targets = await moduleService.listTargets();
249
+ resolvedTarget = targets.find((t) => t.name === targetName);
250
+ if (!resolvedTarget) {
251
+ return res.status(404).json({
252
+ success: false,
253
+ error: { code: 'NOT_FOUND', message: `Module not found: ${targetName}` },
254
+ });
255
+ }
256
+ }
257
+
258
+ const files = await moduleService.getTargetFiles(resolvedTarget);
259
+
260
+ res.json({
261
+ success: true,
262
+ data: {
263
+ target: resolvedTarget.name || targetName,
264
+ files,
265
+ total: files.length,
266
+ },
267
+ });
268
+ })
269
+ );
270
+
271
+ /**
272
+ * POST /api/v1/modules/scan
273
+ * AI 扫描模块,发现候选项
274
+ */
275
+ router.post(
276
+ '/scan',
277
+ asyncHandler(async (req, res) => {
278
+ const { target, targetName, options = {} } = req.body;
279
+
280
+ if (!target && !targetName) {
281
+ throw new ValidationError('target object or targetName is required');
282
+ }
283
+
284
+ const container = getServiceContainer();
285
+ const moduleService = container.get('moduleService');
286
+
287
+ await moduleService.load();
288
+
289
+ let resolvedTarget = target;
290
+ if (!resolvedTarget && targetName) {
291
+ const targets = await moduleService.listTargets();
292
+ resolvedTarget = targets.find((t) => t.name === targetName);
293
+ if (!resolvedTarget) {
294
+ return res.status(404).json({
295
+ success: false,
296
+ error: { code: 'NOT_FOUND', message: `Module not found: ${targetName}` },
297
+ });
298
+ }
299
+ }
300
+
301
+ logger.info('Module scan started via dashboard', {
302
+ target: resolvedTarget.name,
303
+ discoverer: resolvedTarget.discovererId,
304
+ });
305
+ const result = await moduleService.scanTarget(resolvedTarget, options);
306
+
307
+ res.json({
308
+ success: true,
309
+ data: result,
310
+ });
311
+ })
312
+ );
313
+
314
+ // ── 流式 Target 扫描(SSE Session + EventSource 架构) ─────────
315
+
316
+ /**
317
+ * POST /api/v1/modules/scan/stream
318
+ * 创建流式扫描会话,后台异步执行 AI 扫描
319
+ */
320
+ router.post(
321
+ '/scan/stream',
322
+ asyncHandler(async (req, res) => {
323
+ const { target, targetName, options = {} } = req.body;
324
+
325
+ if (!target && !targetName) {
326
+ throw new ValidationError('target object or targetName is required');
327
+ }
328
+
329
+ const container = getServiceContainer();
330
+ const moduleService = container.get('moduleService');
331
+
332
+ await moduleService.load();
333
+
334
+ let resolvedTarget = target;
335
+ if (!resolvedTarget && targetName) {
336
+ const targets = await moduleService.listTargets();
337
+ resolvedTarget = targets.find((t) => t.name === targetName);
338
+ if (!resolvedTarget) {
339
+ return res.status(404).json({
340
+ success: false,
341
+ error: { code: 'NOT_FOUND', message: `Module not found: ${targetName}` },
342
+ });
343
+ }
344
+ }
345
+
346
+ // 创建 SSE session
347
+ const session = createStreamSession('scan');
348
+ const tName = resolvedTarget.name || targetName;
349
+
350
+ // 立即返回 sessionId
351
+ res.json({ sessionId: session.sessionId });
352
+
353
+ // 异步执行扫描,通过 session 推送进度事件
354
+ setImmediate(async () => {
355
+ try {
356
+ logger.info('Module stream scan started', {
357
+ target: tName,
358
+ sessionId: session.sessionId,
359
+ });
360
+ const result = await moduleService.scanTarget(resolvedTarget, {
361
+ ...options,
362
+ onProgress(event) {
363
+ session.send(event);
364
+ },
365
+ });
366
+
367
+ // 发送最终结果
368
+ session.send({
369
+ type: 'scan:result',
370
+ recipes: result.recipes || [],
371
+ scannedFiles: result.scannedFiles || [],
372
+ message: result.message || '',
373
+ noAi: !!result.noAi,
374
+ recipeCount: (result.recipes || []).length,
375
+ fileCount: (result.scannedFiles || []).length,
376
+ });
377
+ session.end();
378
+ } catch (err) {
379
+ logger.error('Module stream scan failed', { target: tName, error: err.message });
380
+ session.error(err.message, 'SCAN_ERROR');
381
+ }
382
+ });
383
+ })
384
+ );
385
+
386
+ /**
387
+ * GET /api/v1/modules/scan/events/:sessionId
388
+ * EventSource SSE 端点 — 消费扫描进度事件
389
+ */
390
+ router.get('/scan/events/:sessionId', (req, res) => {
391
+ const session = getStreamSession(req.params.sessionId);
392
+ if (!session) {
393
+ return res.status(404).json({ success: false, error: 'Session not found or expired' });
394
+ }
395
+
396
+ // ─── SSE Headers ───
397
+ res.setHeader('Content-Type', 'text/event-stream');
398
+ res.setHeader('Cache-Control', 'no-cache');
399
+ res.setHeader('Connection', 'keep-alive');
400
+ res.setHeader('X-Accel-Buffering', 'no');
401
+ res.flushHeaders();
402
+
403
+ if (res.socket) {
404
+ res.socket.setNoDelay(true);
405
+ res.socket.setTimeout(0);
406
+ }
407
+
408
+ function writeEvent(event) {
409
+ if (res.writableEnded) return;
410
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
411
+ }
412
+
413
+ // 1) 回放缓冲区
414
+ let isDone = false;
415
+ for (const event of session.buffer) {
416
+ writeEvent(event);
417
+ if (event.type === 'stream:done' || event.type === 'stream:error') {
418
+ isDone = true;
419
+ }
420
+ }
421
+
422
+ if (isDone || session.completed) {
423
+ res.end();
424
+ return;
425
+ }
426
+
427
+ // 2) 订阅实时事件
428
+ const unsubscribe = session.on((event) => {
429
+ writeEvent(event);
430
+ if (event.type === 'stream:done' || event.type === 'stream:error') {
431
+ unsubscribe();
432
+ clearInterval(heartbeat);
433
+ res.end();
434
+ }
435
+ });
436
+
437
+ // 心跳保活 (每 15 秒)
438
+ const heartbeat = setInterval(() => {
439
+ if (res.writableEnded) {
440
+ clearInterval(heartbeat);
441
+ return;
442
+ }
443
+ res.write(`: ping ${Date.now()}\n\n`);
444
+ }, 15_000);
445
+
446
+ // 客户端断开连接时清理
447
+ res.on('close', () => {
448
+ unsubscribe();
449
+ clearInterval(heartbeat);
450
+ });
451
+ });
452
+
453
+ /**
454
+ * POST /api/v1/modules/scan-project
455
+ * 全项目扫描:AI 提取候选 + Guard 审计
456
+ */
457
+ router.post(
458
+ '/scan-project',
459
+ asyncHandler(async (req, res) => {
460
+ const { options = {} } = req.body;
461
+
462
+ const container = getServiceContainer();
463
+ const moduleService = container.get('moduleService');
464
+
465
+ await moduleService.load();
466
+ logger.info('Full project scan started via dashboard (ModuleService)');
467
+ const result = await moduleService.scanProject(options);
468
+
469
+ res.json({
470
+ success: true,
471
+ data: result,
472
+ });
473
+ })
474
+ );
475
+
476
+ /**
477
+ * POST /api/v1/modules/update-map
478
+ * 刷新模块映射(替代 spm-map)
479
+ */
480
+ router.post(
481
+ '/update-map',
482
+ asyncHandler(async (req, res) => {
483
+ const container = getServiceContainer();
484
+ const moduleService = container.get('moduleService');
485
+
486
+ const result = await moduleService.updateModuleMap({
487
+ aggressive: true,
488
+ });
489
+
490
+ logger.info('Module map updated via dashboard', { result });
491
+ res.json({
492
+ success: true,
493
+ data: result,
494
+ });
495
+ })
496
+ );
497
+
498
+ /**
499
+ * GET /api/v1/modules/project-info
500
+ * 项目信息(检测到的语言、框架等)
501
+ */
502
+ router.get(
503
+ '/project-info',
504
+ asyncHandler(async (req, res) => {
505
+ const container = getServiceContainer();
506
+ const moduleService = container.get('moduleService');
507
+
508
+ await moduleService.load();
509
+ const info = moduleService.getProjectInfo();
510
+
511
+ res.json({
512
+ success: true,
513
+ data: info,
514
+ });
515
+ })
516
+ );
517
+
518
+ /**
519
+ * POST /api/v1/modules/bootstrap
520
+ * 冷启动:快速骨架 + 异步逐维度填充
521
+ */
522
+ router.post(
523
+ '/bootstrap',
524
+ asyncHandler(async (req, res) => {
525
+ const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
526
+
527
+ const container = getServiceContainer();
528
+ const chatAgent = container.get('chatAgent');
529
+
530
+ logger.info('Bootstrap cold start initiated (ModuleService path)');
531
+
532
+ const bootstrapResult = await chatAgent.executeTool('bootstrap_knowledge', {
533
+ maxFiles: maxFiles || 500,
534
+ skipGuard: skipGuard || false,
535
+ contentMaxLines: contentMaxLines || 120,
536
+ loadSkills: true,
537
+ });
538
+
539
+ res.json({
540
+ success: true,
541
+ data: {
542
+ ...bootstrapResult,
543
+ asyncFill: true,
544
+ },
545
+ });
546
+ })
547
+ );
548
+
549
+ /**
550
+ * GET /api/v1/modules/bootstrap/status
551
+ * 查询 bootstrap 异步填充进度
552
+ */
553
+ router.get(
554
+ '/bootstrap/status',
555
+ asyncHandler(async (req, res) => {
556
+ const container = getServiceContainer();
557
+
558
+ let taskManager = null;
559
+ try {
560
+ taskManager = container.get('bootstrapTaskManager');
561
+ } catch {
562
+ /* not registered */
563
+ }
564
+ if (!taskManager) {
565
+ return res.json({
566
+ success: true,
567
+ data: { status: 'idle', message: 'No bootstrap task manager initialized' },
568
+ });
569
+ }
570
+
571
+ res.json({
572
+ success: true,
573
+ data: taskManager.getSessionStatus(),
574
+ });
575
+ })
576
+ );
577
+
578
+ export default router;
@@ -4,11 +4,11 @@
4
4
  */
5
5
 
6
6
  import express from 'express';
7
- import { getPerformanceMonitor } from '../../infrastructure/monitoring/PerformanceMonitor.js';
8
- import { getErrorTracker } from '../../infrastructure/monitoring/ErrorTracker.js';
9
7
  import { getCacheAdapter } from '../../infrastructure/cache/UnifiedCacheAdapter.js';
10
- import { getRealtimeService } from '../../infrastructure/realtime/RealtimeService.js';
11
8
  import Logger from '../../infrastructure/logging/Logger.js';
9
+ import { getErrorTracker } from '../../infrastructure/monitoring/ErrorTracker.js';
10
+ import { getPerformanceMonitor } from '../../infrastructure/monitoring/PerformanceMonitor.js';
11
+ import { getRealtimeService } from '../../infrastructure/realtime/RealtimeService.js';
12
12
 
13
13
  const router = express.Router();
14
14
 
@@ -30,7 +30,7 @@ router.get('/health', async (req, res) => {
30
30
  connectedClients: clientCount,
31
31
  message: `${clientCount} 个客户端已连接`,
32
32
  };
33
- } catch (error) {
33
+ } catch (_error) {
34
34
  // WebSocket 服务未初始化
35
35
  }
36
36
 
@@ -207,7 +207,7 @@ router.get('/realtime', (req, res) => {
207
207
  timestamp: new Date().toISOString(),
208
208
  },
209
209
  });
210
- } catch (error) {
210
+ } catch (_error) {
211
211
  // WebSocket 未启用
212
212
  res.json({
213
213
  success: true,
@@ -242,7 +242,7 @@ router.get('/dashboard', async (req, res) => {
242
242
  enabled: true,
243
243
  connectedClients: realtimeService.getConnectedClients(),
244
244
  };
245
- } catch (error) {
245
+ } catch (_error) {
246
246
  // WebSocket 未初始化
247
247
  }
248
248