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
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  import { createHash } from 'node:crypto';
22
- import { isXcodeFrontmost } from '../../infrastructure/external/XcodeAutomation.js';
22
+ import { isXcodeFrontmost } from './XcodeAutomation.js';
23
23
 
24
24
  /* ────────── 配置 ────────── */
25
25
 
@@ -48,7 +48,9 @@ class SaveEventFilter {
48
48
 
49
49
  // 定期清理过期条目,防止内存泄漏
50
50
  this._cleanupInterval = setInterval(() => this._cleanup(), 60_000);
51
- if (this._cleanupInterval.unref) this._cleanupInterval.unref();
51
+ if (this._cleanupInterval.unref) {
52
+ this._cleanupInterval.unref();
53
+ }
52
54
  }
53
55
 
54
56
  /**
@@ -75,9 +77,8 @@ class SaveEventFilter {
75
77
 
76
78
  // ── Layer 1: Self-write 冷却期 ──
77
79
  const lastSelfWrite = this._selfWrites.get(filePath);
78
- if (lastSelfWrite && (Date.now() - lastSelfWrite) < SELF_WRITE_COOLDOWN) {
80
+ if (lastSelfWrite && Date.now() - lastSelfWrite < SELF_WRITE_COOLDOWN) {
79
81
  if (process.env.ASD_DEBUG === '1') {
80
- console.log(`[SaveFilter] 跳过 self-write 冷却期: ${filePath}`);
81
82
  }
82
83
  return { process: false, reason: 'self-write-cooldown' };
83
84
  }
@@ -87,7 +88,6 @@ class SaveEventFilter {
87
88
  const prevHash = this._contentHashes.get(filePath);
88
89
  if (prevHash && prevHash === hash) {
89
90
  if (process.env.ASD_DEBUG === '1') {
90
- console.log(`[SaveFilter] 内容未变,跳过: ${filePath}`);
91
91
  }
92
92
  return { process: false, reason: 'content-unchanged' };
93
93
  }
@@ -96,7 +96,6 @@ class SaveEventFilter {
96
96
  if (process.platform === 'darwin' && isFocusCheckEnabled()) {
97
97
  if (!isXcodeFrontmost()) {
98
98
  if (process.env.ASD_DEBUG === '1') {
99
- console.log(`[SaveFilter] Xcode 非前台,跳过自动保存: ${filePath}`);
100
99
  }
101
100
  return { process: false, reason: 'xcode-not-frontmost' };
102
101
  }
@@ -147,7 +146,9 @@ class SaveEventFilter {
147
146
  const expiry = 10 * 60 * 1000; // 10 分钟
148
147
 
149
148
  for (const [k, ts] of this._selfWrites) {
150
- if (now - ts > expiry) this._selfWrites.delete(k);
149
+ if (now - ts > expiry) {
150
+ this._selfWrites.delete(k);
151
+ }
151
152
  }
152
153
 
153
154
  // 内容哈希保留更长时间,只清理明显过期的
@@ -0,0 +1,350 @@
1
+ /**
2
+ * XcodeAutomation — Xcode AppleScript 自动化
3
+ *
4
+ * 通过 osascript 向 Xcode 发送键盘事件,实现行级操作:
5
+ * 跳转行、选中行内容、剪切行、粘贴、在行首插入、删除行内容、保存文档。
6
+ *
7
+ * 所有操作都带超时保护(OSASCRIPT_TIMEOUT),Xcode 未运行时安全跳过。
8
+ * 仅支持 macOS。
9
+ */
10
+
11
+ import { execSync, spawnSync } from 'node:child_process';
12
+
13
+ const OSASCRIPT_TIMEOUT = 5000;
14
+
15
+ // ─────────────────────────────────────────────
16
+ // 内部辅助
17
+ // ─────────────────────────────────────────────
18
+
19
+ /**
20
+ * 将行号限制为有效正整数(最小值 1)
21
+ * @param {number} n 原始行号
22
+ * @returns {number} 安全的 1-based 行号
23
+ */
24
+ function _safeLine(n) {
25
+ return Number.isFinite(n) && n > 0 ? n : 1;
26
+ }
27
+
28
+ /**
29
+ * 执行 osascript 并返回是否成功
30
+ * @param {string[]} args osascript 参数数组(每对 `-e`, `script`)
31
+ * @returns {boolean}
32
+ */
33
+ function _run(args) {
34
+ try {
35
+ const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
36
+ return res.status === 0;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ // ─────────────────────────────────────────────
43
+ // 状态查询
44
+ // ─────────────────────────────────────────────
45
+
46
+ /**
47
+ * 检查 Xcode 是否正在运行(不会启动 Xcode)
48
+ */
49
+ export function isXcodeRunning() {
50
+ if (process.platform !== 'darwin') {
51
+ return false;
52
+ }
53
+ try {
54
+ const result = execSync('pgrep -x Xcode', {
55
+ encoding: 'utf8',
56
+ timeout: 2000,
57
+ stdio: 'pipe',
58
+ });
59
+ return result.trim().length > 0;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 检查 Xcode 是否为当前前台应用
67
+ */
68
+ export function isXcodeFrontmost() {
69
+ if (!isXcodeRunning()) {
70
+ return false;
71
+ }
72
+ try {
73
+ const result = execSync(
74
+ 'osascript -e \'tell application "System Events" to get name of first process whose frontmost is true\'',
75
+ { encoding: 'utf8', timeout: OSASCRIPT_TIMEOUT, stdio: 'pipe' }
76
+ );
77
+ return result.trim() === 'Xcode';
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ // ─────────────────────────────────────────────
84
+ // 行操作
85
+ // ─────────────────────────────────────────────
86
+
87
+ /**
88
+ * 跳转到指定行
89
+ *
90
+ * 按键序列:Cmd+L → 输入行号 → Return
91
+ *
92
+ * @param {number} lineNumber 1-based 行号
93
+ * @returns {boolean} 是否成功
94
+ */
95
+ export function jumpToLineInXcode(lineNumber) {
96
+ if (!isXcodeRunning()) {
97
+ return false;
98
+ }
99
+ const n = _safeLine(lineNumber);
100
+ return _run([
101
+ '-e',
102
+ 'tell application "Xcode" to activate',
103
+ '-e',
104
+ 'delay 0.2',
105
+ '-e',
106
+ 'tell application "System Events"',
107
+ '-e',
108
+ ' keystroke "l" using command down',
109
+ '-e',
110
+ ' delay 0.2',
111
+ '-e',
112
+ ` keystroke "${String(n)}"`,
113
+ '-e',
114
+ ' delay 0.2',
115
+ '-e',
116
+ ' key code 36',
117
+ '-e',
118
+ 'end tell',
119
+ ]);
120
+ }
121
+
122
+ /**
123
+ * 剪切指定行的文本内容(不含换行符)
124
+ *
125
+ * 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+X 剪切
126
+ *
127
+ * @param {number} lineNumber 1-based 行号
128
+ * @returns {boolean} 是否成功
129
+ */
130
+ export function cutLineInXcode(lineNumber) {
131
+ if (!isXcodeRunning()) {
132
+ return false;
133
+ }
134
+ const n = _safeLine(lineNumber);
135
+ return _run([
136
+ '-e',
137
+ 'tell application "Xcode" to activate',
138
+ '-e',
139
+ 'delay 0.5',
140
+ '-e',
141
+ 'tell application "System Events"',
142
+ '-e',
143
+ ' keystroke "l" using command down', // Cmd+L: Go to Line
144
+ '-e',
145
+ ' delay 0.5',
146
+ '-e',
147
+ ` keystroke "${String(n)}"`, // 输入行号
148
+ '-e',
149
+ ' delay 0.5',
150
+ '-e',
151
+ ' key code 36', // Return
152
+ '-e',
153
+ ' delay 0.5',
154
+ '-e',
155
+ ' key code 123 using command down', // Cmd+← 行首
156
+ '-e',
157
+ ' delay 0.5',
158
+ '-e',
159
+ ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
160
+ '-e',
161
+ ' delay 0.5',
162
+ '-e',
163
+ ' keystroke "x" using command down', // Cmd+X
164
+ '-e',
165
+ 'end tell',
166
+ ]);
167
+ }
168
+
169
+ /**
170
+ * 删除指定行的文本内容(保留空行,不删除行本身)
171
+ *
172
+ * 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Delete
173
+ *
174
+ * @param {number} lineNumber 1-based 行号
175
+ * @returns {boolean} 是否成功
176
+ */
177
+ export function deleteLineContentInXcode(lineNumber) {
178
+ if (!isXcodeRunning()) {
179
+ return false;
180
+ }
181
+ const n = _safeLine(lineNumber);
182
+ return _run([
183
+ '-e',
184
+ 'tell application "Xcode" to activate',
185
+ '-e',
186
+ 'delay 0.3',
187
+ '-e',
188
+ 'tell application "System Events"',
189
+ '-e',
190
+ ' keystroke "l" using command down',
191
+ '-e',
192
+ ' delay 0.3',
193
+ '-e',
194
+ ` keystroke "${String(n)}"`,
195
+ '-e',
196
+ ' delay 0.3',
197
+ '-e',
198
+ ' key code 36',
199
+ '-e',
200
+ ' delay 0.3',
201
+ '-e',
202
+ ' key code 123 using command down', // Cmd+← 行首
203
+ '-e',
204
+ ' delay 0.2',
205
+ '-e',
206
+ ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
207
+ '-e',
208
+ ' delay 0.2',
209
+ '-e',
210
+ ' key code 51', // Delete 键
211
+ '-e',
212
+ ' delay 0.3',
213
+ '-e',
214
+ 'end tell',
215
+ ]);
216
+ }
217
+
218
+ // ─────────────────────────────────────────────
219
+ // 粘贴操作
220
+ // ─────────────────────────────────────────────
221
+
222
+ /**
223
+ * 执行粘贴(Cmd+V)
224
+ *
225
+ * 调用前须确保剪贴板已写入目标内容。
226
+ * @returns {boolean} 是否成功
227
+ */
228
+ export function pasteInXcode() {
229
+ if (!isXcodeRunning()) {
230
+ return false;
231
+ }
232
+ return _run([
233
+ '-e',
234
+ 'tell application "Xcode" to activate',
235
+ '-e',
236
+ 'delay 0.2',
237
+ '-e',
238
+ 'tell application "System Events"',
239
+ '-e',
240
+ ' keystroke "v" using command down',
241
+ '-e',
242
+ 'end tell',
243
+ ]);
244
+ }
245
+
246
+ /**
247
+ * 选中当前行内容后粘贴替换
248
+ *
249
+ * 假设光标已在目标行(通常由 jumpToLineInXcode 定位后调用)。
250
+ * 按键序列:Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+V 粘贴替换
251
+ *
252
+ * @returns {boolean} 是否成功
253
+ */
254
+ export function selectAndPasteInXcode() {
255
+ if (!isXcodeRunning()) {
256
+ return false;
257
+ }
258
+ return _run([
259
+ '-e',
260
+ 'tell application "Xcode" to activate',
261
+ '-e',
262
+ 'delay 0.5',
263
+ '-e',
264
+ 'tell application "System Events"',
265
+ '-e',
266
+ ' key code 123 using command down', // Cmd+← 行首
267
+ '-e',
268
+ ' delay 0.1',
269
+ '-e',
270
+ ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
271
+ '-e',
272
+ ' delay 0.2',
273
+ '-e',
274
+ ' keystroke "v" using command down', // Cmd+V 粘贴替换
275
+ '-e',
276
+ 'end tell',
277
+ ]);
278
+ }
279
+
280
+ /**
281
+ * 跳转到指定行行首并粘贴剪贴板内容
282
+ *
283
+ * 用于在 import 区域插入新行。
284
+ * 按键序列:Cmd+L → 输入行号 → Return → Cmd+← 行首 → Cmd+V 粘贴
285
+ *
286
+ * @param {number} lineNumber 1-based 行号
287
+ * @returns {boolean} 是否成功
288
+ */
289
+ export function insertAtLineStartInXcode(lineNumber) {
290
+ if (!isXcodeRunning()) {
291
+ return false;
292
+ }
293
+ const n = _safeLine(lineNumber);
294
+ return _run([
295
+ '-e',
296
+ 'tell application "Xcode" to activate',
297
+ '-e',
298
+ 'delay 0.3',
299
+ '-e',
300
+ 'tell application "System Events"',
301
+ '-e',
302
+ ' keystroke "l" using command down', // Cmd+L: Go to Line
303
+ '-e',
304
+ ' delay 0.3',
305
+ '-e',
306
+ ` keystroke "${String(n)}"`, // 输入行号
307
+ '-e',
308
+ ' delay 0.3',
309
+ '-e',
310
+ ' key code 36', // Return
311
+ '-e',
312
+ ' delay 0.3',
313
+ '-e',
314
+ ' key code 123 using command down', // Cmd+← 行首
315
+ '-e',
316
+ ' delay 0.2',
317
+ '-e',
318
+ ' keystroke "v" using command down', // Cmd+V 粘贴
319
+ '-e',
320
+ ' delay 0.3',
321
+ '-e',
322
+ 'end tell',
323
+ ]);
324
+ }
325
+
326
+ // ─────────────────────────────────────────────
327
+ // 文档操作
328
+ // ─────────────────────────────────────────────
329
+
330
+ /**
331
+ * 保存 Xcode 当前活动文档(Cmd+S)
332
+ * @returns {boolean} 是否成功
333
+ */
334
+ export function saveActiveDocumentInXcode() {
335
+ if (!isXcodeRunning()) {
336
+ return false;
337
+ }
338
+ return _run([
339
+ '-e',
340
+ 'tell application "Xcode" to activate',
341
+ '-e',
342
+ 'delay 0.1',
343
+ '-e',
344
+ 'tell application "System Events"',
345
+ '-e',
346
+ ' keystroke "s" using command down',
347
+ '-e',
348
+ 'end tell',
349
+ ]);
350
+ }