chore: 添加 .oxfmtrc.json 并格式化全部代码

This commit is contained in:
2026-06-09 14:22:33 +08:00
parent ebd5bb4051
commit b82f1caf0b
23 changed files with 209 additions and 373 deletions

View File

@@ -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);
});
});

View File

@@ -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);
});
});

View File

@@ -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(

View File

@@ -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("完成前置依赖后再规划文档");
});

View File

@@ -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("自定义内容");
});

View File

@@ -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("归档");
});

View File

@@ -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: [] }],
},
},
};

View File

@@ -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);

View File

@@ -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("- [ ]");
});