diff --git a/src/cli/help.ts b/src/cli/help.ts index 0a8d241..9db5823 100644 --- a/src/cli/help.ts +++ b/src/cli/help.ts @@ -2,10 +2,10 @@ interface CommandHelpDef { name: string; alias: string; description: string; - usage: string; + usageTemplate: string; args: { name: string; desc: string }[]; detail: string; - examples: string[]; + exampleArgs: string[]; } const COMMANDS: Record = { @@ -13,78 +13,78 @@ const COMMANDS: Record = { name: "init", alias: "init <工具...>", description: "初始化 Rune 并注入工具配置", - usage: "rune init <工具...>", + usageTemplate: "init <工具...>", args: [{ name: "<工具...>", desc: "要注入的 AI 工具,如 opencode、claude-code" }], detail: "在当前项目中创建 .rune 目录结构,并注入指定 AI 工具的 command 和 skill 配置文件。", - examples: ["rune init opencode", "rune init opencode claude-code"], + exampleArgs: ["init opencode", "init opencode claude-code"], }, discuss: { name: "discuss", alias: "discuss", description: "讨论:生成讨论阶段提示词", - usage: "rune discuss", + usageTemplate: "discuss", args: [], - detail: "生成 SDD 流程中讨论阶段的提示词,输出到标准输出。需要先运行 rune init 初始化项目。", - examples: ["rune discuss"], + detail: "生成 SDD 流程中讨论阶段的提示词,输出到标准输出。需要先运行 init 初始化项目。", + exampleArgs: ["discuss"], }, plan: { name: "plan", alias: "plan <变更> <文档>", description: "规划:生成指定文档的规划提示词", - usage: "rune plan ", + usageTemplate: "plan ", args: [ { name: "", desc: '变更名称,如 "add-login"' }, { name: "", desc: '文档名称,如 "design"、"task"' }, ], detail: "生成规划阶段指定文档的提示词。依赖的前置文档必须已完成。可用文档由配置中的 plan.documents 定义。", - examples: ["rune plan add-user-auth design", "rune plan add-user-auth task"], + exampleArgs: ["plan add-user-auth design", "plan add-user-auth task"], }, build: { name: "build", alias: "build <名称>", description: "构建:生成构建阶段提示词", - usage: "rune build ", + usageTemplate: "build ", args: [{ name: "", desc: '变更名称,如 "add-login"' }], - detail: "生成构建阶段的提示词。变更目录需已存在(通过 rune plan 创建)。", - examples: ["rune build add-user-auth", "rune build fix-memory-leak"], + detail: "生成构建阶段的提示词。变更目录需已存在(通过 plan 创建)。", + exampleArgs: ["build add-user-auth", "build fix-memory-leak"], }, archive: { name: "archive", alias: "archive <名称>", description: "归档:归档变更并生成提示词", - usage: "rune archive ", + usageTemplate: "archive ", args: [{ name: "", desc: '变更名称,如 "add-login"' }], detail: "将变更目录从 .rune/changes/ 移动到 .rune/archive/,并生成归档阶段提示词。", - examples: ["rune archive add-user-auth", "rune archive fix-memory-leak"], + exampleArgs: ["archive add-user-auth", "archive fix-memory-leak"], }, update: { name: "update", alias: "update <工具...>", description: "更新:更新已注入的编辑器配置", - usage: "rune update <工具...>", + usageTemplate: "update <工具...>", args: [{ name: "<工具...>", desc: "要更新的 AI 工具,如 opencode、claude-code" }], detail: "对比已注入的命令和 skill 文件,与内置版本不一致时覆盖,不存在时新建。用于升级 Rune 后同步编辑器配置。", - examples: ["rune update opencode", "rune update opencode claude-code"], + exampleArgs: ["update opencode", "update opencode claude-code"], }, status: { name: "status", alias: "status [变更]", description: "查看:展示变更状态与下一步建议", - usage: "rune status [change-name]", + usageTemplate: "status [change-name]", args: [{ name: "[change-name]", desc: "可选,指定查看的变更名称" }], detail: "展示各文档完成状态、依赖满足情况、规划进度和下一步建议。不传参数则显示所有变更。", - examples: ["rune status", "rune status add-user-auth"], + exampleArgs: ["status", "status add-user-auth"], }, }; -export function showGlobalHelp(): string { +export function showGlobalHelp(prefix: string = "rune"): string { const lines: string[] = [ "Rune — 基于规格驱动开发(SDD)的 AI 开发辅助工具", "", "用法:", - " rune <命令> [参数]", + ` ${prefix} <命令> [参数]`, "", "命令:", ]; @@ -100,19 +100,24 @@ export function showGlobalHelp(): string { lines.push(""); lines.push("示例:"); - lines.push(" rune init opencode 初始化并注入 OpenCode 配置"); - lines.push(" rune update opencode 更新 OpenCode 配置"); - lines.push(' rune plan add-login design 规划 "add-login" 的设计文档'); - lines.push(" rune status 查看当前变更状态"); + lines.push(` ${prefix} init opencode 初始化并注入 OpenCode 配置`); + lines.push(` ${prefix} update opencode 更新 OpenCode 配置`); + lines.push(` ${prefix} plan add-login design 规划 "add-login" 的设计文档`); + lines.push(` ${prefix} status 查看当前变更状态`); return lines.join("\n"); } -export function showCommandHelp(name: string): string | null { +export function showCommandHelp(name: string, prefix: string = "rune"): string | null { const cmd = COMMANDS[name]; if (!cmd) return null; - const lines: string[] = [`rune ${cmd.name} — ${cmd.description}`, "", "用法:", ` ${cmd.usage}`]; + const lines: string[] = [ + `${prefix} ${cmd.name} — ${cmd.description}`, + "", + "用法:", + ` ${prefix} ${cmd.usageTemplate}`, + ]; if (cmd.args.length > 0) { lines.push(""); @@ -128,8 +133,8 @@ export function showCommandHelp(name: string): string | null { lines.push(""); lines.push("示例:"); - for (const ex of cmd.examples) { - lines.push(` ${ex}`); + for (const ex of cmd.exampleArgs) { + lines.push(` ${prefix} ${ex}`); } return lines.join("\n"); diff --git a/tests/cli/help.test.ts b/tests/cli/help.test.ts index 0c34673..139b7a4 100644 --- a/tests/cli/help.test.ts +++ b/tests/cli/help.test.ts @@ -2,9 +2,20 @@ import { describe, it, expect } from "bun:test"; import { showGlobalHelp, showCommandHelp } from "../../src/cli/help.ts"; describe("showGlobalHelp", () => { - it("包含所有命令行", () => { + it("默认前缀时包含 rune", () => { const output = showGlobalHelp(); expect(output).toContain("rune <命令> [参数]"); + expect(output).toContain("rune init opencode"); + }); + + it("自定义前缀时使用传入前缀", () => { + const output = showGlobalHelp("bunx @lanyuanxiaoyao/rune"); + expect(output).toContain("bunx @lanyuanxiaoyao/rune <命令> [参数]"); + expect(output).toContain("bunx @lanyuanxiaoyao/rune init opencode"); + }); + + it("包含所有命令行", () => { + const output = showGlobalHelp("rune"); expect(output).toContain("init <工具...>"); expect(output).toContain("discuss"); expect(output).toContain("plan <变更> <文档>"); @@ -15,37 +26,30 @@ describe("showGlobalHelp", () => { expect(output).toContain("version"); }); - it("包含示例", () => { - const output = showGlobalHelp(); - expect(output).toContain("rune init opencode"); - expect(output).toContain("rune plan add-login design"); - expect(output).toContain("rune status"); - }); - it("以标题行开头", () => { - const output = showGlobalHelp(); + const output = showGlobalHelp("rune"); expect(output.startsWith("Rune")).toBe(true); }); }); describe("showCommandHelp", () => { it("plan 命令包含用法、参数、描述、示例", () => { - const output = showCommandHelp("plan"); - expect(output).toContain("rune plan "); + const output = showCommandHelp("plan", "bunx @lanyuanxiaoyao/rune"); + expect(output).toContain("bunx @lanyuanxiaoyao/rune plan "); expect(output).toContain(""); expect(output).toContain(""); expect(output).toContain("规划阶段"); - expect(output).toContain("rune plan add-user-auth"); + expect(output).toContain("bunx @lanyuanxiaoyao/rune plan add-user-auth"); }); it("init 命令包含工具参数说明", () => { - const output = showCommandHelp("init"); + const output = showCommandHelp("init", "rune"); expect(output).toContain("rune init <工具...>"); expect(output).toContain("opencode"); }); it("不存在的命令返回 null", () => { - const output = showCommandHelp("nonexistent"); + const output = showCommandHelp("nonexistent", "rune"); expect(output).toBeNull(); }); });