autosnippet 3.2.7 → 3.2.9

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 (147) hide show
  1. package/bin/cli.js +13 -5
  2. package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
  3. package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
  4. package/dashboard/dist/index.html +2 -2
  5. package/lib/cli/AiScanService.js +26 -29
  6. package/lib/cli/SetupService.js +1 -1
  7. package/lib/core/AstAnalyzer.js +27 -5
  8. package/lib/core/analysis/CallEdgeResolver.js +402 -0
  9. package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
  10. package/lib/core/analysis/CallSiteExtractor.js +629 -0
  11. package/lib/core/analysis/DataFlowInferrer.js +57 -0
  12. package/lib/core/analysis/ImportPathResolver.js +189 -0
  13. package/lib/core/analysis/ImportRecord.js +105 -0
  14. package/lib/core/analysis/SymbolTableBuilder.js +211 -0
  15. package/lib/core/ast/ProjectGraph.js +8 -0
  16. package/lib/core/ast/lang-dart.js +352 -5
  17. package/lib/core/ast/lang-go.js +212 -10
  18. package/lib/core/ast/lang-java.js +205 -1
  19. package/lib/core/ast/lang-kotlin.js +330 -1
  20. package/lib/core/ast/lang-python.js +31 -2
  21. package/lib/core/ast/lang-rust.js +284 -3
  22. package/lib/core/ast/lang-swift.js +180 -1
  23. package/lib/core/ast/lang-typescript.js +290 -1
  24. package/lib/core/discovery/index.js +2 -2
  25. package/lib/external/ai/AiProvider.js +66 -172
  26. package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
  27. package/lib/external/mcp/McpServer.js +1 -0
  28. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
  29. package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
  30. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +22 -1
  31. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
  32. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
  33. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
  34. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
  35. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +311 -162
  36. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +102 -7
  37. package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
  38. package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
  39. package/lib/external/mcp/handlers/bootstrap-internal.js +19 -8
  40. package/lib/external/mcp/handlers/consolidated.js +9 -0
  41. package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
  42. package/lib/external/mcp/handlers/guard.js +3 -3
  43. package/lib/external/mcp/handlers/structure.js +62 -0
  44. package/lib/external/mcp/handlers/wiki-external.js +66 -3
  45. package/lib/external/mcp/tools.js +36 -1
  46. package/lib/http/HttpServer.js +1 -1
  47. package/lib/http/middleware/requestLogger.js +1 -0
  48. package/lib/http/routes/ai.js +240 -35
  49. package/lib/http/routes/candidates.js +2 -3
  50. package/lib/http/routes/extract.js +13 -11
  51. package/lib/http/routes/modules.js +2 -2
  52. package/lib/http/routes/recipes.js +9 -5
  53. package/lib/http/routes/remote.js +149 -270
  54. package/lib/http/routes/violations.js +0 -54
  55. package/lib/http/utils/sse-sessions.js +1 -1
  56. package/lib/infrastructure/logging/Logger.js +5 -4
  57. package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
  58. package/lib/injection/ServiceContainer.js +70 -28
  59. package/lib/platform/ScreenCaptureService.js +177 -0
  60. package/lib/platform/ios/index.js +2 -2
  61. package/lib/platform/ios/routes/spm.js +2 -2
  62. package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
  63. package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
  64. package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
  65. package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
  66. package/lib/service/agent/AgentEventBus.js +207 -0
  67. package/lib/service/agent/AgentFactory.js +490 -0
  68. package/lib/service/agent/AgentMessage.js +240 -0
  69. package/lib/service/agent/AgentRouter.js +228 -0
  70. package/lib/service/agent/AgentRuntime.js +1016 -0
  71. package/lib/service/agent/AgentState.js +217 -0
  72. package/lib/service/agent/IntentClassifier.js +331 -0
  73. package/lib/service/agent/LarkTransport.js +389 -0
  74. package/lib/service/agent/capabilities.js +408 -0
  75. package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
  76. package/lib/service/{chat → agent/context}/ExplorationTracker.js +77 -22
  77. package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +14 -2
  78. package/lib/service/agent/core/LoopContext.js +170 -0
  79. package/lib/service/agent/core/MessageAdapter.js +223 -0
  80. package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
  81. package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
  82. package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
  83. package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
  84. package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
  85. package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +91 -123
  86. package/lib/service/agent/domain/insight-producer.js +267 -0
  87. package/lib/service/agent/domain/scan-prompts.js +105 -0
  88. package/lib/service/agent/forced-summary.js +266 -0
  89. package/lib/service/agent/index.js +91 -0
  90. package/lib/service/{chat → agent}/memory/ActiveContext.js +3 -1
  91. package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
  92. package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
  93. package/lib/service/{chat → agent}/memory/SessionStore.js +5 -4
  94. package/lib/service/{chat → agent}/memory/index.js +1 -1
  95. package/lib/service/agent/policies.js +442 -0
  96. package/lib/service/agent/presets.js +303 -0
  97. package/lib/service/agent/strategies.js +717 -0
  98. package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
  99. package/lib/service/agent/tools/ai-analysis.js +75 -0
  100. package/lib/service/{chat → agent}/tools/ast-graph.js +229 -32
  101. package/lib/service/{chat → agent}/tools/composite.js +2 -1
  102. package/lib/service/{chat → agent}/tools/guard.js +1 -121
  103. package/lib/service/{chat → agent}/tools/index.js +33 -22
  104. package/lib/service/{chat → agent}/tools/infrastructure.js +6 -1
  105. package/lib/service/agent/tools/knowledge-graph.js +112 -0
  106. package/lib/service/agent/tools/scan-recipe.js +189 -0
  107. package/lib/service/agent/tools/system-interaction.js +476 -0
  108. package/lib/service/automation/DirectiveDetector.js +0 -1
  109. package/lib/service/automation/FileWatcher.js +0 -8
  110. package/lib/service/automation/handlers/CreateHandler.js +7 -3
  111. package/lib/service/automation/handlers/DraftHandler.js +7 -6
  112. package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
  113. package/lib/service/knowledge/CodeEntityGraph.js +327 -2
  114. package/lib/service/knowledge/KnowledgeService.js +5 -1
  115. package/lib/service/module/ModuleService.js +49 -73
  116. package/lib/service/skills/SignalCollector.js +26 -19
  117. package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
  118. package/lib/service/wiki/WikiGenerator.js +1 -1
  119. package/lib/shared/FieldSpec.js +1 -1
  120. package/lib/shared/PathGuard.js +1 -1
  121. package/lib/shared/StyleGuide.js +1 -1
  122. package/package.json +4 -1
  123. package/resources/native-ui/screenshot.swift +228 -0
  124. package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
  125. package/dashboard/dist/assets/index-DfHY_3ln.js +0 -128
  126. package/lib/core/discovery/SpmDiscoverer.js +0 -5
  127. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -749
  128. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
  129. package/lib/http/routes/spm.js +0 -5
  130. package/lib/infrastructure/external/XcodeAutomation.js +0 -15
  131. package/lib/service/chat/ChatAgent.js +0 -1602
  132. package/lib/service/chat/Memory.js +0 -161
  133. package/lib/service/chat/ProducerAgent.js +0 -431
  134. package/lib/service/chat/ReasoningTrace.js +0 -523
  135. package/lib/service/chat/TaskPipeline.js +0 -357
  136. package/lib/service/chat/WorkingMemory.js +0 -357
  137. package/lib/service/chat/memory/PersistentMemory.js +0 -450
  138. package/lib/service/chat/tools/ai-analysis.js +0 -267
  139. package/lib/service/chat/tools/knowledge-graph.js +0 -234
  140. package/lib/service/chat/tools.js +0 -18
  141. package/lib/service/snippet/PlaceholderConverter.js +0 -5
  142. package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
  143. /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
  144. /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
  145. /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
  146. /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
  147. /package/lib/service/{chat → agent}/tools/query.js +0 -0
