From 03fabd8ab93e5413623d9b94494a89cb0441b318 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Wed, 10 Jun 2026 09:29:53 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=20docs/superpowers?= =?UTF-8?q?=20=E7=9B=AE=E5=BD=95=E7=9A=84=20git=20=E8=B7=9F=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-06-10-tracked-task.md | 524 ------------------ .../specs/2026-06-10-tracked-task-design.md | 115 ---- 2 files changed, 639 deletions(-) delete mode 100644 docs/superpowers/plans/2026-06-10-tracked-task.md delete mode 100644 docs/superpowers/specs/2026-06-10-tracked-task-design.md diff --git a/docs/superpowers/plans/2026-06-10-tracked-task.md b/docs/superpowers/plans/2026-06-10-tracked-task.md deleted file mode 100644 index 031c6a5..0000000 --- a/docs/superpowers/plans/2026-06-10-tracked-task.md +++ /dev/null @@ -1,524 +0,0 @@ -# tracked 任务跟踪模式实现计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 在 metadata 中新增 tracked 布尔开关,控制 plan/build/archive 三阶段是否启用 task.md 任务跟踪。 - -**Architecture:** 在 `RuneConfig.metadata` 新增 `tracked` 字段,默认 `false`,内置默认配置为 `true`。`task-parser.ts` 新增 `validateTaskFormat` 函数做格式校验。`config.ts` 的 `validateConfig` 在 tracked=true 时校验 plan 必须包含 task 文档。`assembler.ts` 的 build/archive 阶段根据 tracked 分支处理。 - -**Tech Stack:** TypeScript, Bun, bun:test - ---- - -### Task 1: 类型变更 — metadata.tracked - -**Files:** - -- Modify: `src/types.ts:38-42` -- Test: `tests/core/config.test.ts` - -- [ ] **Step 1: 写失败测试 — validateConfig 在 tracked=true 时要求 plan.documents 包含 task** - -在 `tests/core/config.test.ts` 的 `validateConfig` describe 块内追加: - -```typescript -it("tracked=true 时 plan.documents 必须包含 task 文档", () => { - const config: RuneConfig = { - stages: { - plan: { - documents: [{ name: "design", prompt: "生成设计" }], - }, - }, - metadata: { tracked: true }, - }; - expect(() => validateConfig(config)).toThrow(ConfigError); -}); - -it("tracked=true 且 plan.documents 包含 task 时不报错", () => { - const config: RuneConfig = { - stages: { - plan: { - documents: [ - { name: "design", prompt: "生成设计" }, - { name: "task", prompt: "生成任务" }, - ], - }, - }, - metadata: { tracked: true }, - }; - expect(() => validateConfig(config)).not.toThrow(); -}); - -it("tracked=false 时 plan.documents 不包含 task 也不报错", () => { - const config: RuneConfig = { - stages: { - plan: { - documents: [{ name: "design", prompt: "生成设计" }], - }, - }, - metadata: { tracked: false }, - }; - expect(() => validateConfig(config)).not.toThrow(); -}); - -it("tracked 未配置时等同于 false,不强制要求 task 文档", () => { - const config: RuneConfig = { - stages: { - plan: { - documents: [{ name: "design", prompt: "生成设计" }], - }, - }, - }; - expect(() => validateConfig(config)).not.toThrow(); -}); -``` - -- [ ] **Step 2: 运行测试确认失败** - -Run: `bun test tests/core/config.test.ts` -Expected: 新增的 "tracked=true 时 plan.documents 必须包含 task 文档" 测试 FAIL(validateConfig 尚无此校验) - -- [ ] **Step 3: 修改 types.ts — metadata 新增 tracked 字段** - -在 `src/types.ts` 的 `RuneConfig` 接口中,修改 `metadata` 类型: - -```typescript -export interface RuneConfig { - stages: StagesConfig; - metadata?: { - command?: string; - tracked?: boolean; - }; -} -``` - -- [ ] **Step 4: 修改 config.ts — validateConfig 新增 tracked 校验** - -在 `src/core/config.ts` 的 `validateConfig` 函数中,在现有校验逻辑之前追加: - -```typescript -if (config.metadata?.tracked && plan) { - const hasTaskDoc = plan.documents.some((d) => d.name === "task"); - if (!hasTaskDoc) { - throw new ConfigError('tracked 开启时 plan.documents 必须包含 name 为 "task" 的文档'); - } -} -``` - -- [ ] **Step 5: 运行测试确认通过** - -Run: `bun test tests/core/config.test.ts` -Expected: ALL PASS - -- [ ] **Step 6: 提交** - -```bash -git add src/types.ts src/core/config.ts tests/core/config.test.ts -git commit -m "feat: metadata.tracked 类型与 validateConfig 校验" -``` - ---- - -### Task 2: validateTaskFormat 函数 - -**Files:** - -- Modify: `src/core/task-parser.ts` -- Test: `tests/core/task-parser.test.ts` - -- [ ] **Step 1: 写失败测试** - -在 `tests/core/task-parser.test.ts` 追加新的 describe 块: - -```typescript -import { validateTaskFormat } from "../../src/core/task-parser.ts"; - -describe("validateTaskFormat", () => { - it("合法 task 内容通过校验", () => { - expect(() => validateTaskFormat("- [x] 已完成\n- [ ] 未完成")).not.toThrow(); - }); - - it("无 checkbox 项时抛错", () => { - expect(() => validateTaskFormat("# 标题\n一些描述")).toThrow(); - }); - - it("空内容抛错", () => { - expect(() => validateTaskFormat("")).toThrow(); - }); - - it("checkbox 文本为空时抛错", () => { - expect(() => validateTaskFormat("- [ ] \n- [x] 有内容")).toThrow(); - }); - - it("checkbox 文本仅空格时抛错", () => { - expect(() => validateTaskFormat("- [ ] ")).toThrow(); - }); - - it("有 checkbox 且文本非空时通过", () => { - expect(() => validateTaskFormat("- [ ] 实现功能A")).not.toThrow(); - }); -}); -``` - -- [ ] **Step 2: 运行测试确认失败** - -Run: `bun test tests/core/task-parser.test.ts` -Expected: 新增 validateTaskFormat 测试 FAIL(函数不存在) - -- [ ] **Step 3: 实现 validateTaskFormat** - -在 `src/core/task-parser.ts` 追加: - -```typescript -export class TaskFormatError extends Error { - constructor(message: string) { - super(message); - this.name = this.constructor.name; - } -} - -export function validateTaskFormat(content: string): void { - const tasks = parseTasks(content); - if (tasks.length === 0) { - throw new TaskFormatError("task.md 必须包含至少一个 checkbox 项"); - } - for (const task of tasks) { - if (task.text.trim() === "") { - throw new TaskFormatError("task.md 中每个 checkbox 项必须有非空描述"); - } - } -} -``` - -- [ ] **Step 4: 运行测试确认通过** - -Run: `bun test tests/core/task-parser.test.ts` -Expected: ALL PASS - -- [ ] **Step 5: 提交** - -```bash -git add src/core/task-parser.ts tests/core/task-parser.test.ts -git commit -m "feat: 新增 validateTaskFormat 校验函数" -``` - ---- - -### Task 3: 默认配置新增 metadata.tracked - -**Files:** - -- Modify: `src/defaults/config.ts:3` -- Test: `tests/defaults/config.test.ts` - -- [ ] **Step 1: 写失败测试** - -在 `tests/defaults/config.test.ts` 的 `defaultConfig` describe 块内追加: - -```typescript -it("默认 metadata.tracked 为 true", () => { - expect(defaultConfig.metadata).toBeDefined(); - expect(defaultConfig.metadata!.tracked).toBe(true); -}); -``` - -- [ ] **Step 2: 运行测试确认失败** - -Run: `bun test tests/defaults/config.test.ts` -Expected: "默认 metadata.tracked 为 true" FAIL - -- [ ] **Step 3: 修改默认配置** - -在 `src/defaults/config.ts` 中,将 `defaultConfig` 增加 `metadata` 字段: - -```typescript -export const defaultConfig: RuneConfig = { - metadata: { - tracked: true, - }, - stages: { -``` - -- [ ] **Step 4: 运行测试确认通过** - -Run: `bun test tests/defaults/config.test.ts` -Expected: ALL PASS - -- [ ] **Step 5: 提交** - -```bash -git add src/defaults/config.ts tests/defaults/config.test.ts -git commit -m "feat: 内置默认配置新增 metadata.tracked: true" -``` - ---- - -### Task 4: build 阶段根据 tracked 分支处理 - -**Files:** - -- Modify: `src/core/assembler.ts:76-120` -- Test: `tests/core/assembler.test.ts` - -- [ ] **Step 1: 写失败测试** - -在 `tests/core/assembler.test.ts` 的 `assembleBuildPrompt` describe 块内追加: - -```typescript -it("tracked=false 时只输出通用提示词", async () => { - const config: RuneConfig = { - stages: { build: { prompt: "按规划文档逐步实现功能" } }, - metadata: { tracked: false }, - }; - const prompt = await assembleBuildPrompt(config, TMP_DIR, "user-auth"); - expect(prompt).toBe("按规划文档逐步实现功能"); -}); - -it("tracked=true 且 task.md 格式不合法时抛错", async () => { - const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth"); - await mkdir(changeDir, { recursive: true }); - await writeFile(join(changeDir, "task.md"), "# 标题\n无 checkbox"); - const config: RuneConfig = { - stages: { build: { prompt: "构建阶段" } }, - metadata: { tracked: true }, - }; - try { - await assembleBuildPrompt(config, TMP_DIR, "user-auth"); - expect.unreachable(); - } catch (e: any) { - expect(e.message).toContain("task.md"); - expect(e.message).toContain("格式"); - } -}); - -it("tracked=true 且 task.md 有空 checkbox 文本时抛错", async () => { - const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth"); - await mkdir(changeDir, { recursive: true }); - await writeFile(join(changeDir, "task.md"), "- [ ] \n- [x] 有内容"); - const config: RuneConfig = { - stages: { build: { prompt: "构建阶段" } }, - metadata: { tracked: true }, - }; - try { - await assembleBuildPrompt(config, TMP_DIR, "user-auth"); - expect.unreachable(); - } catch (e: any) { - expect(e.message).toContain("checkbox"); - } -}); -``` - -- [ ] **Step 2: 运行测试确认失败** - -Run: `bun test tests/core/assembler.test.ts` -Expected: 新增测试 FAIL(当前 assembleBuildPrompt 不检查 tracked,始终读 task.md) - -- [ ] **Step 3: 修改 assembleBuildPrompt** - -修改 `src/core/assembler.ts` 的 `assembleBuildPrompt` 函数: - -```typescript -export async function assembleBuildPrompt( - config: RuneConfig, - projectRoot: string, - changeName: string, -): Promise { - const build = config.stages.build; - if (!build) { - throw new CommandError("构建阶段未配置", { - hint: "请在 .rune/config.yaml 中配置 stages.build", - }); - } - - if (!config.metadata?.tracked) { - return applyCommandPrefix(build.prompt, config); - } - - const changeDir = getChangeDir(projectRoot, changeName); - const taskPath = join(changeDir, "task.md"); - - let taskContent: string; - try { - taskContent = await readFile(taskPath, "utf-8"); - } catch { - const prefix = getPmPrefix(config); - throw new CommandError(`变更 "${changeName}" 尚未完成规划,task.md 不存在`, { - hint: `请先完成规划阶段:${prefix} plan ${changeName} 生成所有规划文档`, - }); - } - - validateTaskFormat(taskContent); - - 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 applyCommandPrefix(parts.join("\n"), config); -} -``` - -同时更新 import,在文件顶部的 import 中加入 `validateTaskFormat`: - -```typescript -import { parseTasks, validateTaskFormat } from "./task-parser.ts"; -``` - -- [ ] **Step 4: 运行测试确认通过** - -Run: `bun test tests/core/assembler.test.ts` -Expected: ALL PASS - -- [ ] **Step 5: 提交** - -```bash -git add src/core/assembler.ts tests/core/assembler.test.ts -git commit -m "feat: build 阶段根据 tracked 分支处理" -``` - ---- - -### Task 5: archive 阶段根据 tracked 分支处理 - -**Files:** - -- Modify: `src/core/assembler.ts:122-160` -- Test: `tests/core/assembler.test.ts` - -- [ ] **Step 1: 写失败测试** - -在 `tests/core/assembler.test.ts` 的 `assembleArchivePrompt` describe 块内追加: - -```typescript -it("tracked=false 时不读取 task.md,只输出通用提示词", async () => { - const config: RuneConfig = { - stages: { archive: { prompt: "确认归档" } }, - metadata: { tracked: false }, - }; - const prompt = await assembleArchivePrompt(config, TMP_DIR, "user-auth"); - expect(prompt).toContain("确认归档"); - expect(prompt).not.toContain("未完成"); -}); - -it("tracked=true 时读取 task.md 并注入未完成任务警告", async () => { - const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth"); - await mkdir(changeDir, { recursive: true }); - await writeFile(join(changeDir, "task.md"), "- [ ] 未完成任务"); - const config: RuneConfig = { - stages: { archive: { prompt: "归档阶段" } }, - metadata: { tracked: true }, - }; - const prompt = await assembleArchivePrompt(config, TMP_DIR, "user-auth"); - expect(prompt).toContain("未完成"); - expect(prompt).toContain("未完成任务"); -}); - -it("tracked=true 且所有任务完成时不注入警告", async () => { - const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth"); - await mkdir(changeDir, { recursive: true }); - await writeFile(join(changeDir, "task.md"), "- [x] 已完成任务"); - const config: RuneConfig = { - stages: { archive: { prompt: "归档阶段" } }, - metadata: { tracked: true }, - }; - const prompt = await assembleArchivePrompt(config, TMP_DIR, "user-auth"); - expect(prompt).not.toContain("未完成"); -}); -``` - -- [ ] **Step 2: 运行测试确认失败** - -Run: `bun test tests/core/assembler.test.ts` -Expected: 新增 "tracked=false 时不读取 task.md" 测试 FAIL(当前 archive 始终读 task.md) - -- [ ] **Step 3: 修改 assembleArchivePrompt** - -修改 `src/core/assembler.ts` 的 `assembleArchivePrompt` 函数: - -```typescript -export async function assembleArchivePrompt( - config: RuneConfig, - projectRoot: string, - changeName: string, -): Promise { - const archive = config.stages.archive; - if (!archive) - throw new CommandError("归档阶段未配置", { - hint: "请在 .rune/config.yaml 中配置 stages.archive", - }); - - const parts: string[] = []; - parts.push(`# 归档阶段:${changeName}\n`); - - if (config.metadata?.tracked) { - const changeDir = getChangeDir(projectRoot, changeName); - const taskPath = join(changeDir, "task.md"); - 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 applyCommandPrefix(parts.join("\n"), config); -} -``` - -- [ ] **Step 4: 运行测试确认通过** - -Run: `bun test tests/core/assembler.test.ts` -Expected: ALL PASS - -- [ ] **Step 5: 提交** - -```bash -git add src/core/assembler.ts tests/core/assembler.test.ts -git commit -m "feat: archive 阶段根据 tracked 分支处理" -``` - ---- - -### Task 6: 全量测试验证 - -**Files:** 无新文件 - -- [ ] **Step 1: 运行全量测试** - -Run: `bun test` -Expected: ALL PASS - -- [ ] **Step 2: 运行代码质量检查** - -Run: `bun run check` -Expected: 无 lint 或格式错误 - -- [ ] **Step 3: 提交最终状态(如有格式修复)** - -如有修复则提交,否则跳过。 diff --git a/docs/superpowers/specs/2026-06-10-tracked-task-design.md b/docs/superpowers/specs/2026-06-10-tracked-task-design.md deleted file mode 100644 index d278ad5..0000000 --- a/docs/superpowers/specs/2026-06-10-tracked-task-design.md +++ /dev/null @@ -1,115 +0,0 @@ -# task.md 任务跟踪(tracked 模式)设计 - -## 背景 - -当前 build 阶段始终读取 task.md 进行任务跟踪,archive 阶段也始终检查 task.md 完成状态。但用户可能不需要任务跟踪流程(例如自定义配置中不含 task 文档),此时 build/archive 强依赖 task.md 是不合理的。 - -需要一种机制让用户控制是否启用任务跟踪,同时保持内置默认配置的现有行为。 - -## 方案 - -在 `metadata` 配置下新增 `tracked: boolean` 字段,控制 plan/build/archive 三阶段的任务跟踪行为。 - -## 配置变更 - -### metadata.tracked - -```yaml -metadata: - command: "bunx @lanyuanxiaoyao/rune" - tracked: true -``` - -- 类型:`boolean`,可选,不配置时默认 `false` -- 内置默认配置中 `tracked: true`(因为内置 plan 包含 task 文档) -- 放在 `metadata` 而非 `build` 阶段内,因为它影响 plan/build/archive 三阶段 - -### 类型变更 - -```typescript -// types.ts -interface RuneConfig { - stages: StagesConfig; - metadata?: { - command?: string; - tracked?: boolean; // 新增 - }; -} -``` - -`BuildStage` 类型不变,不新增字段。 - -## 各阶段行为变更 - -### plan 阶段(config.ts validateConfig) - -当 `metadata.tracked === true` 时,校验 `plan.documents` 中必须包含 `name: "task"` 的文档。不包含则抛出 ConfigError: - -``` -配置错误:tracked 开启时 plan.documents 必须包含 name 为 "task" 的文档 -``` - -当 `metadata.tracked !== true` 时,不进行此校验。 - -### build 阶段(assembler.ts assembleBuildPrompt) - -**tracked === true:** - -1. 读取 `task.md`,如不存在则抛出 CommandError(现有行为) -2. **新增格式校验**:调用 `validateTaskFormat`,不通过则抛出 CommandError -3. 解析任务列表,展示进度和待执行任务(现有行为) -4. 提示词末尾附加任务跟踪相关指令(现有行为) - -**tracked === false:** - -1. 不读取 task.md,不做格式校验 -2. 只输出 `build.prompt` 通用提示词(经 applyCommandPrefix 处理) -3. 提示词中不包含任何任务跟踪相关内容 - -### archive 阶段(assembler.ts assembleArchivePrompt) - -**tracked === true:** 现有行为不变——读 task.md,检查未完成项,注入警告。 - -**tracked === false:** 不读取 task.md,不注入未完成任务警告,只输出 `archive.prompt`(经 applyCommandPrefix 处理)。 - -## 格式校验规则 - -在 `task-parser.ts` 中新增 `validateTaskFormat` 函数: - -``` -校验条件: -1. 文件中至少有一个 checkbox 行(- [ ] 或 - [x]) -2. 每个 checkbox 的文本内容不能为空(即 - [ ] 后面必须有非空文本) - -不通过时抛出 CommandError: -"变更 "<变更名>" 的 task.md 格式不符合要求:必须包含至少一个 checkbox 项,且每项必须有非空描述" -``` - -## 默认配置变更 - -`defaults/config.ts` 中新增: - -```typescript -metadata: { - tracked: true, -} -``` - -## 不变更的部分 - -- `BuildStage` 类型不变 -- `scanner.ts` 的 `taskProgress` 逻辑不变(status 命令是查询行为,始终尝试读取 task.md 做信息展示,不受 tracked 开关影响) -- `task-parser.ts` 的 `parseTasks` 不变,新增 `validateTaskFormat` 函数 -- `pm.ts` 不变 -- `cli/` 目录下的错误类型和输出格式不变,复用现有 CommandError 和 ConfigError - -## 影响范围 - -| 文件 | 变更类型 | 说明 | -| ------------------------- | --------- | -------------------------------------------- | -| `src/types.ts` | 修改 | metadata 新增 tracked 字段 | -| `src/defaults/config.ts` | 修改 | 默认配置新增 metadata.tracked: true | -| `src/core/config.ts` | 修改 | validateConfig 新增 tracked 时 task 文档校验 | -| `src/core/task-parser.ts` | 修改 | 新增 validateTaskFormat 函数 | -| `src/core/assembler.ts` | 修改 | build/archive 阶段根据 tracked 分支处理 | -| 对应测试文件 | 修改/新增 | 覆盖所有新增逻辑 |