autosnippet 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +230 -324
  2. package/bin/api-server.js +1 -1
  3. package/bin/cli.js +204 -244
  4. package/bin/mcp-server.js +5 -3
  5. package/config/knowledge-base.config.js +132 -132
  6. package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
  7. package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
  8. package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
  9. package/dashboard/dist/index.html +3 -3
  10. package/lib/bootstrap.js +8 -8
  11. package/lib/cli/AiScanService.js +86 -40
  12. package/lib/cli/KnowledgeSyncService.js +113 -74
  13. package/lib/cli/SetupService.js +439 -277
  14. package/lib/cli/UpgradeService.js +63 -100
  15. package/lib/core/AstAnalyzer.js +276 -597
  16. package/lib/core/ast/ProjectGraph.js +101 -40
  17. package/lib/core/ast/ensure-grammars.js +232 -0
  18. package/lib/core/ast/index.js +115 -0
  19. package/lib/core/ast/lang-dart.js +661 -0
  20. package/lib/core/ast/lang-go.js +530 -0
  21. package/lib/core/ast/lang-java.js +435 -0
  22. package/lib/core/ast/lang-javascript.js +272 -0
  23. package/lib/core/ast/lang-kotlin.js +423 -0
  24. package/lib/core/ast/lang-objc.js +388 -0
  25. package/lib/core/ast/lang-python.js +371 -0
  26. package/lib/core/ast/lang-swift.js +337 -0
  27. package/lib/core/ast/lang-typescript.js +503 -0
  28. package/lib/core/capability/CapabilityProbe.js +18 -9
  29. package/lib/core/constitution/Constitution.js +2 -3
  30. package/lib/core/constitution/ConstitutionValidator.js +65 -24
  31. package/lib/core/discovery/DartDiscoverer.js +534 -0
  32. package/lib/core/discovery/DiscovererRegistry.js +83 -0
  33. package/lib/core/discovery/GenericDiscoverer.js +225 -0
  34. package/lib/core/discovery/GoDiscoverer.js +541 -0
  35. package/lib/core/discovery/JvmDiscoverer.js +506 -0
  36. package/lib/core/discovery/NodeDiscoverer.js +466 -0
  37. package/lib/core/discovery/ProjectDiscoverer.js +93 -0
  38. package/lib/core/discovery/PythonDiscoverer.js +338 -0
  39. package/lib/core/discovery/SpmDiscoverer.js +5 -0
  40. package/lib/core/discovery/index.js +53 -0
  41. package/lib/core/enhancement/EnhancementPack.js +71 -0
  42. package/lib/core/enhancement/EnhancementRegistry.js +47 -0
  43. package/lib/core/enhancement/android-enhancement.js +102 -0
  44. package/lib/core/enhancement/django-enhancement.js +70 -0
  45. package/lib/core/enhancement/fastapi-enhancement.js +63 -0
  46. package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
  47. package/lib/core/enhancement/go-web-enhancement.js +201 -0
  48. package/lib/core/enhancement/index.js +65 -0
  49. package/lib/core/enhancement/node-server-enhancement.js +88 -0
  50. package/lib/core/enhancement/react-enhancement.js +86 -0
  51. package/lib/core/enhancement/spring-enhancement.js +112 -0
  52. package/lib/core/enhancement/vue-enhancement.js +96 -0
  53. package/lib/core/gateway/Gateway.js +8 -9
  54. package/lib/core/gateway/GatewayActionRegistry.js +1 -1
  55. package/lib/core/permission/PermissionManager.js +12 -8
  56. package/lib/domain/index.js +13 -9
  57. package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
  58. package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
  59. package/lib/domain/knowledge/Lifecycle.js +22 -22
  60. package/lib/domain/knowledge/index.js +9 -12
  61. package/lib/domain/knowledge/values/Constraints.js +31 -21
  62. package/lib/domain/knowledge/values/Content.js +21 -13
  63. package/lib/domain/knowledge/values/Quality.js +31 -18
  64. package/lib/domain/knowledge/values/Reasoning.js +20 -12
  65. package/lib/domain/knowledge/values/Relations.js +37 -25
  66. package/lib/domain/knowledge/values/Stats.js +18 -12
  67. package/lib/domain/knowledge/values/index.js +4 -3
  68. package/lib/domain/snippet/Snippet.js +35 -10
  69. package/lib/external/ai/AiFactory.js +48 -16
  70. package/lib/external/ai/AiProvider.js +184 -90
  71. package/lib/external/ai/providers/ClaudeProvider.js +25 -12
  72. package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
  73. package/lib/external/ai/providers/MockProvider.js +9 -3
  74. package/lib/external/ai/providers/OpenAiProvider.js +51 -29
  75. package/lib/external/mcp/McpServer.js +66 -36
  76. package/lib/external/mcp/errorHandler.js +23 -11
  77. package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
  78. package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
  79. package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
  80. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
  81. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
  82. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
  83. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
  84. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
  85. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
  86. package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
  87. package/lib/external/mcp/handlers/bootstrap.js +691 -168
  88. package/lib/external/mcp/handlers/browse.js +66 -22
  89. package/lib/external/mcp/handlers/candidate.js +118 -35
  90. package/lib/external/mcp/handlers/consolidated.js +49 -17
  91. package/lib/external/mcp/handlers/guard.js +104 -39
  92. package/lib/external/mcp/handlers/knowledge.js +60 -36
  93. package/lib/external/mcp/handlers/search.js +43 -14
  94. package/lib/external/mcp/handlers/skill.js +120 -45
  95. package/lib/external/mcp/handlers/structure.js +240 -86
  96. package/lib/external/mcp/handlers/system.js +42 -12
  97. package/lib/external/mcp/handlers/wiki.js +58 -33
  98. package/lib/external/mcp/tools.js +306 -123
  99. package/lib/http/HttpServer.js +72 -47
  100. package/lib/http/middleware/RateLimiter.js +5 -3
  101. package/lib/http/middleware/errorHandler.js +6 -1
  102. package/lib/http/middleware/requestLogger.js +14 -3
  103. package/lib/http/middleware/roleResolver.js +30 -23
  104. package/lib/http/routes/ai.js +387 -265
  105. package/lib/http/routes/auth.js +81 -61
  106. package/lib/http/routes/candidates.js +430 -320
  107. package/lib/http/routes/commands.js +289 -189
  108. package/lib/http/routes/extract.js +158 -125
  109. package/lib/http/routes/guardRules.js +309 -217
  110. package/lib/http/routes/knowledge.js +213 -154
  111. package/lib/http/routes/modules.js +578 -0
  112. package/lib/http/routes/monitoring.js +6 -6
  113. package/lib/http/routes/recipes.js +104 -93
  114. package/lib/http/routes/search.js +361 -305
  115. package/lib/http/routes/skills.js +145 -98
  116. package/lib/http/routes/snippets.js +42 -30
  117. package/lib/http/routes/spm.js +3 -405
  118. package/lib/http/routes/violations.js +113 -93
  119. package/lib/http/routes/wiki.js +211 -170
  120. package/lib/http/utils/routeHelpers.js +3 -1
  121. package/lib/http/utils/sse-sessions.js +16 -6
  122. package/lib/http/utils/sse.js +15 -5
  123. package/lib/infrastructure/audit/AuditLogger.js +5 -2
  124. package/lib/infrastructure/audit/AuditStore.js +10 -7
  125. package/lib/infrastructure/cache/CacheService.js +3 -1
  126. package/lib/infrastructure/cache/GraphCache.js +8 -4
  127. package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
  128. package/lib/infrastructure/config/ConfigLoader.js +9 -5
  129. package/lib/infrastructure/config/Defaults.js +30 -10
  130. package/lib/infrastructure/config/Paths.js +28 -8
  131. package/lib/infrastructure/config/TriggerSymbol.js +22 -10
  132. package/lib/infrastructure/database/DatabaseConnection.js +15 -10
  133. package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
  134. package/lib/infrastructure/external/ClipboardManager.js +6 -2
  135. package/lib/infrastructure/external/NativeUi.js +50 -43
  136. package/lib/infrastructure/external/OpenBrowser.js +14 -17
  137. package/lib/infrastructure/external/XcodeAutomation.js +14 -258
  138. package/lib/infrastructure/logging/Logger.js +46 -30
  139. package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
  140. package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
  141. package/lib/infrastructure/paths/HeaderResolver.js +25 -9
  142. package/lib/infrastructure/paths/PathFinder.js +34 -12
  143. package/lib/infrastructure/plugin/PluginManager.js +26 -8
  144. package/lib/infrastructure/realtime/RealtimeService.js +2 -2
  145. package/lib/infrastructure/vector/Chunker.js +22 -7
  146. package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
  147. package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
  148. package/lib/infrastructure/vector/VectorStore.js +28 -10
  149. package/lib/injection/ServiceContainer.js +247 -93
  150. package/lib/platform/ios/index.js +63 -0
  151. package/lib/platform/ios/routes/spm.js +437 -0
  152. package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
  153. package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
  154. package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
  155. package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
  156. package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
  157. package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
  158. package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
  159. package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
  160. package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
  161. package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
  162. package/lib/repository/base/BaseRepository.js +7 -9
  163. package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
  164. package/lib/repository/token/TokenUsageStore.js +4 -2
  165. package/lib/service/automation/ActionPipeline.js +1 -1
  166. package/lib/service/automation/AutomationOrchestrator.js +8 -4
  167. package/lib/service/automation/ContextCollector.js +7 -5
  168. package/lib/service/automation/DirectiveDetector.js +23 -16
  169. package/lib/service/automation/FileWatcher.js +112 -56
  170. package/lib/service/automation/TriggerResolver.js +6 -4
  171. package/lib/service/automation/handlers/AlinkHandler.js +24 -12
  172. package/lib/service/automation/handlers/CreateHandler.js +19 -20
  173. package/lib/service/automation/handlers/DraftHandler.js +14 -8
  174. package/lib/service/automation/handlers/GuardHandler.js +93 -63
  175. package/lib/service/automation/handlers/HeaderHandler.js +1 -6
  176. package/lib/service/automation/handlers/SearchHandler.js +155 -88
  177. package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
  178. package/lib/service/candidate/SimilarityService.js +25 -9
  179. package/lib/service/chat/AnalystAgent.js +50 -24
  180. package/lib/service/chat/CandidateGuardrail.js +143 -17
  181. package/lib/service/chat/ChatAgent.js +759 -243
  182. package/lib/service/chat/ContextWindow.js +116 -71
  183. package/lib/service/chat/ConversationStore.js +77 -36
  184. package/lib/service/chat/EpisodicConsolidator.js +47 -23
  185. package/lib/service/chat/HandoffProtocol.js +98 -22
  186. package/lib/service/chat/Memory.js +34 -14
  187. package/lib/service/chat/ProducerAgent.js +40 -20
  188. package/lib/service/chat/ProjectSemanticMemory.js +109 -78
  189. package/lib/service/chat/ReasoningLayer.js +148 -70
  190. package/lib/service/chat/ReasoningTrace.js +44 -32
  191. package/lib/service/chat/TaskPipeline.js +39 -19
  192. package/lib/service/chat/ToolRegistry.js +48 -29
  193. package/lib/service/chat/WorkingMemory.js +44 -18
  194. package/lib/service/chat/tools.js +1096 -494
  195. package/lib/service/context/RecipeExtractor.js +132 -51
  196. package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
  197. package/lib/service/cursor/KnowledgeCompressor.js +25 -22
  198. package/lib/service/cursor/RulesGenerator.js +13 -7
  199. package/lib/service/cursor/SkillsSyncer.js +77 -27
  200. package/lib/service/cursor/TokenBudget.js +2 -2
  201. package/lib/service/cursor/TopicClassifier.js +54 -20
  202. package/lib/service/guard/ComplianceReporter.js +55 -43
  203. package/lib/service/guard/ExclusionManager.js +67 -29
  204. package/lib/service/guard/GuardCheckEngine.js +381 -86
  205. package/lib/service/guard/GuardFeedbackLoop.js +22 -10
  206. package/lib/service/guard/GuardService.js +29 -19
  207. package/lib/service/guard/RuleLearner.js +55 -23
  208. package/lib/service/guard/SourceFileCollector.js +27 -20
  209. package/lib/service/guard/ViolationsStore.js +43 -38
  210. package/lib/service/knowledge/CodeEntityGraph.js +147 -82
  211. package/lib/service/knowledge/ConfidenceRouter.js +12 -10
  212. package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
  213. package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
  214. package/lib/service/knowledge/KnowledgeService.js +222 -112
  215. package/lib/service/module/ModuleService.js +969 -0
  216. package/lib/service/quality/FeedbackCollector.js +27 -15
  217. package/lib/service/quality/QualityScorer.js +78 -24
  218. package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
  219. package/lib/service/recipe/RecipeParser.js +78 -45
  220. package/lib/service/search/CoarseRanker.js +43 -28
  221. package/lib/service/search/CrossEncoderReranker.js +32 -21
  222. package/lib/service/search/InvertedIndex.js +21 -7
  223. package/lib/service/search/MultiSignalRanker.js +90 -28
  224. package/lib/service/search/RetrievalFunnel.js +45 -24
  225. package/lib/service/search/SearchEngine.js +255 -103
  226. package/lib/service/skills/EventAggregator.js +32 -15
  227. package/lib/service/skills/SignalCollector.js +140 -64
  228. package/lib/service/skills/SkillAdvisor.js +79 -42
  229. package/lib/service/skills/SkillHooks.js +16 -14
  230. package/lib/service/snippet/PlaceholderConverter.js +5 -0
  231. package/lib/service/snippet/SnippetFactory.js +116 -99
  232. package/lib/service/snippet/SnippetInstaller.js +234 -62
  233. package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
  234. package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
  235. package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
  236. package/lib/service/wiki/WikiGenerator.js +637 -263
  237. package/lib/shared/DimensionCopyRegistry.js +472 -0
  238. package/lib/shared/LanguageService.js +399 -0
  239. package/lib/shared/PathGuard.js +45 -28
  240. package/lib/shared/RecipeReadinessChecker.js +72 -12
  241. package/lib/shared/constants.js +41 -41
  242. package/lib/shared/errors/BaseError.js +2 -2
  243. package/lib/shared/errors/index.js +4 -4
  244. package/lib/shared/similarity.js +25 -8
  245. package/lib/shared/token-utils.js +6 -2
  246. package/lib/shared/utils/common.js +12 -4
  247. package/package.json +49 -13
  248. package/scripts/bench-real-projects.mjs +256 -0
  249. package/scripts/build-native-ui.js +30 -30
  250. package/scripts/clear-old-vector-index.js +5 -35
  251. package/scripts/clear-vector-cache.js +7 -37
  252. package/scripts/collect-test-project-stats.mjs +160 -0
  253. package/scripts/diagnose-mcp.js +41 -32
  254. package/scripts/ensure-parse-package.js +6 -9
  255. package/scripts/generate-recipe-drafts.js +116 -77
  256. package/scripts/init-db.js +3 -20
  257. package/scripts/init-snippets.js +305 -0
  258. package/scripts/init-vector-db.js +173 -170
  259. package/scripts/install-cursor-skill.js +148 -104
  260. package/scripts/install-full.js +8 -21
  261. package/scripts/install-vscode-copilot.js +146 -145
  262. package/scripts/migrate-md-to-knowledge.mjs +139 -151
  263. package/scripts/postinstall-safe.js +5 -17
  264. package/scripts/recipe-audit.js +106 -82
  265. package/scripts/release.js +283 -323
  266. package/scripts/setup-mcp-config.js +60 -52
  267. package/scripts/verify-context-api.js +20 -20
  268. package/skills/autosnippet-analysis/SKILL.md +10 -6
  269. package/skills/autosnippet-candidates/SKILL.md +27 -26
  270. package/skills/autosnippet-coldstart/SKILL.md +555 -38
  271. package/skills/autosnippet-concepts/SKILL.md +349 -337
  272. package/skills/autosnippet-create/SKILL.md +5 -5
  273. package/skills/autosnippet-reference-dart/SKILL.md +543 -0
  274. package/skills/autosnippet-reference-go/SKILL.md +539 -0
  275. package/skills/autosnippet-reference-java/SKILL.md +534 -0
  276. package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
  277. package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
  278. package/skills/autosnippet-reference-objc/SKILL.md +29 -6
  279. package/skills/autosnippet-reference-python/SKILL.md +800 -0
  280. package/skills/autosnippet-reference-swift/SKILL.md +70 -14
  281. package/skills/autosnippet-structure/SKILL.md +4 -4
  282. package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
  283. package/templates/recipes-setup/README.md +2 -2
  284. package/templates/recipes-setup/_template.md +1 -1
  285. package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
  286. package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
  287. package/resources/asd-entry/main.swift +0 -159
  288. package/scripts/build-asd-entry.js +0 -51
  289. package/scripts/init-xcode-snippets.js +0 -311
  290. package/template.json +0 -39
