diff --git a/packages/cli-workflow/__tests__/help.test.ts b/packages/cli-workflow/__tests__/help.test.ts index 487e7eb..8d829c0 100644 --- a/packages/cli-workflow/__tests__/help.test.ts +++ b/packages/cli-workflow/__tests__/help.test.ts @@ -15,30 +15,66 @@ describe("help command", () => { expect(code).toBe(0); }); - test("help --skill (no topic) returns 0 and lists topics", async () => { + test("no args prints usage (not red) and returns 1", async () => { + const code = await runCli(STORAGE_ROOT, []); + expect(code).toBe(1); + }); +}); + +describe("skill command", () => { + test("skill (no topic) lists topics and returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["skill"]); + expect(code).toBe(0); + }); + + test("skill cli returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["skill", "cli"]); + expect(code).toBe(0); + }); + + test("skill develop returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["skill", "develop"]); + expect(code).toBe(0); + }); + + test("skill author returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["skill", "author"]); + expect(code).toBe(0); + }); + + test("skill unknown returns 1", async () => { + const code = await runCli(STORAGE_ROOT, ["skill", "unknown"]); + expect(code).toBe(1); + }); +}); + +describe("--help flag on groups", () => { + test("workflow --help returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["workflow", "--help"]); + expect(code).toBe(0); + }); + + test("thread --help returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["thread", "--help"]); + expect(code).toBe(0); + }); + + test("cas --help returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["cas", "--help"]); + expect(code).toBe(0); + }); + + test("init --help returns 0", async () => { + const code = await runCli(STORAGE_ROOT, ["init", "--help"]); + expect(code).toBe(0); + }); +}); + +describe("legacy help --skill compat", () => { + test("help --skill still works (lists topics)", async () => { const code = await runCli(STORAGE_ROOT, ["help", "--skill"]); expect(code).toBe(0); }); - - test("help --skill cli returns 0", async () => { - const code = await runCli(STORAGE_ROOT, ["help", "--skill", "cli"]); - expect(code).toBe(0); - }); - - test("help --skill develop returns 0", async () => { - const code = await runCli(STORAGE_ROOT, ["help", "--skill", "develop"]); - expect(code).toBe(0); - }); - - test("help --skill author returns 0", async () => { - const code = await runCli(STORAGE_ROOT, ["help", "--skill", "author"]); - expect(code).toBe(0); - }); - - test("help --skill unknown returns 1", async () => { - const code = await runCli(STORAGE_ROOT, ["help", "--skill", "unknown"]); - expect(code).toBe(1); - }); }); describe("getSkillTopics", () => { @@ -57,7 +93,7 @@ describe("formatSkillIndex", () => { expect(idx).toContain("cli"); expect(idx).toContain("develop"); expect(idx).toContain("author"); - expect(idx).toContain("help --skill "); + expect(idx).toContain("skill "); }); }); diff --git a/packages/cli-workflow/src/cli-dispatch.ts b/packages/cli-workflow/src/cli-dispatch.ts index 89b8740..84060d6 100644 --- a/packages/cli-workflow/src/cli-dispatch.ts +++ b/packages/cli-workflow/src/cli-dispatch.ts @@ -538,6 +538,9 @@ export function formatCliUsage(): string { lines.push(" uncaged-workflow run [...] (shortcut for thread run)"); lines.push(" uncaged-workflow live [...] (shortcut for thread live)"); lines.push(""); + lines.push(" uncaged-workflow skill [topic] agent-consumable reference docs"); + lines.push(" uncaged-workflow help show this usage"); + lines.push(""); lines.push("Environment variables:"); lines.push( " WORKFLOW_STORAGE_ROOT Override storage directory (default: ~/.uncaged/workflow)", @@ -561,9 +564,16 @@ function dispatchGroup( argv: string[], ): Promise | null { const sub = argv[0]; - if (sub === undefined) { - printCliError(`${formatCliUsage()}\n\nerror: unknown ${tableName} subcommand: (none)`); - return Promise.resolve(1); + if (sub === undefined || sub === "--help" || sub === "-h") { + const entries = Object.entries(table); + const lines = [`${tableName} subcommands:\n`]; + for (const [name, e] of entries) { + const args = e.args ? ` ${e.args}` : ""; + lines.push(` uncaged-workflow ${tableName} ${name}${args}`); + lines.push(` ${e.description}\n`); + } + printCliLine(lines.join("\n")); + return Promise.resolve(sub === undefined ? 1 : 0); } const entry = table[sub]; if (entry === undefined) { @@ -618,13 +628,8 @@ async function dispatchCas(storageRoot: string, argv: string[]): Promise // ── Help ──────────────────────────────────────────────────────────────── -async function dispatchHelp(_storageRoot: string, argv: string[]): Promise { - const skillIdx = argv.indexOf("--skill"); - if (skillIdx === -1) { - printCliLine(formatCliUsage()); - return 0; - } - const topic = argv[skillIdx + 1]; +async function dispatchSkill(_storageRoot: string, argv: string[]): Promise { + const topic = argv[0]; if (topic === undefined) { printCliLine(formatSkillIndex()); return 0; @@ -638,6 +643,27 @@ async function dispatchHelp(_storageRoot: string, argv: string[]): Promise { + // Legacy compat: help --skill [topic] → skill [topic] + const skillIdx = argv.indexOf("--skill"); + if (skillIdx !== -1) { + const topic = argv[skillIdx + 1]; + if (topic === undefined) { + printCliLine(formatSkillIndex()); + return 0; + } + const doc = formatSkillTopic(topic); + if (doc === null) { + printCliError(`unknown skill topic: ${topic}\n\n${formatSkillIndex()}`); + return 1; + } + printCliLine(doc); + return 0; + } + printCliLine(formatCliUsage()); + return 0; +} + // ── Top-level command table (Phase 3) ────────────────────────────────── const COMMAND_TABLE: Record = { @@ -647,6 +673,7 @@ const COMMAND_TABLE: Record = { cas: dispatchCas, init: dispatchInit, help: dispatchHelp, + skill: dispatchSkill, // Top-level shortcuts (no deprecation) run: dispatchRun, @@ -672,12 +699,12 @@ const DEPRECATED_ALIASES: Record { if (argv.length === 0) { - printCliError(formatCliUsage()); + printCliLine(formatCliUsage()); return 1; } const command = argv[0]; if (command === undefined) { - printCliError(formatCliUsage()); + printCliLine(formatCliUsage()); return 1; } const rest = argv.slice(1); diff --git a/packages/cli-workflow/src/cmd-help.ts b/packages/cli-workflow/src/cmd-help.ts index a6c7db4..b435c43 100644 --- a/packages/cli-workflow/src/cmd-help.ts +++ b/packages/cli-workflow/src/cmd-help.ts @@ -42,7 +42,7 @@ Available topics: |-------|-------------| ${rows.join("\n")} -Usage: \`uncaged-workflow help --skill \` +Usage: \`uncaged-workflow skill \` `; } diff --git a/packages/workflow-template-develop/src/roles/coder.ts b/packages/workflow-template-develop/src/roles/coder.ts index 0368d9c..1cce9be 100644 --- a/packages/workflow-template-develop/src/roles/coder.ts +++ b/packages/workflow-template-develop/src/roles/coder.ts @@ -11,7 +11,7 @@ export type CoderMeta = z.infer; const CODER_SYSTEM = `You are a **coder**. Read the thread for the plan and work on the NEXT incomplete phase only. -Run \`uncaged-workflow help --skill develop\` for thread ID lookup, CAS commands, and meta output guide. +Run \`uncaged-workflow skill develop\` for thread ID lookup, CAS commands, and meta output guide. ## Reading phase details diff --git a/packages/workflow-template-develop/src/roles/planner.ts b/packages/workflow-template-develop/src/roles/planner.ts index cf2994f..cc311cd 100644 --- a/packages/workflow-template-develop/src/roles/planner.ts +++ b/packages/workflow-template-develop/src/roles/planner.ts @@ -14,7 +14,7 @@ export type PlannerMeta = z.infer; const PLANNER_SYSTEM = `You are a **planner** for a software task. Break the work into **sequential phases** the coder will execute one at a time. -Run \`uncaged-workflow help --skill develop\` for thread ID lookup, CAS commands, and meta output guide. +Run \`uncaged-workflow skill develop\` for thread ID lookup, CAS commands, and meta output guide. ## Storing phase details — MANDATORY