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
@@ -88,8 +88,8 @@ export class ModuleService {
88
88
  #logger;
89
89
 
90
90
  // AI pipeline deps
91
- #aiFactory;
92
- #chatAgent;
91
+ #agentFactory;
92
+ #container;
93
93
  #qualityScorer;
94
94
  #recipeExtractor;
95
95
  #guardCheckEngine;
@@ -98,8 +98,8 @@ export class ModuleService {
98
98
  /**
99
99
  * @param {string} projectRoot
100
100
  * @param {object} [options]
101
- * @param {object} [options.aiFactory]
102
- * @param {object} [options.chatAgent]
101
+ * @param {object} [options.agentFactory]
102
+ * @param {object} [options.container]
103
103
  * @param {object} [options.qualityScorer]
104
104
  * @param {object} [options.recipeExtractor]
105
105
  * @param {object} [options.guardCheckEngine]
@@ -109,8 +109,8 @@ export class ModuleService {
109
109
  this.#projectRoot = projectRoot;
110
110
  this.#registry = getDiscovererRegistry();
111
111
  this.#logger = Logger.getInstance();
112
- this.#aiFactory = options.aiFactory || null;
113
- this.#chatAgent = options.chatAgent || null;
112
+ this.#agentFactory = options.agentFactory || null;
113
+ this.#container = options.container || null;
114
114
  this.#qualityScorer = options.qualityScorer || null;
115
115
  this.#recipeExtractor = options.recipeExtractor || null;
116
116
  this.#guardCheckEngine = options.guardCheckEngine || null;
@@ -314,7 +314,16 @@ export class ModuleService {
314
314
  const allNodes = [];
315
315
  const allEdges = [];
316
316
 
317
+ // 如果有专业 Discoverer(非 generic),则跳过 GenericDiscoverer 的依赖图
318
+ // 避免 generic fallback 生成的冗余根节点(如项目名本身)干扰图结构
319
+ const hasSpecializedDiscoverer = this.#activeDiscoverers.some(
320
+ ({ discoverer }) => discoverer.id !== 'generic'
321
+ );
322
+
317
323
  for (const { discoverer } of this.#activeDiscoverers) {
324
+ if (hasSpecializedDiscoverer && discoverer.id === 'generic') {
325
+ continue;
326
+ }
318
327
  try {
319
328
  const graph = await discoverer.getDependencyGraph();
320
329
  for (const n of graph.nodes || []) {
@@ -434,7 +443,7 @@ export class ModuleService {
434
443
  this.#logger.info(`[ModuleService] scanTarget: ${targetName}, ${files.length} files`);
435
444
 
436
445
  // 3. AI 提取
437
- if (!this.#chatAgent && !this.#aiFactory) {
446
+ if (!this.#agentFactory) {
438
447
  return {
439
448
  recipes: [],
440
449
  scannedFiles,
@@ -472,14 +481,16 @@ export class ModuleService {
472
481
 
473
482
  const result = { recipes, scannedFiles };
474
483
  if (recipes.length === 0) {
475
- // 检查是否因为 API Key 缺失导致 mock / 空结果
476
- const aiInfo = this.#aiFactory
477
- ? (await import('../../external/ai/AiFactory.js')).getAiConfigInfo()
478
- : null;
479
- if (aiInfo && !aiInfo.hasKey) {
480
- result.noAi = true;
481
- result.message = 'AI 未配置,已跳过智能提取。请在 .env 中设置 API Key 后重试。';
482
- } else {
484
+ // 检查是否因为 AI Provider mock 导致空结果
485
+ try {
486
+ const aiProvider = this.#container?.singletons?.aiProvider;
487
+ if (!aiProvider || aiProvider.name === 'mock') {
488
+ result.noAi = true;
489
+ result.message = 'AI 未配置,已跳过智能提取。请在 .env 中设置 API Key 后重试。';
490
+ } else {
491
+ result.message = `AI 提取完成,但未发现可复用的代码模式(${targetName}, ${files.length} 个文件)`;
492
+ }
493
+ } catch {
483
494
  result.message = `AI 提取完成,但未发现可复用的代码模式(${targetName}, ${files.length} 个文件)`;
484
495
  }
485
496
  }
@@ -576,7 +587,7 @@ export class ModuleService {
576
587
  const TOTAL_TIMEOUT = options.totalTimeout || 540000;
577
588
  let timedOut = false;
578
589
 
579
- if (this.#chatAgent || this.#aiFactory) {
590
+ if (this.#agentFactory) {
580
591
  const BATCH_SIZE = options.batchSize || 20;
581
592
 
582
593
  for (let i = 0; i < allFiles.length; i += BATCH_SIZE) {
@@ -750,75 +761,40 @@ export class ModuleService {
750
761
  }
751
762
 
752
763
  /**
753
- * AI 提取 Recipes — 统一入口
764
+ * AI 提取 Recipes — 委托 AgentFactory.scanKnowledge
765
+ *
766
+ * AgentFactory.scanKnowledge 内部创建 insight Agent,
767
+ * Agent(LLM) 直接分析代码 + 使用 AST 工具,输出 Recipe JSON。
754
768
  */
755
769
  async #aiExtractRecipes(targetName, files) {
756
- const AI_EXTRACT_TIMEOUT = 120_000;
770
+ if (!this.#agentFactory) {
771
+ return [];
772
+ }
757
773
 
758
774
  try {
759
- if (this.#chatAgent) {
760
- const extractPromise = this.#chatAgent.executeTool('extract_recipes', {
761
- targetName,
762
- files,
763
- });
764
- const timeoutPromise = new Promise((_, reject) =>
765
- setTimeout(
766
- () => reject(new Error(`AI extraction timeout (${AI_EXTRACT_TIMEOUT / 1000}s)`)),
767
- AI_EXTRACT_TIMEOUT
768
- )
769
- );
770
- const result = await Promise.race([extractPromise, timeoutPromise]);
771
- if (result?.error) {
772
- // API Key 缺失属于配置问题,降为 info 级别避免频繁报错
773
- if (/API_KEY_MISSING|API.Key.未配置|unregistered callers/i.test(result.error)) {
774
- this.#logger.info(`[ModuleService] AI 未启用(未配置 API Key),跳过 AI 提取。`);
775
- } else {
776
- this.#logger.warn(`[ModuleService] AI extraction error: ${result.error}`);
777
- }
778
- return [];
779
- }
780
- return result?.recipes || [];
781
- }
775
+ const result = await this.#agentFactory.scanKnowledge({
776
+ label: targetName, files, task: 'extract',
777
+ });
778
+ const recipes = result.recipes || [];
782
779
 
