autosnippet 3.2.7 → 3.2.8

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 (60) hide show
  1. package/bin/cli.js +7 -0
  2. package/dashboard/dist/assets/index-D5jiDBQG.css +1 -0
  3. package/dashboard/dist/assets/{index-DfHY_3ln.js → index-e5OKj-Ni.js} +38 -38
  4. package/dashboard/dist/index.html +2 -2
  5. package/lib/cli/AiScanService.js +3 -3
  6. package/lib/core/AstAnalyzer.js +26 -4
  7. package/lib/core/analysis/CallEdgeResolver.js +402 -0
  8. package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
  9. package/lib/core/analysis/CallSiteExtractor.js +629 -0
  10. package/lib/core/analysis/DataFlowInferrer.js +57 -0
  11. package/lib/core/analysis/ImportPathResolver.js +189 -0
  12. package/lib/core/analysis/ImportRecord.js +105 -0
  13. package/lib/core/analysis/SymbolTableBuilder.js +211 -0
  14. package/lib/core/ast/ProjectGraph.js +8 -0
  15. package/lib/core/ast/lang-dart.js +352 -5
  16. package/lib/core/ast/lang-go.js +212 -10
  17. package/lib/core/ast/lang-java.js +205 -1
  18. package/lib/core/ast/lang-kotlin.js +330 -1
  19. package/lib/core/ast/lang-python.js +31 -2
  20. package/lib/core/ast/lang-rust.js +284 -3
  21. package/lib/core/ast/lang-swift.js +180 -1
  22. package/lib/core/ast/lang-typescript.js +290 -1
  23. package/lib/external/mcp/McpServer.js +1 -0
  24. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +21 -0
  25. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +5 -4
  26. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
  27. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +70 -4
  28. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +95 -1
  29. package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
  30. package/lib/external/mcp/handlers/bootstrap-internal.js +17 -6
  31. package/lib/external/mcp/handlers/consolidated.js +9 -0
  32. package/lib/external/mcp/handlers/guard.js +3 -3
  33. package/lib/external/mcp/handlers/structure.js +62 -0
  34. package/lib/external/mcp/handlers/wiki-external.js +66 -3
  35. package/lib/external/mcp/tools.js +36 -1
  36. package/lib/http/routes/remote.js +15 -15
  37. package/lib/injection/ServiceContainer.js +6 -11
  38. package/lib/platform/ios/index.js +2 -2
  39. package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
  40. package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
  41. package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
  42. package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
  43. package/lib/service/chat/ChatAgent.js +1 -1
  44. package/lib/service/chat/ChatAgentPrompts.js +13 -1
  45. package/lib/service/chat/ExplorationTracker.js +52 -8
  46. package/lib/service/chat/HandoffProtocol.js +19 -1
  47. package/lib/service/chat/WorkingMemory.js +3 -1
  48. package/lib/service/chat/memory/ActiveContext.js +3 -1
  49. package/lib/service/chat/memory/SessionStore.js +4 -3
  50. package/lib/service/chat/tools/ast-graph.js +229 -32
  51. package/lib/service/chat/tools/index.js +6 -1
  52. package/lib/service/chat/tools/infrastructure.js +5 -0
  53. package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
  54. package/lib/service/knowledge/CodeEntityGraph.js +327 -2
  55. package/lib/service/knowledge/KnowledgeService.js +5 -1
  56. package/lib/service/module/ModuleService.js +9 -0
  57. package/lib/service/wiki/WikiGenerator.js +1 -1
  58. package/lib/shared/PathGuard.js +1 -1
  59. package/package.json +1 -1
  60. package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