@@ -18,17 +18,17 @@ import { IndexingPipeline } from '../infrastructure/vector/IndexingPipeline.js';
18
18
  // ─── P0: Vector Storage ──────────────────────────────
19
19
  import { JsonVectorAdapter } from '../infrastructure/vector/JsonVectorAdapter.js';
20
20
  // ─── P2: SPM ──────────────────────────────────────────
21
- import { SpmService } from '../platform/ios/spm/SpmService.js';
21
+ import { SpmHelper } from '../platform/ios/spm/SpmHelper.js';
22
22
  import { KnowledgeRepositoryImpl } from '../repository/knowledge/KnowledgeRepository.impl.js';
23
23
  // ─── P1: Token Usage Tracking ─────────────────────────
24
24
  import { TokenUsageStore } from '../repository/token/TokenUsageStore.js';
25
25
  // ─── P2: Automation ───────────────────────────────────
26
26
  import { AutomationOrchestrator } from '../service/automation/AutomationOrchestrator.js';
27
27
  import { BootstrapTaskManager } from '../service/bootstrap/BootstrapTaskManager.js';
28
- import { ChatAgent } from '../service/chat/ChatAgent.js';
29
- // ─── P2: ChatAgent (统一 AI Agent) ────────────────────
30
- import { ToolRegistry } from '../service/chat/ToolRegistry.js';
31
- import { ALL_TOOLS } from '../service/chat/tools.js';
28
+ // ─── P2: Agent 统一架构 (v7.2) ────────────────────────
29
+ import { AgentFactory } from '../service/agent/AgentFactory.js';
30
+ import { ToolRegistry } from '../service/agent/tools/ToolRegistry.js';
31
+ import { ALL_TOOLS } from '../service/agent/tools/index.js';
32
32
  // ─── P2: Content Extraction ─────────────────────────────────────