783
- if (this.#aiFactory) {
784
- const {
785
- getProviderWithFallback,
786
- isGeoOrProviderError,
787
- getAvailableFallbacks,
788
- createProvider,
789
- } = this.#aiFactory;
790
- let ai = await getProviderWithFallback();
791
- if (!ai) {
792
- return [];
793
- }
794
-
795
- try {
796
- return await ai.extractRecipes(targetName, files, {});
797
- } catch (primaryErr) {
798
- if (isGeoOrProviderError(primaryErr)) {
799
- const currentProvider = (process.env.ASD_AI_PROVIDER || 'google').toLowerCase();
800
- const fallbacks = getAvailableFallbacks(currentProvider);
801
- for (const fbName of fallbacks) {
802
- try {
803
- ai = createProvider({ provider: fbName });
804
- return await ai.extractRecipes(targetName, files, {});
805
- } catch (fbErr) {
806
- this.#logger.warn(`[ModuleService] fallback "${fbName}" failed: ${fbErr.message}`);
807
- }
808
- }
809
- }
810
- throw primaryErr;
811
- }
780
+ if (recipes.length === 0) {
781
+ this.#logger.info(
782
+ `[ModuleService] Agent 未产出 recipe (${targetName}, ${files.length} files)`,
783
+ );
784
+ } else {
785
+ this.#logger.info(
786
+ `[ModuleService] Agent 提取 ${recipes.length} recipes (${targetName})`,
787
+ );
812
788
  }
789
+ return recipes;
813
790
  } catch (err) {
814
- if (err.code === 'API_KEY_MISSING') {
791
+ if (err.code === 'API_KEY_MISSING' || /API_KEY_MISSING|API.Key.未配置|unregistered callers/i.test(err.message)) {
815
792
  this.#logger.info(`[ModuleService] AI 未启用(未配置 API Key),跳过 AI 提取。`);
816
793
  } else {
817
794
  this.#logger.warn(`[ModuleService] AI extraction failed: ${err.message}`);
818
795
  }
796
+ return [];
819
797
  }
820
-
821
- return [];
822
798
  }
