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
@@ -7,8 +7,8 @@
7
7
  * 支持 toolChoice: 'auto' | 'required' | 'none'
8
8
  */
9
9
 
10
- import { AiProvider } from '../AiProvider.js';
11
10
  import Logger from '../../../infrastructure/logging/Logger.js';
11
+ import { AiProvider } from '../AiProvider.js';
12
12
 
13
13
  const GEMINI_BASE = 'https://generativelanguage.googleapis.com/v1beta';
14
14
  const EMBED_MODEL = 'models/gemini-embedding-001';
@@ -35,7 +35,10 @@ export class GoogleGeminiProvider extends AiProvider {
35
35
  const contents = [];
36
36
 
37
37
  for (const h of history) {
38
- contents.push({ role: h.role === 'assistant' ? 'model' : 'user', parts: [{ text: h.content }] });
38
+ contents.push({
39
+ role: h.role === 'assistant' ? 'model' : 'user',
40
+ parts: [{ text: h.content }],
41
+ });
39
42
  }
40
43
  contents.push({ role: 'user', parts: [{ text: prompt }] });
41
44
 
@@ -85,9 +88,10 @@ export class GoogleGeminiProvider extends AiProvider {
85
88
  } = opts;
86
89
 
87
90
  // 统一消息 → Gemini contents
88
- const contents = messages?.length > 0
89
- ? this.#convertMessages(messages)
90
- : [{ role: 'user', parts: [{ text: prompt }] }];
91
+ const contents =
92
+ messages?.length > 0
93
+ ? this.#convertMessages(messages)
94
+ : [{ role: 'user', parts: [{ text: prompt }] }];
91
95
 
92
96
  const body = {
93
97
  contents,
@@ -99,9 +103,11 @@ export class GoogleGeminiProvider extends AiProvider {
99
103
 
100
104
  // 工具声明: 标准 schema → Gemini functionDeclarations
101
105
  if (toolSchemas?.length > 0) {
102
- body.tools = [{
103
- functionDeclarations: toolSchemas.map(s => this.#toFunctionDeclaration(s)),
104
- }];
106
+ body.tools = [
107
+ {
108
+ functionDeclarations: toolSchemas.map((s) => this.#toFunctionDeclaration(s)),
109
+ },
110
+ ];
105
111
  }
106
112
 
107
113
  // toolChoice → Gemini mode
@@ -170,7 +176,9 @@ export class GoogleGeminiProvider extends AiProvider {
170
176
  pushOrMerge({ role: 'user', parts: [{ text: msg.content || '' }] });
171
177
  } else if (msg.role === 'assistant') {
172
178
  const parts = [];
173
- if (msg.content) parts.push({ text: msg.content });
179
+ if (msg.content) {
180
+ parts.push({ text: msg.content });
181
+ }
174
182
  if (msg.toolCalls?.length > 0) {
175
183
  for (const tc of msg.toolCalls) {
176
184
  const fcPart = { functionCall: { name: tc.name, args: tc.args || {} } };
@@ -181,7 +189,9 @@ export class GoogleGeminiProvider extends AiProvider {
181
189
  parts.push(fcPart);
182
190
  }
183
191
  }
184
- if (parts.length > 0) pushOrMerge({ role: 'model', parts });
192
+ if (parts.length > 0) {
193
+ pushOrMerge({ role: 'model', parts });
194
+ }
185
195
  }
186
196
  }
187
197
 
@@ -198,9 +208,12 @@ export class GoogleGeminiProvider extends AiProvider {
198
208
  */
199
209
  #toGeminiMode(toolChoice) {
200
210
  switch (toolChoice) {
201
- case 'required': return 'ANY';
202
- case 'none': return 'NONE';
203
- default: return 'AUTO';
211
+ case 'required':
212
+ return 'ANY';
213
+ case 'none':
214
+ return 'NONE';
215
+ default:
216
+ return 'AUTO';
204
217
  }
205
218
  }
206
219
 
@@ -227,7 +240,9 @@ export class GoogleGeminiProvider extends AiProvider {
227
240
  const cleaned = { ...schema };
228
241
  delete cleaned.default;
229
242
  delete cleaned.examples;
230
- if (!cleaned.type) cleaned.type = 'object';
243
+ if (!cleaned.type) {
244
+ cleaned.type = 'object';
245
+ }
231
246
 
232
247
  // 递归清理 properties
233
248
  if (cleaned.properties) {
@@ -290,7 +305,9 @@ export class GoogleGeminiProvider extends AiProvider {
290
305
  }
291
306
 
292
307
  if (functionCalls.length > 0) {
293
- this.logger.debug(`[GeminiProvider] native function calls: ${functionCalls.map(fc => fc.name).join(', ')}`);
308
+ this.logger.debug(
309
+ `[GeminiProvider] native function calls: ${functionCalls.map((fc) => fc.name).join(', ')}`
310
+ );
294
311
  return {
295
312
  text: textParts.length > 0 ? textParts.join('\n') : null,
296
313
  functionCalls,
@@ -307,10 +324,12 @@ export class GoogleGeminiProvider extends AiProvider {
307
324
 
308
325
  async summarize(code) {
309
326
  const prompt = `请对以下代码生成结构化摘要,返回 JSON 格式 {title, description, language, patterns: [], keyAPIs: []}:\n\n${code}`;
310
- return await this.chatWithStructuredOutput(prompt, {
311
- temperature: 0.3,
312
- maxTokens: 8192,
313
- }) || { title: '', description: '' };
327
+ return (
328
+ (await this.chatWithStructuredOutput(prompt, {
329
+ temperature: 0.3,
330
+ maxTokens: 8192,
331
+ })) || { title: '', description: '' }
332
+ );
314
333
  }
315
334
 
316
335
  /**
@@ -321,12 +340,7 @@ export class GoogleGeminiProvider extends AiProvider {
321
340
  */
322
341
  async chatWithStructuredOutput(prompt, opts = {}) {
323
342
  return this._withRetry(async () => {
324
- const {
325
- schema,
326
- temperature = 0.3,
327
- maxTokens = 32768,
328
- systemPrompt,
329
- } = opts;
343
+ const { schema, temperature = 0.3, maxTokens = 32768, systemPrompt } = opts;
330
344
 
331
345
  const contents = [{ role: 'user', parts: [{ text: prompt }] }];
332
346
 
@@ -351,7 +365,9 @@ export class GoogleGeminiProvider extends AiProvider {
351
365
  const data = await this._post(url, body);
352
366
  const text = data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
353
367
 
354
- if (!text) return null;
368
+ if (!text) {
369
+ return null;
370
+ }
355
371
 
356
372
  try {
357
373
  return JSON.parse(text);
@@ -370,7 +386,7 @@ export class GoogleGeminiProvider extends AiProvider {
370
386
 
371
387
  for (let i = 0; i < texts.length; i += 100) {
372
388
  const batch = texts.slice(i, i + 100);
373
- const requests = batch.map(t => ({
389
+ const requests = batch.map((t) => ({
374
390
  model: EMBED_MODEL,
375
391
  content: { parts: [{ text: t.slice(0, 8000) }] },
376
392
  }));
@@ -378,7 +394,7 @@ export class GoogleGeminiProvider extends AiProvider {
378
394
  const url = `${GEMINI_BASE}/${EMBED_MODEL}:batchEmbedContents?key=${this.apiKey}`;
379
395
  const data = await this._post(url, { requests });
380
396
  if (data?.embeddings) {
381
- results.push(...data.embeddings.map(e => e.values));
397
+ results.push(...data.embeddings.map((e) => e.values));
382
398
  }
383
399
  }
384
400
 
@@ -386,6 +402,14 @@ export class GoogleGeminiProvider extends AiProvider {
386
402
  }
387
403
 
388
404
  async _post(url, body) {
405
+ if (!this.apiKey) {
406
+ const err = new Error(
407
+ 'Google Gemini API Key 未配置。请在 .env 中设置 ASD_GOOGLE_API_KEY,或运行 asd setup 完成配置。'
408
+ );
409
+ err.code = 'API_KEY_MISSING';
410
+ throw err;
411
+ }
412
+
389
413
  const controller = new AbortController();
390
414
  const timer = setTimeout(() => controller.abort(), this.timeout);
391
415
 
@@ -398,8 +422,13 @@ export class GoogleGeminiProvider extends AiProvider {
398
422
  });
399
423
  if (!res.ok) {
400
424
  let detail = '';
401
- try { const j = await res.json(); detail = j?.error?.message || JSON.stringify(j).slice(0, 300); } catch { /* ignore */ }
402
- const err = new Error(`Gemini API error: ${res.status}${detail ? ' — ' + detail : ''}`);
425
+ try {
426
+ const j = await res.json();
427
+ detail = j?.error?.message || JSON.stringify(j).slice(0, 300);
428
+ } catch {
429
+ /* ignore */
430
+ }
431
+ const err = new Error(`Gemini API error: ${res.status}${detail ? ` — ${detail}` : ''}`);
403
432
  err.status = res.status;
404
433
  throw err;
405
434
  }
@@ -16,13 +16,17 @@ export class MockProvider extends AiProvider {
16
16
 
17
17
  async chat(prompt, context = {}) {
18
18
  this.callLog.push({ method: 'chat', prompt, context });
19
- if (this.responses.chat) return this.responses.chat;
19
+ if (this.responses.chat) {
20
+ return this.responses.chat;
21
+ }
20
22
  return `Mock response for: ${prompt.slice(0, 80)}`;
21
23
  }
22
24
 
23
25
  async summarize(code) {
24
26
  this.callLog.push({ method: 'summarize', code: code?.slice(0, 80) });
25
- if (this.responses.summarize) return this.responses.summarize;
27
+ if (this.responses.summarize) {
28
+ return this.responses.summarize;
29
+ }
26
30
  return {
27
31
  title: 'Mock Summary',
28
32
  description: `Summary of ${code?.length || 0} chars`,
@@ -36,7 +40,9 @@ export class MockProvider extends AiProvider {
36
40
  this.callLog.push({ method: 'embed', text: Array.isArray(text) ? text.length : 1 });
37
41
  const dim = 768;
38
42
  const makeVector = () => Array.from({ length: dim }, () => Math.random() * 2 - 1);
39
- if (Array.isArray(text)) return text.map(() => makeVector());
43
+ if (Array.isArray(text)) {
44
+ return text.map(() => makeVector());
45
+ }
40
46
  return makeVector();
41
47
  }
42
48
 
@@ -7,8 +7,8 @@
7
7
  * - 兼容 DeepSeek / Ollama 等 OpenAI-compatible API
8
8
  */
9
9
 
10
- import { AiProvider } from '../AiProvider.js';
11
10
  import Logger from '../../../infrastructure/logging/Logger.js';
11
+ import { AiProvider } from '../AiProvider.js';
12
12
 
13
13
  const OPENAI_BASE = 'https://api.openai.com/v1';
14
14
 
@@ -80,9 +80,8 @@ export class OpenAiProvider extends AiProvider {
80
80
  messages.push({ role: 'system', content: systemPrompt });
81
81
  }
82
82
 
83
- const srcMessages = unifiedMessages?.length > 0
84
- ? unifiedMessages
85
- : [{ role: 'user', content: prompt }];
83
+ const srcMessages =
84
+ unifiedMessages?.length > 0 ? unifiedMessages : [{ role: 'user', content: prompt }];
86
85
 
87
86
  for (const msg of srcMessages) {
88
87
  if (msg.role === 'user') {
@@ -90,7 +89,7 @@ export class OpenAiProvider extends AiProvider {
90
89
  } else if (msg.role === 'assistant') {
91
90
  const m = { role: 'assistant', content: msg.content || null };
92
91
  if (msg.toolCalls?.length > 0) {
93
- m.tool_calls = msg.toolCalls.map(tc => ({
92
+ m.tool_calls = msg.toolCalls.map((tc) => ({
94
93
  id: tc.id,
95
94
  type: 'function',
96
95
  function: {
@@ -118,7 +117,7 @@ export class OpenAiProvider extends AiProvider {
118
117
 
119
118
  // 标准 tool schemas → OpenAI tools format
120
119
  if (toolSchemas?.length > 0) {
121
- body.tools = toolSchemas.map(s => ({
120
+ body.tools = toolSchemas.map((s) => ({
122
121
  type: 'function',
123
122
  function: {
124
123
  name: s.name,
@@ -129,9 +128,13 @@ export class OpenAiProvider extends AiProvider {
129
128
  }
130
129
 
131
130
  // toolChoice → OpenAI tool_choice
132
- if (toolChoice === 'required') body.tool_choice = 'required';
133
- else if (toolChoice === 'none') body.tool_choice = 'none';
134
- else body.tool_choice = 'auto';
131
+ if (toolChoice === 'required') {
132
+ body.tool_choice = 'required';
133
+ } else if (toolChoice === 'none') {
134
+ body.tool_choice = 'none';
135
+ } else {
136
+ body.tool_choice = 'auto';
137
+ }
135
138
 
136
139
  const data = await this._post(`${this.baseUrl}/chat/completions`, body);
137
140
  return this.#parseToolResponse(data);
@@ -156,25 +159,32 @@ export class OpenAiProvider extends AiProvider {
156
159
  }
157
160
  : null;
158
161
 
159
- if (!choice) return { text: '', functionCalls: null, usage };
162
+ if (!choice) {
163
+ return { text: '', functionCalls: null, usage };
164
+ }
160
165
 
161
166
  const message = choice.message;
162
167
  const text = message?.content || null;
163
168
 
164
169
  if (message?.tool_calls?.length > 0) {
165
170
  const functionCalls = message.tool_calls
166
- .filter(tc => tc.type === 'function')
167
- .map(tc => ({
171
+ .filter((tc) => tc.type === 'function')
172
+ .map((tc) => ({
168
173
  id: tc.id,
169
174
  name: tc.function.name,
170
175
  args: (() => {
171
- try { return JSON.parse(tc.function.arguments || '{}'); }
172
- catch { return {}; }
176
+ try {
177
+ return JSON.parse(tc.function.arguments || '{}');
178
+ } catch {
179
+ return {};
180
+ }
173
181
  })(),
174
182
  }));
175
183
 
176
184
  if (functionCalls.length > 0) {
177
- this.logger.debug(`[OpenAI] native function calls: ${functionCalls.map(fc => fc.name).join(', ')}`);
185
+ this.logger.debug(
186
+ `[OpenAI] native function calls: ${functionCalls.map((fc) => fc.name).join(', ')}`
187
+ );
178
188
  return { text, functionCalls, usage };
179
189
  }
180
190
  }
@@ -184,10 +194,12 @@ export class OpenAiProvider extends AiProvider {
184
194
 
185
195
  async summarize(code) {
186
196
  const prompt = `请对以下代码生成结构化摘要,返回 JSON 格式 {title, description, language, patterns: [], keyAPIs: []}:\n\n${code}`;
187
- return await this.chatWithStructuredOutput(prompt, {
188
- temperature: 0.3,
189
- maxTokens: 4096,
190
- }) || { title: '', description: '' };
197
+ return (
198
+ (await this.chatWithStructuredOutput(prompt, {
199
+ temperature: 0.3,
200
+ maxTokens: 4096,
201
+ })) || { title: '', description: '' }
202
+ );
191
203
  }
192
204
 
193
205
  /**
@@ -198,11 +210,7 @@ export class OpenAiProvider extends AiProvider {
198
210
  */
199
211
  async chatWithStructuredOutput(prompt, opts = {}) {
200
212
  return this._withRetry(async () => {
201
- const {
202
- temperature = 0.3,
203
- maxTokens = 32768,
204
- systemPrompt,
205
- } = opts;
213
+ const { temperature = 0.3, maxTokens = 32768, systemPrompt } = opts;
206
214
 
207
215
  const messages = [];
208
216
  if (systemPrompt) {
@@ -221,7 +229,9 @@ export class OpenAiProvider extends AiProvider {
221
229
  const data = await this._post(`${this.baseUrl}/chat/completions`, body);
222
230
  const text = data?.choices?.[0]?.message?.content || '';
223
231
 
224
- if (!text) return null;
232
+ if (!text) {
233
+ return null;
234
+ }
225
235
 
226
236
  try {
227
237
  return JSON.parse(text);
@@ -240,15 +250,17 @@ export class OpenAiProvider extends AiProvider {
240
250
  try {
241
251
  const body = {
242
252
  model: this.embedModel,
243
- input: texts.map(t => t.slice(0, 8000)),
253
+ input: texts.map((t) => t.slice(0, 8000)),
244
254
  };
245
255
 
246
256
  const data = await this._post(`${this.baseUrl}/embeddings`, body);
247
257
  const embeddings = (data?.data || [])
248
258
  .sort((a, b) => a.index - b.index)
249
- .map(d => d.embedding);
259
+ .map((d) => d.embedding);
250
260
 
251
- if (embeddings.length === 0) return Array.isArray(text) ? [] : [];
261
+ if (embeddings.length === 0) {
262
+ return Array.isArray(text) ? [] : [];
263
+ }
252
264
  return Array.isArray(text) ? embeddings : embeddings[0];
253
265
  } catch (err) {
254
266
  this.logger.warn(`${this.name} embed failed, returning empty`, { error: err.message });
@@ -257,6 +269,16 @@ export class OpenAiProvider extends AiProvider {
257
269
  }
258
270
 
259
271
  async _post(url, body) {
272
+ // Ollama 使用固定 dummy key,不需要校验
273
+ if (!this.apiKey && this.name !== 'ollama') {
274
+ const envKey = this.name === 'deepseek' ? 'ASD_DEEPSEEK_API_KEY' : 'ASD_OPENAI_API_KEY';
275
+ const err = new Error(
276
+ `${this.name} API Key 未配置。请在 .env 中设置 ${envKey},或运行 asd setup 完成配置。`
277
+ );
278
+ err.code = 'API_KEY_MISSING';
279
+ throw err;
280
+ }
281
+
260
282
  const controller = new AbortController();
261
283
  const timer = setTimeout(() => controller.abort(), this.timeout);
262
284
 
@@ -265,7 +287,7 @@ export class OpenAiProvider extends AiProvider {
265
287
  method: 'POST',
266
288
  headers: {
267
289
  'Content-Type': 'application/json',
268
- 'Authorization': `Bearer ${this.apiKey}`,
290
+ Authorization: `Bearer ${this.apiKey}`,
269
291
  },
270
292
  body: JSON.stringify(body),
271
293
  signal: controller.signal,
@@ -17,21 +17,18 @@
17
17
 
18
18
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
19
19
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
- import {
21
- CallToolRequestSchema,
22
- ListToolsRequestSchema,
23
- } from '@modelcontextprotocol/sdk/types.js';
20
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
24
21
  import Logger from '../../infrastructure/logging/Logger.js';
25
22
  import { envelope } from './envelope.js';
26
- import { TOOLS, TOOL_GATEWAY_MAP, TIER_ORDER } from './tools.js';
27
23
  import { wrapHandler } from './errorHandler.js';
24
+ import { TIER_ORDER, TOOL_GATEWAY_MAP, TOOLS } from './tools.js';
28
25
 
29
26
  // ─── Handler 模块 ─────────────────────────────────────────────
30
27
 
31
- import * as systemHandlers from './handlers/system.js';
32
28
  import * as candidateHandlers from './handlers/candidate.js';
33
- import * as knowledgeHandlers from './handlers/knowledge.js';
34
29
  import * as consolidated from './handlers/consolidated.js';
30
+ import * as knowledgeHandlers from './handlers/knowledge.js';
31
+ import * as systemHandlers from './handlers/system.js';
35
32
 
36
33
  // ─── McpServer 类 ─────────────────────────────────────────────
37
34
 
@@ -78,7 +75,9 @@ export class McpServer {
78
75
  });
79
76
 
80
77
  // 注册 Gateway action handlers
81
- const { registerGatewayActions } = await import('../../core/gateway/GatewayActionRegistry.js');
78
+ const { registerGatewayActions } = await import(
79
+ '../../core/gateway/GatewayActionRegistry.js'
80
+ );
82
81
  const gateway = this.container.get('gateway');
83
82
  if (gateway) {
84
83
  registerGatewayActions(gateway, this.container);
@@ -87,7 +86,7 @@ export class McpServer {
87
86
 
88
87
  this.server = new Server(
89
88
  { name: 'autosnippet-v3', version: '3.0.0' },
90
- { capabilities: { tools: {} } },
89
+ { capabilities: { tools: {} } }
91
90
  );
92
91
 
93
92
  this._registerHandlers();
@@ -103,7 +102,7 @@ export class McpServer {
103
102
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
104
103
  const tierName = process.env.ASD_MCP_TIER || 'agent';
105
104
  const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
106
- const visible = TOOLS.filter(t => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier);
105
+ const visible = TOOLS.filter((t) => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier);
107
106
  return { tools: visible };
108
107
  });
109
108
 
@@ -113,10 +112,22 @@ export class McpServer {
113
112
  const t0 = Date.now();
114
113
  try {
115
114
  const result = await this._handleToolCall(name, args || {});
116
- return { content: [{ type: 'text', text: typeof result === 'string' ? result : JSON.stringify(result, null, 2) }] };
115
+ return {
116
+ content: [
117
+ {
118
+ type: 'text',
119
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
120
+ },
121
+ ],
122
+ };
117
123
  } catch (err) {
118
124
  this.logger.error(`MCP tool error: ${name}`, { error: err.message });
119
- const env = envelope({ success: false, message: err.message, errorCode: 'TOOL_ERROR', meta: { tool: name, responseTimeMs: Date.now() - t0 } });
125
+ const env = envelope({
126
+ success: false,
127
+ message: err.message,
128
+ errorCode: 'TOOL_ERROR',
129
+ meta: { tool: name, responseTimeMs: Date.now() - t0 },
130
+ });
120
131
  return { content: [{ type: 'text', text: JSON.stringify(env, null, 2) }], isError: true };
121
132
  }
122
133
  });
@@ -130,7 +141,9 @@ export class McpServer {
130
141
 
131
142
  // 查找 handler 并通过 wrapHandler 统一错误处理
132
143
  const handler = this._resolveHandler(name);
133
- if (!handler) throw new Error(`Unknown tool: ${name}`);
144
+ if (!handler) {
145
+ throw new Error(`Unknown tool: ${name}`);
146
+ }
134
147
 
135
148
  const wrapped = wrapHandler(name, handler);
136
149
  return wrapped(ctx, args);
@@ -143,23 +156,25 @@ export class McpServer {
143
156
  _resolveHandler(name) {
144
157
  const HANDLER_MAP = {
145
158
  // ── Agent 层 (12) ──
146
- autosnippet_health: (ctx) => systemHandlers.health(ctx),
147
- autosnippet_capabilities: () => systemHandlers.capabilities(),
148
- autosnippet_search: (ctx, args) => consolidated.consolidatedSearch(ctx, args),
149
- autosnippet_knowledge: (ctx, args) => consolidated.consolidatedKnowledge(ctx, args),
150
- autosnippet_structure: (ctx, args) => consolidated.consolidatedStructure(ctx, args),
151
- autosnippet_graph: (ctx, args) => consolidated.consolidatedGraph(ctx, args),
152
- autosnippet_guard: (ctx, args) => consolidated.consolidatedGuard(ctx, args),
153
- autosnippet_submit_knowledge: (ctx, args) => consolidated.enhancedSubmitKnowledge(ctx, args),
154
- autosnippet_submit_knowledge_batch: (ctx, args) => knowledgeHandlers.submitKnowledgeBatch(ctx, args),
155
- autosnippet_save_document: (ctx, args) => knowledgeHandlers.saveDocument(ctx, args),
156
- autosnippet_skill: (ctx, args) => consolidated.consolidatedSkill(ctx, args),
157
- autosnippet_bootstrap: (ctx, args) => consolidated.consolidatedBootstrap(ctx, args),
159
+ autosnippet_health: (ctx) => systemHandlers.health(ctx),
160
+ autosnippet_capabilities: () => systemHandlers.capabilities(),
161
+ autosnippet_search: (ctx, args) => consolidated.consolidatedSearch(ctx, args),
162
+ autosnippet_knowledge: (ctx, args) => consolidated.consolidatedKnowledge(ctx, args),
163
+ autosnippet_structure: (ctx, args) => consolidated.consolidatedStructure(ctx, args),
164
+ autosnippet_graph: (ctx, args) => consolidated.consolidatedGraph(ctx, args),
165
+ autosnippet_guard: (ctx, args) => consolidated.consolidatedGuard(ctx, args),
166
+ autosnippet_submit_knowledge: (ctx, args) => consolidated.enhancedSubmitKnowledge(ctx, args),
167
+ autosnippet_submit_knowledge_batch: (ctx, args) =>
168
+ knowledgeHandlers.submitKnowledgeBatch(ctx, args),
169
+ autosnippet_save_document: (ctx, args) => knowledgeHandlers.saveDocument(ctx, args),
170
+ autosnippet_skill: (ctx, args) => consolidated.consolidatedSkill(ctx, args),
171
+ autosnippet_bootstrap: (ctx, args) => consolidated.consolidatedBootstrap(ctx, args),
158
172
  // ── Admin 层 (+4) ──
159
- autosnippet_enrich_candidates: (ctx, args) => candidateHandlers.enrichCandidates(ctx, args),
160
- autosnippet_knowledge_lifecycle: (ctx, args) => knowledgeHandlers.knowledgeLifecycle(ctx, args),
161
- autosnippet_validate_candidate: (ctx, args) => candidateHandlers.validateCandidate(ctx, args),
162
- autosnippet_check_duplicate: (ctx, args) => candidateHandlers.checkDuplicate(ctx, args),
173
+ autosnippet_enrich_candidates: (ctx, args) => candidateHandlers.enrichCandidates(ctx, args),
174
+ autosnippet_knowledge_lifecycle: (ctx, args) =>
175
+ knowledgeHandlers.knowledgeLifecycle(ctx, args),
176
+ autosnippet_validate_candidate: (ctx, args) => candidateHandlers.validateCandidate(ctx, args),
177
+ autosnippet_check_duplicate: (ctx, args) => candidateHandlers.checkDuplicate(ctx, args),
163
178
  };
164
179
  return HANDLER_MAP[name] || null;
165
180
  }
@@ -171,17 +186,23 @@ export class McpServer {
171
186
  */
172
187
  async _gatewayGate(toolName, args) {
173
188
  let mapping = TOOL_GATEWAY_MAP[toolName];
174
- if (!mapping) return; // 只读工具,跳过
189
+ if (!mapping) {
190
+ return; // 只读工具,跳过
191
+ }
175
192
 
176
193
  // 动态 resolver:根据 args 计算实际 action/resource
177
194
  if (typeof mapping.resolver === 'function') {
178
195
  mapping = mapping.resolver(args);
179
- if (!mapping) return; // resolver 返回 null 表示只读操作
196
+ if (!mapping) {
197
+ return; // resolver 返回 null 表示只读操作
198
+ }
180
199
  }
181
200
 
182
201
  try {
183
202
  const gateway = this.container.get('gateway');
184
- if (!gateway) return; // Gateway 未初始化,降级放行
203
+ if (!gateway) {
204
+ return; // Gateway 未初始化,降级放行
205
+ }
185
206
 
186
207
  const result = await gateway.checkOnly({
187
208
  actor: 'external_agent',
@@ -200,7 +221,10 @@ export class McpServer {
200
221
  this.logger.debug(`MCP Gateway gating passed: ${toolName}`, { requestId: result.requestId });
201
222
  } catch (err) {
202
223
  // 区分 Gateway 自身错误 vs 权限拒绝
203
- if (err.message?.startsWith('[PERMISSION_DENIED]') || err.message?.startsWith('[CONSTITUTION_VIOLATION]')) {
224
+ if (
225
+ err.message?.startsWith('[PERMISSION_DENIED]') ||
226
+ err.message?.startsWith('[CONSTITUTION_VIOLATION]')
227
+ ) {
204
228
  throw err;
205
229
  }
206
230
  // Gateway 内部故障不应阻断业务(降级放行 + 记录)
@@ -217,15 +241,21 @@ export class McpServer {
217
241
 
218
242
  const tierName = process.env.ASD_MCP_TIER || 'agent';
219
243
  const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
220
- const visibleCount = TOOLS.filter(t => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier).length;
244
+ const visibleCount = TOOLS.filter(
245
+ (t) => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier
246
+ ).length;
221
247
 
222
248
  this.logger.info(`MCP Server started (stdio) — ${visibleCount} tools [tier=${tierName}]`);
223
249
  process.stderr.write(`AutoSnippet MCP ready — ${visibleCount} tools [tier=${tierName}]\n`);
224
250
  }
225
251
 
226
252
  async shutdown() {
227
- if (this.server) await this.server.close();
228
- if (this.bootstrap) await this.bootstrap.shutdown();
253
+ if (this.server) {
254
+ await this.server.close();
255
+ }
256
+ if (this.bootstrap) {
257
+ await this.bootstrap.shutdown();
258
+ }
229
259
  }
230
260
  }
231
261
 
@@ -10,15 +10,15 @@
10
10
  * @module external/mcp/errorHandler
11
11
  */
12
12
 
13
- import { envelope } from './envelope.js';
13
+ import Logger from '../../infrastructure/logging/Logger.js';
14
14
  import {
15
- ValidationError,
16
- NotFoundError,
17
15
  ConflictError,
18
- PermissionDenied,
19
16
  ConstitutionViolation,
17
+ NotFoundError,
18
+ PermissionDenied,
19
+ ValidationError,
20
20
  } from '../../shared/errors/index.js';
21
- import Logger from '../../infrastructure/logging/Logger.js';
21
+ import { envelope } from './envelope.js';
22
22
 
23
23
  const logger = Logger.getInstance();
24
24
 
@@ -28,12 +28,24 @@ const logger = Logger.getInstance();
28
28
  * @returns {string}
29
29
  */
30
30
  function inferErrorCode(err) {
31
- if (err instanceof ValidationError) return 'VALIDATION_ERROR';
32
- if (err instanceof NotFoundError) return 'NOT_FOUND';
33
- if (err instanceof ConflictError) return 'CONFLICT';
34
- if (err instanceof PermissionDenied) return 'PERMISSION_DENIED';
35
- if (err instanceof ConstitutionViolation) return 'CONSTITUTION_VIOLATION';
36
- if (err.code) return err.code;
31
+ if (err instanceof ValidationError) {
32
+ return 'VALIDATION_ERROR';
33
+ }
34
+ if (err instanceof NotFoundError) {
35
+ return 'NOT_FOUND';
36
+ }
37
+ if (err instanceof ConflictError) {
38
+ return 'CONFLICT';
39
+ }
40
+ if (err instanceof PermissionDenied) {
41
+ return 'PERMISSION_DENIED';
42
+ }
43
+ if (err instanceof ConstitutionViolation) {
44
+ return 'CONSTITUTION_VIOLATION';
45
+ }
46
+ if (err.code) {
47
+ return err.code;
48
+ }
37
49
  return 'INTERNAL_ERROR';
38
50
  }
39
51