@@ -1,407 +1,5 @@
1
1
  /**
2
- * SPM API 路由
3
- * SPM Target 管理、依赖关系图、文件扫描
2
+ * @deprecated Moved to lib/platform/ios/routes/spm.js
3
+ * This re-export shim maintains backward compatibility.
4
4
  */
5
-
6
- import express from 'express';
7
- import { asyncHandler } from '../middleware/errorHandler.js';
8
- import { getServiceContainer } from '../../injection/ServiceContainer.js';
9
- import { ValidationError } from '../../shared/errors/index.js';
10
- import Logger from '../../infrastructure/logging/Logger.js';
11
- import { createStreamSession, getStreamSession } from '../utils/sse-sessions.js';
12
-
13
- const router = express.Router();
14
- const logger = Logger.getInstance();
15
-
16
- /**
17
- * GET /api/v1/spm/targets
18
- * 获取所有 SPM Target 列表
19
- */
20
- router.get('/targets', asyncHandler(async (req, res) => {
21
- const container = getServiceContainer();
22
- const spmService = container.get('spmService');
23
-
24
- const targets = await spmService.listTargets();
25
-
26
- res.json({
27
- success: true,
28
- data: {
29
- targets,
30
- total: targets.length,
31
- },
32
- });
33
- }));
34
-
35
- /**
36
- * GET /api/v1/spm/dep-graph
37
- * 获取 SPM 依赖关系图
38
- */
39
- router.get('/dep-graph', asyncHandler(async (req, res) => {
40
- const container = getServiceContainer();
41
- const spmService = container.get('spmService');
42
-
43
- const level = req.query.level || 'package'; // 'package' | 'target'
44
- const graph = await spmService.getDependencyGraph({ level });
45
-
46
- if (!graph || (!graph.nodes && !graph.packages)) {
47
- return res.json({
48
- success: true,
49
- data: { nodes: [], edges: [], projectRoot: null },
50
- });
51
- }
52
-
53
- // 标准化为 { nodes, edges } 格式
54
- let nodes = [];
55
- let edges = [];
56
-
57
- if (graph.nodes && graph.edges) {
58
- // 已经是标准格式
59
- nodes = graph.nodes;
60
- edges = graph.edges;
61
- } else if (graph.packages) {
62
- // 从 packages 构建图
63
- if (level === 'target') {
64
- for (const [pkgName, pkgInfo] of Object.entries(graph.packages)) {
65
- const targetsInfo = pkgInfo?.targetsInfo || {};
66
- for (const [targetName, info] of Object.entries(targetsInfo)) {
67
- const id = `${pkgName}::${targetName}`;
68
- nodes.push({
69
- id,
70
- label: targetName,
71
- type: 'target',
72
- packageName: pkgName,
73
- });
74
- for (const d of (info?.dependencies || [])) {
75
- if (!d?.name) continue;
76
- const depPkg = d?.package || pkgName;
77
- edges.push({ from: id, to: `${depPkg}::${d.name}`, source: 'base' });
78
- }
79
- }
80
- }
81
- } else {
82
- nodes = Object.keys(graph.packages).map(id => ({
83
- id,
84
- label: id,
85
- type: 'package',
86
- packageDir: graph.packages[id]?.packageDir,
87
- targets: graph.packages[id]?.targets,
88
- }));
89
- for (const [from, tos] of Object.entries(graph.edges || {})) {
90
- for (const to of (tos || [])) {
91
- edges.push({ from, to, source: 'base' });
92
- }
93
- }
94
- }
95
- }
96
-
97
- res.json({
98
- success: true,
99
- data: {
100
- nodes,
101
- edges,
102
- projectRoot: graph.projectRoot || null,
103
- generatedAt: graph.generatedAt || null,
104
- },
105
- });
106
- }));
107
-
108
- /**
109
- * POST /api/v1/spm/target-files
110
- * 获取 Target 的文件列表
111
- */
112
- router.post('/target-files', asyncHandler(async (req, res) => {
113
- const { target, targetName } = req.body;
114
-
115
- if (!target && !targetName) {
116
- throw new ValidationError('target object or targetName is required');
117
- }
118
-
119
- const container = getServiceContainer();
120
- const spmService = container.get('spmService');
121
-
122
- let resolvedTarget = target;
123
- if (!resolvedTarget && targetName) {
124
- const targets = await spmService.listTargets();
125
- resolvedTarget = targets.find(t => t.name === targetName);
126
- if (!resolvedTarget) {
127
- return res.status(404).json({
128
- success: false,
129
- error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
130
- });
131
- }
132
- }
133
-
134
- const files = await spmService.getTargetFiles(resolvedTarget);
135
-
136
- res.json({
137
- success: true,
138
- data: {
139
- target: resolvedTarget.name || targetName,
140
- files,
141
- total: files.length,
142
- },
143
- });
144
- }));
145
-
146
- /**
147
- * POST /api/v1/spm/scan
148
- * AI 扫描 Target,发现候选项
149
- */
150
- router.post('/scan', asyncHandler(async (req, res) => {
151
- const { target, targetName, options = {} } = req.body;
152
-
153
- if (!target && !targetName) {
154
- throw new ValidationError('target object or targetName is required');
155
- }
156
-
157
- const container = getServiceContainer();
158
- const spmService = container.get('spmService');
159
-
160
- let resolvedTarget = target;
161
- if (!resolvedTarget && targetName) {
162
- const targets = await spmService.listTargets();
163
- resolvedTarget = targets.find(t => t.name === targetName);
164
- if (!resolvedTarget) {
165
- return res.status(404).json({
166
- success: false,
167
- error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
168
- });
169
- }
170
- }
171
-
172
- logger.info('SPM scan started via dashboard', { target: resolvedTarget.name });
173
- const result = await spmService.scanTarget(resolvedTarget, options);
174
-
175
- res.json({
176
- success: true,
177
- data: result,
178
- });
179
- }));
180
-
181
- // ── 流式 Target 扫描(SSE Session + EventSource 架构) ─────────
182
-
183
- /**
184
- * POST /api/v1/spm/scan/stream
185
- * 创建流式扫描会话,后台异步执行 AI 扫描
186
- *
187
- * 协议事件(通过 SSE session 缓冲 + EventSource 交付):
188
- * scan:started — 扫描启动
189
- * scan:files-loaded — 文件列表就绪,含 files[] + count
190
- * scan:reading — 读取文件内容中
191
- * scan:ai-extracting — AI 提取开始(耗时阶段)
192
- * scan:enriching — 后处理阶段
193
- * scan:completed — 最终结果 {recipes, scannedFiles, recipeCount, fileCount}
194
- * scan:error — 发生错误
195
- * stream:done — 会话结束标记
196
- */
197
- router.post('/scan/stream', asyncHandler(async (req, res) => {
198
- const { target, targetName, options = {} } = req.body;
199
-
200
- if (!target && !targetName) {
201
- throw new ValidationError('target object or targetName is required');
202
- }
203
-
204
- const container = getServiceContainer();
205
- const spmService = container.get('spmService');
206
-
207
- let resolvedTarget = target;
208
- if (!resolvedTarget && targetName) {
209
- const targets = await spmService.listTargets();
210
- resolvedTarget = targets.find(t => t.name === targetName);
211
- if (!resolvedTarget) {
212
- return res.status(404).json({
213
- success: false,
214
- error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
215
- });
216
- }
217
- }
218
-
219
- // 创建 SSE session
220
- const session = createStreamSession('scan');
221
- const tName = resolvedTarget.name || targetName;
222
-
223
- // 立即返回 sessionId
224
- res.json({ sessionId: session.sessionId });
225
-
226
- // 异步执行扫描,通过 session 推送进度事件
227
- setImmediate(async () => {
228
- try {
229
- logger.info('SPM stream scan started', { target: tName, sessionId: session.sessionId });
230
- const result = await spmService.scanTarget(resolvedTarget, {
231
- ...options,
232
- onProgress(event) {
233
- session.send(event);
234
- },
235
- });
236
-
237
- // 发送最终结果
238
- session.send({
239
- type: 'scan:result',
240
- recipes: result.recipes || [],
241
- scannedFiles: result.scannedFiles || [],
242
- message: result.message || '',
243
- recipeCount: (result.recipes || []).length,
244
- fileCount: (result.scannedFiles || []).length,
245
- });
246
- session.end();
247
- } catch (err) {
248
- logger.error('SPM stream scan failed', { target: tName, error: err.message });
249
- session.error(err.message, 'SCAN_ERROR');
250
- }
251
- });
252
- }));
253
-
254
- /**
255
- * GET /api/v1/spm/scan/events/:sessionId
256
- * EventSource SSE 端点 — 消费扫描进度事件
257
- *
258
- * 复用 chat/events 相同的 SSE 交付模式:回放缓冲 → 订阅实时 → 心跳保活
259
- */
260
- router.get('/scan/events/:sessionId', (req, res) => {
261
- const session = getStreamSession(req.params.sessionId);
262
- if (!session) {
263
- return res.status(404).json({ success: false, error: 'Session not found or expired' });
264
- }
265
-
266
- // ─── SSE Headers ───
267
- res.setHeader('Content-Type', 'text/event-stream');
268
- res.setHeader('Cache-Control', 'no-cache');
269
- res.setHeader('Connection', 'keep-alive');
270
- res.setHeader('X-Accel-Buffering', 'no');
271
- res.flushHeaders();
272
-
273
- if (res.socket) {
274
- res.socket.setNoDelay(true);
275
- res.socket.setTimeout(0);
276
- }
277
-
278
- function writeEvent(event) {
279
- if (res.writableEnded) return;
280
- res.write(`data: ${JSON.stringify(event)}\n\n`);
281
- }
282
-
283
- // 1) 回放缓冲区
284
- let isDone = false;
285
- for (const event of session.buffer) {
286
- writeEvent(event);
287
- if (event.type === 'stream:done' || event.type === 'stream:error') {
288
- isDone = true;
289
- }
290
- }
291
-
292
- if (isDone || session.completed) {
293
- res.end();
294
- return;
295
- }
296
-
297
- // 2) 订阅实时事件
298
- const unsubscribe = session.on((event) => {
299
- writeEvent(event);
300
- if (event.type === 'stream:done' || event.type === 'stream:error') {
301
- unsubscribe();
302
- clearInterval(heartbeat);
303
- res.end();
304
- }
305
- });
306
-
307
- // 心跳保活 (每 15 秒)
308
- const heartbeat = setInterval(() => {
309
- if (res.writableEnded) {
310
- clearInterval(heartbeat);
311
- return;
312
- }
313
- res.write(`: ping ${Date.now()}\n\n`);
314
- }, 15_000);
315
-
316
- // 客户端断开连接时清理
317
- res.on('close', () => {
318
- unsubscribe();
319
- clearInterval(heartbeat);
320
- });
321
- });
322
-
323
- /**
324
- * POST /api/v1/spm/scan-project
325
- * 全项目扫描:AI 提取候选 + Guard 审计
326
- */
327
- router.post('/scan-project', asyncHandler(async (req, res) => {
328
- const { options = {} } = req.body;
329
-
330
- const container = getServiceContainer();
331
- const spmService = container.get('spmService');
332
-
333
- logger.info('Full project scan started via dashboard');
334
- const result = await spmService.scanProject(options);
335
-
336
- res.json({
337
- success: true,
338
- data: result,
339
- });
340
- }));
341
-
342
- /**
343
- * POST /api/v1/spm/bootstrap
344
- * 冷启动:快速骨架 + 异步逐维度填充(v5)
345
- *
346
- * 执行策略:
347
- * ① 同步阶段: Phase 1-4(文件收集 + AST + SPM + Guard + 骨架响应)→ 立即返回
348
- * ② 异步阶段: Phase 5/5.5(逐维度提取 + Candidate/Skill 创建)→ 后台逐一执行
349
- * ③ 进度推送: 通过 Socket.io 实时推送每个维度的完成状态
350
- *
351
- * 前端立即获得骨架 + 任务清单,每个维度完成后通过 Socket.io 推送更新。
352
- */
353
- router.post('/bootstrap', asyncHandler(async (req, res) => {
354
- const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
355
-
356
- const container = getServiceContainer();
357
- const chatAgent = container.get('chatAgent');
358
-
359
- logger.info('Bootstrap cold start initiated (v5: async fill mode)');
360
-
361
- // ── 同步阶段: 快速执行 Phase 1-4 → 返回骨架 ──
362
- const bootstrapResult = await chatAgent.executeTool('bootstrap_knowledge', {
363
- maxFiles: maxFiles || 500,
364
- skipGuard: skipGuard || false,
365
- contentMaxLines: contentMaxLines || 120,
366
- loadSkills: true,
367
- });
368
-
369
- // 立即返回骨架结果给前端
370
- res.json({
371
- success: true,
372
- data: {
373
- ...bootstrapResult,
374
- asyncFill: true, // 告知前端:内容正在异步填充中
375
- },
376
- });
377
-
378
- // 注意:Phase 5/5.5 异步填充已在 bootstrapKnowledge() 内部通过 setImmediate 启动
379
- // 进度通过 BootstrapTaskManager → Socket.io 推送到前端
380
- }));
381
-
382
- /**
383
- * GET /api/v1/spm/bootstrap/status
384
- * 查询当前 bootstrap 异步填充进度
385
- *
386
- * 返回当前 session 的任务状态列表,供前端轮询(Socket.io 不可用时的 fallback)
387
- */
388
- router.get('/bootstrap/status', asyncHandler(async (req, res) => {
389
- const container = getServiceContainer();
390
-
391
- // 从容器获取 BootstrapTaskManager(正式 DI 注册)
392
- let taskManager = null;
393
- try { taskManager = container.get('bootstrapTaskManager'); } catch { /* not registered */ }
394
- if (!taskManager) {
395
- return res.json({
396
- success: true,
397
- data: { status: 'idle', message: 'No bootstrap task manager initialized' },
398
- });
399
- }
400
-
401
- res.json({
402
- success: true,
403
- data: taskManager.getSessionStatus(),
404
- });
405
- }));
406
-
407
- export default router;
5
+ export { default } from '../../platform/ios/routes/spm.js';
@@ -4,124 +4,144 @@
4
4
  */
