autosnippet 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +230 -324
  2. package/bin/api-server.js +1 -1
  3. package/bin/cli.js +204 -244
  4. package/bin/mcp-server.js +5 -3
  5. package/config/knowledge-base.config.js +132 -132
  6. package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
  7. package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
  8. package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
  9. package/dashboard/dist/index.html +3 -3
  10. package/lib/bootstrap.js +8 -8
  11. package/lib/cli/AiScanService.js +86 -40
  12. package/lib/cli/KnowledgeSyncService.js +113 -74
  13. package/lib/cli/SetupService.js +439 -277
  14. package/lib/cli/UpgradeService.js +63 -100
  15. package/lib/core/AstAnalyzer.js +276 -597
  16. package/lib/core/ast/ProjectGraph.js +101 -40
  17. package/lib/core/ast/ensure-grammars.js +232 -0
  18. package/lib/core/ast/index.js +115 -0
  19. package/lib/core/ast/lang-dart.js +661 -0
  20. package/lib/core/ast/lang-go.js +530 -0
  21. package/lib/core/ast/lang-java.js +435 -0
  22. package/lib/core/ast/lang-javascript.js +272 -0
  23. package/lib/core/ast/lang-kotlin.js +423 -0
  24. package/lib/core/ast/lang-objc.js +388 -0
  25. package/lib/core/ast/lang-python.js +371 -0
  26. package/lib/core/ast/lang-swift.js +337 -0
  27. package/lib/core/ast/lang-typescript.js +503 -0
  28. package/lib/core/capability/CapabilityProbe.js +18 -9
  29. package/lib/core/constitution/Constitution.js +2 -3
  30. package/lib/core/constitution/ConstitutionValidator.js +65 -24
  31. package/lib/core/discovery/DartDiscoverer.js +534 -0
  32. package/lib/core/discovery/DiscovererRegistry.js +83 -0
  33. package/lib/core/discovery/GenericDiscoverer.js +225 -0
  34. package/lib/core/discovery/GoDiscoverer.js +541 -0
  35. package/lib/core/discovery/JvmDiscoverer.js +506 -0
  36. package/lib/core/discovery/NodeDiscoverer.js +466 -0
  37. package/lib/core/discovery/ProjectDiscoverer.js +93 -0
  38. package/lib/core/discovery/PythonDiscoverer.js +338 -0
  39. package/lib/core/discovery/SpmDiscoverer.js +5 -0
  40. package/lib/core/discovery/index.js +53 -0
  41. package/lib/core/enhancement/EnhancementPack.js +71 -0
  42. package/lib/core/enhancement/EnhancementRegistry.js +47 -0
  43. package/lib/core/enhancement/android-enhancement.js +102 -0
  44. package/lib/core/enhancement/django-enhancement.js +70 -0
  45. package/lib/core/enhancement/fastapi-enhancement.js +63 -0
  46. package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
  47. package/lib/core/enhancement/go-web-enhancement.js +201 -0
  48. package/lib/core/enhancement/index.js +65 -0
  49. package/lib/core/enhancement/node-server-enhancement.js +88 -0
  50. package/lib/core/enhancement/react-enhancement.js +86 -0
  51. package/lib/core/enhancement/spring-enhancement.js +112 -0
  52. package/lib/core/enhancement/vue-enhancement.js +96 -0
  53. package/lib/core/gateway/Gateway.js +8 -9
  54. package/lib/core/gateway/GatewayActionRegistry.js +1 -1
  55. package/lib/core/permission/PermissionManager.js +12 -8
  56. package/lib/domain/index.js +13 -9
  57. package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
  58. package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
  59. package/lib/domain/knowledge/Lifecycle.js +22 -22
  60. package/lib/domain/knowledge/index.js +9 -12
  61. package/lib/domain/knowledge/values/Constraints.js +31 -21
  62. package/lib/domain/knowledge/values/Content.js +21 -13
  63. package/lib/domain/knowledge/values/Quality.js +31 -18
  64. package/lib/domain/knowledge/values/Reasoning.js +20 -12
  65. package/lib/domain/knowledge/values/Relations.js +37 -25
  66. package/lib/domain/knowledge/values/Stats.js +18 -12
  67. package/lib/domain/knowledge/values/index.js +4 -3
  68. package/lib/domain/snippet/Snippet.js +35 -10
  69. package/lib/external/ai/AiFactory.js +48 -16
  70. package/lib/external/ai/AiProvider.js +184 -90
  71. package/lib/external/ai/providers/ClaudeProvider.js +25 -12
  72. package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
  73. package/lib/external/ai/providers/MockProvider.js +9 -3
  74. package/lib/external/ai/providers/OpenAiProvider.js +51 -29
  75. package/lib/external/mcp/McpServer.js +66 -36
  76. package/lib/external/mcp/errorHandler.js +23 -11
  77. package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
  78. package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
  79. package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
  80. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
  81. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
  82. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
  83. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
  84. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
  85. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
  86. package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
  87. package/lib/external/mcp/handlers/bootstrap.js +691 -168
  88. package/lib/external/mcp/handlers/browse.js +66 -22
  89. package/lib/external/mcp/handlers/candidate.js +118 -35
  90. package/lib/external/mcp/handlers/consolidated.js +49 -17
  91. package/lib/external/mcp/handlers/guard.js +104 -39
  92. package/lib/external/mcp/handlers/knowledge.js +60 -36
  93. package/lib/external/mcp/handlers/search.js +43 -14
  94. package/lib/external/mcp/handlers/skill.js +120 -45
  95. package/lib/external/mcp/handlers/structure.js +240 -86
  96. package/lib/external/mcp/handlers/system.js +42 -12
  97. package/lib/external/mcp/handlers/wiki.js +58 -33
  98. package/lib/external/mcp/tools.js +306 -123
  99. package/lib/http/HttpServer.js +72 -47
  100. package/lib/http/middleware/RateLimiter.js +5 -3
  101. package/lib/http/middleware/errorHandler.js +6 -1
  102. package/lib/http/middleware/requestLogger.js +14 -3
  103. package/lib/http/middleware/roleResolver.js +30 -23
  104. package/lib/http/routes/ai.js +387 -265
  105. package/lib/http/routes/auth.js +81 -61
  106. package/lib/http/routes/candidates.js +430 -320
  107. package/lib/http/routes/commands.js +289 -189
  108. package/lib/http/routes/extract.js +158 -125
  109. package/lib/http/routes/guardRules.js +309 -217
  110. package/lib/http/routes/knowledge.js +213 -154
  111. package/lib/http/routes/modules.js +578 -0
  112. package/lib/http/routes/monitoring.js +6 -6
  113. package/lib/http/routes/recipes.js +104 -93
  114. package/lib/http/routes/search.js +361 -305
  115. package/lib/http/routes/skills.js +145 -98
  116. package/lib/http/routes/snippets.js +42 -30
  117. package/lib/http/routes/spm.js +3 -405
  118. package/lib/http/routes/violations.js +113 -93
  119. package/lib/http/routes/wiki.js +211 -170
  120. package/lib/http/utils/routeHelpers.js +3 -1
  121. package/lib/http/utils/sse-sessions.js +16 -6
  122. package/lib/http/utils/sse.js +15 -5
  123. package/lib/infrastructure/audit/AuditLogger.js +5 -2
  124. package/lib/infrastructure/audit/AuditStore.js +10 -7
  125. package/lib/infrastructure/cache/CacheService.js +3 -1
  126. package/lib/infrastructure/cache/GraphCache.js +8 -4
  127. package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
  128. package/lib/infrastructure/config/ConfigLoader.js +9 -5
  129. package/lib/infrastructure/config/Defaults.js +30 -10
  130. package/lib/infrastructure/config/Paths.js +28 -8
  131. package/lib/infrastructure/config/TriggerSymbol.js +22 -10
  132. package/lib/infrastructure/database/DatabaseConnection.js +15 -10
  133. package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
  134. package/lib/infrastructure/external/ClipboardManager.js +6 -2
  135. package/lib/infrastructure/external/NativeUi.js +50 -43
  136. package/lib/infrastructure/external/OpenBrowser.js +14 -17
  137. package/lib/infrastructure/external/XcodeAutomation.js +14 -258
  138. package/lib/infrastructure/logging/Logger.js +46 -30
  139. package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
  140. package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
  141. package/lib/infrastructure/paths/HeaderResolver.js +25 -9
  142. package/lib/infrastructure/paths/PathFinder.js +34 -12
  143. package/lib/infrastructure/plugin/PluginManager.js +26 -8
  144. package/lib/infrastructure/realtime/RealtimeService.js +2 -2
  145. package/lib/infrastructure/vector/Chunker.js +22 -7
  146. package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
  147. package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
  148. package/lib/infrastructure/vector/VectorStore.js +28 -10
  149. package/lib/injection/ServiceContainer.js +247 -93
  150. package/lib/platform/ios/index.js +63 -0
  151. package/lib/platform/ios/routes/spm.js +437 -0
  152. package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
  153. package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
  154. package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
  155. package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
  156. package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
  157. package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
  158. package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
  159. package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
  160. package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
  161. package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
  162. package/lib/repository/base/BaseRepository.js +7 -9
  163. package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
  164. package/lib/repository/token/TokenUsageStore.js +4 -2
  165. package/lib/service/automation/ActionPipeline.js +1 -1
  166. package/lib/service/automation/AutomationOrchestrator.js +8 -4
  167. package/lib/service/automation/ContextCollector.js +7 -5
  168. package/lib/service/automation/DirectiveDetector.js +23 -16
  169. package/lib/service/automation/FileWatcher.js +112 -56
  170. package/lib/service/automation/TriggerResolver.js +6 -4
  171. package/lib/service/automation/handlers/AlinkHandler.js +24 -12
  172. package/lib/service/automation/handlers/CreateHandler.js +19 -20
  173. package/lib/service/automation/handlers/DraftHandler.js +14 -8
  174. package/lib/service/automation/handlers/GuardHandler.js +93 -63
  175. package/lib/service/automation/handlers/HeaderHandler.js +1 -6
  176. package/lib/service/automation/handlers/SearchHandler.js +155 -88
  177. package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
  178. package/lib/service/candidate/SimilarityService.js +25 -9
  179. package/lib/service/chat/AnalystAgent.js +50 -24
  180. package/lib/service/chat/CandidateGuardrail.js +143 -17
  181. package/lib/service/chat/ChatAgent.js +655 -260
  182. package/lib/service/chat/ContextWindow.js +116 -71
  183. package/lib/service/chat/ConversationStore.js +77 -36
  184. package/lib/service/chat/EpisodicConsolidator.js +47 -23
  185. package/lib/service/chat/HandoffProtocol.js +98 -22
  186. package/lib/service/chat/Memory.js +34 -14
  187. package/lib/service/chat/ProducerAgent.js +40 -20
  188. package/lib/service/chat/ProjectSemanticMemory.js +109 -78
  189. package/lib/service/chat/ReasoningLayer.js +148 -70
  190. package/lib/service/chat/ReasoningTrace.js +44 -32
  191. package/lib/service/chat/TaskPipeline.js +39 -19
  192. package/lib/service/chat/ToolRegistry.js +48 -29
  193. package/lib/service/chat/WorkingMemory.js +44 -18
  194. package/lib/service/chat/tools.js +1096 -494
  195. package/lib/service/context/RecipeExtractor.js +132 -51
  196. package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
  197. package/lib/service/cursor/KnowledgeCompressor.js +25 -22
  198. package/lib/service/cursor/RulesGenerator.js +13 -7
  199. package/lib/service/cursor/SkillsSyncer.js +77 -27
  200. package/lib/service/cursor/TokenBudget.js +2 -2
  201. package/lib/service/cursor/TopicClassifier.js +54 -20
  202. package/lib/service/guard/ComplianceReporter.js +55 -43
  203. package/lib/service/guard/ExclusionManager.js +67 -29
  204. package/lib/service/guard/GuardCheckEngine.js +381 -86
  205. package/lib/service/guard/GuardFeedbackLoop.js +22 -10
  206. package/lib/service/guard/GuardService.js +29 -19
  207. package/lib/service/guard/RuleLearner.js +55 -23
  208. package/lib/service/guard/SourceFileCollector.js +27 -20
  209. package/lib/service/guard/ViolationsStore.js +43 -38
  210. package/lib/service/knowledge/CodeEntityGraph.js +147 -82
  211. package/lib/service/knowledge/ConfidenceRouter.js +12 -10
  212. package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
  213. package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
  214. package/lib/service/knowledge/KnowledgeService.js +222 -112
  215. package/lib/service/module/ModuleService.js +969 -0
  216. package/lib/service/quality/FeedbackCollector.js +27 -15
  217. package/lib/service/quality/QualityScorer.js +78 -24
  218. package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
  219. package/lib/service/recipe/RecipeParser.js +78 -45
  220. package/lib/service/search/CoarseRanker.js +43 -28
  221. package/lib/service/search/CrossEncoderReranker.js +32 -21
  222. package/lib/service/search/InvertedIndex.js +21 -7
  223. package/lib/service/search/MultiSignalRanker.js +90 -28
  224. package/lib/service/search/RetrievalFunnel.js +45 -24
  225. package/lib/service/search/SearchEngine.js +255 -103
  226. package/lib/service/skills/EventAggregator.js +32 -15
  227. package/lib/service/skills/SignalCollector.js +140 -64
  228. package/lib/service/skills/SkillAdvisor.js +79 -42
  229. package/lib/service/skills/SkillHooks.js +16 -14
  230. package/lib/service/snippet/PlaceholderConverter.js +5 -0
  231. package/lib/service/snippet/SnippetFactory.js +116 -99
  232. package/lib/service/snippet/SnippetInstaller.js +234 -62
  233. package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
  234. package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
  235. package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
  236. package/lib/service/wiki/WikiGenerator.js +637 -263
  237. package/lib/shared/DimensionCopyRegistry.js +472 -0
  238. package/lib/shared/LanguageService.js +399 -0
  239. package/lib/shared/PathGuard.js +45 -28
  240. package/lib/shared/RecipeReadinessChecker.js +72 -12
  241. package/lib/shared/constants.js +41 -41
  242. package/lib/shared/errors/BaseError.js +2 -2
  243. package/lib/shared/errors/index.js +4 -4
  244. package/lib/shared/similarity.js +25 -8
  245. package/lib/shared/token-utils.js +6 -2
  246. package/lib/shared/utils/common.js +12 -4
  247. package/package.json +49 -13
  248. package/scripts/bench-real-projects.mjs +256 -0
  249. package/scripts/build-native-ui.js +30 -30
  250. package/scripts/clear-old-vector-index.js +5 -35
  251. package/scripts/clear-vector-cache.js +7 -37
  252. package/scripts/collect-test-project-stats.mjs +160 -0
  253. package/scripts/diagnose-mcp.js +41 -32
  254. package/scripts/ensure-parse-package.js +6 -9
  255. package/scripts/generate-recipe-drafts.js +116 -77
  256. package/scripts/init-db.js +3 -20
  257. package/scripts/init-snippets.js +305 -0
  258. package/scripts/init-vector-db.js +173 -170
  259. package/scripts/install-cursor-skill.js +148 -104
  260. package/scripts/install-full.js +8 -21
  261. package/scripts/install-vscode-copilot.js +146 -145
  262. package/scripts/migrate-md-to-knowledge.mjs +139 -151
  263. package/scripts/postinstall-safe.js +5 -17
  264. package/scripts/recipe-audit.js +106 -82
  265. package/scripts/release.js +283 -323
  266. package/scripts/setup-mcp-config.js +60 -52
  267. package/scripts/verify-context-api.js +20 -20
  268. package/skills/autosnippet-analysis/SKILL.md +10 -6
  269. package/skills/autosnippet-candidates/SKILL.md +27 -26
  270. package/skills/autosnippet-coldstart/SKILL.md +555 -38
  271. package/skills/autosnippet-concepts/SKILL.md +349 -337
  272. package/skills/autosnippet-create/SKILL.md +5 -5
  273. package/skills/autosnippet-reference-dart/SKILL.md +543 -0
  274. package/skills/autosnippet-reference-go/SKILL.md +539 -0
  275. package/skills/autosnippet-reference-java/SKILL.md +534 -0
  276. package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
  277. package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
  278. package/skills/autosnippet-reference-objc/SKILL.md +29 -6
  279. package/skills/autosnippet-reference-python/SKILL.md +800 -0
  280. package/skills/autosnippet-reference-swift/SKILL.md +70 -14
  281. package/skills/autosnippet-structure/SKILL.md +4 -4
  282. package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
  283. package/templates/recipes-setup/README.md +2 -2
  284. package/templates/recipes-setup/_template.md +1 -1
  285. package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
  286. package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
  287. package/resources/asd-entry/main.swift +0 -159
  288. package/scripts/build-asd-entry.js +0 -51
  289. package/scripts/init-xcode-snippets.js +0 -311
  290. package/template.json +0 -39
