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
@@ -271,6 +271,91 @@ export async function runPhase1_6_EntityGraph(astProjectSummary, projectRoot, co
271
271
 
272
272
  // ── Phase 2: 依赖关系 ──────────────────────────────────────
273
273
 
274
+ /**
275
+ * Phase 1.7: 跨文件调用图分析 (Phase 5)
276
+ *
277
+ * 从 AST 的 callSites 构建全局调用图并写入 CodeEntityGraph。
278
+ *
279
+ * @param {object|null} astProjectSummary — AST 分析结果 (含 fileSummaries[].callSites)
280
+ * @param {string} projectRoot
281
+ * @param {object} container — ServiceContainer
282
+ * @param {object} logger
283
+ * @param {object} [incrementalOpts] — 增量分析选项
284
+ * @param {string[]} [incrementalOpts.changedFiles] — 变更文件的相对路径
285
+ * @returns {Promise<{ callGraphResult: object|null, warnings: string[] }>}
286
+ */
287
+ export async function runPhase1_7_CallGraph(astProjectSummary, projectRoot, container, logger, incrementalOpts = null) {
288
+ const warnings = [];
289
+ let callGraphResult = null;
290
+
291
+ if (!astProjectSummary?.fileSummaries?.length) {
292
+ return { callGraphResult, warnings };
293
+ }
294
+
295
+ // 检查是否有 callSites 数据 (Phase 5 提取)
296
+ const hasCallSites = astProjectSummary.fileSummaries.some(
297
+ (f) => f.callSites && f.callSites.length > 0
298
+ );
299
+ if (!hasCallSites) {
300
+ logger.info('[Bootstrap] Call Graph skipped: no call sites extracted');
301
+ return { callGraphResult, warnings };
302
+ }
303
+
304
+ try {
305
+ const { CallGraphAnalyzer } = await import('../../../../../core/analysis/CallGraphAnalyzer.js');
306
+ const { CodeEntityGraph } = await import('../../../../../service/knowledge/CodeEntityGraph.js');
307
+
308
+ const analyzer = new CallGraphAnalyzer(projectRoot);
309
+ const changedFiles = incrementalOpts?.changedFiles;
310
+ const isIncremental = changedFiles?.length > 0 && changedFiles.length <= 10;
311
+
312
+ // Phase 5 分析 (带超时保护 + 渐进式 partial result)
313
+ const result = isIncremental
314
+ ? await analyzer.analyzeIncremental(astProjectSummary, changedFiles, {
315
+ timeout: 15_000,
316
+ maxCallSitesPerFile: 500,
317
+ minConfidence: 0.5,
318
+ })
319
+ : await analyzer.analyze(astProjectSummary, {
320
+ timeout: 15_000,
321
+ maxCallSitesPerFile: 500,
322
+ minConfidence: 0.5,
323
+ });
324
+
325
+ // 写入 CodeEntityGraph
326
+ const db = container.get('database');
327
+ if (db && result && result.callEdges.length > 0) {
328
+ const ceg = new CodeEntityGraph(db, { projectRoot });
329
+
330
+ // 增量模式: 先删除变更文件的旧边
331
+ if (isIncremental) {
332
+ ceg.clearCallGraphForFiles(changedFiles);
333
+ }
334
+
335
+ callGraphResult = ceg.populateCallGraph(result.callEdges, result.dataFlowEdges);
336
+
337
+ const partialTag = result.stats.partial ? ' [partial]' : '';
338
+ const incrTag = isIncremental ? ' [incremental]' : '';
339
+ logger.info(
340
+ `[Bootstrap] Call Graph${incrTag}${partialTag}: ${result.callEdges.length} call edges, ` +
341
+ `${result.dataFlowEdges.length} data flow edges, ` +
342
+ `resolution rate: ${(result.stats.resolvedRate * 100).toFixed(1)}%`
343
+ );
344
+ } else if (result) {
345
+ logger.info(
346
+ `[Bootstrap] Call Graph: ${result.stats.totalCallSites} call sites, 0 resolved edges`
347
+ );
348
+ }
349
+ } catch (e) {
350
+ logger.warn(`[Bootstrap] Call Graph failed (degraded): ${e.message}`);
351
+ warnings.push(`Call Graph failed: ${e.message}`);
352
+ }
353
+
354
+ return { callGraphResult, warnings };
355
+ }
356
+
357
+ // ── Phase 2: 依赖关系 ──────────────────────────────────────
358
+
274
359
  /**
275
360
  * Phase 2: 获取依赖图并写入 knowledge_edges
276
361
  *
@@ -530,7 +615,7 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
530
615
  // ── 清除旧数据 (if requested) ──
531
616
  if (options.clearOldData) {
532
617
  try {
533
- const { clearCheckpoints, clearSnapshots } = await import('../../pipeline/orchestrator.js');
618
+ const { clearCheckpoints, clearSnapshots } = await import('../pipeline/orchestrator.js');
534
619
  await clearCheckpoints(projectRoot);
535
620
  await clearSnapshots(projectRoot, ctx);
536
621
  ctx.logger.info('[Bootstrap] Cleared old checkpoints and snapshots');
@@ -551,10 +636,12 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
551
636
  allFiles, langStats,
552
637
  primaryLang: null, discoverer, allTargets,
553
638
  astProjectSummary: null, astContext: '',
554
- codeEntityResult: null, depGraphData: null,
639
+ codeEntityResult: null, callGraphResult: null,
640
+ depGraphData: null, depEdgesWritten: 0,
555
641
  guardAudit: null, guardEngine: null,
556
642
  activeDimensions: [], enhancementPackInfo: [],
557
- enhancementPatterns: [], langProfile: {},
643
+ enhancementPatterns: [], enhancementGuardRules: [],
644
+ langProfile: {},
558
645
  targetsSummary: [], warnings,
559
646
  report: report || {},
560
647
  incrementalPlan: null,
@@ -567,12 +654,11 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
567
654
  let incrementalPlan = null;
568
655
  if (options.incremental) {
569
656
  try {
570
- const { IncrementalBootstrap } = await import('../../pipeline/IncrementalBootstrap.js');
657
+ const { IncrementalBootstrap } = await import('../pipeline/IncrementalBootstrap.js');
571
658
  const db = ctx.container?.resolve?.('db') ?? ctx.db;
572
659
  if (db) {
573
660
  const ib = new IncrementalBootstrap(db, projectRoot, { logger: ctx.logger });
574
- const dimIds = (await import('./dimension-configs.js')).getDimensionConfigs?.()
575
- ?.map(d => d.id) || [];
661
+ const dimIds = baseDimensions.map(d => d.id);
576
662
  incrementalPlan = await ib.evaluate(allFiles, dimIds);
577
663
  if (report) report.phases.incremental = { plan: incrementalPlan };
578
664
  ctx.logger.info(`[Bootstrap] Incremental mode: ${incrementalPlan.mode}, affected: ${incrementalPlan.affectedDimensions?.length || 0}`);
@@ -598,7 +684,15 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
598
684
  phase1_5.astProjectSummary, projectRoot, ctx.container, ctx.logger
599
685
  );
600
686
  warnings.push(...phase1_6.warnings);
601
- if (report) report.phases.entityGraph = { entityCount: phase1_6.codeEntityResult?.entities?.length || 0, ms: Date.now() - p16Start };
687
+ if (report) report.phases.entityGraph = { entityCount: phase1_6.codeEntityResult?.entitiesUpserted || 0, edgeCount: phase1_6.codeEntityResult?.edgesCreated || 0, ms: Date.now() - p16Start };
688
+
689
+ // ── Phase 1.7: Call Graph (Phase 5) ──
690
+ const p17Start = Date.now();
691
+ const phase1_7 = await runPhase1_7_CallGraph(
692
+ phase1_5.astProjectSummary, projectRoot, ctx.container, ctx.logger
693
+ );
694
+ warnings.push(...phase1_7.warnings);
695
+ if (report) report.phases.callGraph = { result: phase1_7.callGraphResult, ms: Date.now() - p17Start };
602
696
 
603
697
  // ── Phase 2: 依赖图 ──
604
698
  const p2Start = Date.now();
@@ -659,6 +753,7 @@ export async function runAllPhases(projectRoot, ctx, options = {}) {
659
753
  astProjectSummary: phase1_5.astProjectSummary,
660
754
  astContext: phase1_5.astContext,
661
755
  codeEntityResult: phase1_6.codeEntityResult,
756
+ callGraphResult: phase1_7.callGraphResult,
662
757
  depGraphData: phase2.depGraphData,
663
758
  depEdgesWritten: phase2.depEdgesWritten,
664
759
  guardAudit: finalGuardAudit,
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * 参考:
8
8
  * - MetaGPT SOP 驱动模式 (docs/design/external-agent-quality-gap.md §4.2)
9
- * - 内部 Agent 的 AnalystAgent + ProducerAgent 双阶段流程
9
+ * - 内部 Agent 的 PipelineStrategy (Analyze QualityGate → Produce → RejectionGate)
10
10
  *
11
11
  * 设计原则:
12
12
  * - 每个维度 4 个阶段: 扫描 → 验证 → 异常检测 → 提交
@@ -75,6 +75,9 @@ export async function bootstrapExternal(ctx) {
75
75
  contentMaxLines: 120,
76
76
  sourceTag: 'bootstrap-external',
77
77
  summaryPrefix: 'Bootstrap-external scan',
78
+ clearOldData: true,
79
+ generateReport: true,
80
+ incremental: true,
78
81
  });
79
82
 
80
83
  // 空项目 fast-path
@@ -88,9 +91,9 @@ export async function bootstrapExternal(ctx) {
88
91
 
89
92
  const {
90
93
  allFiles, primaryLang, depGraphData, langStats,
91
- astProjectSummary, codeEntityResult, guardAudit,
94
+ astProjectSummary, codeEntityResult, callGraphResult, guardAudit,
92
95
  activeDimensions: dimensions, targetsSummary,
93
- langProfile,
96
+ langProfile, incrementalPlan,
94
97
  } = phaseResults;
95
98
 
96
99
  // ═══════════════════════════════════════════════════════════
@@ -115,6 +118,7 @@ export async function bootstrapExternal(ctx) {
115
118
  allFiles,
116
119
  astProjectSummary,
117
120
  codeEntityResult,
121
+ callGraphResult,
118
122
  depGraphData,
119
123
  guardAudit,
120
124
  langStats,
@@ -138,12 +142,15 @@ export async function bootstrapExternal(ctx) {
138
142
  projectMeta,
139
143
  astData: astProjectSummary,
140
144
  codeEntityResult,
145
+ callGraphResult,
141
146
  depGraphData,
142
147
  guardAudit,
143
148
  targets: targetsSummary,
144
149
  activeDimensions: dimensions,
145
150
  session,
146
151
  languageExtension: buildLanguageExtension(primaryLang), // §7.1
152
+ incrementalPlan,
153
+ languageStats: langStats,
147
154
  });
148
155
 
149
156
  // 附加 warnings
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * 调用方:
8
8
  * - CLI: `asd bootstrap --knowledge`
9
- * - ChatAgent: `bootstrapKnowledgeTool` (infrastructure.js)
9
+ * - AgentRuntime: `bootstrapKnowledgeTool` (infrastructure.js)
10
10
  * - Dashboard HTTP: POST /api/bootstrap/knowledge
11
11
  *
12
12
  * 外部 Agent 路径(Cursor/Copilot 等 IDE Agent)请参见:
@@ -87,6 +87,7 @@ export async function bootstrapKnowledge(ctx, args) {
87
87
  const maxFiles = args.maxFiles || 500;
88
88
  const skipGuard = args.skipGuard || false;
89
89
  const contentMaxLines = args.contentMaxLines || 120;
90
+ const skipAsyncFill = args.skipAsyncFill || false;
90
91
 
91
92
  // ═══════════════════════════════════════════════════════════
92
93
  // Phase 1-4: 共享管线(文件收集→AST→依赖→Guard→维度解析)
@@ -149,7 +150,12 @@ export async function bootstrapKnowledge(ctx, args) {
149
150
  categories: astProjectSummary?.categories?.length || 0,
150
151
  patterns: Object.keys(astProjectSummary?.patternStats || {}),
151
152
  },
152
- codeEntityGraph: phaseReport?.phases?.entityGraph || { entities: 0, edges: 0, durationMs: 0 },
153
+ codeEntityGraph: phaseReport?.phases?.entityGraph || { entityCount: 0, edgeCount: 0, ms: 0 },
154
+ callGraph: phaseReport?.phases?.callGraph ? {
155
+ entities: phaseReport.phases.callGraph.result?.entitiesUpserted || 0,
156
+ edges: phaseReport.phases.callGraph.result?.edgesCreated || 0,
157
+ ms: phaseReport.phases.callGraph.ms || 0,
158
+ } : { entities: 0, edges: 0, ms: 0 },
153
159
  dependencyGraph: { edgesWritten: depEdgesWritten || 0 },
154
160
  enhancementPacks: {
155
161
  matched: enhancementPackInfo,
@@ -274,7 +280,7 @@ export async function bootstrapKnowledge(ctx, args) {
274
280
  expectedOutput: `候选知识(微观代码维度:code-pattern/best-practice/event-and-data-flow + 语言条件扫描)+ Project Skills(宏观叙事维度:code-standard/architecture/project-profile/agent-guidelines + 语言条件扫描)— 共 ${dimensions.length} 个维度`,
275
281
  },
276
282
 
277
- // AST 代码结构分析上下文(供 ChatAgent 使用)
283
+ // AST 代码结构分析上下文(供 Agent 使用)
278
284
  astContext: astContext || null,
279
285
  astSummary: astProjectSummary
280
286
  ? {
@@ -371,12 +377,17 @@ export async function bootstrapKnowledge(ctx, args) {
371
377
  };
372
378
 
373
379
  // 使用 setImmediate 避免阻塞 HTTP 响应
374
- setImmediate(() => {
375
- ctx.logger.info(`[Bootstrap] Dispatching v3 AI-First pipeline`);
376
- fillDimensionsV3(fillContext).catch((e) => {
377
- ctx.logger.error(`[Bootstrap] Async fill (v3) failed: ${e.message}`);
380
+ // skipAsyncFill: CLI 非 --wait 模式跳过异步填充,避免进程退出后 DB 断连
381
+ if (!skipAsyncFill) {
382
+ setImmediate(() => {
383
+ ctx.logger.info(`[Bootstrap] Dispatching v3 AI-First pipeline`);
384
+ fillDimensionsV3(fillContext).catch((e) => {
385
+ ctx.logger.error(`[Bootstrap] Async fill (v3) failed: ${e.message}`);
386
+ });
378
387
  });
379
- });
388
+ } else {
389
+ ctx.logger.info(`[Bootstrap] Async fill skipped (skipAsyncFill=true)`);
390
+ }
380
391
 
381
392
  // ── SkillHooks: onBootstrapStarted (fire-and-forget) ──
382
393
  try {
@@ -99,6 +99,15 @@ export async function consolidatedStructure(ctx, args) {
99
99
  }
100
100
  }
101
101
 
102
+ // ─── autosnippet_call_context (Phase 5) ─────────────────────
103
+
104
+ /**
105
+ * 调用链上下文查询:直接转发到 structure.callContext
106
+ */
107
+ export async function consolidatedCallContext(ctx, args) {
108
+ return structureHandlers.callContext(ctx, args);
109
+ }
110
+
102
111
  // ─── autosnippet_graph (整合 4 → 1) ─────────────────────────
103
112
 
104
113
  /**
@@ -291,7 +291,7 @@ export async function dimensionComplete(ctx, args) {
291
291
  if (isComplete) {
292
292
  // R4: 自动触发 Cursor Delivery
293
293
  try {
294
- const { getServiceContainer } = await import('../../../../injection/ServiceContainer.js');
294
+ const { getServiceContainer } = await import('../../../injection/ServiceContainer.js');
295
295
  const container = getServiceContainer();
296
296
  if (container.services.cursorDeliveryPipeline) {
297
297
  const pipeline = container.get('cursorDeliveryPipeline');
@@ -311,9 +311,9 @@ export async function dimensionComplete(ctx, args) {
311
311
  // R5: 自动触发 Wiki 生成 (fire-and-forget)
312
312
  setImmediate(async () => {
313
313
  try {
314
- const { getServiceContainer: getWikiContainer } = await import('../../../../injection/ServiceContainer.js');
314
+ const { getServiceContainer: getWikiContainer } = await import('../../../injection/ServiceContainer.js');
315
315
  const wikiContainer = getWikiContainer();
316
- const { WikiGenerator } = await import('../../../../service/wiki/WikiGenerator.js');
316
+ const { WikiGenerator } = await import('../../../service/wiki/WikiGenerator.js');
317
317
  const moduleService = wikiContainer.get?.('moduleService');
318
318
  const knowledgeService = wikiContainer.get?.('knowledgeService');
319
319
  if (moduleService && knowledgeService) {
@@ -334,11 +334,11 @@ export async function dimensionComplete(ctx, args) {
334
334
  // R6: 全量 Semantic Memory 固化 (fire-and-forget)
335
335
  setImmediate(async () => {
336
336
  try {
337
- const { EpisodicConsolidator } = await import('../../../service/chat/EpisodicConsolidator.js');
337
+ const { EpisodicConsolidator } = await import('../../../service/agent/domain/EpisodicConsolidator.js');
338
338
  const db = ctx.container.get?.('database') ?? ctx.container.get?.('db');
339
339
  if (db && session.sessionStore) {
340
- const { ProjectSemanticMemory } = await import('../../../service/chat/ProjectSemanticMemory.js');
341
- const semanticMemory = new ProjectSemanticMemory(db, { logger });
340
+ const { PersistentMemory } = await import('../../../service/agent/memory/PersistentMemory.js');
341
+ const semanticMemory = new PersistentMemory(db, { logger });
342
342
  const consolidator = new EpisodicConsolidator(semanticMemory, { logger });
343
343
  const result = await consolidator.consolidate(session.sessionStore, {
344
344
  bootstrapSession: session.id,
@@ -416,14 +416,14 @@ export async function scanProject(ctx, args) {
416
416
 
417
417
  const projectRoot = process.env.ASD_PROJECT_DIR || process.cwd();
418
418
 
419
- // 优先使用 ModuleService(多语言统一入口),回退到 SpmService
419
+ // 优先使用 ModuleService(多语言统一入口),回退到 SpmHelper
420
420
  let service;
421
421
  try {
422
422
  const { ModuleService } = await import('../../../service/module/ModuleService.js');
423
423
  service = new ModuleService(projectRoot);
424
424
  } catch {
425
- const { SpmService } = await import('../../../platform/ios/spm/SpmService.js');
426
- service = new SpmService(projectRoot);
425
+ const { SpmHelper } = await import('../../../platform/ios/spm/SpmHelper.js');
426
+ service = new SpmHelper(projectRoot);
427
427
  }
428
428
  await service.load();
429
429
  const allTargets = await service.listTargets();
@@ -527,6 +527,68 @@ async function _fallbackPathFromRecipe(ctx, fromId, toId) {
527
527
  }
528
528
  }
529
529
 
530
+ // ─── call_context — 调用链上下文 (Phase 5) ──────────────────
531
+
532
+ /**
533
+ * autosnippet_call_context handler
534
+ * 查询方法的调用者、被调用者、影响半径
535
+ */
536
+ export async function callContext(ctx, args) {
537
+ if (!args.methodName) {
538
+ throw new Error('Missing required parameter: methodName');
539
+ }
540
+
541
+ const ceg = ctx.container.get('codeEntityGraph');
542
+ if (!ceg) {
543
+ return envelope({
544
+ success: false,
545
+ message: 'CodeEntityGraph not available — 请先运行 bootstrap',
546
+ meta: { tool: 'autosnippet_call_context' },
547
+ });
548
+ }
549
+
550
+ const direction = args.direction || 'both';
551
+ const maxDepth = Math.min(Math.max(args.maxDepth ?? 2, 1), 5);
552
+ const result = {};
553
+
554
+ try {
555
+ if (direction === 'callers' || direction === 'both') {
556
+ result.callers = ceg.getCallers(args.methodName, maxDepth);
557
+ }
558
+ if (direction === 'callees' || direction === 'both') {
559
+ result.callees = ceg.getCallees(args.methodName, maxDepth);
560
+ }
561
+ if (direction === 'impact') {
562
+ result.impact = ceg.getCallImpactRadius(args.methodName);
563
+ }
564
+ } catch (err) {
565
+ if (err.message?.includes('no such table')) {
566
+ return envelope({
567
+ success: true,
568
+ data: {
569
+ methodName: args.methodName,
570
+ callers: [],
571
+ callees: [],
572
+ note: 'knowledge_edges 表不存在,请运行 bootstrap 后再查询',
573
+ },
574
+ meta: { tool: 'autosnippet_call_context' },
575
+ });
576
+ }
577
+ throw err;
578
+ }
579
+
580
+ return envelope({
581
+ success: true,
582
+ data: {
583
+ methodName: args.methodName,
584
+ direction,
585
+ maxDepth,
586
+ ...result,
587
+ },
588
+ meta: { tool: 'autosnippet_call_context' },
589
+ });
590
+ }
591
+
530
592
  // ─── graph_stats — 图谱统计 ────────────────────────────────
531
593
 
532
594
  export async function graphStats(ctx) {
@@ -60,10 +60,73 @@ export async function wikiPlan(ctx, args) {
60
60
  const session = getActiveSession(container, args.sessionId);
61
61
  const cachedData = session?.phaseCache;
62
62
 
63
- if (cachedData?.projectInfo && cachedData?.astInfo) {
64
- ({ projectInfo, astInfo, moduleInfo, knowledgeInfo } = cachedData);
63
+ if (cachedData?.astProjectSummary) {
64
+ // Bootstrap phase cache WikiGenerator-compatible format 转换
65
+ const allFiles = cachedData.allFiles || [];
66
+ const ast = cachedData.astProjectSummary;
67
+
68
+ // projectInfo: 从 bootstrap 文件列表和语言统计构建
69
+ const filesByModule = {};
70
+ for (const f of allFiles) {
71
+ const mod = f.targetName || '_default';
72
+ if (!filesByModule[mod]) filesByModule[mod] = [];
73
+ filesByModule[mod].push(f.relativePath);
74
+ }
75
+ projectInfo = {
76
+ name: path.basename(projectRoot),
77
+ root: projectRoot,
78
+ sourceFiles: allFiles.map(f => f.relativePath),
79
+ languages: cachedData.langStats || {},
80
+ primaryLanguage: cachedData.primaryLang || 'unknown',
81
+ sourceFilesByModule: filesByModule,
82
+ buildSystems: [],
83
+ };
84
+
85
+ // astInfo: 从 AstAnalyzer 结果构建
86
+ const classesByModule = {};
87
+ const protocolsByModule = {};
88
+ for (const cls of ast.classes || []) {
89
+ const mod = cls.targetName || '_default';
90
+ if (!classesByModule[mod]) classesByModule[mod] = [];
91
+ classesByModule[mod].push(cls.name);
92
+ }
93
+ for (const p of ast.protocols || []) {
94
+ const mod = p.targetName || '_default';
95
+ if (!protocolsByModule[mod]) protocolsByModule[mod] = [];
96
+ protocolsByModule[mod].push(p.name);
97
+ }
98
+ astInfo = {
99
+ classes: (ast.classes || []).map(c => c.name),
100
+ protocols: (ast.protocols || []).map(p => p.name),
101
+ overview: ast.projectMetrics || null,
102
+ classNamesByModule: classesByModule,
103
+ protocolNamesByModule: protocolsByModule,
104
+ };
105
+
106
+ // moduleInfo: 从依赖图和 targets 构建
107
+ moduleInfo = {
108
+ targets: (cachedData.targetsSummary || []).map(t => ({
109
+ name: t.name, type: t.type, fileCount: t.fileCount,
110
+ })),
111
+ depGraph: cachedData.depGraphData || null,
112
+ };
113
+
114
+ // knowledgeInfo: 始终从 DB 获取最新(bootstrap 期间可能已写入知识)
115
+ try {
116
+ const ks = tryGet(container, 'knowledgeService');
117
+ if (ks) {
118
+ const items = await ks.list({ limit: 200 });
119
+ const stats = typeof ks.getStats === 'function' ? await ks.getStats() : null;
120
+ knowledgeInfo = { recipes: items?.items || items || [], stats };
121
+ } else {
122
+ knowledgeInfo = { recipes: [], stats: null };
123
+ }
124
+ } catch {
125
+ knowledgeInfo = { recipes: [], stats: null };
126
+ }
127
+
65
128
  cacheHit = true;
66
- logger.info('[wiki-plan] Reusing bootstrap phase cache');
129
+ logger.info('[wiki-plan] Reusing bootstrap phase cache (converted to WikiGenerator format)');
67
130
  } else {
68
131
  // 无缓存(独立调用 wiki_plan 或进程已重启)→ 重新扫描
69
132
  logger.info('[wiki-plan] No bootstrap cache, running fresh scan...');
@@ -210,7 +210,42 @@ export const TOOLS = [
210
210
  },
211
211
  },
212
212
 
213
- // 6. Guard 检查(统一入口)
213
+ // 6. 调用链上下文 (Phase 5)
214
+ {
215
+ name: 'autosnippet_call_context',
216
+ tier: 'agent',
217
+ description:
218
+ '查询方法的调用链上下文。\n' +
219
+ '• callers: 谁调用了这个方法?(调用者链)\n' +
220
+ '• callees: 这个方法调用了谁?(依赖链)\n' +
221
+ '• impact: 修改此方法的影响半径分析\n' +
222
+ '• both: 同时获取调用者和被调用者',
223
+ inputSchema: {
224
+ type: 'object',
225
+ properties: {
226
+ methodName: {
227
+ type: 'string',
228
+ description: '方法名 (e.g. "UserService.getUser" 或 "getUser")',
229
+ },
230
+ direction: {
231
+ type: 'string',
232
+ enum: ['callers', 'callees', 'both', 'impact'],
233
+ default: 'both',
234
+ description: 'callers=调用者 | callees=被调用者 | both=双向 | impact=影响分析',
235
+ },
236
+ maxDepth: {
237
+ type: 'number',
238
+ default: 2,
239
+ minimum: 1,
240
+ maximum: 5,
241
+ description: '最大遍历深度',
242
+ },
243
+ },
244
+ required: ['methodName'],
245
+ },
246
+ },
247
+
248
+ // 7. Guard 检查(统一入口)
214
249
  {
215
250
  name: 'autosnippet_guard',
216
251
  tier: 'agent',
@@ -36,7 +36,7 @@ import recipesRouter from './routes/recipes.js';
36
36
  import searchRouter from './routes/search.js';
37
37
  import skillsRouter from './routes/skills.js';
38
38
  import snippetRouter from './routes/snippets.js';
39
- import spmRouter from './routes/spm.js';
39
+ import spmRouter from '../platform/ios/routes/spm.js';
40
40
  import taskRouter from './routes/task.js';
41
41
  import remoteRouter from './routes/remote.js';
42
42
  import violationsRouter from './routes/violations.js';
@@ -20,6 +20,7 @@ const SILENT_PATHS = [
20
20
  '/api/health',
21
21
  '/api/realtime/events',
22
22
  '/api/sse',
23
+ '/api/v1/remote/wait',
23
24
  '/socket.io',
24
25
  ];
25
26