33
33
  import { RecipeExtractor } from '../service/context/RecipeExtractor.js';
34
34
  // ─── P3: Cursor Delivery Pipeline ──────────────────────
@@ -59,7 +59,7 @@ import { RetrievalFunnel } from '../service/search/RetrievalFunnel.js';
59
59
  import { SearchEngine } from '../service/search/SearchEngine.js';
60
60
  import { SkillHooks } from '../service/skills/SkillHooks.js';
61
61
  import { VSCodeCodec } from '../service/snippet/codecs/VSCodeCodec.js';
62
- import { XcodeCodec } from '../service/snippet/codecs/XcodeCodec.js';
62
+ import { XcodeCodec } from '../platform/ios/snippet/XcodeCodec.js';
63
63
  import { SnippetFactory } from '../service/snippet/SnippetFactory.js';
64
64
  import { SnippetInstaller } from '../service/snippet/SnippetInstaller.js';
65
65
  import { DimensionCopy } from '../shared/DimensionCopyRegistry.js';
@@ -121,7 +121,7 @@ export class ServiceContainer {
121
121
  this.singletons.skillHooks = bootstrapComponents.skillHooks;
122
122
  }
123
123
 
124
- // AiFactory 模块引用(用于 SpmService AI 扫描)
124
+ // AiFactory 模块引用(用于 SpmHelper AI 扫描)
125
125
  try {
126
126
  this.singletons._aiFactory = await import('../external/ai/AiFactory.js');
127
127
  } catch {
@@ -177,12 +177,15 @@ export class ServiceContainer {
177
177
  // ═══ AI Provider 热重载标记 ═══
178
178
  // 记录哪些 singleton key 持有 aiProvider 引用,在 reloadAiProvider() 时需要清除
179
179
  this._aiDependentSingletons = [
180
- 'chatAgent',
180
+ 'agentFactory',
181
181
  'searchEngine',
182
182
  'retrievalFunnel',
183
183
  'indexingPipeline',
184
184
  ];
185
185
 
186
+ // ═══ 容器级语言偏好 ═══
187
+ this.singletons._lang = null;
188
+
186
189
  // 注册仓储
187
190
  this._registerRepositories();
188
191
 
@@ -211,7 +214,7 @@ export class ServiceContainer {
211
214
  * 流程:
212
215
  * 1. 替换 singletons.aiProvider
213
216
  * 2. 重新创建 _embedProvider(如果主 provider 不支持 embedding)
214
- * 3. 清除已缓存的依赖 AI 的 singleton(ChatAgent / SearchEngine 等),
217
+ * 3. 清除已缓存的依赖 AI 的 singleton(SearchEngine 等),
215
218
  * 下次 get() 时会用新 provider 重新创建
216
219
  *
217
220
  * @param {import('../external/ai/AiProvider.js').AiProvider} newProvider
@@ -658,18 +661,11 @@ export class ServiceContainer {
658
661
  return this.singletons.feedbackCollector;
659
662
  });
660
663
 
661
- // SpmService (with AI + tool injection)
664
+ // SpmHelper (SPM 包结构解析与依赖操作辅助)
662
665
  this.register('spmService', () => {
663
666
  if (!this.singletons.spmService) {
664
667
  const projectRoot = this.singletons._projectRoot || process.cwd();
665
- this.singletons.spmService = new SpmService(projectRoot, {
666
- aiFactory: this.singletons._aiFactory || null,
667
- chatAgent: this.singletons.chatAgent || null,
668
- qualityScorer: this.get('qualityScorer'),
669
- recipeExtractor: this.singletons._recipeExtractor || null,
670
- guardCheckEngine: this.get('guardCheckEngine'),
671
- violationsStore: this.get('violationsStore'),
672
- });
668
+ this.singletons.spmService = new SpmHelper(projectRoot);
673
669
  }
674
670
  return this.singletons.spmService;
675
671
  });
@@ -687,8 +683,8 @@ export class ServiceContainer {
687
683
  if (!this.singletons.moduleService) {
688
684
  const projectRoot = this.singletons._projectRoot || process.cwd();
689
685
  this.singletons.moduleService = new ModuleService(projectRoot, {
690
- aiFactory: this.singletons._aiFactory || null,
691
- chatAgent: this.singletons.chatAgent || null,
686
+ agentFactory: this.get('agentFactory'),
687
+ container: this,
692
688
  qualityScorer: this.get('qualityScorer'),
693
689
  recipeExtractor: this.singletons._recipeExtractor || null,
694
690
  guardCheckEngine: this.get('guardCheckEngine'),
@@ -714,7 +710,7 @@ export class ServiceContainer {
714
710
  return getEnhancementRegistry();
715
711
  });
716
712
 
717
- // ToolRegistry (ChatAgent 的工具注册表)
713
+ // ToolRegistry (Agent 工具注册表)
718
714
  this.register('toolRegistry', () => {
719
715
  if (!this.singletons.toolRegistry) {
720
716
  const registry = new ToolRegistry();
@@ -730,21 +726,23 @@ export class ServiceContainer {
730
726
  return this.singletons.projectGraph || null;
731
727
  });
732
728
 
733
- // AI Provider(供 MCP handler / ChatAgent / 任意服务层使用)
729
+ // AI Provider(供 MCP handler / AgentRuntime / 任意服务层使用)
734
730
  this.register('aiProvider', () => this.singletons.aiProvider || null);
735
731
 
736
- // ChatAgent (统一 AI Agent — Agent + ToolRegistry 覆盖全部 AI 能力)
737
- this.register('chatAgent', () => {
738
- if (!this.singletons.chatAgent) {
732
+ // AgentFactory (v7.2 统一 Agent 创建工厂 Preset + DI AgentRuntime)
733
+ this.register('agentFactory', () => {
734
+ if (!this.singletons.agentFactory) {
739
735
  const toolRegistry = this.get('toolRegistry');
740
736
  const aiProvider = this.singletons.aiProvider || null;
741
- this.singletons.chatAgent = new ChatAgent({
737
+ const projectRoot = this.singletons._projectRoot || process.cwd();
738
+ this.singletons.agentFactory = new AgentFactory({
739
+ container: this,
742
740
  toolRegistry,
743
741
  aiProvider,
744
- container: this,
742
+ projectRoot,
745
743
  });
746
744
  }
747
- return this.singletons.chatAgent;
745
+ return this.singletons.agentFactory;
748
746
  });
749
747
 
750
748
  // SkillHooks (Skill 生命周期钩子 — 加载 skills/*/hooks.js)
@@ -766,9 +764,11 @@ export class ServiceContainer {
766
764
  if (!this.singletons.cursorDeliveryPipeline) {
767
765
  const knowledgeService = this.get('knowledgeService');
768
766
  const projectRoot = this.singletons._projectRoot || process.cwd();
767
+ const database = this.get('database');
769
768
  this.singletons.cursorDeliveryPipeline = new CursorDeliveryPipeline({
770
769
  knowledgeService,
771
770
  projectRoot,
771
+ database,
772
772
  logger: this.logger,
773
773
  });
774
774
  }
@@ -819,6 +819,48 @@ export class ServiceContainer {
819
819
  });
820
820
  }
821
821
 
822
+ // ─── 容器级语言偏好 ─────
823
+
824
+ /**
825
+ * 获取当前默认 UI 语言偏好
826
+ * @returns {'zh'|'en'|null}
827
+ */
828
+ getLang() {
829
+ return this.singletons._lang || null;
830
+ }
831
+
832
+ /**
833
+ * 设置默认 UI 语言偏好(影响 Agent 回复语言)
834
+ * @param {'zh'|'en'|null} lang
835
+ */
836
+ setLang(lang) {
837
+ this.singletons._lang = lang || null;
838
+ }
839
+
840
+ // ─── 工具执行上下文构建器 ─────────────────────
841
+
842
+ /**
843
+ * 构建 ToolRegistry.execute() 所需的上下文对象。
844
+ *
845
+ * 工具执行上下文构建
846
+ * 迁移后: 所有直接调用 ToolRegistry 的站点都使用此方法
847
+ *
848
+ * @param {Object} [extras] — 合并到上下文的额外字段
849
+ * @returns {Object} 工具执行上下文
850
+ */
851
+ buildToolContext(extras = {}) {
852
+ return {
853
+ container: this,
854
+ aiProvider: this.singletons.aiProvider || null,
855
+ projectRoot: this.singletons._projectRoot || process.cwd(),
856
+ logger: this.logger,
857
+ source: extras.source || 'system',
858
+ lang: extras.lang || this.singletons._lang || null,
859
+ fileCache: this.singletons._fileCache || null,
860
+ ...extras,
861
+ };
862
+ }
863
+
822
864
  /**
823
865
  * 注册服务或工厂函数
824
866
  */
@@ -0,0 +1,177 @@
1
+ /**
2
+ * ScreenCaptureService — macOS 原生截图服务
3
+ *
4
+ * 使用 ScreenCaptureKit (via Swift CLI) 截取窗口/屏幕画面。
5
+ * 息屏可用,无需 OBS。
6
+ *
7
+ * 依赖:resources/native-ui/screenshot.swift 编译产物
8
+ */
9
+
10
+ import { existsSync, mkdirSync, statSync as fstatSync } from 'node:fs';
11
+ import { join, dirname } from 'node:path';
12
+ import { execSync, execFileSync } from 'node:child_process';
13
+ import { fileURLToPath } from 'node:url';
14
+ import Logger from '../infrastructure/logging/Logger.js';
15
+
16
+ const logger = Logger.getInstance();
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ /** Swift 源文件路径 */
22
+ const SWIFT_SRC = join(__dirname, '../../resources/native-ui/screenshot.swift');
23
+ /** 编译产物路径 */
24
+ const BINARY_PATH = join(__dirname, '../../resources/native-ui/screenshot');
25
+
26
+ let _binaryReady = false;
27
+
28
+ /**
29
+ * 确保 Swift 截图工具已编译
30
+ * @returns {{ready: boolean, error?: string}}
31
+ */
32
+ function ensureBinary() {
33
+ if (_binaryReady && existsSync(BINARY_PATH)) return { ready: true };
34
+
35
+ if (!existsSync(SWIFT_SRC)) {
36
+ return { ready: false, error: `Swift source not found: ${SWIFT_SRC}` };
37
+ }
38
+
39
+ // 检查是否需要重新编译(源文件比二进制新)
40
+ const needsBuild = !existsSync(BINARY_PATH) ||
41
+ (() => {
42
+ try {
43
+ const srcTime = fstatSync(SWIFT_SRC).mtimeMs;
44
+ const binTime = fstatSync(BINARY_PATH).mtimeMs;
45
+ return srcTime > binTime;
46
+ } catch {
47
+ return true;
48
+ }
49
+ })();
50
+
51
+ if (needsBuild) {
52
+ logger.info('[Screenshot] Compiling screenshot tool...');
53
+ try {
54
+ execSync(
55
+ `swiftc -O -framework ScreenCaptureKit -framework AppKit "${SWIFT_SRC}" -o "${BINARY_PATH}"`,
56
+ { timeout: 60000, stdio: ['pipe', 'pipe', 'pipe'] },
57
+ );
58
+ logger.info('[Screenshot] ✅ Compiled successfully');
59
+ } catch (err) {
60
+ const stderr = err.stderr?.toString() || err.message;
61
+ logger.error(`[Screenshot] Compilation failed: ${stderr}`);
62
+ return { ready: false, error: `Swift compilation failed: ${stderr.slice(0, 200)}` };
63
+ }
64
+ }
65
+
66
+ _binaryReady = true;
67
+ return { ready: true };
68
+ }
69
+
70
+ /**
71
+ * 执行截图工具
72
+ * @param {string[]} args — CLI 参数
73
+ * @returns {{success: boolean, data?: object, error?: string}}
74
+ */
75
+ function exec(args = []) {
76
+ const check = ensureBinary();
77
+ if (!check.ready) return { success: false, error: check.error };
78
+
79
+ try {
80
+ const stdout = execFileSync(BINARY_PATH, args, {
81
+ timeout: 15000,
82
+ encoding: 'utf-8',
83
+ maxBuffer: 1024 * 1024,
84
+ stdio: ['pipe', 'pipe', 'pipe'],
85
+ });
86
+
87
+ const result = JSON.parse(stdout.trim());
88
+ return { success: true, data: result };
89
+ } catch (err) {
90
+ // 尝试从 stderr 中解析 JSON 错误
91
+ const stderr = err.stderr?.toString() || '';
92
+ try {
93
+ const errObj = JSON.parse(stderr.trim());
94
+ return { success: false, error: errObj.error || stderr };
95
+ } catch {
96
+ return { success: false, error: stderr || err.message };
97
+ }
98
+ }
99
+ }
100
+
101
+ // ═══════════════════════════════════════════════════════
102
+ // 公开 API
103
+ // ═══════════════════════════════════════════════════════
104
+
105
+ /**
106
+ * 截取屏幕或窗口画面(核心功能 — 息屏可用)
107
+ *
108
+ * @param {object} opts
109
+ * @param {string} [opts.windowTitle] — 窗口标题关键词(模糊匹配),默认截整屏
110
+ * @param {string} [opts.format] — 'jpeg' | 'png'
111
+ * @param {number} [opts.scale] — 缩放因子 (0.25-2.0)
112
+ * @param {string} [opts.outputPath] — 输出路径,默认临时目录
113
+ * @returns {Promise<{success: boolean, path?: string, width?: number, height?: number, error?: string}>}
114
+ */
115
+ export async function screenshot(opts = {}) {
116
+ const args = [];
117
+
118
+ if (opts.windowTitle) {
119
+ args.push('--window', opts.windowTitle);
120
+ }
121
+ if (opts.format) {
122
+ args.push('--format', opts.format);
123
+ }
124
+ if (opts.scale) {
125
+ args.push('--scale', String(opts.scale));
126
+ }
127
+ if (opts.outputPath) {
128
+ args.push('--output', opts.outputPath);
129
+ }
130
+
131
+ const result = exec(args);
132
+
133
+ if (result.success && result.data) {
134
+ logger.info(`[Screenshot] ✅ Captured: ${result.data.path} (${result.data.width}x${result.data.height})`);
135
+ return {
136
+ success: true,
137
+ path: result.data.path,
138
+ width: result.data.width,
139
+ height: result.data.height,
140
+ format: result.data.format,
141
+ bytes: result.data.bytes,
142
+ };
143
+ }
144
+
145
+ logger.warn(`[Screenshot] ❌ Failed: ${result.error}`);
146
+ return { success: false, error: result.error };
147
+ }
148
+
149
+ /**
150
+ * 列出所有可截取的窗口
151
+ * @returns {Promise<{success: boolean, windows?: Array, error?: string}>}
152
+ */
153
+ export async function listWindows() {
154
+ const result = exec(['--list-windows']);
155
+
156
+ if (result.success && result.data) {
157
+ return { success: true, windows: result.data };
158
+ }
159
+
160
+ return { success: false, error: result.error };
161
+ }
162
+
163
+ /**
164
+ * 检查截图服务是否可用
165
+ * @returns {{available: boolean, error?: string}}
166
+ */
167
+ export function isScreenCaptureAvailable() {
168
+ const check = ensureBinary();
169
+ if (!check.ready) return { available: false, error: check.error };
170
+
171
+ // 尝试列出窗口来验证权限
172
+ const result = exec(['--list-windows']);
173
+ return {
174
+ available: result.success,
175
+ error: result.success ? undefined : result.error,
176
+ };
177
+ }
@@ -14,7 +14,7 @@
14
14
  * PlaceholderConverter.js — Xcode <#…#> ↔ VSCode ${N:…} 占位符转换
15
15
  *
16
16
  * spm/
17
- * SpmService.js Swift Package Manager 服务(Target / 依赖 / 策略)
17
+ * SpmHelper.js SPM 包结构解析与依赖操作辅助工具
18
18
  * SpmDiscoverer.js — SPM 项目自动发现(ProjectDiscoverer 接口)
19
19
  * PackageSwiftParser.js — Package.swift 解析器
20
20
  * DependencyGraph.js — SPM Target 依赖图
@@ -36,7 +36,7 @@ export { PackageSwiftParser } from './spm/PackageSwiftParser.js';
36
36
  export { PolicyEngine } from './spm/PolicyEngine.js';
37
37
  export { SpmDiscoverer } from './spm/SpmDiscoverer.js';
38
38
  // ── Swift Package Manager ──
39
- export { SpmService } from './spm/SpmService.js';
39
+ export { SpmHelper } from './spm/SpmHelper.js';
40
40
  export { saveEventFilter } from './xcode/SaveEventFilter.js';
41
41
  // ── Xcode IDE 自动化 ──
42
42
  export {
@@ -379,12 +379,12 @@ router.post(
379
379
  const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
380
380
 
381
381
  const container = getServiceContainer();
382
- const chatAgent = container.get('chatAgent');
382
+ const agentFactory = container.get('agentFactory');
383
383
 
384
384
  logger.info('Bootstrap cold start initiated (v5: async fill mode)');
385
385
 
386
386
  // ── 同步阶段: 快速执行 Phase 1-4 → 返回骨架 ──
387
- const bootstrapResult = await chatAgent.executeTool('bootstrap_knowledge', {
387
+ const bootstrapResult = await agentFactory.bootstrapKnowledge({
388
388
  maxFiles: maxFiles || 500,
389
389
  skipGuard: skipGuard || false,
390
390
  contentMaxLines: contentMaxLines || 120,
@@ -218,10 +218,11 @@ export class PackageSwiftParser {
218
218
 
219
219
  #extractDependencies(content) {
220
220
  const deps = [];
221
- const re = /\.package\s*\(\s*url\s*:\s*"([^"]+)"[^)]*\)/g;
221
+
222
+ // 1. URL 依赖: .package(url: "...", ...)
223
+ const urlRe = /\.package\s*\(\s*url\s*:\s*"([^"]+)"[^)]*\)/g;
222
224
  let m;
223
- while ((m = re.exec(content)) !== null) {
224
- // 提取版本约束
225
+ while ((m = urlRe.exec(content)) !== null) {
225
226
  const block = m[0];
226
227
  const fromMatch = block.match(/from\s*:\s*"([^"]+)"/);
227
228
  const exactMatch = block.match(/exact\s*:\s*"([^"]+)"/);
@@ -231,6 +232,16 @@ export class PackageSwiftParser {
231
232
  type: 'package',
232
233
  });
233
234
  }
235
+
236
+ // 2. Local path 依赖: .package(path: "...")
237
+ const pathRe = /\.package\s*\(\s*path\s*:\s*"([^"]+)"\s*\)/g;
238
+ while ((m = pathRe.exec(content)) !== null) {
239
+ deps.push({
240
+ path: m[1],
241
+ type: 'local',
242
+ });
243
+ }
244
+
234
245
  return deps;
235
246
  }
236
247
 
@@ -1,17 +1,18 @@
1
1
  /**
2
2
  * @module SpmDiscoverer
3
- * @description 包装现有 SpmService,适配 ProjectDiscoverer 接口
3
+ * @description 包装现有 SpmHelper,适配 ProjectDiscoverer 接口
4
4
  *
5
5
  * 检测: 项目根或子目录存在 Package.swift
6
6
  */
7
7
 
8
8
  import { existsSync, readdirSync } from 'node:fs';
9
- import { basename, join, relative } from 'node:path';
9
+ import { basename, dirname, join, relative } from 'node:path';
10
10
  import { ProjectDiscoverer } from '../../../core/discovery/ProjectDiscoverer.js';
11
11
  import { LanguageService } from '../../../shared/LanguageService.js';
12
+ import { PackageSwiftParser } from './PackageSwiftParser.js';
12
13
 
13
14
  export class SpmDiscoverer extends ProjectDiscoverer {
14
- /** @type {import('./SpmService.js').SpmService|null} */
15
+ /** @type {import('./SpmHelper.js').SpmHelper|null} */
15
16
  #spm = null;
16
17
  #projectRoot = null;
17
18
 
@@ -52,9 +53,9 @@ export class SpmDiscoverer extends ProjectDiscoverer {
52
53
 
53
54
  async load(projectRoot) {
54
55
  this.#projectRoot = projectRoot;
55
- // 动态加载 SpmService(避免循环导入)
56
- const { SpmService } = await import('./SpmService.js');
57
- this.#spm = new SpmService(projectRoot);
56
+ // 动态加载 SpmHelper(避免循环导入)
57
+ const { SpmHelper } = await import('./SpmHelper.js');
58
+ this.#spm = new SpmHelper(projectRoot);
58
59
  await this.#spm.load();
59
60
  }
60
61
 
@@ -97,21 +98,126 @@ export class SpmDiscoverer extends ProjectDiscoverer {
97
98
  }
98
99
 
99
100
  async getDependencyGraph() {
100
- if (!this.#spm) {
101
+ if (!this.#projectRoot) {
102
+ return { nodes: [], edges: [] };
103
+ }
104
+
105
+ // 直接用 PackageSwiftParser 构建依赖图,不依赖 SpmHelper
106
+ const parser = new PackageSwiftParser(this.#projectRoot);
107
+ const allPkgPaths = parser.findAllPackageSwifts(this.#projectRoot);
108
+
109
+ if (allPkgPaths.length === 0) {
101
110
  return { nodes: [], edges: [] };
102
111
  }
103
- const raw = await this.#spm.getDependencyGraph();
104
- return {
105
- nodes: (raw.nodes || []).map((n) => (typeof n === 'string' ? n : n.id || n.name)),
106
- edges: (raw.edges || []).map((e) => ({
107
- from: e.from,
108
- to: e.to,
109
- type: 'depends_on',
110
- })),
111
- };
112
+
113
+ const nodes = [];
114
+ const edges = [];
115
+ const pkgNameSet = new Set();
116
+ // targetName → 所属 packageName 映射(用于跨包 target 依赖解析)
117
+ const targetToPkg = new Map();
118
+
119
+ // ── 第一遍:收集所有 package + target 节点 ──
120
+ const allParsed = [];
121
+ // 记录 umbrella 包名(无 targets 且无 products 的纯组织性入口包),不作为图节点
122
+ const umbrellaNames = new Set();
123
+ for (const pkgPath of allPkgPaths) {
124
+ try {
125
+ const parsed = parser.parse(pkgPath);
126
+ if (pkgNameSet.has(parsed.name)) continue;
127
+ pkgNameSet.add(parsed.name);
128
+ allParsed.push({ ...parsed, _dir: dirname(pkgPath) });
129
+
130
+ // 跳过 umbrella 包(无 targets + 无 products)——它只是组织子包的入口
131
+ const hasTargets = parsed.targets && parsed.targets.length > 0;
132
+ const hasProducts = parsed.products && parsed.products.length > 0;
133
+ if (!hasTargets && !hasProducts) {
134
+ umbrellaNames.add(parsed.name);
135
+ continue;
136
+ }
137
+
138
+ // package 节点
139
+ nodes.push({
140
+ id: parsed.name,
141
+ label: parsed.name,
142
+ type: 'package',
143
+ fullPath: dirname(pkgPath),
144
+ targetCount: parsed.targets.length,
145
+ });
146
+
147
+ // target 节点
148
+ for (const t of parsed.targets) {
149
+ nodes.push({
150
+ id: t.name,
151
+ label: t.name,
152
+ type: 'target',
153
+ parent: parsed.name,
154
+ targetType: t.type,
155
+ });
156
+ targetToPkg.set(t.name, parsed.name);
157
+ }
158
+
159
+ // product name → package(product 名可能和 target 名不同)
160
+ for (const prod of parsed.products || []) {
161
+ if (!targetToPkg.has(prod.name)) {
162
+ targetToPkg.set(prod.name, parsed.name);
163
+ }
164
+ }
165
+ } catch {
166
+ // 解析失败,跳过
167
+ }
168
+ }
169
+
170
+ // ── 第二遍:构建 edges ──
171
+ for (const parsed of allParsed) {
172
+ // 跳过 umbrella 包的边
173
+ if (umbrellaNames.has(parsed.name)) continue;
174
+
175
+ // 包级 local path 依赖
176
+ for (const dep of parsed.dependencies || []) {
177
+ if (dep.type === 'local' && dep.path) {
178
+ const depPkgSwift = join(parsed._dir, dep.path, 'Package.swift');
179
+ if (existsSync(depPkgSwift)) {
180
+ try {
181
+ const depParsed = parser.parse(depPkgSwift);
182
+ // 跳过指向 umbrella 包的边
183
+ if (!umbrellaNames.has(depParsed.name)) {
184
+ edges.push({ from: parsed.name, to: depParsed.name, type: 'depends_on' });
185
+ }
186
+ } catch {
187
+ const targetName = basename(dep.path);
188
+ if (!umbrellaNames.has(targetName)) {
189
+ edges.push({ from: parsed.name, to: targetName, type: 'depends_on' });
190
+ }
191
+ }
192
+ }
193
+ } else if (dep.url) {
194
+ const remoteName = basename(dep.url).replace(/\.git$/, '');
195
+ if (!pkgNameSet.has(remoteName)) {
196
+ pkgNameSet.add(remoteName);
197
+ nodes.push({ id: remoteName, label: remoteName, type: 'remote', indirect: true });
198
+ }
199
+ edges.push({ from: parsed.name, to: remoteName, type: 'depends_on' });
200
+ }
201
+ }
202
+
203
+ // target 级依赖
204
+ for (const t of parsed.targets || []) {
205
+ // target → parent package (contains)
206
+ edges.push({ from: parsed.name, to: t.name, type: 'contains' });
207
+
208
+ for (const depName of t.dependencies || []) {
209
+ // target → target 依赖(跳过指向 umbrella 包的)
210
+ if (!umbrellaNames.has(depName)) {
211
+ edges.push({ from: t.name, to: depName, type: 'depends_on' });
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ return { nodes, edges };
112
218
  }
113
219
 
114
- /** 获取底层 SpmService(向后兼容) */
220
+ /** 获取底层 SpmHelper(向后兼容) */
115
221
  getSpmService() {
116
222
  return this.#spm;
117
223
  }