103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import { readFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import type { RuneConfig } from "../types.ts";
|
|
import { getChangeDir } from "./config.ts";
|
|
import { parseTasks } from "./task-parser.ts";
|
|
|
|
export function assembleDiscussPrompt(config: RuneConfig): string {
|
|
const discuss = config.stages.discuss;
|
|
if (!discuss) throw new Error("discuss 阶段未配置");
|
|
return discuss.prompt;
|
|
}
|
|
|
|
export async function assemblePlanPrompt(
|
|
config: RuneConfig,
|
|
projectRoot: string,
|
|
changeName: string,
|
|
): Promise<string> {
|
|
const plan = config.stages.plan;
|
|
if (!plan) throw new Error("plan 阶段未配置");
|
|
|
|
const changeDir = getChangeDir(projectRoot, changeName);
|
|
const parts: string[] = [];
|
|
|
|
parts.push(`# 规划阶段:${changeName}\n`);
|
|
parts.push("请为当前变更生成以下文档:\n");
|
|
|
|
for (const doc of plan.documents) {
|
|
parts.push(`## 文档:${doc.name}.md`);
|
|
parts.push(doc.prompt);
|
|
|
|
const docPath = join(changeDir, `${doc.name}.md`);
|
|
if (existsSync(docPath)) {
|
|
const existing = await readFile(docPath, "utf-8");
|
|
parts.push(`\n### 已有内容(请在此基础上修订):\n${existing}`);
|
|
}
|
|
|
|
if (doc.template) {
|
|
const rendered = doc.template.replace(/\{\{change-name\}\}/g, changeName);
|
|
parts.push(`\n### 格式模板:\n${rendered}`);
|
|
}
|
|
|
|
parts.push("");
|
|
}
|
|
|
|
parts.push(`请将文档写入目录:${changeDir}`);
|
|
return parts.join("\n");
|
|
}
|
|
|
|
export async function assembleBuildPrompt(
|
|
config: RuneConfig,
|
|
projectRoot: string,
|
|
changeName: string,
|
|
): Promise<string> {
|
|
const build = config.stages.build;
|
|
if (!build) throw new Error("build 阶段未配置");
|
|
|
|
const changeDir = getChangeDir(projectRoot, changeName);
|
|
const taskPath = join(changeDir, "task.md");
|
|
|
|
let taskContent: string;
|
|
try {
|
|
taskContent = await readFile(taskPath, "utf-8");
|
|
} catch {
|
|
throw new Error(`task.md not found in ${changeDir}`);
|
|
}
|
|
|
|
const tasks = parseTasks(taskContent);
|
|
const pendingTasks = tasks.filter((t) => !t.checked);
|
|
|
|
if (pendingTasks.length === 0) {
|
|
return `所有任务已完成。变更 "${changeName}" 可以归档。`;
|
|
}
|
|
|
|
const parts: string[] = [];
|
|
parts.push(`# 构建阶段:${changeName}\n`);
|
|
parts.push(build.prompt);
|
|
parts.push(`\n## 任务列表\n`);
|
|
parts.push(taskContent);
|
|
parts.push(`\n## 待执行任务(共 ${pendingTasks.length} 项)`);
|
|
for (const task of pendingTasks) {
|
|
parts.push(`- [ ] ${task.text}`);
|
|
}
|
|
parts.push(
|
|
`\n请从第一个待执行任务开始。完成后更新 ${taskPath} 中的 checkbox。`,
|
|
);
|
|
|
|
return parts.join("\n");
|
|
}
|
|
|
|
export function assembleArchivePrompt(
|
|
config: RuneConfig,
|
|
changeName: string,
|
|
): string {
|
|
const archive = config.stages.archive;
|
|
if (!archive) throw new Error("archive 阶段未配置");
|
|
|
|
const parts: string[] = [];
|
|
parts.push(`# 归档阶段:${changeName}\n`);
|
|
parts.push(archive.prompt);
|
|
return parts.join("\n");
|
|
}
|