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.
- package/bin/cli.js +13 -5
- package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
- package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +26 -29
- package/lib/cli/SetupService.js +1 -1
- package/lib/core/AstAnalyzer.js +27 -5
- package/lib/core/analysis/CallEdgeResolver.js +402 -0
- package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
- package/lib/core/analysis/CallSiteExtractor.js +629 -0
- package/lib/core/analysis/DataFlowInferrer.js +57 -0
- package/lib/core/analysis/ImportPathResolver.js +189 -0
- package/lib/core/analysis/ImportRecord.js +105 -0
- package/lib/core/analysis/SymbolTableBuilder.js +211 -0
- package/lib/core/ast/ProjectGraph.js +8 -0
- package/lib/core/ast/lang-dart.js +352 -5
- package/lib/core/ast/lang-go.js +212 -10
- package/lib/core/ast/lang-java.js +205 -1
- package/lib/core/ast/lang-kotlin.js +330 -1
- package/lib/core/ast/lang-python.js +31 -2
- package/lib/core/ast/lang-rust.js +284 -3
- package/lib/core/ast/lang-swift.js +180 -1
- package/lib/core/ast/lang-typescript.js +290 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
- package/lib/external/mcp/McpServer.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +22 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +311 -162
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +102 -7
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
- package/lib/external/mcp/handlers/bootstrap-internal.js +19 -8
- package/lib/external/mcp/handlers/consolidated.js +9 -0
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/external/mcp/handlers/guard.js +3 -3
- package/lib/external/mcp/handlers/structure.js +62 -0
- package/lib/external/mcp/handlers/wiki-external.js +66 -3
- package/lib/external/mcp/tools.js +36 -1
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/middleware/requestLogger.js +1 -0
- package/lib/http/routes/ai.js +240 -35
- package/lib/http/routes/candidates.js +2 -3
- package/lib/http/routes/extract.js +13 -11
- package/lib/http/routes/modules.js +2 -2
- package/lib/http/routes/recipes.js +9 -5
- package/lib/http/routes/remote.js +149 -270
- package/lib/http/routes/violations.js +0 -54
- package/lib/http/utils/sse-sessions.js +1 -1
- package/lib/infrastructure/logging/Logger.js +5 -4
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
- package/lib/injection/ServiceContainer.js +70 -28
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/index.js +2 -2
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
- package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
- package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +490 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1016 -0
- package/lib/service/agent/AgentState.js +217 -0
- package/lib/service/agent/IntentClassifier.js +331 -0
- package/lib/service/agent/LarkTransport.js +389 -0
- package/lib/service/agent/capabilities.js +408 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +77 -22
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +14 -2
- package/lib/service/agent/core/LoopContext.js +170 -0
- package/lib/service/agent/core/MessageAdapter.js +223 -0
- package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
- package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
- package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
- package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
- package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
- package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +91 -123
- package/lib/service/agent/domain/insight-producer.js +267 -0
- package/lib/service/agent/domain/scan-prompts.js +105 -0
- package/lib/service/agent/forced-summary.js +266 -0
- package/lib/service/agent/index.js +91 -0
- package/lib/service/{chat → agent}/memory/ActiveContext.js +3 -1
- package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
- package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
- package/lib/service/{chat → agent}/memory/SessionStore.js +5 -4
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +303 -0
- package/lib/service/agent/strategies.js +717 -0
- package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
- package/lib/service/agent/tools/ai-analysis.js +75 -0
- package/lib/service/{chat → agent}/tools/ast-graph.js +229 -32
- package/lib/service/{chat → agent}/tools/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +33 -22
- package/lib/service/{chat → agent}/tools/infrastructure.js +6 -1
- package/lib/service/agent/tools/knowledge-graph.js +112 -0
- package/lib/service/agent/tools/scan-recipe.js +189 -0
- package/lib/service/agent/tools/system-interaction.js +476 -0
- package/lib/service/automation/DirectiveDetector.js +0 -1
- package/lib/service/automation/FileWatcher.js +0 -8
- package/lib/service/automation/handlers/CreateHandler.js +7 -3
- package/lib/service/automation/handlers/DraftHandler.js +7 -6
- package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
- package/lib/service/knowledge/CodeEntityGraph.js +327 -2
- package/lib/service/knowledge/KnowledgeService.js +5 -1
- package/lib/service/module/ModuleService.js +49 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/service/wiki/WikiGenerator.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/PathGuard.js +1 -1
- package/lib/shared/StyleGuide.js +1 -1
- package/package.json +4 -1
- package/resources/native-ui/screenshot.swift +228 -0
- package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
- package/dashboard/dist/assets/index-DfHY_3ln.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -749
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
- package/lib/http/routes/spm.js +0 -5
- package/lib/infrastructure/external/XcodeAutomation.js +0 -15
- package/lib/service/chat/ChatAgent.js +0 -1602
- package/lib/service/chat/Memory.js +0 -161
- package/lib/service/chat/ProducerAgent.js +0 -431
- package/lib/service/chat/ReasoningTrace.js +0 -523
- package/lib/service/chat/TaskPipeline.js +0 -357
- package/lib/service/chat/WorkingMemory.js +0 -357
- package/lib/service/chat/memory/PersistentMemory.js +0 -450
- package/lib/service/chat/tools/ai-analysis.js +0 -267
- package/lib/service/chat/tools/knowledge-graph.js +0 -234
- package/lib/service/chat/tools.js +0 -18
- package/lib/service/snippet/PlaceholderConverter.js +0 -5
- package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
- /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
- /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
- /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
- /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
- /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
|
-
#
|
|
92
|
-
#
|
|
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.
|
|
102
|
-
* @param {object} [options.
|
|
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.#
|
|
113
|
-
this.#
|
|
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.#
|
|
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
|
-
// 检查是否因为
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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.#
|
|
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
|
-
|
|
770
|
+
if (!this.#agentFactory) {
|
|
771
|
+
return [];
|
|
772
|
+
}
|
|
757
773
|
|
|
758
774
|
try {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
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 (
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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
|
-
* 通过
|
|
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 →
|
|
14
|
-
* →
|
|
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 驱动 — 所有分析决策由
|
|
30
|
+
* 4. AI 驱动 — 所有分析决策由 AgentRuntime 完成
|
|
31
31
|
* 5. 自适应 — AI 根据信号密度动态调整执行频率
|
|
32
32
|
*
|
|
33
33
|
* 前提条件:
|
|
34
|
-
* 需要可用的 AI Provider
|
|
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
|
-
#
|
|
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.
|
|
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
|
-
|
|
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.#
|
|
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
|
-
|
|
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. 调用
|
|
221
|
-
this.#logger.debug('[SignalCollector] invoking
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
* 从
|
|
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 —
|
|
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.#
|
|
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 '
|
|
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/
|
|
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]
|
package/lib/shared/FieldSpec.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 消费方:
|
|
13
13
|
* - UnifiedValidator.js → 字段完整性检查
|
|
14
14
|
* - dimension-text.js → SUBMISSION_SCHEMA / REQUIRED_FIELDS_DESCRIPTION
|
|
15
|
-
* -
|
|
15
|
+
* - bootstrap-producer.js → STYLE_GUIDE 字段列表
|
|
16
16
|
* - MissionBriefingBuilder.js → submissionSpec 字段描述
|
|
17
17
|
* - lifecycle.js → JSON Schema required 数组
|
|
18
18
|
* - consolidated.js → 前置校验
|
package/lib/shared/PathGuard.js
CHANGED
|
@@ -141,7 +141,7 @@ class PathGuard {
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Layer 1: 断言路径在允许的边界范围内
|
|
144
|
-
* 用于修改已有文件的场景(如 XcodeIntegration 插入 header、
|
|
144
|
+
* 用于修改已有文件的场景(如 XcodeIntegration 插入 header、SpmHelper 修改 Package.swift)
|
|
145
145
|
* @param {string} targetPath - 要写入的绝对路径
|
|
146
146
|
* @throws {PathGuardError}
|
|
147
147
|
*/
|
package/lib/shared/StyleGuide.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autosnippet",
|
|
3
|
-
"version": "3.2.
|
|
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
|
+
}
|