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
@@ -1,16 +1,17 @@
1
1
  /**
2
- * ast-graph.js — AST 结构化分析 + Agent Memory 工具 (10)
2
+ * ast-graph.js — AST 结构化分析 + Agent Memory 工具 (11)
3
3
  *
4
4
  * 44. get_project_overview 项目 AST 概览
5
5
  * 45. get_class_hierarchy 类继承层级
6
- * 46. get_class_info 类详细信息
7
- * 47. get_protocol_info 协议详细信息
6
+ * 46. get_class_info 类/结构体详细信息 (跨语言)
7
+ * 47. get_protocol_info 协议/接口/trait 详细信息 (跨语言)
8
8
  * 48. get_method_overrides 方法覆写查询
9
- * 49. get_category_map Category 扩展映射
9
+ * 49. get_category_map Category/Extension 扩展映射
10
10
  * 50. get_previous_analysis 前序维度分析结果
11
11
  * 51. note_finding 记录关键发现
12
12
  * 52. get_previous_evidence 检索前序维度证据
13
13
  * 53. query_code_graph 查询代码实体图谱
14
+ * 54. query_call_graph 查询方法调用链 (Phase 5)
14
15
  */
15
16
 
16
17
  // ════════════════════════════════════════════════════════════
@@ -36,7 +37,8 @@ function _getProjectGraph(ctx) {
36
37
  export const getProjectOverview = {
37
38
  name: 'get_project_overview',
38
39
  description:
39
- '获取项目的整体结构概览:文件统计、模块列表、入口点、类/协议/Category 数量。' +
40
+ '获取项目的整体结构概览:文件统计、语言分布、模块列表、入口点、类/协议/Category 数量。' +
41
+ '支持所有语言 (Swift/ObjC/Java/Kotlin/Python/TS/JS/Dart/Rust/Go 等)。' +
40
42
  '适用场景:了解项目规模和架构布局,规划探索路径。',
41
43
  parameters: {
42
44
  type: 'object',
@@ -49,13 +51,30 @@ export const getProjectOverview = {
49
51
  }
50
52
 
51
53
  const o = graph.getOverview();
54
+
55
+ // §P2: 从文件扩展名统计语言分布
56
+ const langStats = {};
57
+ for (const filePath of graph.getAllFilePaths?.() || []) {
58
+ const ext = filePath.split('.').pop();
59
+ if (ext) langStats[ext] = (langStats[ext] || 0) + 1;
60
+ }
61
+
52
62
  const lines = [
53
63
  `📊 项目 AST 概览 (构建耗时 ${o.buildTimeMs}ms)`,
54
64
  ``,
55
- `文件: ${o.totalFiles} | 类: ${o.totalClasses} | 协议: ${o.totalProtocols} | Category: ${o.totalCategories} | 方法: ${o.totalMethods}`,
56
- ``,
57
- `── 模块 ──`,
65
+ `文件: ${o.totalFiles} | 类: ${o.totalClasses} | 协议/接口: ${o.totalProtocols} | Category/Extension: ${o.totalCategories} | 方法: ${o.totalMethods}`,
58
66
  ];
67
+
68
+ // 语言分布
69
+ const langEntries = Object.entries(langStats).sort((a, b) => b[1] - a[1]);
70
+ if (langEntries.length > 0) {
71
+ lines.push(``, `── 语言分布 ──`);
72
+ for (const [ext, count] of langEntries) {
73
+ lines.push(` .${ext}: ${count} 个文件`);
74
+ }
75
+ }
76
+
77
+ lines.push(``, `── 模块 ──`);
59
78
  for (const mod of o.topLevelModules) {
60
79
  const count = o.classesPerModule[mod] || 0;
61
80
  lines.push(` ${mod}/ — ${count} 个类`);
@@ -76,7 +95,8 @@ export const getProjectOverview = {
76
95
  export const getClassHierarchy = {
77
96
  name: 'get_class_hierarchy',
78
97
  description:
79
- '查看指定类的继承链(向上到根类)和直接子类列表。' +
98
+ '查看指定类/结构体的继承链(向上到根类)和直接子类列表。' +
99
+ '支持所有语言的类继承体系 (Swift class/struct, Java/Kotlin class, Python class, TS class, Dart class, Rust struct, Go struct 等)。' +
80
100
  '传入 className 查看指定类,不传则返回项目中所有根类及其子树。',
81
101
  parameters: {
82
102
  type: 'object',
@@ -138,7 +158,9 @@ export const getClassHierarchy = {
138
158
  // ────────────────────────────────────────────────────────────
139
159
  export const getClassInfo = {
140
160
  name: 'get_class_info',
141
- description: '获取指定类的详细信息: 属性、方法签名、导入、继承关系、Category 扩展。',
161
+ description:
162
+ '获取指定类/结构体的详细信息: 属性、方法签名、导入、继承关系、扩展。' +
163
+ '跨语言通用:ObjC @interface, Swift class/struct, Java/Kotlin class, Python class, TS/JS class, Dart class, Rust struct impl, Go struct 等。',
142
164
  parameters: {
143
165
  type: 'object',
144
166
  properties: {
@@ -169,7 +191,7 @@ export const getClassInfo = {
169
191
  ];
170
192
 
171
193
  if (info.protocols.length > 0) {
172
- lines.push(`遵循: <${info.protocols.join(', ')}>`);
194
+ lines.push(`实现: ${info.protocols.join(', ')}`);
173
195
  }
174
196
 
175
197
  if (info.properties.length > 0) {
@@ -195,7 +217,7 @@ export const getClassInfo = {
195
217
  }
196
218
 
197
219
  if (cats.length > 0) {
198
- lines.push(``, `── Category 扩展 (${cats.length}) ──`);
220
+ lines.push(``, `── 扩展 (${cats.length}) ──`);
199
221
  for (const cat of cats) {
200
222
  const methodNames = cat.methods.map((m) => m.selector).join(', ');
201
223
  lines.push(` ${info.name}(${cat.categoryName}) — ${cat.filePath} — [${methodNames}]`);
@@ -218,7 +240,9 @@ export const getClassInfo = {
218
240
  // ────────────────────────────────────────────────────────────
219
241
  export const getProtocolInfo = {
220
242
  name: 'get_protocol_info',
221
- description: '获取指定协议的定义(必选/可选方法)及所有遵循该协议的类。',
243
+ description:
244
+ '获取指定协议/接口/trait 的定义(必选/可选方法)及所有实现者列表。' +
245
+ '跨语言通用:ObjC @protocol, Swift protocol, Java/Kotlin interface, Python ABC, TS interface, Dart abstract class, Rust trait, Go interface 等。',
222
246
  parameters: {
223
247
  type: 'object',
224
248
  properties: {
@@ -235,36 +259,36 @@ export const getProtocolInfo = {
235
259
  const protocolName = params.protocolName || params.protocol_name;
236
260
  const info = graph.getProtocolInfo(protocolName);
237
261
  if (!info) {
238
- return `未找到协议 "${protocolName}"。可以使用 get_project_overview 查看项目中的所有协议。`;
262
+ return `未找到协议/接口 "${protocolName}"。可以使用 get_project_overview 查看项目中的所有协议/接口。`;
239
263
  }
240
264
 
241
- const lines = [`📋 @protocol ${info.name}`, `文件: ${info.filePath}:${info.line}`];
265
+ const lines = [`📋 ${info.name}`, `文件: ${info.filePath}:${info.line}`];
242
266
 
243
267
  if (info.inherits.length > 0) {
244
- lines.push(`继承: <${info.inherits.join(', ')}>`);
268
+ lines.push(`继承: ${info.inherits.join(', ')}`);
245
269
  }
246
270
 
247
271
  if (info.requiredMethods.length > 0) {
248
- lines.push(``, `── @required (${info.requiredMethods.length}) ──`);
272
+ lines.push(``, `── 必须实现 (${info.requiredMethods.length}) ──`);
249
273
  for (const m of info.requiredMethods) {
250
274
  lines.push(` ${m.isClassMethod ? '+' : '-'} ${m.selector} → ${m.returnType}`);
251
275
  }
252
276
  }
253
277
 
254
278
  if (info.optionalMethods.length > 0) {
255
- lines.push(``, `── @optional (${info.optionalMethods.length}) ──`);
279
+ lines.push(``, `── 可选实现 (${info.optionalMethods.length}) ──`);
256
280
  for (const m of info.optionalMethods) {
257
281
  lines.push(` ${m.isClassMethod ? '+' : '-'} ${m.selector} → ${m.returnType}`);
258
282
  }
259
283
  }
260
284
 
261
285
  if (info.conformers.length > 0) {
262
- lines.push(``, `── 遵循者 (${info.conformers.length}) ──`);
286
+ lines.push(``, `── 实现者 (${info.conformers.length}) ──`);
263
287
  for (const c of info.conformers) {
264
288
  lines.push(` ${c}`);
265
289
  }
266
290
  } else {
267
- lines.push(``, `⚠️ 暂未发现遵循此协议的类`);
291
+ lines.push(``, `⚠️ 暂未发现实现此协议/接口的类`);
268
292
  }
269
293
 
270
294
  return lines.join('\n');
@@ -276,7 +300,9 @@ export const getProtocolInfo = {
276
300
  // ────────────────────────────────────────────────────────────
277
301
  export const getMethodOverrides = {
278
302
  name: 'get_method_overrides',
279
- description: '查找覆写了指定方法的所有子类。适用于理解方法在继承树中的多态行为。',
303
+ description:
304
+ '查找覆写了指定方法的所有子类。适用于理解方法在继承树中的多态行为。' +
305
+ '跨语言通用:支持 ObjC/Swift/Java/Kotlin/Python/TS/Dart 等语言的方法覆写。',
280
306
  parameters: {
281
307
  type: 'object',
282
308
  properties: {
@@ -314,7 +340,9 @@ export const getMethodOverrides = {
314
340
  export const getCategoryMap = {
315
341
  name: 'get_category_map',
316
342
  description:
317
- '获取指定类或整个项目的 ObjC Category 扩展映射。Category 是 ObjC 的核心模式,了解它有助于发现功能划分。',
343
+ '获取指定类的 Category/Extension 扩展映射。' +
344
+ 'ObjC Category、Swift Extension 等语言的类扩展机制。了解它有助于发现功能划分。' +
345
+ '不传 className 则返回整个项目中有扩展的类列表。',
318
346
  parameters: {
319
347
  type: 'object',
320
348
  properties: {
@@ -455,7 +483,9 @@ export const noteFinding = {
455
483
  const coordinator = ctx._memoryCoordinator;
456
484
  if (coordinator) {
457
485
  const finding = params.finding || '';
458
- const evidence = params.evidence || '';
486
+ // P0 Fix: AI 可能传入 array/object,强制转为 string
487
+ const rawEvidence = params.evidence;
488
+ const evidence = typeof rawEvidence === 'string' ? rawEvidence : Array.isArray(rawEvidence) ? rawEvidence.join(', ') : rawEvidence ? String(rawEvidence) : '';
459
489
  const importance = params.importance || 5;
460
490
  const round = ctx._currentRound || 0;
461
491
  const scopeId = ctx._dimensionScopeId || undefined;
@@ -547,8 +577,8 @@ export const queryCodeGraph = {
547
577
  },
548
578
  entity_type: {
549
579
  type: 'string',
550
- enum: ['class', 'protocol', 'category', 'module', 'pattern'],
551
- description: '实体类型过滤 (可选)',
580
+ enum: ['class', 'protocol', 'category', 'module', 'pattern', 'method'],
581
+ description: '实体类型过滤 (可选,含 Phase 5 method 类型)',
552
582
  },
553
583
  max_depth: {
554
584
  type: 'number',
@@ -559,14 +589,17 @@ export const queryCodeGraph = {
559
589
  },
560
590
  handler: async (params, ctx) => {
561
591
  try {
562
- const { CodeEntityGraph } = await import('../../knowledge/CodeEntityGraph.js');
563
- const db = ctx?.container?.get('database');
564
- if (!db) {
565
- return '代码实体图谱不可用: 数据库未初始化';
592
+ // 优先从 container 获取单例 CEG,避免每次创建新实例
593
+ let ceg = ctx?.container?.get?.('codeEntityGraph');
594
+ if (!ceg) {
595
+ const { CodeEntityGraph } = await import('../../knowledge/CodeEntityGraph.js');
596
+ const db = ctx?.container?.get?.('database');
597
+ if (!db) {
598
+ return '代码实体图谱不可用: 数据库未初始化';
599
+ }
600
+ const projectRoot = ctx?.projectRoot || process.env.ASD_PROJECT_DIR || '';
601
+ ceg = new CodeEntityGraph(db, { projectRoot });
566
602
  }
567
-
568
- const projectRoot = ctx?.projectRoot || process.env.ASD_PROJECT_DIR || '';
569
- const ceg = new CodeEntityGraph(db, { projectRoot });
570
603
  const maxDepth = params.max_depth || 3;
571
604
 
572
605
  switch (params.action) {
@@ -680,3 +713,167 @@ export const queryCodeGraph = {
680
713
  }
681
714
  },
682
715
  };
716
+
717
+ // ────────────────────────────────────────────────────────────
718
+ // 54. query_call_graph — 查询方法调用链 (Phase 5)
719
+ // ────────────────────────────────────────────────────────────
720
+ export const queryCallGraph = {
721
+ name: 'query_call_graph',
722
+ description:
723
+ '查询方法的调用链上下文 (Call Graph)。支持 10+ 种编程语言 (Swift/ObjC/Java/Kotlin/Python/TS/JS/Dart/Rust/Go)。\n' +
724
+ '• callers: 谁调用了这个方法?(向上追踪调用者链)\n' +
725
+ '• callees: 这个方法调用了谁?(向下追踪依赖链)\n' +
726
+ '• both: 同时获取调用者和被调用者\n' +
727
+ '• impact: 修改此方法的影响半径分析 (受影响文件数)\n' +
728
+ '• search: 按名称搜索方法实体\n' +
729
+ '⚠️ 需要先完成 bootstrap 才有数据。方法名格式: "ClassName.methodName" 或简写 "methodName"。',
730
+ parameters: {
731
+ type: 'object',
732
+ properties: {
733
+ methodName: {
734
+ type: 'string',
735
+ description:
736
+ '方法名 (e.g. "UserService.getUser", "viewDidLoad", "handleClick")。' +
737
+ 'search 模式下为搜索关键词。',
738
+ },
739
+ direction: {
740
+ type: 'string',
741
+ enum: ['callers', 'callees', 'both', 'impact', 'search'],
742
+ description:
743
+ 'callers=调用者 | callees=被调用者 | both=双向 | impact=影响分析 | search=搜索方法实体',
744
+ },
745
+ maxDepth: {
746
+ type: 'number',
747
+ description: '最大遍历深度 (1-5, 默认 2)',
748
+ },
749
+ },
750
+ required: ['methodName'],
751
+ },
752
+ handler: async (params, ctx) => {
753
+ try {
754
+ // 前置: 必填参数校验 (fail-fast)
755
+ const methodName = params.methodName || params.method_name;
756
+ if (!methodName) {
757
+ return '请提供 methodName 参数。';
758
+ }
759
+
760
+ // 优先从 container 获取单例 CEG,避免每次创建新实例
761
+ let ceg = ctx?.container?.get?.('codeEntityGraph');
762
+ if (!ceg) {
763
+ // fallback: 手动构建
764
+ const { CodeEntityGraph } = await import('../../knowledge/CodeEntityGraph.js');
765
+ const db = ctx?.container?.get?.('database');
766
+ if (!db) {
767
+ return '调用图查询不可用: 数据库未初始化。';
768
+ }
769
+ const projectRoot = ctx?.projectRoot || process.env.ASD_PROJECT_DIR || '';
770
+ ceg = new CodeEntityGraph(db, { projectRoot });
771
+ }
772
+
773
+ const direction = params.direction || 'both';
774
+ const maxDepth = Math.min(Math.max(Number(params.maxDepth || params.max_depth) || 2, 1), 5);
775
+
776
+ // search 模式: 按名称搜索方法实体
777
+ if (direction === 'search') {
778
+ const results = ceg.searchEntities(methodName, { type: 'method', limit: 15 });
779
+ if (results.length === 0) {
780
+ // 降级: 不限类型搜索
781
+ const allResults = ceg.searchEntities(methodName, { limit: 15 });
782
+ if (allResults.length === 0) {
783
+ return `未找到匹配 "${methodName}" 的实体。请确认 bootstrap 已完成。`;
784
+ }
785
+ const lines = [`🔍 搜索 "${methodName}" (${allResults.length} 条, 无 method 类型匹配):`];
786
+ for (const e of allResults) {
787
+ lines.push(` • [${e.entityType}] ${e.name}${e.filePath ? ` (${e.filePath})` : ''}`);
788
+ }
789
+ lines.push(``, `💡 提示: 调用图查询需使用 method 实体的 entityId。`);
790
+ return lines.join('\n');
791
+ }
792
+ const lines = [`🔍 方法搜索 "${methodName}" (${results.length} 条):`];
793
+ for (const e of results) {
794
+ lines.push(` • ${e.name}${e.filePath ? ` (${e.filePath})` : ''}`);
795
+ }
796
+ lines.push(``, `💡 复制上面的完整方法名,用 callers/callees/both/impact 查询其调用链。`);
797
+ return lines.join('\n');
798
+ }
799
+
800
+ // impact 模式
801
+ if (direction === 'impact') {
802
+ const impact = ceg.getCallImpactRadius(methodName);
803
+ const lines = [
804
+ `⚡ 修改 "${methodName}" 的影响半径:`,
805
+ ` 直接调用者: ${impact.directCallers}`,
806
+ ` 传递性调用者 (depth≤3): ${impact.transitiveCallers}`,
807
+ ];
808
+ if (impact.affectedFiles.length > 0) {
809
+ lines.push(` 受影响文件 (${impact.affectedFiles.length}):`);
810
+ for (const f of impact.affectedFiles.slice(0, 15)) {
811
+ lines.push(` 📄 ${f}`);
812
+ }
813
+ if (impact.affectedFiles.length > 15) {
814
+ lines.push(` ... 还有 ${impact.affectedFiles.length - 15} 个文件`);
815
+ }
816
+ } else {
817
+ lines.push(` 未发现受影响文件 (可能是叶子方法或 bootstrap 无调用图数据)。`);
818
+ }
819
+ return lines.join('\n');
820
+ }
821
+
822
+ // callers / callees / both 模式
823
+ const result = {};
824
+ if (direction === 'callers' || direction === 'both') {
825
+ result.callers = ceg.getCallers(methodName, maxDepth);
826
+ }
827
+ if (direction === 'callees' || direction === 'both') {
828
+ result.callees = ceg.getCallees(methodName, maxDepth);
829
+ }
830
+
831
+ const totalCallers = result.callers?.length || 0;
832
+ const totalCallees = result.callees?.length || 0;
833
+
834
+ if (totalCallers === 0 && totalCallees === 0) {
835
+ return (
836
+ `"${methodName}" 在调用图中没有找到调用关系。\n\n` +
837
+ `可能原因:\n` +
838
+ ` 1. 方法名拼写不匹配 — 尝试 search 模式: query_call_graph({methodName:"关键词", direction:"search"})\n` +
839
+ ` 2. 该方法是入口点/叶子节点,无上下游\n` +
840
+ ` 3. bootstrap 尚未执行或不包含此文件`
841
+ );
842
+ }
843
+
844
+ const lines = [`📞 "${methodName}" 的调用链 (depth≤${maxDepth}):`];
845
+
846
+ if (result.callers?.length > 0) {
847
+ lines.push(``, `── 调用者 (谁调用了它, ${result.callers.length} 条) ──`);
848
+ for (const c of result.callers.slice(0, 20)) {
849
+ const indent = ' '.repeat(c.depth);
850
+ const typeTag = c.callType !== 'unknown' ? ` [${c.callType}]` : '';
851
+ lines.push(`${indent}⬆ ${c.caller}${typeTag}`);
852
+ }
853
+ if (result.callers.length > 20) {
854
+ lines.push(` ... 还有 ${result.callers.length - 20} 个调用者`);
855
+ }
856
+ }
857
+
858
+ if (result.callees?.length > 0) {
859
+ lines.push(``, `── 被调用者 (它调用了谁, ${result.callees.length} 条) ──`);
860
+ for (const c of result.callees.slice(0, 20)) {
861
+ const indent = ' '.repeat(c.depth);
862
+ const typeTag = c.callType !== 'unknown' ? ` [${c.callType}]` : '';
863
+ lines.push(`${indent}⬇ ${c.callee}${typeTag}`);
864
+ }
865
+ if (result.callees.length > 20) {
866
+ lines.push(` ... 还有 ${result.callees.length - 20} 个被调用者`);
867
+ }
868
+ }
869
+
870
+ return lines.join('\n');
871
+ } catch (err) {
872
+ // 表不存在 → 未 bootstrap
873
+ if (err.message?.includes('no such table')) {
874
+ return '调用图数据不可用 — knowledge_edges 表不存在,请先运行 bootstrap。';
875
+ }
876
+ return `调用图查询失败: ${err.message}`;
877
+ }
878
+ },
879
+ };
@@ -11,7 +11,7 @@ import {
11
11
  refineBootstrapCandidates,
12
12
  summarizeCode,
13
13
  } from './ai-analysis.js';
14
- // ── AST 结构化分析 + Agent Memory (10) ──
14
+ // ── AST 结构化分析 + Agent Memory (11) ──
15
15
  import {
16
16
  getCategoryMap,
17
17
  getClassHierarchy,
@@ -22,6 +22,7 @@ import {
22
22
  getProjectOverview,
23
23
  getProtocolInfo,
24
24
  noteFinding,
25
+ queryCallGraph,
25
26
  queryCodeGraph,
26
27
  } from './ast-graph.js';
27
28
  // ── 组合工具 + 元工具 (6) ──
@@ -158,6 +159,8 @@ export {
158
159
  getPreviousEvidence,
159
160
  // 代码实体图谱
160
161
  queryCodeGraph,
162
+ // 调用图查询 (Phase 5)
163
+ queryCallGraph,
161
164
  };
162
165
 
163
166
  // ── ALL_TOOLS 数组(与原始 tools.js 顺序一致)──
@@ -234,6 +237,8 @@ export const ALL_TOOLS = [
234
237
  getPreviousEvidence,
235
238
  // 代码实体图谱 (1) — Phase E
236
239
  queryCodeGraph,
240
+ // 调用图查询 (1) — Phase 5
241
+ queryCallGraph,
237
242
  ];
238
243
 
239
244
  export default ALL_TOOLS;
@@ -206,6 +206,10 @@ export const bootstrapKnowledgeTool = {
206
206
  type: 'boolean',
207
207
  description: '是否加载 Skills 增强维度定义(推荐开启),默认 true',
208
208
  },
209
+ skipAsyncFill: {
210
+ type: 'boolean',
211
+ description: '跳过异步 AI 填充(CLI 非 --wait 模式下使用,避免 DB 断连)',
212
+ },
209
213
  },
210
214
  },
211
215
  handler: async (params, ctx) => {
@@ -218,6 +222,7 @@ export const bootstrapKnowledgeTool = {
218
222
  skipGuard: params.skipGuard || false,
219
223
  contentMaxLines: params.contentMaxLines || 120,
220
224
  loadSkills: params.loadSkills ?? true,
225
+ skipAsyncFill: params.skipAsyncFill || false,
221
226
  }
222
227
  );
223
228
  // bootstrapKnowledge 返回 envelope JSON string,解析提取 data
@@ -35,11 +35,12 @@ export class CursorDeliveryPipeline {
35
35
  * @param {string} [options.projectName] - 项目名称
36
36
  * @param {Object} [options.logger] - 日志器
37
37
  */
38
- constructor({ knowledgeService, projectRoot, projectName, logger }) {
38
+ constructor({ knowledgeService, projectRoot, projectName, logger, database }) {
39
39
  this.knowledgeService = knowledgeService;
40
40
  this.projectRoot = projectRoot;
41
41
  this.projectName = projectName || this._inferProjectName(projectRoot);
42
42
  this.logger = logger || console;
43
+ this.database = database || null;
43
44
 
44
45
  // 子模块
45
46
  this.compressor = new KnowledgeCompressor();
@@ -87,6 +88,18 @@ export class CursorDeliveryPipeline {
87
88
  const channelB = this._generateChannelB(patterns, facts);
88
89
  stats.channelB = channelB;
89
90
 
91
+ // ── Channel B+: Call Graph Architecture Rules (Phase 5.2) ──
92
+ const archResult = this._generateCallGraphArchitectureRules();
93
+ if (archResult) {
94
+ stats.channelB.topicCount++;
95
+ stats.channelB.totalTokens += archResult.tokensUsed;
96
+ stats.channelB.topics['call-architecture'] = {
97
+ patternsCount: archResult.insightsCount,
98
+ factsCount: 0,
99
+ tokensUsed: archResult.tokensUsed,
100
+ };
101
+ }
102
+
90
103
  // ── Channel C: Skills Sync ──
91
104
  const channelC = await this._generateChannelC();
92
105
  stats.channelC = channelC;
@@ -326,6 +339,159 @@ export class CursorDeliveryPipeline {
326
339
  return result;
327
340
  }
328
341
 
342
+ /**
343
+ * Channel B+ — Call Graph Architecture Rules (Phase 5.2)
344
+ * 从调用图拓扑分析架构分层,生成 architecture smart rule
345
+ * @private
346
+ * @returns {{ insightsCount: number, tokensUsed: number, filePath: string }|null}
347
+ */
348
+ _generateCallGraphArchitectureRules() {
349
+ if (!this.database) return null;
350
+
351
+ try {
352
+ const db = typeof this.database.getDb === 'function' ? this.database.getDb() : this.database;
353
+
354
+ // 查询调用边中的跨目录调用模式
355
+ const callEdges = db.prepare(
356
+ `SELECT from_id, to_id, metadata_json FROM knowledge_edges
357
+ WHERE relation = 'calls' AND metadata_json LIKE '%phase5%'`
358
+ ).all();
359
+
360
+ if (!callEdges || callEdges.length < 5) return null;
361
+
362
+ // 提取 caller/callee 对应的文件路径
363
+ const entityFiles = new Map();
364
+ const entities = db.prepare(
365
+ `SELECT entity_id, file_path FROM code_entities WHERE entity_type = 'method'`
366
+ ).all();
367
+ for (const e of entities) {
368
+ entityFiles.set(e.entity_id, e.file_path);
369
+ }
370
+
371
+ // 构建目录级调用矩阵
372
+ const dirCalls = new Map(); // 'src/controllers' → Map('src/services' → count)
373
+ for (const edge of callEdges) {
374
+ const callerFile = entityFiles.get(edge.from_id);
375
+ const calleeFile = entityFiles.get(edge.to_id);
376
+ if (!callerFile || !calleeFile || callerFile === calleeFile) continue;
377
+
378
+ const callerDir = this._extractLayerDir(callerFile);
379
+ const calleeDir = this._extractLayerDir(calleeFile);
380
+ if (!callerDir || !calleeDir || callerDir === calleeDir) continue;
381
+
382
+ if (!dirCalls.has(callerDir)) dirCalls.set(callerDir, new Map());
383
+ const targets = dirCalls.get(callerDir);
384
+ targets.set(calleeDir, (targets.get(calleeDir) || 0) + 1);
385
+ }
386
+
387
+ if (dirCalls.size === 0) return null;
388
+
389
+ // 检测架构层: 入度高(被调用多)的目录是底层服务, 出度高(调用多)的是上层
390
+ const dirInDegree = new Map();
391
+ const dirOutDegree = new Map();
392
+ for (const [from, targets] of dirCalls) {
393
+ for (const [to, count] of targets) {
394
+ dirOutDegree.set(from, (dirOutDegree.get(from) || 0) + count);
395
+ dirInDegree.set(to, (dirInDegree.get(to) || 0) + count);
396
+ }
397
+ }
398
+
399
+ // 生成架构洞察
400
+ const lines = [];
401
+ lines.push('## Call Graph Architecture');
402
+ lines.push('');
403
+
404
+ // 分层推断: 按 (inDegree - outDegree) 排序, 值越大 = 越底层
405
+ const allDirs = new Set([...dirInDegree.keys(), ...dirOutDegree.keys()]);
406
+ const layers = [...allDirs].map((dir) => ({
407
+ dir,
408
+ inDegree: dirInDegree.get(dir) || 0,
409
+ outDegree: dirOutDegree.get(dir) || 0,
410
+ layerScore: (dirInDegree.get(dir) || 0) - (dirOutDegree.get(dir) || 0),
411
+ })).sort((a, b) => b.layerScore - a.layerScore);
412
+
413
+ // 分层标签
414
+ const total = layers.length;
415
+ let insightsCount = 0;
416
+
417
+ if (total >= 2) {
418
+ lines.push('### Architecture Layers (inferred from call graph)');
419
+ lines.push('');
420
+
421
+ for (let i = 0; i < layers.length && i < 10; i++) {
422
+ const l = layers[i];
423
+ let layerLabel;
424
+ if (i < total * 0.33) layerLabel = '🔽 low-level (service/repository)';
425
+ else if (i < total * 0.66) layerLabel = '↔️ mid-level (business logic)';
426
+ else layerLabel = '🔼 high-level (controller/UI)';
427
+
428
+ lines.push(`- \`${l.dir}/\` — ${layerLabel} (in:${l.inDegree} out:${l.outDegree})`);
429
+ insightsCount++;
430
+ }
431
+ lines.push('');
432
+ }
433
+
434
+ // 核心调用链
435
+ const hotPaths = [...dirCalls.entries()]
436
+ .flatMap(([from, targets]) =>
437
+ [...targets.entries()].map(([to, count]) => ({ from, to, count }))
438
+ )
439
+ .sort((a, b) => b.count - a.count)
440
+ .slice(0, 8);
441
+
442
+ if (hotPaths.length > 0) {
443
+ lines.push('### Key Call Paths');
444
+ lines.push('');
445
+ for (const p of hotPaths) {
446
+ lines.push(`- \`${p.from}/\` → \`${p.to}/\` (${p.count} calls)`);
447
+ insightsCount++;
448
+ }
449
+ lines.push('');
450
+ }
451
+
452
+ if (insightsCount === 0) return null;
453
+
454
+ // 构建 description (用于 smart rule 关联性判断)
455
+ const dirList = layers.map((l) => l.dir).join(', ');
456
+ const description = `Architecture layer analysis for ${this.projectName}. ` +
457
+ `Relevant when editing files in: ${dirList}. ` +
458
+ `Call graph shows ${callEdges.length} cross-file call relationships.`;
459
+
460
+ const body = lines.join('\n');
461
+ const writeResult = this.rulesGenerator.writeSmartRules('call-architecture', body, description);
462
+
463
+ this.logger.info?.(
464
+ `[CursorDelivery] Channel B+: call-architecture — ${insightsCount} insights → ${writeResult.filePath}`
465
+ );
466
+
467
+ return {
468
+ insightsCount,
469
+ tokensUsed: writeResult.tokensUsed,
470
+ filePath: writeResult.filePath,
471
+ };
472
+ } catch (err) {
473
+ this.logger.warn?.(`[CursorDelivery] Call graph architecture rules failed: ${err.message}`);
474
+ return null;
475
+ }
476
+ }
477
+
478
+ /**
479
+ * 从文件路径中提取层级目录 (第一或第二级有意义的目录)
480
+ * @private
481
+ */
482
+ _extractLayerDir(filePath) {
483
+ if (!filePath) return null;
484
+ const parts = filePath.split('/').filter(Boolean);
485
+ // 跳过 src/ lib/ app/ 等通用前缀
486
+ const skipPrefixes = new Set(['src', 'lib', 'app', 'pkg', 'internal', 'cmd']);
487
+ let startIdx = 0;
488
+ if (parts.length > 1 && skipPrefixes.has(parts[0])) {
489
+ startIdx = 1;
490
+ }
491
+ // 取第一个有意义的目录
492
+ return parts[startIdx] || parts[0] || null;
493
+ }
494
+
329
495
  /**
330
496
  * Channel C 生成
331
497
  * @private