chore: 移除 docs/superpowers 目录的 git 跟踪

This commit is contained in:
2026-06-10 09:29:53 +08:00
parent e3067d017e
commit 03fabd8ab9
2 changed files with 0 additions and 639 deletions

View File

@@ -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 文档" 测试 FAILvalidateConfig 尚无此校验)
- [ ] **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<string> {
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<string> {
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: 提交最终状态(如有格式修复)**
如有修复则提交,否则跳过。

View File

@@ -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 分支处理 |
| 对应测试文件 | 修改/新增 | 覆盖所有新增逻辑 |