@@ -13,8 +13,8 @@
13
13
  * §9 完整插入流程 — cut 触发行 → preflight → headers → offset → paste
14
14
  */
15
15
 
16
- import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs';
17
- import { basename, dirname, relative, resolve as pathResolve, sep } from 'node:path';
16
+ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
17
+ import { basename, dirname, resolve as pathResolve, relative, sep } from 'node:path';
18
18
  import { saveEventFilter } from './SaveEventFilter.js';
19
19
 
20
20
  // ═══════════════════════════════════════════════════════════════
@@ -22,16 +22,20 @@ import { saveEventFilter } from './SaveEventFilter.js';
22
22
  // ═══════════════════════════════════════════════════════════════
23
23
 
24
24
  function _sleep(ms) {
25
- return new Promise(resolve => setTimeout(resolve, ms));
25
+ return new Promise((resolve) => setTimeout(resolve, ms));
26
26
  }
27
27
 
28
28
  /**
29
29
  * 在 import 行末尾附加来源标记注释
30
30
  */
31
31
  function _withAutoSnippetNote(importLine) {
32
- if (!importLine) return importLine;
32
+ if (!importLine) {
33
+ return importLine;
34
+ }
33
35
  const note = '// AutoSnippet: 自动插入';
34
- if (importLine.includes(note)) return importLine;
36
+ if (importLine.includes(note)) {
37
+ return importLine;
38
+ }
35
39
  return `${importLine} ${note}`;
36
40
  }
