import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; import type { ActiveRun, KdPhase } from "./types.ts"; import { PHASE_ARTIFACTS } from "./types.ts"; import { runArtifactPath, runRoot } from "./paths.ts"; import type { ProductProfile } from "../product/profile.ts"; import { PLAN_REQUIRED_CHECK_LINES, formatPromptLines } from "./prompt-policy.ts"; export function ensureRunDirectories(cwd: string, run: ActiveRun): void { mkdirSync(runRoot(cwd, run), { recursive: true }); mkdirSync(runArtifactPath(cwd, run, "evidence"), { recursive: true }); } export function artifactExists(cwd: string, run: ActiveRun, artifactName: string): boolean { try { return statSync(runArtifactPath(cwd, run, artifactName)).isFile(); } catch { return false; } } export function phaseArtifactPath(cwd: string, run: ActiveRun, phase: KdPhase): string { return runArtifactPath(cwd, run, PHASE_ARTIFACTS[phase]); } export function readArtifact(cwd: string, run: ActiveRun, phase: KdPhase): string | undefined { const path = phaseArtifactPath(cwd, run, phase); try { return existsSync(path) ? readFileSync(path, "utf8") : undefined; } catch { return undefined; } } export function writeArtifact(cwd: string, run: ActiveRun, phase: KdPhase, content: string): string { ensureRunDirectories(cwd, run); const path = phaseArtifactPath(cwd, run, phase); writeFileSync(path, content.endsWith("\n") ? content : `${content}\n`, "utf8"); run.artifacts[phase] = PHASE_ARTIFACTS[phase]; return path; } export function ensureArtifact(cwd: string, run: ActiveRun, phase: KdPhase, content: string): string { const existing = readArtifact(cwd, run, phase); if (existing !== undefined) return phaseArtifactPath(cwd, run, phase); return writeArtifact(cwd, run, phase, content); } export function defaultArtifactContent(phase: KdPhase, goal?: string, profile?: ProductProfile): string { switch (phase) { case "discuss": return [ "# 需求讨论上下文", "", `- 需求目标:${goal ?? "未知"}`, `- 金蝶产品:${profile?.product ?? "未知"}`, "- 版本:未知", `- 技术栈:${profile?.techStack ?? "未知"}`, `- 语言:${profile?.language ?? "未知"}`, "- 需求条目:未知", "- 目标对象:未知", "- 依赖和批次:未知", "- 非目标范围:未知", "- 待确认问题:", " - 从需求来源中提取可确认事实;只把无法判断且阻塞推进的关键点列为待确认。", "", ].join("\n"); case "spec": return [ "# 需求规格", "", "## 验收标准", "", "## 需求条目", "", "## 数据对象、字段或影响范围", "", "## 依赖和批次", "", "## 异常行为和性能约束", "", "## 假设和风险", "", ].join("\n"); case "plan": return [ "# 实施计划", "", "## 已检查的项目结构", "", "## 待读取文件", "", "## 目标源码根 / 路径", "", "## 允许修改的文件", "", "## 产品实现范围", "", "- 是否涉及产品实现、构建、元数据或 SDK 查证:待确认", "- 判断依据:待确认", "", "## 必需的金蝶查证项", "", ...formatPromptLines(PLAN_REQUIRED_CHECK_LINES), "", "## 需求条目 / 依赖 / 批次", "", "## 验收与验证对应关系", "", "## 执行步骤", "", "- [ ] STEP-001:检查现有目标文件,确认精确修改位置。", "- [ ] STEP-002:运行一个会失败的红灯检查,并记录红灯证据。", "- [ ] STEP-003:只实现已批准的文件改动。", "- [ ] STEP-004:再次运行同一检查,并记录绿灯证据。", "- [ ] STEP-005:记录变更文件和步骤证据。", "", "## TDD / 红绿检查", "", "- 红灯证据:evidence/tdd-red.md", "- 绿灯证据:evidence/tdd-green.md", "- 红绿检查命令或工具:未知", "- Java 语法/编译检查:使用当前项目 Gradle 命令,例如 `./gradlew build`、`.\\gradlew.bat build` 或 `./gradlew :模块:build`。", "- C# 语法/编译检查:使用 `dotnet build`、`dotnet build <.sln>` 或 `dotnet build <.csproj>`。", "- 允许的检查:本地 SDK 签名查证、官方 API/基类/方法查证、元数据查证、kd_check、Gradle/dotnet 构建输出、项目已有测试框架、外部接口最小验证。", "- 测试框架:使用项目已有测试基础设施。", "- 命令无法运行时记录真实阻塞原因和残余风险,不能作为绿灯证据。", "- 自动化测试不可执行时,记录一个产品相关、实现前应失败且实现后应通过的检查。", "- SDK 方法签名事实必须来自当前项目 jar/dll、构建输出或官方元数据。", "", "## 验证命令", "", "## 回滚 / 影响控制", "", ].join("\n"); case "execute": return [ "# 执行记录", "", "## 步骤结果", "", "每个计划步骤按以下格式记录:", "", "- [x] STEP-001:已完成。证据:evidence/step-001.md", "", "## 变更文件", "", "## 偏离计划说明", "", ].join("\n"); case "verify": return ["# 验证记录", "", "## 命令", "", "## 证据", "", "## 残余风险", ""].join("\n"); case "ship": return ["# 交付摘要", "", "## 摘要", "", "## 验证证据", "", "## 风险", "", "## 后续事项", ""].join("\n"); } }