5
5
 
6
6
  import express from 'express';
7
- import { asyncHandler } from '../middleware/errorHandler.js';
7
+ import Logger from '../../infrastructure/logging/Logger.js';
8
8
  import { getServiceContainer } from '../../injection/ServiceContainer.js';
9
9
  import { ValidationError } from '../../shared/errors/index.js';
10
- import Logger from '../../infrastructure/logging/Logger.js';
10
+ import { asyncHandler } from '../middleware/errorHandler.js';
11
11
 
12
12
  const router = express.Router();
13
- const logger = Logger.getInstance();
13
+ const _logger = Logger.getInstance();
14
14
 
15
15
  /**
16
16
  * GET /api/v1/violations
17
17
  * 获取 Guard 违规记录列表
18
18
  */
19
- router.get('/', asyncHandler(async (req, res) => {
20
- const container = getServiceContainer();
21
- const violationsStore = container.get('violationsStore');
22
-
23
- const { severity, ruleId, file } = req.query;
24
- const page = parseInt(req.query.page, 10) || 1;
25
- const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200);
26
-
27
- const filters = {};
28
- if (severity) filters.severity = severity;
29
- if (ruleId) filters.ruleId = ruleId;
30
- if (file) filters.file = file;
31
-
32
- const result = await violationsStore.list(filters, { page, limit });
33
-
34
- res.json({
35
- success: true,
36
- data: result,
37
- });
38
- }));
19
+ router.get(
20
+ '/',
21
+ asyncHandler(async (req, res) => {
22
+ const container = getServiceContainer();
23
+ const violationsStore = container.get('violationsStore');
24
+
25
+ const { severity, ruleId, file } = req.query;
26
+ const page = parseInt(req.query.page, 10) || 1;
27
+ const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200);
28
+
29
+ const filters = {};
30
+ if (severity) {
31
+ filters.severity = severity;
32
+ }
33
+ if (ruleId) {
34
+ filters.ruleId = ruleId;
35
+ }
36
+ if (file) {
37
+ filters.file = file;
38
+ }
39
+
40
+ const result = await violationsStore.list(filters, { page, limit });
41
+
42
+ res.json({
43
+ success: true,
44
+ data: result,
45
+ });
46
+ })
47
+ );
39
48
 
