diff --git a/src/cli/help.ts b/src/cli/help.ts new file mode 100644 index 0000000..e264631 --- /dev/null +++ b/src/cli/help.ts @@ -0,0 +1,138 @@ +interface CommandHelpDef { + name: string; + alias: string; + description: string; + usage: string; + args: { name: string; desc: string }[]; + detail: string; + examples: string[]; +} + +const COMMANDS: Record = { + init: { + name: "init", + alias: "init <工具...>", + description: "初始化 Rune 并注入工具配置", + usage: "rune init <工具...>", + args: [{ name: "<工具...>", desc: "要注入的 AI 工具,如 opencode、claude-code" }], + detail: "在当前项目中创建 .rune 目录结构,并注入指定 AI 工具的 command 和 skill 配置文件。", + examples: [ + "rune init opencode", + "rune init opencode claude-code", + ], + }, + discuss: { + name: "discuss", + alias: "discuss", + description: "讨论:生成讨论阶段提示词", + usage: "rune discuss", + args: [], + detail: "生成 SDD 流程中讨论阶段的提示词,输出到标准输出。需要先运行 rune init 初始化项目。", + examples: ["rune discuss"], + }, + plan: { + name: "plan", + alias: "plan <名称>", + description: "规划:生成规划阶段提示词", + usage: "rune plan ", + args: [{ name: "", desc: "变更名称,如 \"add-login\"" }], + detail: "生成规划阶段的提示词。变更目录将创建在 .rune/changes// 下。", + examples: [ + "rune plan add-user-auth", + "rune plan fix-memory-leak", + ], + }, + build: { + name: "build", + alias: "build <名称>", + description: "构建:生成构建阶段提示词", + usage: "rune build ", + args: [{ name: "", desc: "变更名称,如 \"add-login\"" }], + detail: "生成构建阶段的提示词。变更目录需已存在(通过 rune plan 创建)。", + examples: [ + "rune build add-user-auth", + "rune build fix-memory-leak", + ], + }, + archive: { + name: "archive", + alias: "archive <名称>", + description: "归档:归档变更并生成提示词", + usage: "rune archive ", + args: [{ name: "", desc: "变更名称,如 \"add-login\"" }], + detail: "将变更目录从 .rune/changes/ 移动到 .rune/archive/,并生成归档阶段提示词。", + examples: [ + "rune archive add-user-auth", + "rune archive fix-memory-leak", + ], + }, + status: { + name: "status", + alias: "status", + description: "查看:列出当前进行中的变更", + usage: "rune status", + args: [], + detail: "扫描 .rune/changes/ 目录,列出所有进行中的变更及其任务进度。", + examples: ["rune status"], + }, +}; + +export function showGlobalHelp(): string { + const lines: string[] = [ + "Rune — 基于规格驱动开发(SDD)的 AI 开发辅助工具", + "", + "用法:", + " rune <命令> [参数]", + "", + "命令:", + ]; + + const entries = Object.values(COMMANDS); + const maxAliasLen = Math.max(...entries.map((c) => c.alias.length)); + for (const cmd of entries) { + lines.push(` ${cmd.alias.padEnd(maxAliasLen)} ${cmd.description}`); + } + + lines.push(` ${"help".padEnd(maxAliasLen)} 显示帮助信息`); + lines.push(` ${"version".padEnd(maxAliasLen)} 显示版本号`); + + lines.push(""); + lines.push("示例:"); + lines.push(" rune init opencode 初始化并注入 OpenCode 配置"); + lines.push(" rune plan add-login 开始规划 \"add-login\" 变更"); + lines.push(" rune status 查看当前变更状态"); + + return lines.join("\n"); +} + +export function showCommandHelp(name: string): string | null { + const cmd = COMMANDS[name]; + if (!cmd) return null; + + const lines: string[] = [ + `rune ${cmd.name} — ${cmd.description}`, + "", + "用法:", + ` ${cmd.usage}`, + ]; + + if (cmd.args.length > 0) { + lines.push(""); + lines.push("参数:"); + for (const arg of cmd.args) { + lines.push(` ${arg.name} ${arg.desc}`); + } + } + + lines.push(""); + lines.push("描述:"); + lines.push(` ${cmd.detail}`); + + lines.push(""); + lines.push("示例:"); + for (const ex of cmd.examples) { + lines.push(` ${ex}`); + } + + return lines.join("\n"); +} diff --git a/tests/cli/help.test.ts b/tests/cli/help.test.ts new file mode 100644 index 0000000..f5228cb --- /dev/null +++ b/tests/cli/help.test.ts @@ -0,0 +1,50 @@ +import { describe, it, expect } from "bun:test"; +import { showGlobalHelp, showCommandHelp } from "../../src/cli/help.ts"; + +describe("showGlobalHelp", () => { + it("包含所有命令行", () => { + const output = showGlobalHelp(); + expect(output).toContain("rune <命令> [参数]"); + expect(output).toContain("init <工具...>"); + expect(output).toContain("discuss"); + expect(output).toContain("plan <名称>"); + expect(output).toContain("build <名称>"); + expect(output).toContain("archive <名称>"); + expect(output).toContain("status"); + expect(output).toContain("help"); + expect(output).toContain("version"); + }); + + it("包含示例", () => { + const output = showGlobalHelp(); + expect(output).toContain("rune init opencode"); + expect(output).toContain("rune plan add-login"); + expect(output).toContain("rune status"); + }); + + it("以标题行开头", () => { + const output = showGlobalHelp(); + expect(output.startsWith("Rune")).toBe(true); + }); +}); + +describe("showCommandHelp", () => { + it("plan 命令包含用法、参数、描述、示例", () => { + const output = showCommandHelp("plan"); + expect(output).toContain("rune plan "); + expect(output).toContain(""); + expect(output).toContain("规划阶段"); + expect(output).toContain("rune plan add-user-auth"); + }); + + it("init 命令包含工具参数说明", () => { + const output = showCommandHelp("init"); + expect(output).toContain("rune init <工具...>"); + expect(output).toContain("opencode"); + }); + + it("不存在的命令返回 null", () => { + const output = showCommandHelp("nonexistent"); + expect(output).toBeNull(); + }); +});