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
@@ -0,0 +1,476 @@
1
+ /**
2
+ * system-interaction.js — 系统交互工具 (3)
3
+ *
4
+ * 为 Agent 提供与本地操作系统交互的能力:
5
+ *
6
+ * 1. run_safe_command 安全执行终端命令 (受 SafetyPolicy 约束)
7
+ * 2. write_project_file 写入/创建项目文件 (受文件范围约束)
8
+ * 3. get_environment_info 获取运行环境信息
9
+ *
10
+ * ⚠️ 安全设计:
11
+ * - run_safe_command 在工具层即执行命令黑名单/白名单检查
12
+ * - write_project_file 在工具层即执行文件路径范围检查
13
+ * - 两者均依赖 AgentRuntime 注入的 safetyPolicy 上下文
14
+ * - 即使 safetyPolicy 未注入,工具自身也有基础安全兜底
15
+ *
16
+ * @module system-interaction
17
+ */
18
+
19
+ import { execFile } from 'node:child_process';
20
+ import fs from 'node:fs';
21
+ import os from 'node:os';
22
+ import path from 'node:path';
23
+ import { promisify } from 'node:util';
24
+
25
+ const execFileAsync = promisify(execFile);
26
+
27
+ // ─── 常量 ────────────────────────────────────────────
28
+
29
+ /** 工具层兜底: 始终拒绝的危险命令模式 (无论 SafetyPolicy 是否注入) */
30
+ const HARDCODED_BLACKLIST = [
31
+ /\brm\s+-rf\s+[\/~]/,
32
+ /\bsudo\b/,
33
+ /\bmkfs\b/,
34
+ /\bdd\s+if=/,
35
+ /\b(shutdown|reboot|halt)\b/,
36
+ />\s*\/dev\//,
37
+ /\bcurl\b.*\|\s*(bash|sh)/,
38
+ /\bchmod\s+777/,
39
+ /\bpasswd\b/,
40
+ /\bkillall\b/,
41
+ /\bfork\s*bomb/i,
42
+ /:\(\)\s*\{\s*:\|:\s*&\s*\}\s*;/, // fork bomb pattern
43
+ ];
44
+
45
+ /** 工具层兜底: 无 SafetyPolicy 时仅允许的安全命令前缀 */
46
+ const FALLBACK_SAFE_PREFIXES = [
47
+ 'ls', 'cat', 'head', 'tail', 'grep', 'find', 'wc',
48
+ 'echo', 'pwd', 'date', 'which', 'file', 'stat',
49
+ 'git log', 'git status', 'git diff', 'git branch', 'git show',
50
+ 'npm list', 'npm outdated', 'node -v', 'npm -v',
51
+ 'python --version', 'python3 --version',
52
+ 'env', 'printenv',
53
+ ];
54
+
55
+ /** 命令执行超时 (ms) */
56
+ const COMMAND_TIMEOUT = 30_000;
57
+
58
+ /** 输出截断长度 (bytes) */
59
+ const MAX_OUTPUT_LENGTH = 16_000;
60
+
61
+ /** 文件写入最大尺寸 (bytes) */
62
+ const MAX_WRITE_SIZE = 512 * 1024;
63
+
64
+ // ─── 内部工具函数 ────────────────────────────────────
65
+
66
+ /**
67
+ * 硬编码黑名单检查 — 工具层兜底, 无论是否有 SafetyPolicy 都生效
68
+ */
69
+ function _isHardBlacklisted(command) {
70
+ for (const pattern of HARDCODED_BLACKLIST) {
71
+ if (pattern.test(command)) return true;
72
+ }
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * 无 SafetyPolicy 时的白名单兜底
78
+ */
79
+ function _isFallbackSafe(command) {
80
+ const trimmed = command.trim();
81
+ return FALLBACK_SAFE_PREFIXES.some(prefix => trimmed.startsWith(prefix));
82
+ }
83
+
84
+ /**
85
+ * 截断过长输出
86
+ */
87
+ function _truncate(text, max = MAX_OUTPUT_LENGTH) {
88
+ if (!text || text.length <= max) return text;
89
+ return `${text.slice(0, max)}\n\n... [输出已截断, 共 ${text.length} 字符]`;
90
+ }
91
+
92
+ /**
93
+ * 获取 projectRoot — 优先从 context 获取, 兜底用 cwd
94
+ */
95
+ function _getProjectRoot(ctx) {
96
+ return ctx.projectRoot || ctx.container?.get?.('projectRoot') || process.cwd();
97
+ }
98
+
99
+ // ═══════════════════════════════════════════════════════
100
+ // 1. run_safe_command — 安全执行终端命令
101
+ // ═══════════════════════════════════════════════════════
102
+
103
+ export const runSafeCommand = {
104
+ name: 'run_safe_command',
105
+ description:
106
+ '在项目目录下安全执行终端命令。' +
107
+ '命令受安全策略约束: 危险命令(sudo/rm -rf/shutdown 等)被自动拦截。' +
108
+ '适用于: 查看 git 状态、运行测试、检查依赖版本、执行构建等。' +
109
+ '超时 30 秒, 输出超过 16KB 会被截断。' +
110
+ '如果需要管道或重定向, 请用 sh -c "..." 包装。',
111
+ parameters: {
112
+ type: 'object',
113
+ properties: {
114
+ command: {
115
+ type: 'string',
116
+ description: '要执行的终端命令, 如 "git status" 或 "npm test"',
117
+ },
118
+ cwd: {
119
+ type: 'string',
120
+ description: '工作目录 (相对于项目根目录), 缺省为项目根目录',
121
+ },
122
+ timeout: {
123
+ type: 'number',
124
+ description: '超时时间(毫秒), 默认 30000',
125
+ },
126
+ },
127
+ required: ['command'],
128
+ },
129
+ handler: async (params, ctx) => {
130
+ const { command, cwd, timeout } = params;
131
+ const projectRoot = _getProjectRoot(ctx);
132
+
133
+ if (!command || typeof command !== 'string' || command.trim().length === 0) {
134
+ return { error: '命令不能为空' };
135
+ }
136
+
137
+ // ── 安全检查 Layer 1: 硬编码黑名单 (无条件拦截) ──
138
+ if (_isHardBlacklisted(command)) {
139
+ return { error: `安全拦截: 命令 "${command}" 匹配危险模式, 已被阻止执行` };
140
+ }
141
+
142
+ // ── 安全检查 Layer 2: SafetyPolicy (如果注入) ──
143
+ const safetyPolicy = ctx.safetyPolicy || null;
144
+ if (safetyPolicy) {
145
+ const check = safetyPolicy.checkCommand(command);
146
+ if (!check.safe) {
147
+ return { error: `SafetyPolicy 拦截: ${check.reason}` };
148
+ }
149
+ } else {
150
+ // 无 SafetyPolicy 时使用白名单兜底
151
+ if (!_isFallbackSafe(command)) {
152
+ return {
153
+ error: `无安全策略: 命令 "${command}" 不在安全白名单中。` +
154
+ `允许的命令前缀: ${FALLBACK_SAFE_PREFIXES.join(', ')}`,
155
+ };
156
+ }
157
+ }
158
+
159
+ // ── 解析工作目录 ──
160
+ let workDir = projectRoot;
161
+ if (cwd) {
162
+ workDir = path.isAbsolute(cwd) ? cwd : path.resolve(projectRoot, cwd);
163
+ // 范围检查
164
+ if (!workDir.startsWith(path.resolve(projectRoot))) {
165
+ return { error: `工作目录 "${cwd}" 超出项目范围 "${projectRoot}"` };
166
+ }
167
+ }
168
+
169
+ if (!fs.existsSync(workDir)) {
170
+ return { error: `工作目录 "${workDir}" 不存在` };
171
+ }
172
+
173
+ // ── 执行命令 ──
174
+ const effectiveTimeout = timeout || COMMAND_TIMEOUT;
175
+
176
+ try {
177
+ const { stdout, stderr } = await execFileAsync('sh', ['-c', command], {
178
+ cwd: workDir,
179
+ timeout: effectiveTimeout,
180
+ maxBuffer: 1024 * 1024, // 1MB 缓冲
181
+ env: {
182
+ ...process.env,
183
+ // 禁用交互式 pager
184
+ GIT_PAGER: 'cat',
185
+ PAGER: 'cat',
186
+ LESS: '-FRX',
187
+ },
188
+ });
189
+
190
+ return {
191
+ exitCode: 0,
192
+ stdout: _truncate(stdout),
193
+ stderr: _truncate(stderr),
194
+ command,
195
+ cwd: workDir,
196
+ };
197
+ } catch (err) {
198
+ // 超时
199
+ if (err.killed) {
200
+ return {
201
+ error: `命令执行超时 (${effectiveTimeout}ms)`,
202
+ command,
203
+ stdout: _truncate(err.stdout || ''),
204
+ stderr: _truncate(err.stderr || ''),
205
+ };
206
+ }
207
+
208
+ // 非零退出
209
+ return {
210
+ exitCode: err.code ?? 1,
211
+ stdout: _truncate(err.stdout || ''),
212
+ stderr: _truncate(err.stderr || err.message || ''),
213
+ command,
214
+ cwd: workDir,
215
+ };
216
+ }
217
+ },
218
+ };
219
+
220
+ // ═══════════════════════════════════════════════════════
221
+ // 2. write_project_file — 写入项目文件
222
+ // ═══════════════════════════════════════════════════════
223
+
224
+ export const writeProjectFile = {
225
+ name: 'write_project_file',
226
+ description:
227
+ '在项目目录内创建或覆盖写入文件。' +
228
+ '自动创建不存在的中间目录。文件路径必须在项目范围内。' +
229
+ '适用于: 生成配置文件、创建代码文件、写入分析报告等。' +
230
+ '最大写入 512KB。',
231
+ parameters: {
232
+ type: 'object',
233
+ properties: {
234
+ filePath: {
235
+ type: 'string',
236
+ description: '目标文件路径 (相对于项目根目录或绝对路径)',
237
+ },
238
+ content: {
239
+ type: 'string',
240
+ description: '要写入的文件内容',
241
+ },
242
+ append: {
243
+ type: 'boolean',
244
+ description: '是否追加模式 (默认 false = 覆盖写入)',
245
+ },
246
+ },
247
+ required: ['filePath', 'content'],
248
+ },
249
+ handler: async (params, ctx) => {
250
+ const { filePath, content, append } = params;
251
+ const projectRoot = _getProjectRoot(ctx);
252
+
253
+ if (!filePath || typeof filePath !== 'string') {
254
+ return { error: '文件路径不能为空' };
255
+ }
256
+ if (typeof content !== 'string') {
257
+ return { error: '文件内容必须为字符串' };
258
+ }
259
+
260
+ // ── 大小限制 ──
261
+ if (Buffer.byteLength(content, 'utf-8') > MAX_WRITE_SIZE) {
262
+ return { error: `文件内容超过大小限制 (${MAX_WRITE_SIZE / 1024}KB)` };
263
+ }
264
+
265
+ // ── 路径解析与安全检查 ──
266
+ const resolved = path.isAbsolute(filePath)
267
+ ? path.resolve(filePath)
268
+ : path.resolve(projectRoot, filePath);
269
+
270
+ const scopeRoot = path.resolve(projectRoot);
271
+ if (!resolved.startsWith(scopeRoot + path.sep) && resolved !== scopeRoot) {
272
+ return { error: `文件路径 "${filePath}" 超出项目范围 "${projectRoot}"` };
273
+ }
274
+
275
+ // SafetyPolicy 路径检查
276
+ const safetyPolicy = ctx.safetyPolicy || null;
277
+ if (safetyPolicy) {
278
+ const check = safetyPolicy.checkFilePath(resolved);
279
+ if (!check.safe) {
280
+ return { error: `SafetyPolicy 拦截: ${check.reason}` };
281
+ }
282
+ }
283
+
284
+ // ── 危险路径兜底 ──
285
+ const dangerousPatterns = [
286
+ /node_modules\//,
287
+ /\.git\//,
288
+ /\.env$/,
289
+ /\.env\.local$/,
290
+ /package-lock\.json$/,
291
+ /yarn\.lock$/,
292
+ /pnpm-lock\.yaml$/,
293
+ ];
294
+ const relPath = path.relative(scopeRoot, resolved);
295
+ for (const p of dangerousPatterns) {
296
+ if (p.test(relPath)) {
297
+ return { error: `安全拦截: 不允许写入 "${relPath}" (匹配受保护路径模式)` };
298
+ }
299
+ }
300
+
301
+ // ── 写入文件 ──
302
+ try {
303
+ // 确保目录存在
304
+ const dir = path.dirname(resolved);
305
+ if (!fs.existsSync(dir)) {
306
+ fs.mkdirSync(dir, { recursive: true });
307
+ }
308
+
309
+ if (append) {
310
+ fs.appendFileSync(resolved, content, 'utf-8');
311
+ } else {
312
+ fs.writeFileSync(resolved, content, 'utf-8');
313
+ }
314
+
315
+ const stat = fs.statSync(resolved);
316
+ return {
317
+ success: true,
318
+ filePath: relPath,
319
+ absolutePath: resolved,
320
+ size: stat.size,
321
+ mode: append ? 'append' : 'overwrite',
322
+ };
323
+ } catch (err) {
324
+ return { error: `写入文件失败: ${err.message}` };
325
+ }
326
+ },
327
+ };
328
+
329
+ // ═══════════════════════════════════════════════════════
330
+ // 3. get_environment_info — 获取运行环境信息
331
+ // ═══════════════════════════════════════════════════════
332
+
333
+ export const getEnvironmentInfo = {
334
+ name: 'get_environment_info',
335
+ description:
336
+ '获取当前运行环境的系统信息。' +
337
+ '包括: 操作系统、Node.js 版本、项目路径、Git 分支、依赖管理器等。' +
338
+ '适用于: 环境诊断、构建问题排查、项目状态检查。',
339
+ parameters: {
340
+ type: 'object',
341
+ properties: {
342
+ sections: {
343
+ type: 'array',
344
+ items: {
345
+ type: 'string',
346
+ enum: ['os', 'node', 'git', 'project', 'all'],
347
+ },
348
+ description: '要获取的信息部分, 默认 ["all"]',
349
+ },
350
+ },
351
+ required: [],
352
+ },
353
+ handler: async (params, ctx) => {
354
+ const sections = params.sections || ['all'];
355
+ const all = sections.includes('all');
356
+ const projectRoot = _getProjectRoot(ctx);
357
+ const info = {};
358
+
359
+ // ── OS 信息 ──
360
+ if (all || sections.includes('os')) {
361
+ info.os = {
362
+ platform: os.platform(),
363
+ arch: os.arch(),
364
+ release: os.release(),
365
+ hostname: os.hostname(),
366
+ uptime: `${Math.floor(os.uptime() / 3600)}h ${Math.floor((os.uptime() % 3600) / 60)}m`,
367
+ memory: {
368
+ total: `${Math.round(os.totalmem() / (1024 * 1024 * 1024))}GB`,
369
+ free: `${Math.round(os.freemem() / (1024 * 1024 * 1024))}GB`,
370
+ },
371
+ cpus: os.cpus().length,
372
+ shell: process.env.SHELL || process.env.COMSPEC || 'unknown',
373
+ };
374
+ }
375
+
376
+ // ── Node 信息 ──
377
+ if (all || sections.includes('node')) {
378
+ info.node = {
379
+ version: process.version,
380
+ execPath: process.execPath,
381
+ pid: process.pid,
382
+ env: {
383
+ NODE_ENV: process.env.NODE_ENV || 'unset',
384
+ npm_package_version: process.env.npm_package_version || 'N/A',
385
+ },
386
+ };
387
+
388
+ // npm/pnpm/yarn 版本
389
+ for (const pm of ['npm', 'pnpm', 'yarn']) {
390
+ try {
391
+ const { stdout } = await execFileAsync(pm, ['--version'], {
392
+ timeout: 5000,
393
+ });
394
+ info.node[`${pm}_version`] = stdout.trim();
395
+ } catch {
396
+ // 未安装, 跳过
397
+ }
398
+ }
399
+ }
400
+
401
+ // ── Git 信息 ──
402
+ if (all || sections.includes('git')) {
403
+ info.git = {};
404
+ try {
405
+ const { stdout: branch } = await execFileAsync(
406
+ 'git', ['branch', '--show-current'],
407
+ { cwd: projectRoot, timeout: 5000 },
408
+ );
409
+ info.git.branch = branch.trim();
410
+
411
+ const { stdout: status } = await execFileAsync(
412
+ 'git', ['status', '--porcelain'],
413
+ { cwd: projectRoot, timeout: 5000 },
414
+ );
415
+ const lines = status.trim().split('\n').filter(Boolean);
416
+ info.git.dirty = lines.length > 0;
417
+ info.git.changedFiles = lines.length;
418
+
419
+ const { stdout: lastCommit } = await execFileAsync(
420
+ 'git', ['log', '-1', '--format=%h %s (%cr)'],
421
+ { cwd: projectRoot, timeout: 5000 },
422
+ );
423
+ info.git.lastCommit = lastCommit.trim();
424
+
425
+ const { stdout: remoteUrl } = await execFileAsync(
426
+ 'git', ['remote', 'get-url', 'origin'],
427
+ { cwd: projectRoot, timeout: 5000 },
428
+ );
429
+ info.git.remote = remoteUrl.trim();
430
+ } catch {
431
+ info.git.error = '非 Git 仓库或 Git 未安装';
432
+ }
433
+ }
434
+
435
+ // ── 项目信息 ──
436
+ if (all || sections.includes('project')) {
437
+ info.project = {
438
+ root: projectRoot,
439
+ };
440
+
441
+ // package.json
442
+ const pkgPath = path.join(projectRoot, 'package.json');
443
+ if (fs.existsSync(pkgPath)) {
444
+ try {
445
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
446
+ info.project.name = pkg.name;
447
+ info.project.version = pkg.version;
448
+ info.project.type = pkg.type || 'commonjs';
449
+ info.project.dependencies = Object.keys(pkg.dependencies || {}).length;
450
+ info.project.devDependencies = Object.keys(pkg.devDependencies || {}).length;
451
+ } catch { /* invalid package.json */ }
452
+ }
453
+
454
+ // Podfile / Cartfile / build.gradle / CMakeLists / Makefile 检测
455
+ const projectIndicators = [
456
+ { file: 'Podfile', type: 'CocoaPods (iOS)' },
457
+ { file: 'Cartfile', type: 'Carthage (iOS)' },
458
+ { file: 'Package.swift', type: 'Swift Package Manager' },
459
+ { file: 'build.gradle', type: 'Gradle (Android/Java)' },
460
+ { file: 'pom.xml', type: 'Maven (Java)' },
461
+ { file: 'CMakeLists.txt', type: 'CMake (C/C++)' },
462
+ { file: 'Makefile', type: 'Make' },
463
+ { file: 'Cargo.toml', type: 'Cargo (Rust)' },
464
+ { file: 'go.mod', type: 'Go Modules' },
465
+ { file: 'requirements.txt', type: 'pip (Python)' },
466
+ { file: 'pyproject.toml', type: 'Python project' },
467
+ { file: 'Gemfile', type: 'Bundler (Ruby)' },
468
+ ];
469
+ info.project.buildSystems = projectIndicators
470
+ .filter(({ file }) => fs.existsSync(path.join(projectRoot, file)))
471
+ .map(({ type }) => type);
472
+ }
473
+
474
+ return info;
475
+ },
476
+ };
@@ -38,7 +38,6 @@ export const REGEX = {
38
38
  IMPORT_OBJC: /^#import\s*<[A-Za-z0-9_]+\/[A-Za-z0-9_+.-]+\.h>$/,
39
39
  IMPORT_SWIFT: /^import\s*\w+$/,
40
40
  SEARCH_MARK: /\/\/\s*(?:autosnippet|as):(?:search|s)(\s|$)/,
41
- DRAFT_FILE: /^_draft_.*\.md$/i,
42
41
  };
43
42
 
44
43
  /**
@@ -18,7 +18,6 @@ import { detectTriggers, REGEX } from './DirectiveDetector.js';
18
18
  import { handleAlink } from './handlers/AlinkHandler.js';
19
19
  /* ── Handler imports ── */
20
20
  import { handleCreate } from './handlers/CreateHandler.js';
21
- import { handleDraft } from './handlers/DraftHandler.js';
22
21
  import { handleGuard } from './handlers/GuardHandler.js';
23
22
  import { handleHeader } from './handlers/HeaderHandler.js';
24
23
  import { handleSearch } from './handlers/SearchHandler.js';
@@ -53,8 +52,6 @@ const DEFAULT_FILE_PATTERN = [
53
52
  '**/*.cpp',
54
53
  '**/*.cc',
55
54
  '**/*.hpp',
56
- // Draft
57
- '**/_draft_*.md',
58
55
  ];
59
56
  const IGNORED = [
60
57
  '**/node_modules/**',
@@ -221,11 +218,6 @@ export class FileWatcher {
221
218
 
222
219
  const filename = basename(fullPath);
223
220
 
224
- // _draft_*.md 文件自动处理
225
- if (REGEX.DRAFT_FILE.test(filename)) {
226
- await handleDraft(this, fullPath, relativePath, data);
227
- }
228
-
229
221
  // 检测指令
230
222
  const triggers = detectTriggers(data, filename);
231
223
 
@@ -16,7 +16,7 @@ import { REGEX } from '../DirectiveDetector.js';
16
16
  * @param {string} createOption 'c' | 'f' | undefined
17
17
  */
18
18
  export async function handleCreate(watcher, fullPath, relativePath, createOption) {
19
- const XA = await import('../../../infrastructure/external/XcodeAutomation.js');
19
+ const XA = await import('../../../platform/ios/xcode/XcodeAutomation.js');
20
20
  const CM = await import('../../../infrastructure/external/ClipboardManager.js');
21
21
 
22
22
  // 1. 读剪贴板(仅 -c 模式)
@@ -137,8 +137,12 @@ async function silentCreateCandidate(watcher, text, relativePath) {
137
137
  try {
138
138
  const { getServiceContainer } = await import('../../../injection/ServiceContainer.js');
139
139
  const container = getServiceContainer();
140
- const chatAgent = container.get('chatAgent');
141
- const aiResult = await chatAgent.executeTool('summarize_code', { code: text, language: lang });
140
+ const agentFactory = container.get('agentFactory');
141
+ const aiResult = await agentFactory.scanKnowledge({
142
+ label: title,
143
+ files: [{ name: title, content: text, language: lang }],
144
+ task: 'summarize',
145
+ });
142
146
  if (aiResult && !aiResult.error) {
143
147
  title = aiResult.title || title;
144
148
  summary = aiResult.summary || '';
@@ -53,22 +53,23 @@ export async function handleDraft(watcher, fullPath, relativePath, content) {
53
53
  }
54
54
  }
55
55
 
56
- // AI 摘要回退(通过 ChatAgent 统一入口)
56
+ // AI 摘要回退(通过 Agent 统一管道)
57
57
  try {
58
58
  const { getServiceContainer } = await import('../../../injection/ServiceContainer.js');
59
59
  const container = getServiceContainer();
60
- const chatAgent = container.get('chatAgent');
60
+ const agentFactory = container.get('agentFactory');
61
61
  const lang = LanguageService.inferLang(relativePath) || 'unknown';
62
- const result = await chatAgent.executeTool('summarize_code', {
63
- code: content,
64
- language: lang,
62
+ const result = await agentFactory.scanKnowledge({
63
+ label: relativePath,
64
+ files: [{ name: relativePath, content, language: lang }],
65
+ task: 'summarize',
65
66
  });
66
67
  if (result && !result.error && result.title && result.code) {
67
68
  await watcher._appendCandidates([result], 'draft-file');
68
69
  watcher._notify(`已创建候选「${result.title}」`);
69
70
  }
70
71
  } catch {
71
- /* ChatAgent 不可用 */
72
+ /* AgentFactory 不可用 */
72
73
  }
73
74
  } catch (e) {
74
75
  console.warn('[Watcher] 草稿文件解析失败:', e.message);