40
49
  /**
41
50
  * GET /api/v1/violations/stats
42
51
  * 获取违规统计摘要
43
52
  */
44
- router.get('/stats', asyncHandler(async (req, res) => {
45
- const container = getServiceContainer();
46
- const violationsStore = container.get('violationsStore');
53
+ router.get(
54
+ '/stats',
55
+ asyncHandler(async (req, res) => {
56
+ const container = getServiceContainer();
57
+ const violationsStore = container.get('violationsStore');
47
58
 
48
- const stats = await violationsStore.getStats();
59
+ const stats = await violationsStore.getStats();
49
60
 
50
- res.json({
51
- success: true,
52
- data: stats,
53
- });
54
- }));
61
+ res.json({
62
+ success: true,
63
+ data: stats,
64
+ });
65
+ })
66
+ );
55
67
 
56
68
  /**
57
69
  * POST /api/v1/violations/clear
58
70
  * 清除违规记录
59
71
  */
60
- router.post('/clear', asyncHandler(async (req, res) => {
61
- const container = getServiceContainer();
62
- const violationsStore = container.get('violationsStore');
63
-
64
- const { ruleId, file, all } = req.body;
65
-
66
- let cleared = 0;
67
- if (all) {
68
- cleared = await violationsStore.clearAll();
69
- } else {
70
- cleared = await violationsStore.clear({ ruleId, file });
71
- }
72
-
73
- res.json({
74
- success: true,
75
- data: { cleared },
76
- });
77
- }));
72
+ router.post(
73
+ '/clear',
74
+ asyncHandler(async (req, res) => {
75
+ const container = getServiceContainer();
76
+ const violationsStore = container.get('violationsStore');
77
+
78
+ const { ruleId, file, all } = req.body;
79
+
80
+ let cleared = 0;
81
+ if (all) {
82
+ cleared = await violationsStore.clearAll();
83
+ } else {
84
+ cleared = await violationsStore.clear({ ruleId, file });
85
+ }
86
+
87
+ res.json({
88
+ success: true,
89
+ data: { cleared },
90
+ });
91
+ })
92
+ );
78
93
 