@@ -0,0 +1,367 @@
1
+ /**
2
+ * @module CallGraphAnalyzer
3
+ * @description Phase 5: 顶层编排器 — 协调 Call Graph 分析的全流程
4
+ *
5
+ * 流水线:
6
+ * 1. CallSiteExtractor — 从 AST 提取调用点 (已在 AstAnalyzer 二次遍历中完成)
7
+ * 2. SymbolTableBuilder — 构建全局符号表
8
+ * 3. ImportPathResolver — 导入路径解析器
9
+ * 4. CallEdgeResolver — 调用点 → 调用边
10
+ * 5. DataFlowInferrer — 调用边 → 数据流边
11
+ *
12
+ * 输出:
13
+ * { callEdges, dataFlowEdges, stats }
14
+ */
15
+
16
+ import { SymbolTableBuilder } from './SymbolTableBuilder.js';
17
+ import { ImportPathResolver } from './ImportPathResolver.js';
18
+ import { CallEdgeResolver } from './CallEdgeResolver.js';
19
+ import { DataFlowInferrer } from './DataFlowInferrer.js';
20
+
21
+ /**
22
+ * @typedef {object} CallGraphResult
23
+ * @property {import('./CallEdgeResolver.js').ResolvedEdge[]} callEdges
24
+ * @property {import('./DataFlowInferrer.js').DataFlowEdge[]} dataFlowEdges
25
+ * @property {CallGraphStats} stats
26
+ */
27
+
28
+ /**
29
+ * @typedef {object} CallGraphStats
30
+ * @property {number} totalCallSites — 总调用点数
31
+ * @property {number} resolvedCallSites — 成功解析的调用点数
32
+ * @property {number} resolvedRate — 解析成功率 (0-1)
33
+ * @property {number} totalEdges — 总边数 (call + data_flow)
34
+ * @property {number} filesProcessed — 处理的文件数
35
+ * @property {number} symbolCount — 符号表大小
36
+ * @property {number} durationMs — 总耗时
37
+ */
38
+
39
+ /**
40
+ * @typedef {object} AnalyzeOptions
41
+ * @property {number} [timeout=15000] — 超时(ms)
42
+ * @property {number} [maxCallSitesPerFile=500] — 每文件最多调用点
43
+ * @property {number} [minConfidence=0.5] — 最低置信度
44
+ */
45
+
46
+ export class CallGraphAnalyzer {
47
+ /**
48
+ * @param {string} projectRoot
49
+ */
50
+ constructor(projectRoot) {
51
+ this.projectRoot = projectRoot;
52
+ }
53
+
54
+ /**
55
+ * 执行完整的调用图分析
56
+ *
57
+ * @param {object} astProjectSummary — analyzeProject() 的输出 (需包含 callSites)
58
+ * @param {AnalyzeOptions} [options]
59
+ * @returns {Promise<CallGraphResult>}
60
+ */
61
+ async analyze(astProjectSummary, options = {}) {
62
+ const t0 = Date.now();
63
+ const timeout = options.timeout || 15_000;
64
+ const maxCallSitesPerFile = options.maxCallSitesPerFile || 500;
65
+
66
+ if (!astProjectSummary?.fileSummaries?.length) {
67
+ return this._emptyResult(Date.now() - t0);
68
+ }
69
+
70
+ // ── 渐进式超时: 逐文件检查超时,返回 partial result ──
71
+ const deadline = t0 + timeout;
72
+ const result = await this._doAnalyze(astProjectSummary, maxCallSitesPerFile, deadline);
73
+ result.stats.durationMs = Date.now() - t0;
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * 增量分析 — 仅重新分析变更文件及其依赖方
79
+ *
80
+ * @param {object} astProjectSummary — analyzeProject() 的全量输出
81
+ * @param {string[]} changedFiles — 变更文件的相对路径列表
82
+ * @param {AnalyzeOptions} [options]
83
+ * @returns {Promise<CallGraphResult>}
84
+ */
85
+ async analyzeIncremental(astProjectSummary, changedFiles, options = {}) {
86
+ const t0 = Date.now();
87
+ const timeout = options.timeout || 15_000;
88
+ const maxCallSitesPerFile = options.maxCallSitesPerFile || 500;
89
+
90
+ if (!astProjectSummary?.fileSummaries?.length || !changedFiles?.length) {
91
+ return this._emptyResult(Date.now() - t0);
92
+ }
93
+
94
+ // ── 超过 10 个文件变更 → 回退全量分析 ──
95
+ if (changedFiles.length > 10) {
96
+ return this.analyze(astProjectSummary, options);
97
+ }
98
+
99
+ const deadline = t0 + timeout;
100
+ const changedSet = new Set(changedFiles);
101
+
102
+ // ── Step 1: 构建全局符号表 (始终全量,确保跨文件符号可解析) ──
103
+ const symbolTable = SymbolTableBuilder.build(astProjectSummary);
104
+
105
+ // ── Step 2: 构建 ImportPathResolver ──
106
+ const allFiles = astProjectSummary.fileSummaries.map((f) => f.file);
107
+ const importResolver = new ImportPathResolver(this.projectRoot, allFiles);
108
+
109
+ // ── Step 3: 找到依赖变更文件的所有文件 (reverse dependency) ──
110
+ const affectedFiles = new Set(changedFiles);
111
+ for (const fileSummary of astProjectSummary.fileSummaries) {
112
+ const imports = symbolTable.fileImports.get(fileSummary.file) || [];
113
+ for (const imp of imports) {
114
+ const resolved = importResolver.resolve(String(imp), fileSummary.file);
115
+ if (resolved && changedSet.has(resolved)) {
116
+ affectedFiles.add(fileSummary.file);
117
+ break;
118
+ }
119
+ }
120
+ }
121
+
122
+ // ── Step 4: 仅对受影响文件解析调用边 ──
123
+ const fileCount = astProjectSummary.fileSummaries.length;
124
+ const tier = _computeTier(fileCount);
125
+ const useCHA = tier === 'full-cha';
126
+ const inheritanceGraph = useCHA ? (astProjectSummary.inheritanceGraph || []) : [];
127
+ const callEdgeResolver = new CallEdgeResolver(symbolTable, importResolver, inheritanceGraph);
128
+ const allCallEdges = [];
129
+ let totalCallSites = 0;
130
+ let processedFiles = 0;
131
+
132
+ for (const fileSummary of astProjectSummary.fileSummaries) {
133
+ if (!affectedFiles.has(fileSummary.file)) continue;
134
+
135
+ const callSites = fileSummary.callSites || [];
136
+ if (callSites.length === 0) continue;
137
+
138
+ // 超时检查
139
+ if (Date.now() > deadline) {
140
+ return {
141
+ callEdges: allCallEdges,
142
+ dataFlowEdges: DataFlowInferrer.infer(allCallEdges),
143
+ stats: {
144
+ totalCallSites,
145
+ resolvedCallSites: allCallEdges.length,
146
+ resolvedRate: totalCallSites > 0 ? allCallEdges.length / totalCallSites : 0,
147
+ totalEdges: allCallEdges.length,
148
+ filesProcessed: processedFiles,
149
+ symbolCount: symbolTable.declarations.size,
150
+ durationMs: Date.now() - t0,
151
+ tier,
152
+ partial: true,
153
+ incremental: true,
154
+ processedFiles,
155
+ totalFiles: affectedFiles.size,
156
+ changedFiles: changedFiles.length,
157
+ affectedFiles: affectedFiles.size,
158
+ },
159
+ };
160
+ }
161
+
162
+ const limitedCallSites =
163
+ callSites.length > maxCallSitesPerFile
164
+ ? callSites.slice(0, maxCallSitesPerFile)
165
+ : callSites;
166
+
167
+ totalCallSites += limitedCallSites.length;
168
+
169
+ const edges = callEdgeResolver.resolveFile(limitedCallSites, fileSummary.file);
170
+ allCallEdges.push(...edges);
171
+ processedFiles++;
172
+ }
173
+
174
+ // ── Step 5: 推断数据流 ──
175
+ const dataFlowEdges = DataFlowInferrer.infer(allCallEdges);
176
+
177
+ return {
178
+ callEdges: allCallEdges,
179
+ dataFlowEdges,
180
+ stats: {
181
+ totalCallSites,
182
+ resolvedCallSites: allCallEdges.length,
183
+ resolvedRate: totalCallSites > 0 ? allCallEdges.length / totalCallSites : 0,
184
+ totalEdges: allCallEdges.length + dataFlowEdges.length,
185
+ filesProcessed: processedFiles,
186
+ symbolCount: symbolTable.declarations.size,
187
+ durationMs: Date.now() - t0,
188
+ tier,
189
+ incremental: true,
190
+ changedFiles: changedFiles.length,
191
+ affectedFiles: affectedFiles.size,
192
+ },
193
+ };
194
+ }
195
+
196
+ /**
197
+ * @private 实际分析逻辑
198
+ *
199
+ * 分级降级策略 (§5.2):
200
+ * - <100 文件 → 完整分析 (含 CHA)
201
+ * - 100-500 → 完整分析,禁用 CHA
202
+ * - 500-2000 → 抽样分析 (核心目录优先)
203
+ * - >2000 → 仅模块级 import graph (跳过调用边解析)
204
+ *
205
+ * 渐进式超时 (§13 Issue #15):
206
+ * 每处理完一个文件检查 deadline,超时时返回已有的 partial result
207
+ *
208
+ * @param {object} astProjectSummary
209
+ * @param {number} maxCallSitesPerFile
210
+ * @param {number} deadline — Date.now() + timeout
211
+ */
212
+ async _doAnalyze(astProjectSummary, maxCallSitesPerFile, deadline) {
213
+ const fileCount = astProjectSummary.fileSummaries.length;
214
+
215
+ // ── 分级降级 ──
216
+ const tier = _computeTier(fileCount);
217
+ let fileSummaries = astProjectSummary.fileSummaries;
218
+
219
+ if (tier === 'import-only') {
220
+ // >2000 文件: 仅返回 import graph,不解析调用边
221
+ return {
222
+ callEdges: [],
223
+ dataFlowEdges: [],
224
+ stats: {
225
+ totalCallSites: 0,
226
+ resolvedCallSites: 0,
227
+ resolvedRate: 0,
228
+ totalEdges: 0,
229
+ filesProcessed: fileCount,
230
+ symbolCount: 0,
231
+ durationMs: 0,
232
+ tier: 'import-only',
233
+ },
234
+ };
235
+ }
236
+
237
+ if (tier === 'sampled') {
238
+ // 500-2000 文件: 抽样核心目录 (仅限制 call site 解析范围)
239
+ fileSummaries = _sampleCoreFiles(fileSummaries, 500);
240
+ }
241
+
242
+ // ── Step 2: 构建符号表 (始终使用全量文件,确保跨文件符号可解析) ──
243
+ const symbolTable = SymbolTableBuilder.build(astProjectSummary);
244
+
245
+ // ── Step 3: 构建 ImportPathResolver (全量文件索引) ──
246
+ const allFiles = astProjectSummary.fileSummaries.map((f) => f.file);
247
+ const importResolver = new ImportPathResolver(this.projectRoot, allFiles);
248
+
249
+ // ── Step 4: 解析调用边 (逐文件 + 超时检查) ──
250
+ const useCHA = tier === 'full-cha';
251
+ const inheritanceGraph = useCHA ? (astProjectSummary.inheritanceGraph || []) : [];
252
+ const callEdgeResolver = new CallEdgeResolver(symbolTable, importResolver, inheritanceGraph);
253
+ const allCallEdges = [];
254
+ let totalCallSites = 0;
255
+ let processedFiles = 0;
256
+ const totalFiles = fileSummaries.filter((f) => f.callSites?.length > 0).length;
257
+
258
+ for (const fileSummary of fileSummaries) {
259
+ const callSites = fileSummary.callSites || [];
260
+ if (callSites.length === 0) continue;
261
+
262
+ // ── 渐进式超时: 每文件检查 deadline ──
263
+ if (Date.now() > deadline) {
264
+ const dataFlowEdges = DataFlowInferrer.infer(allCallEdges);
265
+ return {
266
+ callEdges: allCallEdges,
267
+ dataFlowEdges,
268
+ stats: {
269
+ totalCallSites,
270
+ resolvedCallSites: allCallEdges.length,
271
+ resolvedRate: totalCallSites > 0 ? allCallEdges.length / totalCallSites : 0,
272
+ totalEdges: allCallEdges.length + dataFlowEdges.length,
273
+ filesProcessed: processedFiles,
274
+ symbolCount: symbolTable.declarations.size,
275
+ durationMs: 0,
276
+ tier,
277
+ partial: true,
278
+ processedFiles,
279
+ totalFiles,
280
+ },
281
+ };
282
+ }
283
+
284
+ // 防护: 限制每文件调用点数 (防止超大文件)
285
+ const limitedCallSites =
286
+ callSites.length > maxCallSitesPerFile
287
+ ? callSites.slice(0, maxCallSitesPerFile)
288
+ : callSites;
289
+
290
+ totalCallSites += limitedCallSites.length;
291
+
292
+ const edges = callEdgeResolver.resolveFile(limitedCallSites, fileSummary.file);
293
+ allCallEdges.push(...edges);
294
+ processedFiles++;
295
+ }
296
+
297
+ // ── Step 5: 推断数据流 ──
298
+ const dataFlowEdges = DataFlowInferrer.infer(allCallEdges);
299
+
300
+ // ── Stats ──
301
+ const stats = {
302
+ totalCallSites,
303
+ resolvedCallSites: allCallEdges.length,
304
+ resolvedRate: totalCallSites > 0 ? allCallEdges.length / totalCallSites : 0,
305
+ totalEdges: allCallEdges.length + dataFlowEdges.length,
306
+ filesProcessed: processedFiles,
307
+ symbolCount: symbolTable.declarations.size,
308
+ durationMs: 0, // 由外层填充
309
+ tier,
310
+ };
311
+
312
+ return { callEdges: allCallEdges, dataFlowEdges, stats };
313
+ }
314
+
315
+ /**
316
+ * @private 空结果
317
+ */
318
+ _emptyResult(durationMs) {
319
+ return {
320
+ callEdges: [],
321
+ dataFlowEdges: [],
322
+ stats: {
323
+ totalCallSites: 0,
324
+ resolvedCallSites: 0,
325
+ resolvedRate: 0,
326
+ totalEdges: 0,
327
+ filesProcessed: 0,
328
+ symbolCount: 0,
329
+ durationMs,
330
+ },
331
+ };
332
+ }
333
+ }
334
+
335
+ // ── 分级降级辅助 ──────────────────────────────────────────
336
+
337
+ /**
338
+ * 根据文件数量确定分析层级
339
+ * @param {number} fileCount
340
+ * @returns {'full-cha'|'full'|'sampled'|'import-only'}
341
+ */
342
+ function _computeTier(fileCount) {
343
+ if (fileCount < 100) return 'full-cha';
344
+ if (fileCount <= 500) return 'full';
345
+ if (fileCount <= 2000) return 'sampled';
346
+ return 'import-only';
347
+ }
348
+
349
+ /**
350
+ * 抽样核心文件 — 优先选取 src/、lib/、app/、core/ 等核心目录
351
+ * @param {object[]} fileSummaries
352
+ * @param {number} limit
353
+ * @returns {object[]}
354
+ */
355
+ function _sampleCoreFiles(fileSummaries, limit) {
356
+ const CORE_DIRS = /\/(src|lib|app|core|pkg|internal|domain|service|controller|handler|api)\//i;
357
+ const scored = fileSummaries.map((f) => ({
358
+ f,
359
+ score: CORE_DIRS.test(f.file) ? 2 : 1,
360
+ callSiteCount: f.callSites?.length || 0,
361
+ }));
362
+ // 排序: 核心目录优先,有调用点的优先
363
+ scored.sort((a, b) => b.score - a.score || b.callSiteCount - a.callSiteCount);
364
+ return scored.slice(0, limit).map((s) => s.f);
365
+ }
366
+
367
+ export default CallGraphAnalyzer;