823
799
 
824
800
  /**
@@ -2,7 +2,7 @@
2
2
  * SignalCollector — AI 驱动的后台行为分析与 Skill 推荐引擎
3
3
  *
4
4
  * 在 `asd ui` 运行时作为后台守护进程运行,周期性收集多维度信号并
5
- * 通过 ChatAgent(AI ReAct 循环)进行深度分析,生成 Skill 推荐。
5
+ * 通过 AgentFactory(统一 Agent 系统)进行深度分析,生成 Skill 推荐。
6
6
  *
7
7
  * 三种工作模式:
8
8
  * - off — 不收集,不推荐
@@ -10,8 +10,8 @@
10
10
  * - auto — 收集信号 → AI 分析 → 推送推荐 + AI 自动创建 Skill
11
11
  *
12
12
  * 核心架构:
13
- * 每次 tick → 收集 6 维度信号 → 构造分析 prompt → ChatAgent.execute()
14
- * → AI ReAct 循环(可调用 suggest_skills / create_skill 等工具)
13
+ * 每次 tick → 收集 6 维度信号 → 构造分析 prompt → AgentFactory.createChat()
14
+ * → Agent 执行(可调用 suggest_skills / create_skill 等工具)
15
15
  * → 解析 AI 响应(suggestions + nextIntervalMinutes + summary)
16
16
  * → 推送建议 → 动态调整下次执行间隔
17
17
  *
@@ -27,11 +27,11 @@
27
27
  * 1. 静默 — 不打断用户,后台运行,所有错误降级
28
28
  * 2. 增量 — 只分析上次快照以来的新数据
29
29
  * 3. 去重 — 同一推荐仅推送一次
30
- * 4. AI 驱动 — 所有分析决策由 ChatAgent 完成
30
+ * 4. AI 驱动 — 所有分析决策由 AgentRuntime 完成
31
31
  * 5. 自适应 — AI 根据信号密度动态调整执行频率
32
32
  *
33
33
  * 前提条件:
34
- * 需要可用的 AI Provider(chatAgent.hasAI === true)
34
+ * 需要可用的 AI Provider
35
35
  *
36
36
  * 生命周期:
37
37
  * new SignalCollector(opts) → instance.start() → ... → instance.stop()
@@ -52,7 +52,8 @@ const SNAPSHOT_FILE = 'signal-snapshot.json';
52
52
  export class SignalCollector {
53
53
  #projectRoot;
54
54
  #db;
55
- #chatAgent; // ChatAgent 实例 — AI 核心
55
+ #agentFactory; // AgentFactory 实例 — 统一 Agent 系统
56
+ #container; // ServiceContainer — 用于获取 aiProvider 等
56
57
  #mode; // 'off' | 'suggest' | 'auto'
57
58
  #intervalMs;
58
59
  #timer = null;
@@ -68,7 +69,8 @@ export class SignalCollector {
68
69
  * @param {object} opts
69
70
  * @param {string} opts.projectRoot — 用户项目根目录
70
71
  * @param {object} [opts.database] — better-sqlite3 实例
71
- * @param {object} [opts.chatAgent] ChatAgent 实例
72
+ * @param {object} [opts.agentFactory] AgentFactory 实例
73
+ * @param {object} [opts.container] — ServiceContainer 实例
72
74
  * @param {string} [opts.mode] — 'off' | 'suggest' | 'auto'
73
75
  * @param {number} [opts.intervalMs] — 初始收集间隔(毫秒),后续由 AI 动态调整
74
76
  * @param {function} [opts.onSuggestions] — 新建议回调 (suggestions[]) => void
@@ -76,14 +78,16 @@ export class SignalCollector {
76
78
  constructor({
77
79
  projectRoot,
78
80
  database = null,
79
- chatAgent = null,
81
+ agentFactory = null,
82
+ container = null,
80
83
  mode = 'auto',
81
84
  intervalMs = DEFAULT_INTERVAL_MS,
82
85
  onSuggestions = null,
83
86
  }) {
84
87
  this.#projectRoot = projectRoot;
85
88
  this.#db = database;
86
- this.#chatAgent = chatAgent;
89
+ this.#agentFactory = agentFactory;
90
+ this.#container = container;
87
91
  this.#mode = ['off', 'suggest', 'auto'].includes(mode) ? mode : 'auto';
88
92
  this.#intervalMs = Math.max(Math.min(intervalMs, MAX_INTERVAL_MS), MIN_INTERVAL_MS);
89
93
  this.#logger = Logger.getInstance();
@@ -115,7 +119,8 @@ export class SignalCollector {
115
119
  this.#logger.info('[SignalCollector] mode=off, skipping start');
116
120
  return;
117
121
  }
118
- if (!this.#chatAgent?.hasAI) {
122
+ const aiProvider = this.#container?.get('aiProvider');
123
+ if (!aiProvider || aiProvider.name === 'mock') {
119
124
  this.#logger.info('[SignalCollector] no AI provider available, skipping start');
120
125
  return;
121
126
  }
@@ -217,12 +222,14 @@ export class SignalCollector {
217
222
  // 2. 构造分析 prompt
218
223
  const prompt = this.#buildAnalysisPrompt(signals);
219
224
 
220
- // 3. 调用 ChatAgent AI 分析(source: 'system' 确保 Memory 隔离)
221
- this.#logger.debug('[SignalCollector] invoking ChatAgent for analysis...');
222
- const { reply, toolCalls } = await this.#chatAgent.execute(prompt, {
223
- history: [],
224
- source: 'system',
225
- });
225
+ // 3. 调用 Agent 系统进行 AI 分析
226
+ this.#logger.debug('[SignalCollector] invoking Agent for analysis...');
227
+ const agent = this.#agentFactory.createChat({ lang: 'en' });
228
+ const { AgentMessage } = await import('../agent/AgentMessage.js');
229
+ const message = AgentMessage.internal(prompt, { source: 'signal_collector' });
230
+ const result = await agent.execute(message);
231
+ const reply = result?.reply ?? result?.text ?? '';
232
+ const toolCalls = result?.toolCalls ?? [];
226
233
 
227
234
  // 4. 解析 AI 响应 — 使用 AiProvider.extractJSON 统一 structured output 解析
228
235
  const parsed = this.#parseStructuredReply(reply);
@@ -514,13 +521,13 @@ ${JSON.stringify(signals.codeChanges, null, 2)}
514
521
  // ═══════════════════════════════════════════════════════
515
522
 
516
523
  /**
517
- * 从 ChatAgent ReAct 回复中提取结构化 JSON
524
+ * 从 AgentRuntime ReAct 回复中提取结构化 JSON
518
525
  *
519
526
  * 优先级链:
520
527
  * 1. AiProvider.extractJSON (支持 markdown 清理、截断修复、trailing comma 等)
521
528
  * 2. 最后一行 JSON 回退 (兼容 prompt 要求的 "最后一行输出 JSON" 格式)
522
529
  *
523
- * @param {string} reply — ChatAgent.execute() 的回复文本
530
+ * @param {string} reply — AgentRuntime.execute() 的回复文本
524
531
  * @returns {{ suggestions: Array, nextIntervalMinutes: number|null, summary: string }}
525
532
  */
