UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

172 lines (171 loc) 8.47 kB
/** * MCP Handler — 外部 Agent 驱动的 Bootstrap (External-Agent-Driven) * * `autosnippet_bootstrap` 的主入口(无参数): * Phase 1-4 同步执行(文件收集 / AST / 依赖图 / Guard) * → 构建 Mission Briefing 一次性返回 * → 不启动 Phase 5 异步 AI pipeline * → 等待外部 Agent (Cursor/Copilot) 主动提交知识 + 完成维度 * * 与 bootstrap-internal.js 的关系: * - 本文件: 外部 Agent 路径 — Agent 自己分析代码 + 提交知识,不需要 AI Provider * - bootstrap-internal.js: 内部 Agent 路径 — 内置 Analyst/Producer pipeline,需要 API Key * - 两者共享 Phase 1-4 的分析逻辑 → bootstrap/shared/bootstrap-phases.js * * @module handlers/bootstrap-external */ import path from 'node:path'; import { CleanupService } from '#service/cleanup/CleanupService.js'; import { resolveProjectRoot } from '#shared/resolveProjectRoot.js'; import { buildProjectSnapshot } from '#types/project-snapshot-builder.js'; import { toSessionCache } from '#types/snapshot-views.js'; import { envelope } from '../envelope.js'; import { buildMissionBriefing } from './bootstrap/MissionBriefingBuilder.js'; import { runAllPhases } from './bootstrap/shared/bootstrap-phases.js'; import { getOrCreateSessionManager } from './bootstrap/shared/session-helpers.js'; import { buildLanguageExtension } from './LanguageExtensions.js'; // ── 主入口 ───────────────────────────────────────────────────── /** * bootstrapExternal — 外部 Agent 驱动的一键冷启动 * * 无参数调用,返回 Mission Briefing。 * Phase 1-4 复用现有 bootstrap.js 逻辑,Phase 5 不启动。 * * @param ctx { container, logger, startedAt } * @returns envelope({ success, data: MissionBriefing }) */ export async function bootstrapExternal(ctx) { const t0 = Date.now(); const projectRoot = resolveProjectRoot(ctx.container); // ═══════════════════════════════════════════════════════════ // Step 1: 全量清理 (CleanupService.fullReset) // ═══════════════════════════════════════════════════════════ const db = ctx.container.get('database'); const cleanupService = new CleanupService({ projectRoot, db, logger: ctx.logger, }); const cleanupResult = await cleanupService.fullReset(); // ═══════════════════════════════════════════════════════════ // Phase 1-4: 共享数据收集管线(永远全量,无增量检测) // ═══════════════════════════════════════════════════════════ const phaseResults = await runAllPhases(projectRoot, ctx, { maxFiles: 500, contentMaxLines: 120, sourceTag: 'bootstrap-external', summaryPrefix: 'Bootstrap-external scan', clearOldData: true, generateReport: true, incremental: false, }); // 空项目 fast-path if (phaseResults.isEmpty) { return envelope({ success: true, data: { message: 'No source files found. Nothing to bootstrap.' }, meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 }, }); } const { allFiles, primaryLang, depGraphData, langStats, astProjectSummary, codeEntityResult, callGraphResult, guardAudit, activeDimensions: dimensions, targetsSummary, localPackageModules, langProfile, } = phaseResults; // ── Build immutable ProjectSnapshot ── const snapshot = buildProjectSnapshot({ projectRoot, sourceTag: 'bootstrap-external', ...phaseResults, report: phaseResults.report, }); // ═══════════════════════════════════════════════════════════ // Phase 4: 构建 Mission Briefing // ═══════════════════════════════════════════════════════════ // 创建 BootstrapSession const sessionManager = getOrCreateSessionManager(ctx.container); const session = sessionManager.createSession({ projectRoot, dimensions, projectContext: { projectName: path.basename(projectRoot), primaryLang, fileCount: allFiles.length, modules: depGraphData?.nodes?.length || 0, }, }); // 缓存 Phase 结果供 wiki_plan 复用 session.setSnapshotCache(toSessionCache(snapshot)); // 构建 projectMeta const projectMeta = { name: path.basename(projectRoot), primaryLanguage: primaryLang, secondaryLanguages: langProfile.secondary || [], isMultiLang: langProfile.isMultiLang || false, fileCount: allFiles.length, projectType: snapshot.discoverer.id, projectRoot, }; // 构建 Mission Briefing const briefing = buildMissionBriefing({ projectMeta, astData: astProjectSummary, codeEntityResult, callGraphResult, depGraphData, guardAudit, targets: targetsSummary, activeDimensions: dimensions, session, languageExtension: buildLanguageExtension(primaryLang), // §7.1 languageStats: langStats, panoramaResult: snapshot.panorama, // §M1: Phase 2.2 全景数据 localPackageModules, // 本地子包模块信息 }); // 附加 warnings if (phaseResults.warnings.length > 0) { briefing.meta = briefing.meta || {}; briefing.meta.warnings = [...(briefing.meta.warnings || []), ...phaseResults.warnings]; } ctx.logger.info(`[BootstrapExternal] Mission Briefing ready: ${allFiles.length} files, ${dimensions.length} dims, ` + `${briefing.meta?.responseSizeKB || '?'}KB — session ${session.id}`); return envelope({ success: true, data: { cleanup: { deletedRecipes: cleanupResult.deletedFiles, clearedTables: cleanupResult.clearedTables.length, dbCleared: true, errors: cleanupResult.errors, trash: cleanupResult.trash ?? null, purgedTrash: cleanupResult.purgedTrash ?? null, }, ...briefing, }, message: `⚠️ Bootstrap 仅完成第一步(项目扫描),你必须继续完成全部 ${dimensions.length} 个维度的分析。` + `请立即按 executionPlan.tiers 的顺序,对每个维度执行:` + `(1) 用你的代码阅读能力分析该维度相关文件 → ` + `(2) 调用 autosnippet_submit_knowledge_batch 提交候选知识(**每维度最少 3 条,目标 5 条**,不同关注点拆为独立候选) → ` + `(3) 调用 autosnippet_dimension_complete 标记维度完成。` + `不要停下来等待用户确认,直接开始第一个维度。`, meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 }, }); } /** * 获取当前 active session(供其他 handler 使用) * * 当指定了 sessionId 时,如果 active session 已过期但 id 匹配, * 仍然返回该 session(支持新 bootstrap 创建后旧 session 的 dimension_complete 继续工作)。 */ export function getActiveSession(container, sessionId) { const mgr = getOrCreateSessionManager(container); const session = mgr.getSession(sessionId); if (session) { return session; } // 当指定了 sessionId 但 active session 已过期/被替换时, // 尝试用 getAnySession() 恢复 — 防止正在进行的维度完成调用因新 bootstrap 而失败 if (sessionId) { const anySession = mgr.getAnySession(); if (anySession && anySession.id === sessionId) { return anySession; } } return null; }