Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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":"<team-id>"}'
linear projects list
Expand Down
20 changes: 19 additions & 1 deletion packages/cli/src/help/root-help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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":"<team-id>"}'
linear initiatives list
Expand All @@ -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":"<team-id>"}' --json
`;

export const issueBranchHelpText = `
Examples:
linear issues branch ANN-123
linear issues branch ANN-123 --json

JSON path:
.data.branchName
`;
6 changes: 4 additions & 2 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown> {
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -468,11 +469,13 @@ export function createProgram(authManager = new AuthManager()): Command {
issuesCommand?.commands
.find((command) => command.name() === "create")
?.option("--template <id-or-name>", "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("<id-or-identifier>", "Issue id (UUID) or identifier (e.g. ANN-123)")
.addHelpText("after", issueBranchHelpText)
.action(async (idOrIdentifier, _, cmd) => {
const globals = getGlobalOptions(cmd);

Expand All @@ -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")
Expand Down
35 changes: 33 additions & 2 deletions packages/cli/tests/help.test.ts
Original file line number Diff line number Diff line change
@@ -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 =
Expand All @@ -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");
Expand Down
Loading