import { join } from "node:path"; import type { ActiveRun } from "./types.ts"; import { nextPhaseForRun, phaseOrderForRun } from "./types.ts"; import { runsDir } from "./paths.ts"; import { inspectGate } from "./gates.ts"; import { formatProductProfile, isKnownProduct, profileForProduct, resolveProductProfile } from "../product/profile.ts"; import { buildControlFrame } from "./control-frame.ts"; import type { KdNextAction } from "./next-action.ts"; export interface FormatStatusOptions { detail?: boolean; } export function formatStatus(cwd: string, run: ActiveRun | undefined, options: FormatStatusOptions = {}): string { if (!run) { return ["当前没有正在处理的需求。", "", "开始方式:", "/kd <需求>"].join("\n"); } const refreshed = { ...run, gate: inspectGate(cwd, run) }; const next = nextPhaseForRun(refreshed) ?? "done"; const frame = buildControlFrame(cwd, refreshed, "status"); const summary = [ refreshed.goal ? `需求:${refreshed.goal}` : undefined, `阶段:${refreshed.phase}`, `产品:${formatStatusProduct(refreshed)}`, `检查:${refreshed.gate.passed ? "通过" : "未通过"}`, refreshed.gate.reason ? `原因:${refreshed.gate.reason}` : undefined, `下一步:${formatNextActionSummary(frame.nextAction)}`, `建议命令:${suggestedCommand(frame.nextAction, frame.openQuestion?.id, refreshed)}`, ] .filter(Boolean) .join("\n"); if (!options.detail) return summary; return [ summary, "", "详情:", `需求ID:${refreshed.id}`, `状态:${refreshed.status ?? "active"}`, `模式:${refreshed.mode}(${phaseOrderForRun(refreshed).join(" -> ")})`, `产品详情:${formatProductProfile(refreshed.profile ?? profileForProduct(refreshed.product))}`, `下一阶段:${next}`, `版本:${refreshed.version ?? "未选择"}`, `风险:${formatRisk(refreshed)}`, `工作目录:${join(runsDir(cwd), refreshed.id)}`, ].join("\n"); } function formatRisk(run: ActiveRun): string { const level = run.riskAssessment?.level ?? "未知"; const reason = run.riskAssessment?.reason?.trim(); return reason ? `${level}(${reason})` : level; } function formatStatusProduct(run: ActiveRun): string { const profile = run.profile ?? profileForProduct(run.product); if (!isKnownProduct(profile.product)) return "未确认"; return profile.displayName; } function formatNextActionSummary(action: KdNextAction): string { if (action.kind === "answer-question") return action.reason; if (action.kind === "advance-phase" && action.targetPhase) return `进入 ${action.targetPhase} 阶段。`; if (action.kind === "collect-metadata-evidence") return `补齐元数据证据${action.evidencePath ? `:${action.evidencePath}` : ""}。`; if (action.kind === "handle-gate") return action.reason; if (action.kind === "fix-consistency") return "修复运行状态一致性问题。"; if (action.kind === "resume-snapshot") return action.reason; return action.instruction; } function suggestedCommand(action: KdNextAction, questionId: string | undefined, run: ActiveRun): string { if (action.kind === "answer-question") return `/kd answer ${questionId ?? "Q-001"} <答案>`; if (action.kind === "advance-phase") return "/kd"; if (action.kind === "collect-metadata-evidence") return "/kd"; if (action.kind === "handle-gate") return suggestedGateCommand(action.reason, run); if (action.kind === "phase-work") return "/kd"; if (action.kind === "resume-snapshot") return "/kd"; return "/kd status"; } function suggestedGateCommand(reason: string, run: ActiveRun): string { if (/必须使用 normal 模式|当前任务包含/.test(reason)) return "/kd normal"; if (/产品未知|确认产品|产品类型/.test(reason)) return suggestedProductCommand(run); if (/风险等级|风险原因|残余风险/.test(reason)) return "/kd risk low <原因>"; if (/缺少必需产物|缺少 .*?(CONTEXT|SPEC|PLAN|EXECUTION|VERIFY|SHIP)\.md|阶段文档/i.test(reason)) return "/kd doc"; if (/evidence\/verify-pass\.md|真实验证命令|kd_verify_result/i.test(reason)) return "/kd verify "; if (/evidence\/sdk-signature\.md/i.test(reason)) return "kd_sdk_signature product=<产品> query=<类或方法>"; if (/evidence\/ksql-lint\.txt/i.test(reason)) return "kd_ksql_lint product=<产品> path="; if (/evidence\/(data-source\.md|cosmic-metadata\.json)|元数据证据|数据源证据/i.test(reason)) return "/kd"; if (/TDD|tdd-red|tdd-green/i.test(reason)) return "/kd doc plan"; return "/kd check"; } function suggestedProductCommand(run: ActiveRun): string { const inferred = resolveProductProfile(run.goal ?? "").product; if (isKnownProduct(inferred)) return `/kd ${inferred}`; return "/kd cangqiong 或 /kd enterprise"; }