chore: 添加 .oxfmtrc.json 并格式化全部代码
This commit is contained in:
@@ -82,9 +82,7 @@ describe("injectClaudeCode", () => {
|
||||
describe("updateClaudeCode", () => {
|
||||
it("文件不存在时创建", async () => {
|
||||
await updateClaudeCode(TMP_DIR);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".claude", "commands", "rune-discuss.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".claude", "commands", "rune-discuss.md"))).toBe(true);
|
||||
});
|
||||
|
||||
it("文件存在且内容一致时不覆盖", async () => {
|
||||
@@ -104,10 +102,7 @@ describe("updateClaudeCode", () => {
|
||||
|
||||
it("文件存在但内容不一致时覆盖", async () => {
|
||||
await injectClaudeCode(TMP_DIR);
|
||||
await writeFile(
|
||||
join(TMP_DIR, ".claude", "commands", "rune-discuss.md"),
|
||||
"旧内容",
|
||||
);
|
||||
await writeFile(join(TMP_DIR, ".claude", "commands", "rune-discuss.md"), "旧内容");
|
||||
|
||||
await updateClaudeCode(TMP_DIR);
|
||||
const content = await readFile(
|
||||
@@ -120,8 +115,6 @@ describe("updateClaudeCode", () => {
|
||||
|
||||
it("更新 status 命令", async () => {
|
||||
await updateClaudeCode(TMP_DIR);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".claude", "commands", "rune-status.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".claude", "commands", "rune-status.md"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,9 +24,9 @@ describe("injectOpenCode", () => {
|
||||
for (const stage of ["discuss", "plan", "build", "archive"]) {
|
||||
expect(commands).toContain(`rune-${stage}.md`);
|
||||
expect(skills).toContain(`rune-${stage}`);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "skills", `rune-${stage}`, "SKILL.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "skills", `rune-${stage}`, "SKILL.md"))).toBe(
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -38,9 +38,7 @@ describe("injectOpenCode", () => {
|
||||
|
||||
expect(commands).toContain("rune-status.md");
|
||||
expect(skills).toContain("rune-status");
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "skills", "rune-status", "SKILL.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "skills", "rune-status", "SKILL.md"))).toBe(true);
|
||||
});
|
||||
|
||||
it("command 文件包含 skill 调用指令", async () => {
|
||||
@@ -112,12 +110,8 @@ describe("injectOpenCode", () => {
|
||||
describe("updateOpenCode", () => {
|
||||
it("文件不存在时创建", async () => {
|
||||
await updateOpenCode(TMP_DIR);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "commands", "rune-discuss.md")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "skills", "rune-discuss", "SKILL.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "commands", "rune-discuss.md"))).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "skills", "rune-discuss", "SKILL.md"))).toBe(true);
|
||||
});
|
||||
|
||||
it("文件存在且内容一致时不覆盖", async () => {
|
||||
@@ -137,10 +131,7 @@ describe("updateOpenCode", () => {
|
||||
|
||||
it("文件存在但内容不一致时覆盖", async () => {
|
||||
await injectOpenCode(TMP_DIR);
|
||||
await writeFile(
|
||||
join(TMP_DIR, ".opencode", "commands", "rune-discuss.md"),
|
||||
"旧内容",
|
||||
);
|
||||
await writeFile(join(TMP_DIR, ".opencode", "commands", "rune-discuss.md"), "旧内容");
|
||||
|
||||
await updateOpenCode(TMP_DIR);
|
||||
const content = await readFile(
|
||||
@@ -153,11 +144,7 @@ describe("updateOpenCode", () => {
|
||||
|
||||
it("更新 status 命令和 skill", async () => {
|
||||
await updateOpenCode(TMP_DIR);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "commands", "rune-status.md")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
existsSync(join(TMP_DIR, ".opencode", "skills", "rune-status", "SKILL.md")),
|
||||
).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "commands", "rune-status.md"))).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".opencode", "skills", "rune-status", "SKILL.md"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { formatError } from "../../src/cli/output.ts";
|
||||
import {
|
||||
UsageError,
|
||||
ConfigError,
|
||||
CommandError,
|
||||
InternalError,
|
||||
} from "../../src/cli/errors.ts";
|
||||
import { UsageError, ConfigError, CommandError, InternalError } from "../../src/cli/errors.ts";
|
||||
|
||||
describe("formatError", () => {
|
||||
it("只输出错误行(无 hint/usage)", () => {
|
||||
@@ -25,15 +20,13 @@ describe("formatError", () => {
|
||||
usage: "rune plan <change-name>",
|
||||
});
|
||||
const output = formatError(err);
|
||||
expect(output).toBe(
|
||||
"错误: 缺少参数\n\n用法: rune plan <change-name>",
|
||||
);
|
||||
expect(output).toBe("错误: 缺少参数\n\n用法: rune plan <change-name>");
|
||||
});
|
||||
|
||||
it("输出完整格式(错误 + 用法 + 提示)", () => {
|
||||
const err = new UsageError("缺少必填参数 <change-name>", {
|
||||
usage: "rune plan <change-name>",
|
||||
hint: "请指定变更名称,如 \"add-login\"",
|
||||
hint: '请指定变更名称,如 "add-login"',
|
||||
});
|
||||
const output = formatError(err);
|
||||
expect(output).toBe(
|
||||
|
||||
@@ -33,9 +33,7 @@ describe("formatChangeStatus", () => {
|
||||
|
||||
it("显示文档依赖信息(dependMet 为 false 且 config 中有依赖)", () => {
|
||||
const status = makeStatus({
|
||||
documents: [
|
||||
{ name: "task", completed: false, dependMet: false },
|
||||
],
|
||||
documents: [{ name: "task", completed: false, dependMet: false }],
|
||||
});
|
||||
const config: RuneConfig = {
|
||||
stages: {
|
||||
@@ -53,9 +51,7 @@ describe("formatChangeStatus", () => {
|
||||
|
||||
it("dependMet 为 false 但无 config 时不显示文档依赖信息", () => {
|
||||
const status = makeStatus({
|
||||
documents: [
|
||||
{ name: "task", completed: false, dependMet: false },
|
||||
],
|
||||
documents: [{ name: "task", completed: false, dependMet: false }],
|
||||
});
|
||||
const output = formatChangeStatus(status);
|
||||
expect(output).not.toContain("(依赖");
|
||||
@@ -102,18 +98,14 @@ describe("formatChangeStatus", () => {
|
||||
describe("suggestNextStep", () => {
|
||||
it("规划未完成时返回下一个可规划文档", () => {
|
||||
const status = makeStatus({
|
||||
documents: [
|
||||
{ name: "design", completed: false, dependMet: true },
|
||||
],
|
||||
documents: [{ name: "design", completed: false, dependMet: true }],
|
||||
});
|
||||
expect(suggestNextStep(status)).toContain("rune plan test-change design");
|
||||
});
|
||||
|
||||
it("规划未完成且依赖未满足时提示完成前置依赖", () => {
|
||||
const status = makeStatus({
|
||||
documents: [
|
||||
{ name: "design", completed: false, dependMet: false },
|
||||
],
|
||||
documents: [{ name: "design", completed: false, dependMet: false }],
|
||||
});
|
||||
expect(suggestNextStep(status)).toBe("完成前置依赖后再规划文档");
|
||||
});
|
||||
|
||||
@@ -22,10 +22,7 @@ describe("runInit", () => {
|
||||
expect(existsSync(join(TMP_DIR, ".rune"))).toBe(true);
|
||||
expect(existsSync(join(TMP_DIR, ".rune", "config.yaml"))).toBe(true);
|
||||
|
||||
const content = await readFile(
|
||||
join(TMP_DIR, ".rune", "config.yaml"),
|
||||
"utf-8",
|
||||
);
|
||||
const content = await readFile(join(TMP_DIR, ".rune", "config.yaml"), "utf-8");
|
||||
expect(content).toContain("# Rune 配置文件");
|
||||
expect(content).toContain("stages:");
|
||||
});
|
||||
@@ -46,16 +43,10 @@ describe("runInit", () => {
|
||||
|
||||
it("重复 init 不覆盖 config.yaml", async () => {
|
||||
await runInit(TMP_DIR, ["opencode"]);
|
||||
await writeFile(
|
||||
join(TMP_DIR, ".rune", "config.yaml"),
|
||||
"自定义内容",
|
||||
);
|
||||
await writeFile(join(TMP_DIR, ".rune", "config.yaml"), "自定义内容");
|
||||
|
||||
await runInit(TMP_DIR, ["opencode"]);
|
||||
const content = await readFile(
|
||||
join(TMP_DIR, ".rune", "config.yaml"),
|
||||
"utf-8",
|
||||
);
|
||||
const content = await readFile(join(TMP_DIR, ".rune", "config.yaml"), "utf-8");
|
||||
expect(content).toBe("自定义内容");
|
||||
});
|
||||
|
||||
|
||||
@@ -38,12 +38,7 @@ describe("assembleDiscussPrompt", () => {
|
||||
|
||||
describe("assemblePlanPrompt", () => {
|
||||
it("包含指定文档名称和提示词", async () => {
|
||||
const prompt = await assemblePlanPrompt(
|
||||
defaultConfig,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
"design",
|
||||
);
|
||||
const prompt = await assemblePlanPrompt(defaultConfig, TMP_DIR, "user-auth", "design");
|
||||
expect(prompt).toContain("user-auth");
|
||||
expect(prompt).toContain("design");
|
||||
expect(prompt).not.toContain("task");
|
||||
@@ -53,23 +48,13 @@ describe("assemblePlanPrompt", () => {
|
||||
const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(join(changeDir, "design.md"), "# 已有设计");
|
||||
const prompt = await assemblePlanPrompt(
|
||||
defaultConfig,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
"design",
|
||||
);
|
||||
const prompt = await assemblePlanPrompt(defaultConfig, TMP_DIR, "user-auth", "design");
|
||||
expect(prompt).toContain("已有设计");
|
||||
expect(prompt).toContain("在此基础上修订");
|
||||
});
|
||||
|
||||
it("替换模板中的 {{change-name}}", async () => {
|
||||
const prompt = await assemblePlanPrompt(
|
||||
defaultConfig,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
"design",
|
||||
);
|
||||
const prompt = await assemblePlanPrompt(defaultConfig, TMP_DIR, "user-auth", "design");
|
||||
expect(prompt).toContain("user-auth 设计文档");
|
||||
expect(prompt).not.toContain("{{change-name}}");
|
||||
});
|
||||
@@ -85,23 +70,13 @@ describe("assemblePlanPrompt", () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const prompt = await assemblePlanPrompt(
|
||||
config,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
"task",
|
||||
);
|
||||
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",
|
||||
);
|
||||
const prompt = await assemblePlanPrompt(defaultConfig, TMP_DIR, "user-auth", "design");
|
||||
expect(prompt).not.toContain("依赖说明");
|
||||
});
|
||||
|
||||
@@ -120,12 +95,7 @@ describe("assemblePlanPrompt", () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const prompt = await assemblePlanPrompt(
|
||||
config,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
"task",
|
||||
);
|
||||
const prompt = await assemblePlanPrompt(config, TMP_DIR, "user-auth", "task");
|
||||
expect(prompt).toContain("已完成");
|
||||
});
|
||||
|
||||
@@ -154,15 +124,8 @@ describe("assembleBuildPrompt", () => {
|
||||
it("包含待执行任务列表", async () => {
|
||||
const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(
|
||||
join(changeDir, "task.md"),
|
||||
`- [x] 任务一\n- [ ] 任务二\n- [ ] 任务三`,
|
||||
);
|
||||
const prompt = await assembleBuildPrompt(
|
||||
defaultConfig,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
);
|
||||
await writeFile(join(changeDir, "task.md"), `- [x] 任务一\n- [ ] 任务二\n- [ ] 任务三`);
|
||||
const prompt = await assembleBuildPrompt(defaultConfig, TMP_DIR, "user-auth");
|
||||
expect(prompt).toContain("任务二");
|
||||
expect(prompt).toContain("待执行任务");
|
||||
expect(prompt).toContain("共 2 项");
|
||||
@@ -171,15 +134,8 @@ describe("assembleBuildPrompt", () => {
|
||||
it("所有任务完成时提示可归档", async () => {
|
||||
const changeDir = join(TMP_DIR, ".rune", "changes", "user-auth");
|
||||
await mkdir(changeDir, { recursive: true });
|
||||
await writeFile(
|
||||
join(changeDir, "task.md"),
|
||||
`- [x] 任务一\n- [x] 任务二`,
|
||||
);
|
||||
const prompt = await assembleBuildPrompt(
|
||||
defaultConfig,
|
||||
TMP_DIR,
|
||||
"user-auth",
|
||||
);
|
||||
await writeFile(join(changeDir, "task.md"), `- [x] 任务一\n- [x] 任务二`);
|
||||
const prompt = await assembleBuildPrompt(defaultConfig, TMP_DIR, "user-auth");
|
||||
expect(prompt).toContain("已完成");
|
||||
expect(prompt).toContain("归档");
|
||||
});
|
||||
|
||||
@@ -91,10 +91,7 @@ describe("loadConfig", () => {
|
||||
it("YAML 解析错误时返回默认配置", async () => {
|
||||
const runeDir = join(TMP_DIR, ".rune");
|
||||
await mkdir(runeDir, { recursive: true });
|
||||
await writeFile(
|
||||
join(runeDir, "config.yaml"),
|
||||
`stages: [invalid yaml {{{`,
|
||||
);
|
||||
await writeFile(join(runeDir, "config.yaml"), `stages: [invalid yaml {{{`);
|
||||
const config = await loadConfig(TMP_DIR);
|
||||
expect(config.stages.discuss).toBeDefined();
|
||||
});
|
||||
@@ -125,9 +122,7 @@ describe("validateConfig", () => {
|
||||
const config: RuneConfig = {
|
||||
stages: {
|
||||
plan: {
|
||||
documents: [
|
||||
{ name: "task", prompt: "生成任务", depend: ["nonexistent"] },
|
||||
],
|
||||
documents: [{ name: "task", prompt: "生成任务", depend: ["nonexistent"] }],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -138,9 +133,7 @@ describe("validateConfig", () => {
|
||||
const config: RuneConfig = {
|
||||
stages: {
|
||||
plan: {
|
||||
documents: [
|
||||
{ name: "design", prompt: "生成设计", depend: ["design"] },
|
||||
],
|
||||
documents: [{ name: "design", prompt: "生成设计", depend: ["design"] }],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -170,9 +163,7 @@ describe("validateConfig", () => {
|
||||
const config: RuneConfig = {
|
||||
stages: {
|
||||
plan: {
|
||||
documents: [
|
||||
{ name: "design", prompt: "生成设计", depend: [] },
|
||||
],
|
||||
documents: [{ name: "design", prompt: "生成设计", depend: [] }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -25,10 +25,7 @@ describe("scanChanges", () => {
|
||||
const changesDir = join(TMP_DIR, ".rune", "changes");
|
||||
await mkdir(join(changesDir, "user-auth"), { recursive: true });
|
||||
await writeFile(join(changesDir, "user-auth", "design.md"), "# 设计");
|
||||
await writeFile(
|
||||
join(changesDir, "user-auth", "task.md"),
|
||||
`- [x] 任务一\n- [ ] 任务二`,
|
||||
);
|
||||
await writeFile(join(changesDir, "user-auth", "task.md"), `- [x] 任务一\n- [ ] 任务二`);
|
||||
|
||||
const changes = await scanChanges(TMP_DIR);
|
||||
expect(changes).toHaveLength(1);
|
||||
|
||||
@@ -25,32 +25,24 @@ describe("defaultConfig", () => {
|
||||
});
|
||||
|
||||
it("plan 的 task 文档配置存在", () => {
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find(
|
||||
(d) => d.name === "task",
|
||||
);
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find((d) => d.name === "task");
|
||||
expect(taskDoc).toBeDefined();
|
||||
expect(taskDoc!.prompt).toBeTruthy();
|
||||
});
|
||||
|
||||
it("task 文档依赖 design", () => {
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find(
|
||||
(d) => d.name === "task",
|
||||
);
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find((d) => d.name === "task");
|
||||
expect(taskDoc!.depend).toEqual(["design"]);
|
||||
});
|
||||
|
||||
it("design 文档有 template", () => {
|
||||
const designDoc = defaultConfig.stages.plan!.documents.find(
|
||||
(d) => d.name === "design",
|
||||
);
|
||||
const designDoc = defaultConfig.stages.plan!.documents.find((d) => d.name === "design");
|
||||
expect(designDoc!.template).toBeTruthy();
|
||||
expect(designDoc!.template).toContain("{{change-name}}");
|
||||
});
|
||||
|
||||
it("task 文档有 template", () => {
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find(
|
||||
(d) => d.name === "task",
|
||||
);
|
||||
const taskDoc = defaultConfig.stages.plan!.documents.find((d) => d.name === "task");
|
||||
expect(taskDoc!.template).toBeTruthy();
|
||||
expect(taskDoc!.template).toContain("- [ ]");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user