diff --git a/src/cli.ts b/src/cli.ts index 196df96..a28b6e4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -287,9 +287,13 @@ cli.command("archive ", "归档阶段").action(async (changeName: s const config = await loadConfig(root); 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}`); - await rename(src, dest); + if (existsSync(dest)) { + throw new CommandError(`归档目标 "${today}-${changeName}" 已存在`, { + hint: `同一天同一变更名只能归档一次。如需重新归档,请先删除 .rune/archive/${today}-${changeName} 目录`, + }); + } + await rename(changeDir, dest); console.log(prompt); }); diff --git a/src/core/assembler.ts b/src/core/assembler.ts index 9ed6c1f..5d82a02 100644 --- a/src/core/assembler.ts +++ b/src/core/assembler.ts @@ -152,9 +152,13 @@ export async function assembleArchivePrompt( const incompleteTasks = tasks.filter((t) => !t.checked); if (incompleteTasks.length > 0) { parts.push("## ⚠️ 警告:存在未完成的任务\n"); - parts.push(`请先读取 ${taskPath} 检查是否有未完成的任务。`); - parts.push("如有未完成任务,询问用户是否确认在任务未全部完成的情况下归档。"); - parts.push("如用户确认,则继续归档;否则中止并返回构建阶段。"); + parts.push("以下任务尚未完成:"); + for (const t of incompleteTasks) { + parts.push(`- [ ] ${t.text}`); + } + parts.push(""); + parts.push("询问用户是否确认在任务未全部完成的情况下归档。"); + parts.push("如用户确认,则继续执行归档操作;否则中止并返回构建阶段。"); parts.push(""); } } catch { diff --git a/src/defaults/config.ts b/src/defaults/config.ts index ff50aca..f5e8d8f 100644 --- a/src/defaults/config.ts +++ b/src/defaults/config.ts @@ -298,14 +298,13 @@ rune status - 完成所有任务后,提示用户可以使用 /archive <变更名> 归档`, }, archive: { - prompt: `当前变更已进入归档阶段。 + prompt: `当前变更已进入归档阶段,变更目录已移动到 .rune/archive/ 中。 -请确认: -1. 所有任务已标记为已完成 -2. 如有未完成项,提醒用户并中止归档 -3. 如全部完成,总结本轮变更内容 - -归档后,建议用户使用 /discuss 开始下一轮需求讨论。`, +请执行以下归档操作: +1. 读取变更目录下的 design.md 等设计文档和 task.md,全面了解本轮变更内容 +2. 总结变更摘要:变更目的、涉及的文件和模块、已完成的工作 +3. 如有未完成任务,在摘要中注明未完成的事项 +4. 告知用户归档已完成,建议使用 /discuss 开始下一轮需求讨论`, }, }, }; diff --git a/tests/core/assembler.test.ts b/tests/core/assembler.test.ts index e281c20..f191761 100644 --- a/tests/core/assembler.test.ts +++ b/tests/core/assembler.test.ts @@ -223,7 +223,7 @@ describe("assembleArchivePrompt", () => { expect(prompt).not.toContain("未完成"); }); - it("tracked=true 时引导 AI 检查 task.md 而非内嵌任务内容", async () => { + it("tracked=true 时内嵌未完成任务列表并引导用户确认", async () => { const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth"); await mkdir(changeDir, { recursive: true }); await writeFile(join(changeDir, "task.md"), "- [ ] 未完成任务"); @@ -232,9 +232,9 @@ describe("assembleArchivePrompt", () => { metadata: { tracked: true }, }; const prompt = await assembleArchivePrompt(config, TMP_DIR, "user-auth"); - expect(prompt).toContain("task.md"); expect(prompt).toContain("未完成"); - expect(prompt).not.toContain("- [ ] 未完成任务"); + expect(prompt).toContain("未完成任务"); + expect(prompt).toContain("是否确认"); }); it("tracked=true 且所有任务完成时不注入警告", async () => { diff --git a/tests/integration/flow.test.ts b/tests/integration/flow.test.ts index 3591a55..6b8fc67 100644 --- a/tests/integration/flow.test.ts +++ b/tests/integration/flow.test.ts @@ -189,9 +189,8 @@ describe("archive 校验", () => { const prompt = await assembleArchivePrompt(config, TMP_DIR, "incomplete-task"); expect(prompt).toContain("警告"); - expect(prompt).toContain("task.md"); expect(prompt).toContain("未完成"); - expect(prompt).not.toContain("- [ ] 未完成任务"); + expect(prompt).toContain("未完成任务"); expect(prompt).toContain("是否确认"); });