diff --git a/README.md b/README.md index 693a2c6..fec215f 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,18 @@ pnpm verify ## Run CLI ```bash -pnpm --filter @wiseiodev/linear-cli dev -- --help -pnpm --filter @wiseiodev/linear-cli dev -- issues list --json +pnpm --filter @wiseiodev/linear-cli dev --help +pnpm --filter @wiseiodev/linear-cli dev issues list --json +``` + +## Help Discovery + +```bash +linear --help +linear issues --help +linear issues branch --help +linear issues branch ANN-123 --json +# branch name is in .data.branchName ``` ## Example Commands @@ -53,7 +63,7 @@ linear skills install issue-triage # CRUD linear issues list --limit 10 -linear issues branch ANN-123 +linear issues branch ANN-123 --json linear issues browse linear issues create --input '{"title":"Investigate bug","teamId":""}' linear projects list diff --git a/packages/cli/src/help/root-help.ts b/packages/cli/src/help/root-help.ts index 2bddcf0..91fb296 100644 --- a/packages/cli/src/help/root-help.ts +++ b/packages/cli/src/help/root-help.ts @@ -6,8 +6,10 @@ Examples: linear auth login --manual linear auth status --json linear auth api-key-set --api-key "$LINEAR_API_KEY" + linear issues --help + linear issues branch --help linear issues list --limit 10 - linear issues branch ANN-123 + linear issues branch ANN-123 --json linear issues browse linear issues create --template "Bug Report" --input '{"teamId":""}' linear initiatives list @@ -24,3 +26,19 @@ Docs: - SDK: https://linear.app/developers/sdk - OAuth: https://linear.app/developers/oauth-2-0-authentication `; + +export const issuesHelpText = ` +Examples: + linear issues --help + linear issues list --limit 10 --json + linear issues create --template "Bug Report" --input '{"teamId":""}' --json +`; + +export const issueBranchHelpText = ` +Examples: + linear issues branch ANN-123 + linear issues branch ANN-123 --json + +JSON path: + .data.branchName +`; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 8013e1a..f10826b 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -40,7 +40,7 @@ import open from "open"; import { runInteractiveOAuthLogin } from "./auth/login.js"; import { registerResourceCommand } from "./commands/resource.js"; import { renderEnvelope } from "./formatters/output.js"; -import { rootHelpText } from "./help/root-help.js"; +import { issueBranchHelpText, issuesHelpText, rootHelpText } from "./help/root-help.js"; import { getGlobalOptions } from "./runtime/options.js"; function isRecord(value: unknown): value is Record { @@ -225,6 +225,7 @@ async function resolveIssueTemplateId( export function createProgram(authManager = new AuthManager()): Command { const program = new Command(); + program.configureHelp({ showGlobalOptions: true }); program .name("linear") @@ -468,11 +469,13 @@ export function createProgram(authManager = new AuthManager()): Command { issuesCommand?.commands .find((command) => command.name() === "create") ?.option("--template ", "Apply a template by exact id or exact name"); + issuesCommand?.addHelpText("after", issuesHelpText); issuesCommand ?.command("branch") .description("Show branch name for an issue id or identifier") .argument("", "Issue id (UUID) or identifier (e.g. ANN-123)") + .addHelpText("after", issueBranchHelpText) .action(async (idOrIdentifier, _, cmd) => { const globals = getGlobalOptions(cmd); @@ -492,7 +495,6 @@ export function createProgram(authManager = new AuthManager()): Command { process.exitCode = 1; } }); - issuesCommand ?.command("browse") .description("Browse issues in the interactive terminal table") diff --git a/packages/cli/tests/help.test.ts b/packages/cli/tests/help.test.ts index 8988958..17b0ca1 100644 --- a/packages/cli/tests/help.test.ts +++ b/packages/cli/tests/help.test.ts @@ -1,12 +1,37 @@ +import type { Command } from "commander"; import { describe, expect, test } from "vitest"; import { createProgram } from "../src/index.js"; +function captureRenderedHelp(command?: Command): string { + if (!command) { + return ""; + } + + let output = ""; + command.configureOutput({ + writeOut: (str: string) => { + output += str; + }, + writeErr: (str: string) => { + output += str; + }, + }); + command.outputHelp(); + return output; +} + describe("help output", () => { test("contains docs and skills commands", () => { const program = createProgram(); const help = program.helpInformation(); - const issuesHelp = - program.commands.find((command) => command.name() === "issues")?.helpInformation() ?? ""; + const issuesCommand = program.commands.find((command) => command.name() === "issues"); + const issuesHelp = issuesCommand?.helpInformation() ?? ""; + const issueBranchCommand = issuesCommand?.commands.find( + (command) => command.name() === "branch", + ); + const issueBranchHelp = issueBranchCommand?.helpInformation() ?? ""; + const renderedIssuesHelp = captureRenderedHelp(issuesCommand); + const renderedIssueBranchHelp = captureRenderedHelp(issueBranchCommand); const initiativesHelp = program.commands.find((command) => command.name() === "initiatives")?.helpInformation() ?? ""; const documentsHelp = @@ -27,6 +52,12 @@ describe("help output", () => { expect(help).toContain("templates"); expect(issuesHelp).toContain("branch"); expect(issuesHelp).toContain("browse"); + expect(renderedIssuesHelp).toContain("Global Options:"); + expect(renderedIssuesHelp).toContain("--json"); + expect(renderedIssueBranchHelp).toContain("Global Options:"); + expect(renderedIssueBranchHelp).toContain("--json"); + expect(renderedIssueBranchHelp).toContain(".data.branchName"); + expect(issueBranchHelp).toContain("id-or-identifier"); expect(initiativesHelp).toContain("create"); expect(documentsHelp).toContain("list"); expect(templatesHelp).toContain("list");