diff --git a/src/core/assembler.ts b/src/core/assembler.ts index 2d3fbab..6a5647b 100644 --- a/src/core/assembler.ts +++ b/src/core/assembler.ts @@ -2,6 +2,7 @@ import { existsSync } from "node:fs"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; import type { RuneConfig } from "../types.ts"; +import { CommandError } from "../cli/errors.ts"; import { getChangeDir } from "./config.ts"; import { parseTasks } from "./task-parser.ts"; @@ -69,7 +70,11 @@ export async function assembleBuildPrompt( changeName: string, ): Promise { const build = config.stages.build; - if (!build) throw new Error("build 阶段未配置"); + if (!build) { + throw new CommandError("构建阶段未配置", { + hint: "请在 .rune/config.yaml 中配置 stages.build", + }); + } const changeDir = getChangeDir(projectRoot, changeName); const taskPath = join(changeDir, "task.md"); @@ -78,7 +83,9 @@ export async function assembleBuildPrompt( try { taskContent = await readFile(taskPath, "utf-8"); } catch { - throw new Error(`task.md not found in ${changeDir}`); + throw new CommandError(`变更 "${changeName}" 尚未完成规划,task.md 不存在`, { + hint: `请先完成规划阶段:rune plan ${changeName} 生成所有规划文档`, + }); } const tasks = parseTasks(taskContent); diff --git a/tests/core/assembler.test.ts b/tests/core/assembler.test.ts index 77fda23..45d5604 100644 --- a/tests/core/assembler.test.ts +++ b/tests/core/assembler.test.ts @@ -184,10 +184,15 @@ describe("assembleBuildPrompt", () => { expect(prompt).toContain("归档"); }); - it("task.md 不存在时抛出错误", async () => { - await expect( - assembleBuildPrompt(defaultConfig, TMP_DIR, "nonexistent"), - ).rejects.toThrow("task.md not found"); + it("task.md 不存在时抛出 CommandError 并附带提示", async () => { + try { + await assembleBuildPrompt(defaultConfig, TMP_DIR, "nonexistent"); + expect.unreachable(); + } catch (e: any) { + expect(e.message).toContain("尚未完成规划"); + expect(e.message).toContain("nonexistent"); + expect(e.hint).toContain("rune plan"); + } }); });