526
533
  #parseStructuredReply(reply) {
@@ -531,7 +538,7 @@ ${JSON.stringify(signals.codeChanges, null, 2)}
531
538
 
532
539
  try {
533
540
  // 策略 1: 通过 AiProvider.extractJSON 统一解析
534
- const aiProvider = this.#chatAgent?.aiProvider;
541
+ const aiProvider = this.#container?.get('aiProvider');
535
542
  if (aiProvider && typeof aiProvider.extractJSON === 'function') {
536
543
  const obj = aiProvider.extractJSON(reply, '{', '}');
537
544
  if (obj && Array.isArray(obj.suggestions)) {
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { join } from 'node:path';
12
- import { PlaceholderConverter } from '../PlaceholderConverter.js';
12
+ import { PlaceholderConverter } from '../../../platform/ios/snippet/PlaceholderConverter.js';
13
13
  import { SnippetCodec } from './SnippetCodec.js';
14
14
 
15
15
  /** AutoSnippet language → VSCode snippet scope */
@@ -82,7 +82,7 @@ export class WikiGenerator {
82
82
  * @param {object} deps
83
83
  * @param {string} deps.projectRoot
84
84
  * @param {import('../../service/module/ModuleService.js').ModuleService} [deps.moduleService]
85
- * @param {import('../../platform/ios/spm/SpmService.js').SpmService} [deps.spmService] — 向后兼容
85
+ * @param {import('../../platform/ios/spm/SpmHelper.js').SpmHelper} [deps.spmService] — 向后兼容
86
86
  * @param {import('../../service/knowledge/KnowledgeService.js').KnowledgeService} [deps.knowledgeService]
87
87
  * @param {import('../../core/ast/ProjectGraph.js').default} [deps.projectGraph]
88
88
  * @param {import('../../service/knowledge/CodeEntityGraph.js').CodeEntityGraph} [deps.codeEntityGraph]
@@ -12,7 +12,7 @@
12
12
  * 消费方:
13
13
  * - UnifiedValidator.js → 字段完整性检查
14
14
  * - dimension-text.js → SUBMISSION_SCHEMA / REQUIRED_FIELDS_DESCRIPTION
15
- * - ProducerAgent.js → STYLE_GUIDE 字段列表
15
+ * - bootstrap-producer.js → STYLE_GUIDE 字段列表
16
16
  * - MissionBriefingBuilder.js → submissionSpec 字段描述
17
17
  * - lifecycle.js → JSON Schema required 数组
18
18
  * - consolidated.js → 前置校验
@@ -141,7 +141,7 @@ class PathGuard {
141
141
 
142
142
  /**
143
143
  * Layer 1: 断言路径在允许的边界范围内
144
- * 用于修改已有文件的场景(如 XcodeIntegration 插入 header、SpmService 修改 Package.swift)
144
+ * 用于修改已有文件的场景(如 XcodeIntegration 插入 header、SpmHelper 修改 Package.swift)
145
145
  * @param {string} targetPath - 要写入的绝对路径
146
146
  * @throws {PathGuardError}
147
147
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * StyleGuide.js — 项目特写写作指南(唯一权威来源)
3
3
  *
4
- * ProducerAgent.js 提取,供内部 Agent (Producer) 和
4
+ * 供内部 Agent (bootstrap-producer.js) 和
5
5
  * 外部 Agent (MissionBriefing submissionSpec) 共享使用。
6
6
  *
7
7
  * @module shared/StyleGuide
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.2.7",
3
+ "version": "3.2.9",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "lib/bootstrap.js",
@@ -11,6 +11,7 @@
11
11
  "postinstall": "node scripts/postinstall-safe.js",
12
12
  "prepublishOnly": "npm run build:dashboard && node scripts/build-native-ui.js",
13
13
  "build:native-ui": "swiftc resources/native-ui/main.swift resources/native-ui/combined-window.swift -o resources/native-ui/native-ui -framework AppKit",
14
+ "build:screenshot": "swiftc -O -framework ScreenCaptureKit -framework AppKit resources/native-ui/screenshot.swift -o resources/native-ui/screenshot",
14
15
  "install:cursor-skill": "node scripts/install-cursor-skill.js",
15
16
  "install:cursor-skill:mcp": "node scripts/install-cursor-skill.js --mcp",
16
17
  "install:vscode-copilot": "node scripts/install-vscode-copilot.js",
@@ -116,6 +117,8 @@
116
117
  "resources/native-ui/main.swift",
117
118
  "resources/native-ui/combined-window.swift",
118
119
  "resources/native-ui/native-ui",
120
+ "resources/native-ui/screenshot.swift",
121
+ "resources/native-ui/screenshot",
119
122
  "resources/native-ui/README.md",
120
123
  "resources/openChrome.applescript",
121
124
  "resources/grammars"
@@ -0,0 +1,228 @@
1
+ import Foundation
2
+ import ScreenCaptureKit
3
+ import AppKit
4
+ import CoreGraphics
5
+
6
+ /**
7
+ * AutoSnippet Screenshot Tool
8
+ *
9
+ * 使用 macOS ScreenCaptureKit 原生 API 截取窗口/屏幕画面。
10
+ * 息屏时可用(不依赖显示器输出)。无需 OBS。
11
+ *
12
+ * Usage:
13
+ * screenshot # 截取整个主屏幕
14
+ * screenshot --window "Visual Studio Code" # 截取匹配标题的窗口
15
+ * screenshot --list-windows # 列出所有可截取窗口 (JSON)
16
+ * screenshot --output /tmp/shot.png # 指定输出路径
17
+ * screenshot --format jpeg # 指定格式 (png|jpeg)
18
+ * screenshot --scale 0.5 # 缩放因子
19
+ *
20
+ * Requirements: macOS 12.3+, Screen Recording permission
21
+ */
22
+
23
+ // MARK: - 输出格式
24
+ enum ImageFormat: String {
25
+ case png, jpeg
26
+ }
27
+
28
+ // MARK: - 参数解析
29
+ struct Args {
30
+ var windowTitle: String? = nil
31
+ var listWindows = false
32
+ var outputPath: String? = nil
33
+ var format: ImageFormat = .jpeg
34
+ var scale: CGFloat = 1.0
35
+
36
+ static func parse(_ args: [String]) -> Args {
37
+ var result = Args()
38
+ var i = 1 // skip executable name
39
+ while i < args.count {
40
+ switch args[i] {
41
+ case "--window", "-w":
42
+ i += 1
43
+ if i < args.count { result.windowTitle = args[i] }
44
+ case "--list-windows", "-l":
45
+ result.listWindows = true
46
+ case "--output", "-o":
47
+ i += 1
48
+ if i < args.count { result.outputPath = args[i] }
49
+ case "--format", "-f":
50
+ i += 1
51
+ if i < args.count {
52
+ result.format = ImageFormat(rawValue: args[i].lowercased()) ?? .jpeg
53
+ }
54
+ case "--scale", "-s":
55
+ i += 1
56
+ if i < args.count { result.scale = CGFloat(Double(args[i]) ?? 1.0) }
57
+ case "--help", "-h":
58
+ printUsage()
59
+ exit(0)
60
+ default:
61
+ break
62
+ }
63
+ i += 1
64
+ }
65
+ return result
66
+ }
67
+
68
+ static func printUsage() {
69
+ let usage = """
70
+ AutoSnippet Screenshot Tool (ScreenCaptureKit)
71
+
72
+ Usage:
73
+ screenshot Capture main screen
74
+ screenshot --window "Code" Capture window matching title
75
+ screenshot --list-windows List capturable windows (JSON)
76
+ screenshot --output /path/to/file.png Specify output path
77
+ screenshot --format jpeg|png Image format (default: jpeg)
78
+ screenshot --scale 0.5 Scale factor (default: 1.0)
79
+ """
80
+ fputs(usage + "\n", stderr)
81
+ }
82
+ }
83
+
84
+ // MARK: - 主逻辑
85
+
86
+ @available(macOS 12.3, *)
87
+ func run() async {
88
+ let args = Args.parse(CommandLine.arguments)
89
+
90
+ // 获取可共享内容(窗口/屏幕列表)
91
+ let content: SCShareableContent
92
+ do {
93
+ content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false)
94
+ } catch {
95
+ fputs("{\"error\":\"ScreenCaptureKit access denied: \\(error.localizedDescription). Grant Screen Recording permission in System Settings.\"}\n", stderr)
96
+ exit(1)
97
+ }
98
+
99
+ // --list-windows: 输出 JSON 窗口列表
100
+ if args.listWindows {
101
+ let windows = content.windows.compactMap { w -> [String: Any]? in
102
+ guard let app = w.owningApplication else { return nil }
103
+ return [
104
+ "windowID": w.windowID,
105
+ "title": w.title ?? "",
106
+ "app": app.applicationName,
107
+ "bundleID": app.bundleIdentifier,
108
+ "width": w.frame.width,
109
+ "height": w.frame.height,
110
+ "onScreen": w.isOnScreen,
111
+ ]
112
+ }
113
+ if let data = try? JSONSerialization.data(withJSONObject: windows, options: [.prettyPrinted, .sortedKeys]),
114
+ let json = String(data: data, encoding: .utf8) {
115
+ print(json)
116
+ }
117
+ exit(0)
118
+ }
119
+
120
+ // 确定截取目标 — 统一使用 desktopIndependentWindow(亮屏/息屏均可用)
121
+ var filter: SCContentFilter
122
+
123
+ // 筛选有效窗口(面积 > 100x100,排除菜单栏等微型窗口)
124
+ let validWindows = content.windows.filter { $0.frame.width > 100 && $0.frame.height > 100 }
125
+
126
+ if let title = args.windowTitle {
127
+ // 模糊匹配窗口标题或 app 名,选面积最大的
128
+ let pattern = title.lowercased()
129
+ let candidates = validWindows.filter { w in
130
+ let wTitle = (w.title ?? "").lowercased()
131
+ let appName = (w.owningApplication?.applicationName ?? "").lowercased()
132
+ return wTitle.contains(pattern) || appName.contains(pattern)
133
+ }
134
+ let matched = candidates.max(by: { $0.frame.width * $0.frame.height < $1.frame.width * $1.frame.height })
135
+
136
+ guard let window = matched else {
137
+ let windowList: String = validWindows.prefix(20).compactMap {
138
+ "\($0.owningApplication?.applicationName ?? "?") - \($0.title ?? "(no title)") [\(Int($0.frame.width))x\(Int($0.frame.height))]"
139
+ }.joined(separator: "\n ")
140
+ fputs("{\"error\":\"No window matching '\(title)'. Available:\\n \(windowList)\"}\n", stderr)
141
+ exit(1)
142
+ }
143
+
144
+ filter = SCContentFilter(desktopIndependentWindow: window)
145
+ } else {
146
+ // 未指定窗口 — 自动选最大窗口
147
+ guard let largest = validWindows.max(by: { $0.frame.width * $0.frame.height < $1.frame.width * $1.frame.height }) else {
148
+ fputs("{\"error\":\"No capturable window available\"}\n", stderr)
149
+ exit(1)
150
+ }
151
+ filter = SCContentFilter(desktopIndependentWindow: largest)
152
+ }
153
+
154
+ // 配置截图参数
155
+ let config = SCStreamConfiguration()
156
+ let sourceRect = filter.contentRect
157
+ config.width = Int(sourceRect.width * args.scale)
158
+ config.height = Int(sourceRect.height * args.scale)
159
+ config.showsCursor = false
160
+ config.capturesAudio = false
161
+
162
+ // 截图
163
+ let image: CGImage
164
+ do {
165
+ image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: config)
166
+ } catch {
167
+ fputs("{\"error\":\"Screenshot failed: \\(error.localizedDescription)\"}\n", stderr)
168
+ exit(1)
169
+ }
170
+
171
+ // 编码为图片数据
172
+ let bitmapRep = NSBitmapImageRep(cgImage: image)
173
+ let imageData: Data?
174
+ switch args.format {
175
+ case .jpeg:
176
+ imageData = bitmapRep.representation(using: .jpeg, properties: [.compressionFactor: 0.85])
177
+ case .png:
178
+ imageData = bitmapRep.representation(using: .png, properties: [:])
179
+ }
180
+
181
+ guard let data = imageData else {
182
+ fputs("{\"error\":\"Image encoding failed\"}\n", stderr)
183
+ exit(1)
184
+ }
185
+
186
+ // 确定输出路径
187
+ let ext = args.format == .png ? "png" : "jpg"
188
+ let outputPath = args.outputPath ?? NSTemporaryDirectory() + "asd-screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).\(ext)"
189
+
190
+ do {
191
+ try data.write(to: URL(fileURLWithPath: outputPath))
192
+ } catch {
193
+ fputs("{\"error\":\"Write failed: \\(error.localizedDescription)\"}\n", stderr)
194
+ exit(1)
195
+ }
196
+
197
+ // 输出 JSON 结果到 stdout
198
+ let result: [String: Any] = [
199
+ "success": true,
200
+ "path": outputPath,
201
+ "width": image.width,
202
+ "height": image.height,
203
+ "format": args.format.rawValue,
204
+ "bytes": data.count,
205
+ ]
206
+ if let jsonData = try? JSONSerialization.data(withJSONObject: result, options: [.sortedKeys]),
207
+ let json = String(data: jsonData, encoding: .utf8) {
208
+ print(json)
209
+ }
210
+ }
211
+
212
+ // MARK: - Entry Point
213
+
214
+ // 初始化 AppKit 连接到 WindowServer(解决 CGS_REQUIRE_INIT)
215
+ let app = NSApplication.shared
216
+ app.setActivationPolicy(.prohibited)
217
+
218
+ if #available(macOS 12.3, *) {
219
+ let semaphore = DispatchSemaphore(value: 0)
220
+ Task {
221
+ await run()
222
+ semaphore.signal()
223
+ }
224
+ semaphore.wait()
225
+ } else {
226
+ fputs("{\"error\":\"Requires macOS 12.3 or later\"}\n", stderr)
227
+ exit(1)
228
+ }