feat: archive 阶段校验 task 完成状态,未完成时注入警告提示词
This commit is contained in:
@@ -201,7 +201,7 @@ cli.command("archive <change-name>", "归档阶段").action(
|
||||
});
|
||||
}
|
||||
const config = await loadConfig(root);
|
||||
const prompt = assembleArchivePrompt(config, changeName);
|
||||
const prompt = await assembleArchivePrompt(config, root, changeName);
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const src = changeDir;
|
||||
const dest = join(getArchiveDir(root), `${today}-${changeName}`);
|
||||
|
||||
@@ -111,15 +111,39 @@ export async function assembleBuildPrompt(
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
export function assembleArchivePrompt(
|
||||
export async function assembleArchivePrompt(
|
||||
config: RuneConfig,
|
||||
projectRoot: string,
|
||||
changeName: string,
|
||||
): string {
|
||||
): Promise<string> {
|
||||
const archive = config.stages.archive;
|
||||
if (!archive) throw new Error("archive 阶段未配置");
|
||||
|
||||
const changeDir = getChangeDir(projectRoot, changeName);
|
||||
const taskPath = join(changeDir, "task.md");
|
||||
|
||||
const parts: string[] = [];
|
||||
parts.push(`# 归档阶段:${changeName}\n`);
|
||||
|
||||
try {
|
||||
const taskContent = await readFile(taskPath, "utf-8");
|
||||
const tasks = parseTasks(taskContent);
|
||||
const incompleteTasks = tasks.filter((t) => !t.checked);
|
||||
if (incompleteTasks.length > 0) {
|
||||
parts.push("## ⚠️ 警告:存在未完成的任务\n");
|
||||
parts.push(`以下 ${incompleteTasks.length} 个任务尚未完成:`);
|
||||
for (const t of incompleteTasks) {
|
||||
parts.push(`- [ ] ${t.text}`);
|
||||
}
|
||||
parts.push("");
|
||||
parts.push("请询问用户是否确认在任务未全部完成的情况下归档。");
|
||||
parts.push("如用户确认,则继续归档;否则中止并返回构建阶段。");
|
||||
parts.push("");
|
||||
}
|
||||
} catch {
|
||||
// task.md 不存在时不追加警告
|
||||
}
|
||||
|
||||
parts.push(archive.prompt);
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
@@ -197,8 +197,10 @@ describe("assembleBuildPrompt", () => {
|
||||
});
|
||||
|
||||
describe("assembleArchivePrompt", () => {
|
||||
it("返回归档提示词", () => {
|
||||
const prompt = assembleArchivePrompt(defaultConfig, "user-auth");
|
||||
it("返回归档提示词", async () => {
|
||||
const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
const prompt = await assembleArchivePrompt(defaultConfig, TMP_DIR, "user-auth");
|
||||
expect(prompt).toContain("user-auth");
|
||||
expect(prompt).toContain("归档");
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("完整 SDD 流程", () => {
|
||||
const buildPrompt2 = await assembleBuildPrompt(config, TMP_DIR, changeName);
|
||||
expect(buildPrompt2).toContain("已完成");
|
||||
|
||||
const archivePrompt = assembleArchivePrompt(config, changeName);
|
||||
const archivePrompt = await assembleArchivePrompt(config, TMP_DIR, changeName);
|
||||
expect(archivePrompt).toContain("归档");
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const src = getChangeDir(TMP_DIR, changeName);
|
||||
@@ -174,3 +174,46 @@ describe("变更名校验", () => {
|
||||
expect(validRegex.test("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("archive 校验", () => {
|
||||
it("task 未全部完成时注入警告提示词", async () => {
|
||||
await runInit(TMP_DIR, ["opencode"]);
|
||||
const config = await loadConfig(TMP_DIR);
|
||||
|
||||
const changeDir = getChangeDir(TMP_DIR, "incomplete-task");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(join(changeDir, "design.md"), "# 设计");
|
||||
await writeFile(join(changeDir, "task.md"), "- [x] 已完成任务\n- [ ] 未完成任务");
|
||||
|
||||
const prompt = await assembleArchivePrompt(config, TMP_DIR, "incomplete-task");
|
||||
expect(prompt).toContain("警告");
|
||||
expect(prompt).toContain("未完成任务");
|
||||
expect(prompt).toContain("是否确认");
|
||||
});
|
||||
|
||||
it("task 全部完成时不注入警告", async () => {
|
||||
await runInit(TMP_DIR, ["opencode"]);
|
||||
const config = await loadConfig(TMP_DIR);
|
||||
|
||||
const changeDir = getChangeDir(TMP_DIR, "complete-task");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(join(changeDir, "design.md"), "# 设计");
|
||||
await writeFile(join(changeDir, "task.md"), "- [x] 已完成任务");
|
||||
|
||||
const prompt = await assembleArchivePrompt(config, TMP_DIR, "complete-task");
|
||||
expect(prompt).not.toContain("警告");
|
||||
expect(prompt).toContain("归档阶段");
|
||||
});
|
||||
|
||||
it("task.md 不存在时不追加警告", async () => {
|
||||
await runInit(TMP_DIR, ["opencode"]);
|
||||
const config = await loadConfig(TMP_DIR);
|
||||
|
||||
const changeDir = getChangeDir(TMP_DIR, "no-task");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(join(changeDir, "design.md"), "# 设计");
|
||||
|
||||
const prompt = await assembleArchivePrompt(config, TMP_DIR, "no-task");
|
||||
expect(prompt).not.toContain("警告");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user