37
41
 
@@ -49,19 +53,30 @@ function _parseHeaderString(header) {
49
53
  const t = header.trim();
50
54
  // #import <Module/Header.h>
51
55
  let m = t.match(/^#(?:import|include)\s+<([^/> ]+)\/([^>]+)>/);
52
- if (m) return { moduleName: m[1], headerName: m[2], isAngle: true };
56
+ if (m) {
57
+ return { moduleName: m[1], headerName: m[2], isAngle: true };
58
+ }
53
59
  // #import <Module> (framework umbrella)
54
60
  m = t.match(/^#(?:import|include)\s+<([^>]+)>/);
55
- if (m) return { moduleName: m[1], headerName: '', isAngle: true };
61
+ if (m) {
62
+ return { moduleName: m[1], headerName: '', isAngle: true };
63
+ }
56
64
  // #import "Header.h" or #import "Dir/Header.h"
57
65
  m = t.match(/^#(?:import|include)\s+"([^"]+)"/);
58
66
  if (m) {
59
67
  const parts = m[1].split('/');
60
- return { moduleName: '', headerName: parts[parts.length - 1], isAngle: false, quotedPath: m[1] };
68
+ return {
69
+ moduleName: '',
70
+ headerName: parts[parts.length - 1],
71
+ isAngle: false,
72
+ quotedPath: m[1],
73
+ };
61
74
  }
62
75
  // @import Module;
63
76
  m = t.match(/^@import\s+(\w+)/);
64
- if (m) return { moduleName: m[1], headerName: '', isAngle: false, isAtImport: true };
77
+ if (m) {
78
+ return { moduleName: m[1], headerName: '', isAngle: false, isAtImport: true };
79
+ }
65
80
  // import Module (Swift)
66
81
  m = t.match(/^import\s+(\w+)/);
67
82
  if (m && !['class', 'struct', 'enum', 'protocol', 'func', 'var', 'let'].includes(m[1])) {
@@ -88,19 +103,25 @@ function _parseHeaderString(header) {
88
103
  * @returns {string|null} 相对路径 (如 "Foo.h" 或 "../SubDir/Foo.h"),null 表示未找到
89
104
  */
90
105
  function _findHeaderRelativePath(headerName, currentFilePath, projectRoot) {
91
- if (!headerName || !currentFilePath) return null;
106
+ if (!headerName || !currentFilePath) {
107
+ return null;
108
+ }
92
109
  try {
93
110
  const currentDir = dirname(currentFilePath);
94
111
 
95
112
  // 1. 同目录检查
96
113
  const sameDir = pathResolve(currentDir, headerName);
97
- if (existsSync(sameDir)) return headerName;
114
+ if (existsSync(sameDir)) {
115
+ return headerName;
116
+ }
98
117
 
99
118
  // 2. 向上找 Sources/ 或 target 根目录,在其下递归搜索
100
119
  const searchRoots = [];
101
120
  if (projectRoot) {
102
121
  const sourcesDir = pathResolve(projectRoot, 'Sources');
103
- if (existsSync(sourcesDir)) searchRoots.push(sourcesDir);
122
+ if (existsSync(sourcesDir)) {
123
+ searchRoots.push(sourcesDir);
124
+ }
104
125
  searchRoots.push(projectRoot);
105
126
  }
106
127
  // 也从当前文件向上找 Sources 目录
@@ -112,7 +133,9 @@ function _findHeaderRelativePath(headerName, currentFilePath, projectRoot) {
112
133
  break;
113
134
  }
114
135
  const parent = dirname(dir);
115
- if (parent === dir) break;
136
+ if (parent === dir) {
137
+ break;
138
+ }
116
139
  dir = parent;
117
140
  }
118
141
 
@@ -137,25 +160,37 @@ function _findHeaderRelativePath(headerName, currentFilePath, projectRoot) {
137
160
  * 递归查找文件(限最大深度)
138
161
  */
139
162
  function _findFileRecursive(dir, fileName, maxDepth) {
140
- if (maxDepth <= 0) return null;
163
+ if (maxDepth <= 0) {
164
+ return null;
165
+ }
141
166
  try {
142
167
  const entries = readdirSync(dir);
143
168
  // 先在当前层查找
144
169
  for (const e of entries) {
145
- if (e === fileName) return pathResolve(dir, e);
170
+ if (e === fileName) {
171
+ return pathResolve(dir, e);
172
+ }
146
173
  }
147
174
  // 再递归子目录(跳过隐藏目录和常见无关目录)
148
175
  for (const e of entries) {
149
- if (e.startsWith('.') || e === 'node_modules' || e === 'build' || e === 'DerivedData') continue;
176
+ if (e.startsWith('.') || e === 'node_modules' || e === 'build' || e === 'DerivedData') {
177
+ continue;
178
+ }
150
179
  const full = pathResolve(dir, e);
151
180
  try {
152
181
  if (statSync(full).isDirectory()) {
153
182
  const found = _findFileRecursive(full, fileName, maxDepth - 1);
154
- if (found) return found;
183
+ if (found) {
184
+ return found;
185
+ }
155
186
  }
156
- } catch { /* 跳过不可访问的目录 */ }
187
+ } catch {
188
+ /* 跳过不可访问的目录 */
189
+ }
157
190
  }
158
- } catch { /* 跳过不可读目录 */ }
191
+ } catch {
192
+ /* 跳过不可读目录 */
193
+ }
159
194
  return null;
160
195
  }
161
196
 
@@ -184,24 +219,34 @@ function _resolveHeaderFormat(rawHeader, ctx) {
184
219
  // Swift: 始终 `import Module`
185
220
  if (isSwift || parsed.isSwiftImport) {
186
221
  // 已经是完整 swift import 语句
187
- if (parsed.isSwiftImport) return rawHeader.trim();
222
+ if (parsed.isSwiftImport) {
223
+ return rawHeader.trim();
224
+ }
188
225
  // 从 ObjC 格式推断 swift import
189
226
  const mod = parsed.moduleName || headerModuleName || '';
190
- if (mod) return `import ${mod}`;
227
+ if (mod) {
228
+ return `import ${mod}`;
229
+ }
191
230
  return rawHeader.trim(); // 无法推断,原样返回
192
231
  }
193
232
 
194
233
  // @import 保持原样(模块级引用不受 target 影响)
195
- if (parsed.isAtImport) return rawHeader.trim();
234
+ if (parsed.isAtImport) {
235
+ return rawHeader.trim();
236
+ }
196
237
 
197
238
  // 已经是尖括号格式 → 保持(明确的跨模块引用)
198
- if (parsed.isAngle) return rawHeader.trim();
239
+ if (parsed.isAngle) {
240
+ return rawHeader.trim();
241
+ }
199
242
 
200
243
  // ── ObjC: 判断同 target vs 跨 target ──
201
244
  const effectiveModule = parsed.moduleName || headerModuleName || '';
202
245
 
203
246
  // 如果没有 target 信息,无法判断,保持原样
204
- if (!currentTarget || !effectiveModule) return rawHeader.trim();
247
+ if (!currentTarget || !effectiveModule) {
248
+ return rawHeader.trim();
249
+ }
205
250
 
206
251
  const isSameTarget = currentTarget === effectiveModule;
207
252
 
@@ -209,10 +254,16 @@ function _resolveHeaderFormat(rawHeader, ctx) {
209
254
  // 同 target → 引号格式,计算相对路径
210
255
  if (parsed.headerName && fullPath) {
211
256
  const relPath = _findHeaderRelativePath(parsed.headerName, fullPath, projectRoot);
212
- if (relPath) return `#import "${relPath}"`;
257
+ if (relPath) {
258
+ return `#import "${relPath}"`;
259
+ }
260
+ }
261
+ if (parsed.quotedPath) {
262
+ return `#import "${parsed.quotedPath}"`;
263
+ }
264
+ if (parsed.headerName) {
265
+ return `#import "${parsed.headerName}"`;
213
266
  }
214
- if (parsed.quotedPath) return `#import "${parsed.quotedPath}"`;
215
- if (parsed.headerName) return `#import "${parsed.headerName}"`;
216
267
  return rawHeader.trim();
217
268
  }
218
269
 
@@ -226,16 +277,56 @@ function _resolveHeaderFormat(rawHeader, ctx) {
226
277
 
227
278
  /** 常见 Apple 系统框架(无需 SPM 依赖检查) */
228
279
  const _SYSTEM_FRAMEWORKS = new Set([
229
- 'Foundation', 'UIKit', 'AppKit', 'SwiftUI', 'Combine', 'CoreFoundation',
230
- 'CoreGraphics', 'CoreData', 'CoreAnimation', 'CoreLocation', 'CoreMedia',
231
- 'CoreImage', 'CoreText', 'CoreVideo', 'QuartzCore', 'AVFoundation',
232
- 'AVKit', 'WebKit', 'MapKit', 'Metal', 'MetalKit', 'ARKit', 'SceneKit',
233
- 'SpriteKit', 'GameKit', 'GameplayKit', 'HealthKit', 'HomeKit', 'CloudKit',
234
- 'StoreKit', 'PhotosUI', 'Photos', 'Contacts', 'ContactsUI', 'EventKit',
235
- 'UserNotifications', 'MessageUI', 'MultipeerConnectivity', 'NetworkExtension',
236
- 'SafariServices', 'AuthenticationServices', 'LocalAuthentication',
237
- 'Security', 'CryptoKit', 'Accelerate', 'os', 'Darwin', 'ObjectiveC',
238
- 'Dispatch', 'XCTest',
280
+ 'Foundation',
281
+ 'UIKit',
282
+ 'AppKit',
283
+ 'SwiftUI',
284
+ 'Combine',
285
+ 'CoreFoundation',
286
+ 'CoreGraphics',
287
+ 'CoreData',
288
+ 'CoreAnimation',
289
+ 'CoreLocation',
290
+ 'CoreMedia',
291
+ 'CoreImage',
292
+ 'CoreText',
293
+ 'CoreVideo',
294
+ 'QuartzCore',
295
+ 'AVFoundation',
296
+ 'AVKit',
297
+ 'WebKit',
298
+ 'MapKit',
299
+ 'Metal',
300
+ 'MetalKit',
301
+ 'ARKit',
302
+ 'SceneKit',
303
+ 'SpriteKit',
304
+ 'GameKit',
305
+ 'GameplayKit',
306
+ 'HealthKit',
307
+ 'HomeKit',
308
+ 'CloudKit',
309
+ 'StoreKit',
310
+ 'PhotosUI',
311
+ 'Photos',
312
+ 'Contacts',
313
+ 'ContactsUI',
314
+ 'EventKit',
315
+ 'UserNotifications',
316
+ 'MessageUI',
317
+ 'MultipeerConnectivity',
318
+ 'NetworkExtension',
319
+ 'SafariServices',
320
+ 'AuthenticationServices',
321
+ 'LocalAuthentication',
322
+ 'Security',
323
+ 'CryptoKit',
324
+ 'Accelerate',
325
+ 'os',
326
+ 'Darwin',
327
+ 'ObjectiveC',
328
+ 'Dispatch',
329
+ 'XCTest',
239
330
  ]);
240
331
 
241
332
  // ═══════════════════════════════════════════════════════════════
@@ -247,14 +338,18 @@ const _SYSTEM_FRAMEWORKS = new Set([
247
338
  */
248
339
  function _collectImportsFromFile(filePath, isSwift) {
249
340
  try {
250
- if (!existsSync(filePath)) return [];
341
+ if (!existsSync(filePath)) {
342
+ return [];
343
+ }
251
344
  const content = readFileSync(filePath, 'utf8');
252
345
  const lines = content.split(/\r?\n/);
253
346
  const imports = [];
254
347
  for (const line of lines) {
255
348
  const t = line.trim();
256
349
  if (isSwift) {
257
- if (t.startsWith('import ')) imports.push(t);
350
+ if (t.startsWith('import ')) {
351
+ imports.push(t);
352
+ }
258
353
  } else {
259
354
  if (t.startsWith('#import ') || t.startsWith('@import ') || t.startsWith('#include ')) {
260
355
  imports.push(t);
@@ -272,11 +367,15 @@ function _collectImportsFromFile(filePath, isSwift) {
272
367
  */
273
368
  function _collectImportsFromHeaderFile(sourcePath, importArray) {
274
369
  const dotIndex = sourcePath.lastIndexOf('.');
275
- if (dotIndex <= 0) return;
276
- const headerPath = sourcePath.substring(0, dotIndex) + '.h';
370
+ if (dotIndex <= 0) {
371
+ return;
372
+ }
373
+ const headerPath = `${sourcePath.substring(0, dotIndex)}.h`;
277
374
  const importReg = /^#import\s*<[A-Za-z0-9_]+\/[A-Za-z0-9_+.-]+\.h>$/;
278
375
  try {
279
- if (!existsSync(headerPath)) return;
376
+ if (!existsSync(headerPath)) {
377
+ return;
378
+ }
280
379
  const data = readFileSync(headerPath, 'utf8');
281
380
  for (const line of data.split('\n')) {
282
381
  const t = line.trim();
@@ -284,7 +383,9 @@ function _collectImportsFromHeaderFile(sourcePath, importArray) {
284
383
  importArray.push(t);
285
384
  }
286
385
  }
287
- } catch { /* ignore */ }
386
+ } catch {
387
+ /* ignore */
388
+ }
288
389
  }
289
390
 
290
391
  /**
@@ -307,7 +408,9 @@ function _checkImportStatus(importArray, headerLine, isSwift) {
307
408
 
308
409
  if (isSwift) {
309
410
  const m = trimmed.match(/^import\s+(\w+)/);
310
- if (m) moduleName = m[1];
411
+ if (m) {
412
+ moduleName = m[1];
413
+ }
311
414
  headerFileName = moduleName;
312
415
  } else {
313
416
  const angle = trimmed.match(/<([^/]+)\/([^>]+)>/);
@@ -359,9 +462,13 @@ function _checkImportStatus(importArray, headerLine, isSwift) {
359
462
  if (headerFileNameLower) {
360
463
  let importedFileName = null;
361
464
  const a = impT.match(/<[^/]+\/([^>]+)>/);
362
- if (a) importedFileName = a[1].toLowerCase();
465
+ if (a) {
466
+ importedFileName = a[1].toLowerCase();
467
+ }
363
468
  const q = impT.match(/"([^"]+)"/);
364
- if (q) importedFileName = basename(q[1]).toLowerCase();
469
+ if (q) {
470
+ importedFileName = basename(q[1]).toLowerCase();
471
+ }
365
472
  if (importedFileName && importedFileName === headerFileNameLower) {
366
473
  return { hasHeader: false, hasModule: false, hasSimilarHeader: true };
367
474
  }
@@ -390,9 +497,15 @@ function _inferModulesFromHeaders(headers) {
390
497
  const t = h.trim();
391
498
  let m;
392
499
  m = t.match(/^#import\s+<([^/> ]+)/);
393
- if (m) { modules.add(m[1]); continue; }
500
+ if (m) {
501
+ modules.add(m[1]);
502
+ continue;
503
+ }
394
504
  m = t.match(/^@import\s+(\w+)/);
395
- if (m) { modules.add(m[1]); continue; }
505
+ if (m) {
506
+ modules.add(m[1]);
507
+ continue;
508
+ }
396
509
  m = t.match(/^import\s+(\w+)/);
397
510
  if (m && !['class', 'struct', 'enum', 'protocol'].includes(m[1])) {
398
511
  modules.add(m[1]);
@@ -412,7 +525,9 @@ function _inferModulesFromHeaders(headers) {
412
525
  * review — 依赖缺失但可添加,需用户确认
413
526
  */
414
527
  function _evaluateDepResult(ensureResult, from, to) {
415
- if (ensureResult.exists) return { action: 'continue' };
528
+ if (ensureResult.exists) {
529
+ return { action: 'continue' };
530
+ }
416
531
  if (!ensureResult.canAdd) {
417
532
  return { action: 'block', reason: ensureResult.reason || 'cycleBlocked', from, to };
418
533
  }
@@ -429,33 +544,34 @@ function _handleDepReview(ctx) {
429
544
  const { spmService, currentTarget, mod, ensureResult, NU, depWarnings, label = '' } = ctx;
430
545
 
431
546
  const fixMode = spmService.getFixMode();
432
- const buttons = fixMode === 'fix'
433
- ? ['直接插入(信任架构)', '提示操作插入', '自动修复依赖', '取消操作']
434
- : ['直接插入(信任架构)', '提示操作插入', '取消操作'];
547
+ const buttons =
548
+ fixMode === 'fix'
549
+ ? ['直接插入(信任架构)', '提示操作插入', '自动修复依赖', '取消操作']
550
+ : ['直接插入(信任架构)', '提示操作插入', '取消操作'];
435
551
 
436
552
  const crossTag = ensureResult.crossPackage ? ' (跨包)' : '';
437
553
  const prefix = label ? `[${label}] ` : '';
438
- console.log(` ⚠️ ${prefix}依赖缺失: ${currentTarget} -> ${mod}`);
439
554
 
440
555
  const userChoice = NU.promptWithButtons(
441
556
  `检测到依赖缺失:${currentTarget} -> ${mod}${crossTag}\n\n请选择处理方式:`,
442
557
  buttons,
443
- 'AutoSnippet SPM 依赖决策',
558
+ 'AutoSnippet SPM 依赖决策'
444
559
  );
445
560
 
446
- if (userChoice === '取消操作' || (!userChoice && !['直接插入(信任架构)', '提示操作插入', '自动修复依赖'].includes(userChoice))) {
561
+ if (
562
+ userChoice === '取消操作' ||
563
+ (!userChoice && !['直接插入(信任架构)', '提示操作插入', '自动修复依赖'].includes(userChoice))
564
+ ) {
447
565
  return { blocked: true };
448
566
  }
449
567
 
450
568
  if (userChoice === '提示操作插入') {
451
- console.log(` 📋 ${prefix}提示操作:依赖缺失 ${currentTarget} -> ${mod}`);
452
569
  depWarnings.set(mod, `${currentTarget} -> ${mod}`);
453
570
  }
454
571
 
455
572
  if (userChoice === '自动修复依赖') {
456
573
  const fixResult = spmService.addDependency(currentTarget, mod);
457
574
  if (fixResult.ok) {
458
- console.log(` ✅ ${prefix}已自动补齐依赖: ${currentTarget} -> ${mod}${fixResult.crossPackage ? ' (跨包)' : ''} (${fixResult.file})`);
459
575
  NU.notify(`已补齐依赖:${currentTarget} -> ${mod}`, 'AutoSnippet SPM');
460
576
  } else {
461
577
  console.warn(` ⚠️ ${prefix}自动修复失败: ${fixResult.error},继续插入`);
@@ -482,9 +598,11 @@ function _handleDepReview(ctx) {
482
598
  * @returns {boolean}
483
599
  */
484
600
  function _writeImportLineXcode(importLine, insertLine, XA, CM) {
485
- if (!XA.isXcodeRunning()) return false;
601
+ if (!XA.isXcodeRunning()) {
602
+ return false;
603
+ }
486
604
  try {
487
- const contentToWrite = String(importLine).trim() + '\n';
605
+ const contentToWrite = `${String(importLine).trim()}\n`;
488
606
  const previousClipboard = CM.read();
489
607
 
490
608
  CM.write(contentToWrite);
@@ -516,7 +634,9 @@ function _writeImportLineFile(filePath, importLine, isSwift) {
516
634
  for (let i = 0; i < lines.length; i++) {
517
635
  const t = lines[i].trim();
518
636
  if (isSwift) {
519
- if (t.startsWith('import ') && !t.startsWith('import (')) lastImportIdx = i;
637
+ if (t.startsWith('import ') && !t.startsWith('import (')) {
638
+ lastImportIdx = i;
639
+ }
520
640
  } else {
521
641
  if (t.startsWith('#import ') || t.startsWith('#include ') || t.startsWith('@import ')) {
522
642
  lastImportIdx = i;
@@ -543,14 +663,20 @@ function _writeImportLineFile(filePath, importLine, isSwift) {
543
663
  */
544
664
  function _getLastImportLine(filePath) {
545
665
  try {
546
- if (!existsSync(filePath)) return 0;
666
+ if (!existsSync(filePath)) {
667
+ return 0;
668
+ }
547
669
  const content = readFileSync(filePath, 'utf8');
548
670
  const lines = content.split(/\r?\n/);
549
671
  let lastIdx = -1;
550
672
  for (let i = 0; i < lines.length; i++) {
551
673
  const t = lines[i].trim();
552
- if (t.startsWith('#import ') || t.startsWith('@import ')
553
- || t.startsWith('#include ') || t.startsWith('import ')) {
674
+ if (
675
+ t.startsWith('#import ') ||
676
+ t.startsWith('@import ') ||
677
+ t.startsWith('#include ') ||
678
+ t.startsWith('import ')
679
+ ) {
554
680
  lastIdx = i;
555
681
  }
556
682
  }
@@ -601,14 +727,16 @@ function _computePasteLineNumber(triggerLineNumber, headerInsertCount, filePath,
601
727
  * @returns {Promise<{inserted: string[], skipped: string[], cancelled: boolean}>}
602
728
  */
603
729
  export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
604
- const XA = await import('../../infrastructure/external/XcodeAutomation.js');
605
- const CM = await import('../../infrastructure/external/ClipboardManager.js');
606
- const NU = await import('../../infrastructure/external/NativeUi.js');
730
+ const XA = await import('./XcodeAutomation.js');
731
+ const CM = await import('../../../infrastructure/external/ClipboardManager.js');
732
+ const NU = await import('../../../infrastructure/external/NativeUi.js');
607
733
 
608
734
  const result = { inserted: [], skipped: [], cancelled: false };
609
735
  /** @type {Map<string, string>} 模块名 → 提示注释('提示操作插入'按钮选择时记录) */
610
736
  const depWarnings = opts.depWarnings instanceof Map ? new Map(opts.depWarnings) : new Map();
611
- if (!headers || headers.length === 0) return result;
737
+ if (!headers || headers.length === 0) {
738
+ return result;
739
+ }
612
740
 
613
741
  const isSwift = opts.isSwift ?? fullPath.endsWith('.swift');
614
742
 
@@ -619,7 +747,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
619
747
  _collectImportsFromHeaderFile(fullPath, importArray);
620
748
  }
621
749
 
622
- // ── Step 2: SPM 服务准备 ──
750
+ // ── Step 2: SPM/模块 服务准备 ──
623
751
  // 优先复用 opts 传入的 spmService/currentTarget(避免与 _preflightDeps 重复 load)
624
752
  let spmService = opts._spmService || null;
625
753
  let currentTarget = opts._currentTarget || null;
@@ -628,21 +756,27 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
628
756
  if (opts.moduleName && !inferredModules.includes(opts.moduleName)) {
629
757
  inferredModules.push(opts.moduleName);
630
758
  }
631
- const thirdPartyModules = inferredModules.filter(m => !_SYSTEM_FRAMEWORKS.has(m));
759
+ const thirdPartyModules = inferredModules.filter((m) => !_SYSTEM_FRAMEWORKS.has(m));
632
760
  if (thirdPartyModules.length > 0) {
633
761
  try {
634
- const { ServiceContainer } = await import('../../injection/ServiceContainer.js');
762
+ const { ServiceContainer } = await import('../../../injection/ServiceContainer.js');
635
763
  const container = ServiceContainer.getInstance();
636
764
  spmService = container.get('spmService');
637
765
  if (spmService) {
638
766
  if (spmService.getFixMode() === 'off') {
639
767
  spmService = null;
640
768
  } else {
641
- try { await spmService.load(); } catch { /* Package.swift 不存在则跳过 */ }
769
+ try {
770
+ await spmService.load();
771
+ } catch {
772
+ /* Package.swift 不存在则跳过 */
773
+ }
642
774
  currentTarget = spmService.resolveCurrentTarget(fullPath);
643
775
  }
644
776
  }
645
- } catch { /* SPM 检查异常不阻断 */ }
777
+ } catch {
778
+ /* SPM 检查异常不阻断 */
779
+ }
646
780
  }
647
781
  }
648
782
 
@@ -650,15 +784,21 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
650
784
  const xcodeReady = XA.isXcodeRunning();
651
785
  // 从当前文件内容计算 import 插入基准行(1-based)
652
786
  let content;
653
- try { content = readFileSync(fullPath, 'utf8'); } catch { return result; }
787
+ try {
788
+ content = readFileSync(fullPath, 'utf8');
789
+ } catch {
790
+ return result;
791
+ }
654
792
  const baseInsertLine = findImportInsertLine(content, isSwift) + 1; // 0-based → 1-based
655
- let xcodeOffset = 0; // 每次 Xcode 插入成功后 +1(修正多条 header 行号偏移)
793
+ let xcodeOffset = 0; // 每次 Xcode 插入成功后 +1(修正多条 header 行号偏移)
656
794
  let fileWriteUsed = false; // 一旦使用文件写入,后续全部走文件写入(避免 Xcode reload 冲突)
657
795
 
658
796
  // ── Step 4: 逐条处理 ──
659
797
  for (const header of headers) {
660
798
  const headerTrimmed = header.trim();
661
- if (!headerTrimmed) continue;
799
+ if (!headerTrimmed) {
800
+ continue;
801
+ }
662
802
 
663
803
  // ── 三级去重 ──
664
804
  // 先按原始格式检查,再按解析后格式检查(同一 header 可能格式不同)
@@ -670,21 +810,19 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
670
810
  projectRoot: watcher?.projectRoot || null,
671
811
  });
672
812
  const status = _checkImportStatus(importArray, headerTrimmed, isSwift);
673
- const statusResolved = (preResolvedHeader !== headerTrimmed)
674
- ? _checkImportStatus(importArray, preResolvedHeader, isSwift)
675
- : status;
813
+ const statusResolved =
814
+ preResolvedHeader !== headerTrimmed
815
+ ? _checkImportStatus(importArray, preResolvedHeader, isSwift)
816
+ : status;
676
817
  if (status.hasHeader || statusResolved.hasHeader) {
677
- console.log(` ⏭️ 已存在(精确匹配): ${preResolvedHeader}`);
678
818
  result.skipped.push(preResolvedHeader);
679
819
  continue;
680
820
  }
681
821
  if (status.hasModule || statusResolved.hasModule) {
682
- console.log(` ⏭️ 已存在(模块匹配): ${preResolvedHeader}`);
683
822
  result.skipped.push(preResolvedHeader);
684
823
  continue;
685
824
  }
686
825
  if (status.hasSimilarHeader || statusResolved.hasSimilarHeader) {
687
- console.log(` ⏭️ 已存在(相似头文件): ${preResolvedHeader}`);
688
826
  result.skipped.push(preResolvedHeader);
689
827
  continue;
690
828
  }
@@ -693,7 +831,9 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
693
831
  const headerModules = _inferModulesFromHeaders([headerTrimmed]);
694
832
  if (spmService && currentTarget && !opts.skipDepCheck) {
695
833
  for (const mod of headerModules) {
696
- if (_SYSTEM_FRAMEWORKS.has(mod) || mod === currentTarget) continue;
834
+ if (_SYSTEM_FRAMEWORKS.has(mod) || mod === currentTarget) {
835
+ continue;
836
+ }
697
837
 
698
838
  const ensureResult = spmService.ensureDependency(currentTarget, mod);
699
839
  const decision = _evaluateDepResult(ensureResult, currentTarget, mod);
@@ -702,7 +842,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
702
842
  console.warn(` ⛔ 依赖被阻止: ${currentTarget} -> ${mod} (${decision.reason})`);
703
843
  NU.notify(
704
844
  `已阻止依赖注入\n${currentTarget} -> ${mod}\n${decision.reason}`,
705
- 'AutoSnippet SPM 依赖策略',
845
+ 'AutoSnippet SPM 依赖策略'
706
846
  );
707
847
  result.cancelled = true;
708
848
  return result;
@@ -710,10 +850,14 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
710
850
 
711
851
  if (decision.action === 'review') {
712
852
  const reviewResult = _handleDepReview({
713
- spmService, currentTarget, mod, ensureResult, NU, depWarnings,
853
+ spmService,
854
+ currentTarget,
855
+ mod,
856
+ ensureResult,
857
+ NU,
858
+ depWarnings,
714
859
  });
715
860
  if (reviewResult.blocked) {
716
- console.log(` ⏹️ 用户取消`);
717
861
  result.cancelled = true;
718
862
  return result;
719
863
  }
@@ -724,9 +868,9 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
724
868
  // ── 构建带注释后缀的 import 行 ──
725
869
  // 复用 dedup 阶段已计算的 preResolvedHeader
726
870
  const resolvedHeader = preResolvedHeader;
727
- const depHint = headerModules.find(m => depWarnings.has(m));
871
+ const depHint = headerModules.find((m) => depWarnings.has(m));
728
872
  const importLine = depHint
729
- ? _withAutoSnippetNote(resolvedHeader) + ` // ⚠️ 依赖缺失: ${depWarnings.get(depHint)},需手动补齐 Package.swift`
873
+ ? `${_withAutoSnippetNote(resolvedHeader)} // ⚠️ 依赖缺失: ${depWarnings.get(depHint)},需手动补齐 Package.swift`
730
874
  : _withAutoSnippetNote(resolvedHeader);
731
875
 
732
876
  // ── 写入:Xcode 自动化优先 → 文件写入回退 ──
@@ -747,11 +891,9 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
747
891
 
748
892
  result.inserted.push(resolvedHeader);
749
893
  importArray.push(resolvedHeader); // 添加到去重列表(用解析后格式)
750
- console.log(` + ${resolvedHeader}`);
751
894
  }
752
895
 
753
896
  if (result.inserted.length > 0) {
754
- console.log(` 📦 已添加 ${result.inserted.length} 个依赖`);
755
897
  }
756
898
  return result;
757
899
  }
@@ -776,17 +918,16 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
776
918
  * @param {import('./FileWatcher.js').FileWatcher} watcher
777
919
  */
778
920
  export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine) {
779
- const XA = await import('../../infrastructure/external/XcodeAutomation.js');
780
- const CM = await import('../../infrastructure/external/ClipboardManager.js');
781
- const NU = await import('../../infrastructure/external/NativeUi.js');
921
+ const XA = await import('./XcodeAutomation.js');
922
+ const CM = await import('../../../infrastructure/external/ClipboardManager.js');
923
+ const NU = await import('../../../infrastructure/external/NativeUi.js');
782
924
 
783
925
  const code = selected.code || '';
784
926
  if (!code) {
785
- console.log(` ℹ️ 选中项无代码内容`);
786
927
  return;
787
928
  }
788
929
 
789
- const headersToInsert = (selected.headers || []).filter(h => h && h.trim());
930
+ const headersToInsert = (selected.headers || []).filter((h) => h?.trim());
790
931
  const isSwift = fullPath.endsWith('.swift');
791
932
 
792
933
  // ═══════════════════════════════════════════════════════
@@ -805,7 +946,9 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
805
946
  }
806
947
  // ── Step 1: 找到触发行号 ──
807
948
  let content;
808
- try { content = readFileSync(fullPath, 'utf8'); } catch {
949
+ try {
950
+ content = readFileSync(fullPath, 'utf8');
951
+ } catch {
809
952
  return _fileInsertFallback(fullPath, selected, triggerLine, headersToInsert, watcher);
810
953
  }
811
954
  const triggerLineNumber = findTriggerLineNumber(content, triggerLine);
@@ -827,7 +970,6 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
827
970
  if (headersToInsert.length > 0) {
828
971
  const preflight = await _preflightDeps(fullPath, headersToInsert, selected, NU);
829
972
  if (preflight.blocked) {
830
- console.log(` ⏹️ 依赖检查被阻止,跳过代码插入`);
831
973
  return;
832
974
  }
833
975
  if (preflight.depWarnings && preflight.depWarnings.size > 0) {
@@ -843,7 +985,9 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
843
985
  if (!cutOk) {
844
986
  console.warn(` ⚠️ 自动剪切失败,降级为文件写入`);
845
987
  // Preflight 已通过,skipDepCheck 避免重复弹窗
846
- return _fileInsertFallback(fullPath, selected, triggerLine, headersToInsert, watcher, { skipDepCheck: true });
988
+ return _fileInsertFallback(fullPath, selected, triggerLine, headersToInsert, watcher, {
989
+ skipDepCheck: true,
990
+ });
847
991
  }
848
992
  await _sleep(300);
849
993
 
@@ -853,13 +997,8 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
853
997
  while (codeLines.length > 0 && !codeLines[codeLines.length - 1].trim()) {
854
998
  codeLines.pop();
855
999
  }
856
- const indentedLines = codeLines.map(line => line ? indent + line : line);
857
- // 注释标记
858
- const commentMarker = _generateInsertMarker(fullPath, selected);
859
- const markedLines = commentMarker
860
- ? [indent + commentMarker, ...indentedLines]
861
- : indentedLines;
862
- const indentedCode = markedLines.join('\n');
1000
+ const indentedLines = codeLines.map((line) => (line ? indent + line : line));
1001
+ const indentedCode = indentedLines.join('\n');
863
1002
 
864
1003
  // ── Step 5: 插入 Headers ──
865
1004
  let headerInsertCount = 0;
@@ -873,7 +1012,6 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
873
1012
  _currentTarget: _currentTargetCached,
874
1013
  });
875
1014
  if (headerResult.cancelled) {
876
- console.log(` ⏹️ Headers 插入被取消`);
877
1015
  return;
878
1016
  }
879
1017
  headerInsertCount = headerResult.inserted.length;
@@ -886,7 +1024,7 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
886
1024
  triggerLineNumber,
887
1025
  headerInsertCount,
888
1026
  fullPath,
889
- { forceOffset: headerInsertCount > 0, expectedHeaderCount: headerInsertCount },
1027
+ { forceOffset: headerInsertCount > 0, expectedHeaderCount: headerInsertCount }
890
1028
  );
891
1029
 
892
1030
  // 如果 headers 通过文件写入,等待 Xcode reload
@@ -907,8 +1045,6 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
907
1045
  XA.selectAndPasteInXcode();
908
1046
  await _sleep(300);
909
1047
  });
910
-
911
- console.log(` ✅ 代码已粘贴到 Xcode(可 Cmd+Z 撤销)`);
912
1048
  NU.notify(`已插入「${selected.title || '代码片段'}」`, 'AutoSnippet');
913
1049
  return;
914
1050
  }
@@ -937,44 +1073,67 @@ async function _preflightDeps(fullPath, headers, selected, NU) {
937
1073
  if (selected.moduleName && !inferredModules.includes(selected.moduleName)) {
938
1074
  inferredModules.push(selected.moduleName);
939
1075
  }
940
- const thirdPartyModules = inferredModules.filter(m => !_SYSTEM_FRAMEWORKS.has(m));
941
- if (thirdPartyModules.length === 0) return result;
1076
+ const thirdPartyModules = inferredModules.filter((m) => !_SYSTEM_FRAMEWORKS.has(m));
1077
+ if (thirdPartyModules.length === 0) {
1078
+ return result;
1079
+ }
942
1080
 
943
1081
  try {
944
- const { ServiceContainer } = await import('../../injection/ServiceContainer.js');
1082
+ const { ServiceContainer } = await import('../../../injection/ServiceContainer.js');
945
1083
  const container = ServiceContainer.getInstance();
946
1084
  const spmService = container.get('spmService');
947
- if (!spmService) return result;
1085
+ if (!spmService) {
1086
+ return result;
1087
+ }
948
1088
 
949
1089
  // Fix Mode 检查:off 模式完全跳过
950
- if (spmService.getFixMode() === 'off') return result;
1090
+ if (spmService.getFixMode() === 'off') {
1091
+ return result;
1092
+ }
951
1093
 
952
- try { await spmService.load(); } catch { return result; }
1094
+ try {
1095
+ await spmService.load();
1096
+ } catch {
1097
+ return result;
1098
+ }
953
1099
 
954
1100
  const currentTarget = spmService.resolveCurrentTarget(fullPath);
955
- if (!currentTarget) return result;
1101
+ if (!currentTarget) {
1102
+ return result;
1103
+ }
956
1104
 
957
1105
  for (const mod of thirdPartyModules) {
958
- if (mod === currentTarget) continue;
1106
+ if (mod === currentTarget) {
1107
+ continue;
1108
+ }
959
1109
 
960
1110
  const ensureResult = spmService.ensureDependency(currentTarget, mod);
961
1111
  const decision = _evaluateDepResult(ensureResult, currentTarget, mod);
962
1112
 
963
1113
  if (decision.action === 'block') {
964
- console.warn(` ⛔ [Preflight] 依赖被阻止: ${currentTarget} -> ${mod} (${decision.reason})`);
1114
+ console.warn(
1115
+ ` ⛔ [Preflight] 依赖被阻止: ${currentTarget} -> ${mod} (${decision.reason})`
1116
+ );
965
1117
  NU.notify(
966
1118
  `已阻止依赖注入\n${currentTarget} -> ${mod}\n${decision.reason}`,
967
- 'AutoSnippet SPM 依赖策略',
1119
+ 'AutoSnippet SPM 依赖策略'
968
1120
  );
969
1121
  result.blocked = true;
970
1122
  return result;
971
1123
  }
972
1124
 
973
1125
  if (decision.action === 'review') {
974
- if (!result.depWarnings) result.depWarnings = new Map();
1126
+ if (!result.depWarnings) {
1127
+ result.depWarnings = new Map();
1128
+ }
975
1129
  const reviewResult = _handleDepReview({
976
- spmService, currentTarget, mod, ensureResult, NU,
977
- depWarnings: result.depWarnings, label: 'Preflight',
1130
+ spmService,
1131
+ currentTarget,
1132
+ mod,
1133
+ ensureResult,
1134
+ NU,
1135
+ depWarnings: result.depWarnings,
1136
+ label: 'Preflight',
978
1137
  });
979
1138
  if (reviewResult.blocked) {
980
1139
  result.blocked = true;
@@ -997,14 +1156,23 @@ async function _preflightDeps(fullPath, headers, selected, NU) {
997
1156
  // §13 文件写入降级
998
1157
  // ═══════════════════════════════════════════════════════════════
999
1158
 
1000
- async function _fileInsertFallback(fullPath, selected, triggerLine, headersToInsert, watcher, opts = {}) {
1159
+ async function _fileInsertFallback(
1160
+ fullPath,
1161
+ selected,
1162
+ triggerLine,
1163
+ headersToInsert,
1164
+ watcher,
1165
+ opts = {}
1166
+ ) {
1001
1167
  // 先写 headers
1002
1168
  if (headersToInsert.length > 0) {
1003
1169
  const headerResult = await insertHeaders(watcher, fullPath, headersToInsert, {
1004
1170
  moduleName: selected.moduleName || null,
1005
1171
  skipDepCheck: opts.skipDepCheck || false, // Preflight 已通过时跳过重复检查
1006
1172
  });
1007
- if (headerResult.cancelled) return;
1173
+ if (headerResult.cancelled) {
1174
+ return;
1175
+ }
1008
1176
  }
1009
1177
 
1010
1178
  // 再替换触发行为代码
@@ -1033,27 +1201,20 @@ async function _fileInsertFallback(fullPath, selected, triggerLine, headersToIns
1033
1201
  while (codeLines.length > 0 && !codeLines[codeLines.length - 1].trim()) {
1034
1202
  codeLines.pop();
1035
1203
  }
1036
- const indentedLines = codeLines.map(line => line ? indent + line : line);
1037
-
1038
- const commentMarker = _generateInsertMarker(fullPath, selected);
1039
- const markedLines = commentMarker
1040
- ? [indent + commentMarker, ...indentedLines]
1041
- : indentedLines;
1204
+ const indentedLines = codeLines.map((line) => (line ? indent + line : line));
1042
1205
 
1043
- while (markedLines.length > 0 && !markedLines[markedLines.length - 1].trim()) {
1044
- markedLines.pop();
1206
+ while (indentedLines.length > 0 && !indentedLines[indentedLines.length - 1].trim()) {
1207
+ indentedLines.pop();
1045
1208
  }
1046
1209
 
1047
- const newLines = [...lines.slice(0, found), ...markedLines, ...lines.slice(found + 1)];
1210
+ const newLines = [...lines.slice(0, found), ...indentedLines, ...lines.slice(found + 1)];
1048
1211
  const newContent = newLines.join('\n');
1049
1212
  saveEventFilter.markWrite(fullPath, newContent);
1050
1213
  writeFileSync(fullPath, newContent, 'utf8');
1051
- console.log(` ✅ 代码已写入文件(替换触发行)`);
1052
1214
  } else {
1053
- const appendContent = content + '\n' + code + '\n';
1215
+ const appendContent = `${content}\n${code}\n`;
1054
1216
  saveEventFilter.markWrite(fullPath, appendContent);
1055
1217
  writeFileSync(fullPath, appendContent, 'utf8');
1056
- console.log(` ✅ 代码已追加到文件末尾`);
1057
1218
  }
1058
1219
  } catch (err) {
1059
1220
  console.warn(` ⚠️ 文件写入失败: ${err.message}`);
@@ -1070,16 +1231,27 @@ function _generateInsertMarker(filePath, selected) {
1070
1231
  const trigger = selected.trigger ? `[${selected.trigger}]` : '';
1071
1232
  const recipeName = selected.name ? ` from ${selected.name}` : '';
1072
1233
  const timestamp = new Date().toLocaleString('zh-CN', {
1073
- year: 'numeric', month: '2-digit', day: '2-digit',
1074
- hour: '2-digit', minute: '2-digit',
1234
+ year: 'numeric',
1235
+ month: '2-digit',
1236
+ day: '2-digit',
1237
+ hour: '2-digit',
1238
+ minute: '2-digit',
1075
1239
  });
1076
1240
 
1077
1241
  const marker = `🤖 AutoSnippet${trigger}${recipeName} @ ${timestamp}`;
1078
1242
 
1079
- if (['.py', '.rb'].includes(ext)) return `# ${marker}`;
1080
- if (['.lua', '.sql'].includes(ext)) return `-- ${marker}`;
1081
- if (['.html', '.xml', '.svg'].includes(ext)) return `<!-- ${marker} -->`;
1082
- if (['.css', '.scss', '.less'].includes(ext)) return `/* ${marker} */`;
1243
+ if (['.py', '.rb'].includes(ext)) {
1244
+ return `# ${marker}`;
1245
+ }
1246
+ if (['.lua', '.sql'].includes(ext)) {
1247
+ return `-- ${marker}`;
1248
+ }
1249
+ if (['.html', '.xml', '.svg'].includes(ext)) {
1250
+ return `<!-- ${marker} -->`;
1251
+ }
1252
+ if (['.css', '.scss', '.less'].includes(ext)) {
1253
+ return `/* ${marker} */`;
1254
+ }
1083
1255
  return `// ${marker}`;
1084
1256
  } catch {
1085
1257
  return null;
@@ -1094,11 +1266,15 @@ function _generateInsertMarker(filePath, selected) {
1094
1266
  * 查找触发行的行号(1-based,-1 表示未找到)
1095
1267
  */
1096
1268
  export function findTriggerLineNumber(content, triggerLine) {
1097
- if (!content || !triggerLine) return -1;
1269
+ if (!content || !triggerLine) {
1270
+ return -1;
1271
+ }
1098
1272
  const needle = triggerLine.trim();
1099
1273
  const lines = content.split(/\r?\n/);
1100
1274
  for (let i = 0; i < lines.length; i++) {
1101
- if (lines[i].trim() === needle) return i + 1;
1275
+ if (lines[i].trim() === needle) {
1276
+ return i + 1;
1277
+ }
1102
1278
  }
1103
1279
  return -1;
1104
1280
  }
@@ -1112,9 +1288,13 @@ export function findImportInsertLine(content, isSwift) {
1112
1288
  for (let i = 0; i < lines.length; i++) {
1113
1289
  const t = lines[i].trim();
1114
1290
  if (isSwift) {
1115
- if (t.startsWith('import ') && !t.startsWith('import (')) lastImportLine = i;
1291
+ if (t.startsWith('import ') && !t.startsWith('import (')) {
1292
+ lastImportLine = i;
1293
+ }
1116
1294
  } else {
1117
- if (t.startsWith('#import') || t.startsWith('@import')) lastImportLine = i;
1295
+ if (t.startsWith('#import') || t.startsWith('@import')) {
1296
+ lastImportLine = i;
1297
+ }
1118
1298
  }
1119
1299
  }
1120
1300
  return lastImportLine >= 0 ? lastImportLine + 1 : 0;