79
94
  /**
80
95
  * POST /api/v1/violations/rules/generate
81
96
  * AI 根据语义描述生成 Guard 规则
82
97
  */
83
- router.post('/rules/generate', asyncHandler(async (req, res) => {
84
- const { description } = req.body;
85
-
86
- if (!description || typeof description !== 'string' || !description.trim()) {
87
- throw new ValidationError('description is required');
88
- }
89
-
90
- const container = getServiceContainer();
91
- const chatAgent = container.get('chatAgent');
92
- const result = await chatAgent.executeTool('generate_guard_rule', {
93
- description: description.trim(),
94
- language: 'objc',
95
- severity: 'warning',
96
- });
97
-
98
- if (result?.error) {
99
- throw new ValidationError(result.error);
100
- }
101
-
102
- // 从 generate_guard_rule 工具返回的 rule 中提取并规范化
103
- const rule = result.rule || result;
104
-
105
- const normalized = {
106
- ruleId: String(rule.name || rule.ruleId || '').trim().replace(/\s+/g, '-'),
107
- message: String(rule.description || rule.message || '').trim(),
108
- severity: rule.severity === 'error' ? 'error' : 'warning',
109
- pattern: String(rule.pattern || '').trim(),
110
- languages: Array.isArray(rule.languages)
111
- ? rule.languages.filter(l => l === 'objc' || l === 'swift')
112
- : ['objc', 'swift'],
113
- note: rule.note != null ? String(rule.note).trim() : (rule.description_cn || ''),
114
- dimension: ['file', 'target', 'project'].includes(rule.dimension) ? rule.dimension : '',
115
- };
116
-
117
- if (!normalized.ruleId || !normalized.message || !normalized.pattern) {
118
- throw new ValidationError('AI 返回的规则缺少 ruleId、message pattern');
119
- }
120
-
121
- res.json({
122
- success: true,
123
- data: normalized,
124
- });
125
- }));
98
+ router.post(
99
+ '/rules/generate',
100
+ asyncHandler(async (req, res) => {
101
+ const { description } = req.body;
102
+
103
+ if (!description || typeof description !== 'string' || !description.trim()) {
104
+ throw new ValidationError('description is required');
105
+ }
106
+
107
+ const container = getServiceContainer();
108
+ const chatAgent = container.get('chatAgent');
109
+ const result = await chatAgent.executeTool('generate_guard_rule', {
110
+ description: description.trim(),
111
+ language: 'objc',
112
+ severity: 'warning',
113
+ });
114
+
115
+ if (result?.error) {
116
+ throw new ValidationError(result.error);
117
+ }
118
+
119
+ // 从 generate_guard_rule 工具返回的 rule 中提取并规范化
120
+ const rule = result.rule || result;
121
+
122
+ const normalized = {
123
+ ruleId: String(rule.name || rule.ruleId || '')
124
+ .trim()
125
+ .replace(/\s+/g, '-'),
126
+ message: String(rule.description || rule.message || '').trim(),
127
+ severity: rule.severity === 'error' ? 'error' : 'warning',
128
+ pattern: String(rule.pattern || '').trim(),
129
+ languages: Array.isArray(rule.languages) && rule.languages.length > 0
130
+ ? rule.languages
131
+ : ['objc', 'swift'],
132
+ note: rule.note != null ? String(rule.note).trim() : rule.description_cn || '',
133
+ dimension: ['file', 'target', 'project'].includes(rule.dimension) ? rule.dimension : '',
134
+ };
135
+
136
+ if (!normalized.ruleId || !normalized.message || !normalized.pattern) {
137
+ throw new ValidationError('AI 返回的规则缺少 ruleId、message 或 pattern');
138
+ }
139
+
140
+ res.json({
141
+ success: true,
142
+ data: normalized,
143
+ });
144
+ })
145
+ );
126
146
 
127
147
  export default router;