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
|
@@ -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 {
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
import { ToolRegistry } from '../service/
|
|
31
|
-
import { ALL_TOOLS } from '../service/
|
|
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 '../
|
|
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 模块引用(用于
|
|
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
|
-
'
|
|
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(
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
691
|
-
|
|
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 (
|
|
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 /
|
|
729
|
+
// AI Provider(供 MCP handler / AgentRuntime / 任意服务层使用)
|
|
734
730
|
this.register('aiProvider', () => this.singletons.aiProvider || null);
|
|
735
731
|
|
|
736
|
-
//
|
|
737
|
-
this.register('
|
|
738
|
-
if (!this.singletons.
|
|
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.
|
|
737
|
+
const projectRoot = this.singletons._projectRoot || process.cwd();
|
|
738
|
+
this.singletons.agentFactory = new AgentFactory({
|
|
739
|
+
container: this,
|
|
742
740
|
toolRegistry,
|
|
743
741
|
aiProvider,
|
|
744
|
-
|
|
742
|
+
projectRoot,
|
|
745
743
|
});
|
|
746
744
|
}
|
|
747
|
-
return this.singletons.
|
|
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
|
-
*
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
221
|
+
|
|
222
|
+
// 1. URL 依赖: .package(url: "...", ...)
|
|
223
|
+
const urlRe = /\.package\s*\(\s*url\s*:\s*"([^"]+)"[^)]*\)/g;
|
|
222
224
|
let m;
|
|
223
|
-
while ((m =
|
|
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 包装现有
|
|
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('./
|
|
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
|
-
// 动态加载
|
|
56
|
-
const {
|
|
57
|
-
this.#spm = new
|
|
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.#
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
/** 获取底层
|
|
220
|
+
/** 获取底层 SpmHelper(向后兼容) */
|
|
115
221
|
getSpmService() {
|
|
116
222
|
return this.#spm;
|
|
117
223
|
}
|