feat: assemblePlanPrompt 改为按单文档组装,增加依赖说明
This commit is contained in:
@@ -74,8 +74,12 @@ cli.command("plan <change-name>", "规划阶段").action(
|
|||||||
const root = requireProjectRoot();
|
const root = requireProjectRoot();
|
||||||
await mkdir(getChangeDir(root, changeName), { recursive: true });
|
await mkdir(getChangeDir(root, changeName), { recursive: true });
|
||||||
const config = await loadConfig(root);
|
const config = await loadConfig(root);
|
||||||
const prompt = await assemblePlanPrompt(config, root, changeName);
|
const plan = config.stages.plan;
|
||||||
|
if (!plan) throw new Error("plan 阶段未配置");
|
||||||
|
for (const doc of plan.documents) {
|
||||||
|
const prompt = await assemblePlanPrompt(config, root, changeName, doc.name);
|
||||||
console.log(prompt);
|
console.log(prompt);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,21 @@ export async function assemblePlanPrompt(
|
|||||||
config: RuneConfig,
|
config: RuneConfig,
|
||||||
projectRoot: string,
|
projectRoot: string,
|
||||||
changeName: string,
|
changeName: string,
|
||||||
|
documentName: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const plan = config.stages.plan;
|
const plan = config.stages.plan;
|
||||||
if (!plan) throw new Error("plan 阶段未配置");
|
if (!plan) throw new Error("plan 阶段未配置");
|
||||||
|
|
||||||
|
const doc = plan.documents.find((d) => d.name === documentName);
|
||||||
|
if (!doc) {
|
||||||
|
throw new Error(`文档 "${documentName}" 不在配置的 plan.documents 中`);
|
||||||
|
}
|
||||||
|
|
||||||
const changeDir = getChangeDir(projectRoot, changeName);
|
const changeDir = getChangeDir(projectRoot, changeName);
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
|
|
||||||
parts.push(`# 规划阶段:${changeName}\n`);
|
parts.push(`# 规划阶段:${changeName}`);
|
||||||
parts.push("请为当前变更生成以下文档:\n");
|
parts.push("");
|
||||||
|
|
||||||
for (const doc of plan.documents) {
|
|
||||||
parts.push(`## 文档:${doc.name}.md`);
|
parts.push(`## 文档:${doc.name}.md`);
|
||||||
parts.push(doc.prompt);
|
parts.push(doc.prompt);
|
||||||
|
|
||||||
@@ -40,10 +44,22 @@ export async function assemblePlanPrompt(
|
|||||||
parts.push(`\n### 格式模板:\n${rendered}`);
|
parts.push(`\n### 格式模板:\n${rendered}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push("");
|
if (doc.depend && doc.depend.length > 0) {
|
||||||
|
parts.push("\n---\n");
|
||||||
|
parts.push("## 依赖说明\n");
|
||||||
|
parts.push("本文档依赖以下前置文档:");
|
||||||
|
|
||||||
|
for (const dep of doc.depend) {
|
||||||
|
const depPath = join(changeDir, `${dep}.md`);
|
||||||
|
const depCompleted = existsSync(depPath);
|
||||||
|
const status = depCompleted ? "已完成" : "未完成";
|
||||||
|
parts.push(`- ${dep}.md(${status})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push(`请将文档写入目录:${changeDir}`);
|
parts.push("\n请先阅读已完成的前置文档,确保内容一致。");
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.push(`\n请将文档写入目录:${changeDir}`);
|
||||||
return parts.join("\n");
|
return parts.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,16 +37,16 @@ describe("assembleDiscussPrompt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("assemblePlanPrompt", () => {
|
describe("assemblePlanPrompt", () => {
|
||||||
it("包含变更名称和文档指引", async () => {
|
it("包含指定文档名称和提示词", async () => {
|
||||||
const prompt = await assemblePlanPrompt(
|
const prompt = await assemblePlanPrompt(
|
||||||
defaultConfig,
|
defaultConfig,
|
||||||
TMP_DIR,
|
TMP_DIR,
|
||||||
"user-auth",
|
"user-auth",
|
||||||
|
"design",
|
||||||
);
|
);
|
||||||
expect(prompt).toContain("user-auth");
|
expect(prompt).toContain("user-auth");
|
||||||
expect(prompt).toContain("design");
|
expect(prompt).toContain("design");
|
||||||
expect(prompt).toContain("task");
|
expect(prompt).not.toContain("task");
|
||||||
expect(prompt).toContain("格式模板");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("包含已有文档内容(重复 plan 场景)", async () => {
|
it("包含已有文档内容(重复 plan 场景)", async () => {
|
||||||
@@ -57,6 +57,7 @@ describe("assemblePlanPrompt", () => {
|
|||||||
defaultConfig,
|
defaultConfig,
|
||||||
TMP_DIR,
|
TMP_DIR,
|
||||||
"user-auth",
|
"user-auth",
|
||||||
|
"design",
|
||||||
);
|
);
|
||||||
expect(prompt).toContain("已有设计");
|
expect(prompt).toContain("已有设计");
|
||||||
expect(prompt).toContain("在此基础上修订");
|
expect(prompt).toContain("在此基础上修订");
|
||||||
@@ -67,12 +68,67 @@ describe("assemblePlanPrompt", () => {
|
|||||||
defaultConfig,
|
defaultConfig,
|
||||||
TMP_DIR,
|
TMP_DIR,
|
||||||
"user-auth",
|
"user-auth",
|
||||||
|
"design",
|
||||||
);
|
);
|
||||||
expect(prompt).toContain("user-auth 设计文档");
|
expect(prompt).toContain("user-auth 设计文档");
|
||||||
expect(prompt).toContain("user-auth 任务列表");
|
|
||||||
expect(prompt).not.toContain("{{change-name}}");
|
expect(prompt).not.toContain("{{change-name}}");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("包含依赖说明(有依赖时)", async () => {
|
||||||
|
const config: RuneConfig = {
|
||||||
|
stages: {
|
||||||
|
plan: {
|
||||||
|
documents: [
|
||||||
|
{ name: "design", prompt: "生成设计" },
|
||||||
|
{ name: "task", prompt: "生成任务", depend: ["design"] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const prompt = await assemblePlanPrompt(
|
||||||
|
config,
|
||||||
|
TMP_DIR,
|
||||||
|
"user-auth",
|
||||||
|
"task",
|
||||||
|
);
|
||||||
|
expect(prompt).toContain("依赖说明");
|
||||||
|
expect(prompt).toContain("design.md");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("无依赖时不包含依赖说明", async () => {
|
||||||
|
const prompt = await assemblePlanPrompt(
|
||||||
|
defaultConfig,
|
||||||
|
TMP_DIR,
|
||||||
|
"user-auth",
|
||||||
|
"design",
|
||||||
|
);
|
||||||
|
expect(prompt).not.toContain("依赖说明");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("依赖说明标注完成状态", async () => {
|
||||||
|
const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth");
|
||||||
|
await mkdir(changeDir, { recursive: true });
|
||||||
|
await writeFile(join(changeDir, "design.md"), "# 设计");
|
||||||
|
|
||||||
|
const config: RuneConfig = {
|
||||||
|
stages: {
|
||||||
|
plan: {
|
||||||
|
documents: [
|
||||||
|
{ name: "design", prompt: "生成设计" },
|
||||||
|
{ name: "task", prompt: "生成任务", depend: ["design"] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const prompt = await assemblePlanPrompt(
|
||||||
|
config,
|
||||||
|
TMP_DIR,
|
||||||
|
"user-auth",
|
||||||
|
"task",
|
||||||
|
);
|
||||||
|
expect(prompt).toContain("已完成");
|
||||||
|
});
|
||||||
|
|
||||||
it("使用自定义 plan 配置", async () => {
|
it("使用自定义 plan 配置", async () => {
|
||||||
const config: RuneConfig = {
|
const config: RuneConfig = {
|
||||||
stages: {
|
stages: {
|
||||||
@@ -87,7 +143,7 @@ describe("assemblePlanPrompt", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const prompt = await assemblePlanPrompt(config, TMP_DIR, "my-feature");
|
const prompt = await assemblePlanPrompt(config, TMP_DIR, "my-feature", "spec");
|
||||||
expect(prompt).toContain("spec");
|
expect(prompt).toContain("spec");
|
||||||
expect(prompt).toContain("my-feature 规格");
|
expect(prompt).toContain("my-feature 规格");
|
||||||
expect(prompt).not.toContain("design");
|
expect(prompt).not.toContain("design");
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("完整 SDD 流程", () => {
|
|||||||
|
|
||||||
const changeName = "user-auth";
|
const changeName = "user-auth";
|
||||||
await mkdir(getChangeDir(TMP_DIR, changeName), { recursive: true });
|
await mkdir(getChangeDir(TMP_DIR, changeName), { recursive: true });
|
||||||
const planPrompt = await assemblePlanPrompt(config, TMP_DIR, changeName);
|
const planPrompt = await assemblePlanPrompt(config, TMP_DIR, changeName, "design");
|
||||||
expect(planPrompt).toContain("user-auth");
|
expect(planPrompt).toContain("user-auth");
|
||||||
|
|
||||||
const changeDir = getChangeDir(TMP_DIR, changeName);
|
const changeDir = getChangeDir(TMP_DIR, changeName);
|
||||||
@@ -118,7 +118,7 @@ describe("完整 SDD 流程", () => {
|
|||||||
expect(discussPrompt).toBe("自定义讨论");
|
expect(discussPrompt).toBe("自定义讨论");
|
||||||
|
|
||||||
await mkdir(getChangeDir(TMP_DIR, "test"), { recursive: true });
|
await mkdir(getChangeDir(TMP_DIR, "test"), { recursive: true });
|
||||||
const planPrompt = await assemblePlanPrompt(config, TMP_DIR, "test");
|
const planPrompt = await assemblePlanPrompt(config, TMP_DIR, "test", "spec");
|
||||||
expect(planPrompt).toContain("spec");
|
expect(planPrompt).toContain("spec");
|
||||||
expect(planPrompt).toContain("test 规格");
|
expect(planPrompt).toContain("test 规格");
|
||||||
expect(planPrompt).not.toContain("design");
|
expect(planPrompt).not.toContain("design");
|
||||||
|
|||||||
Reference in New Issue
Block a user