Skip to main content

outfitter-cli

Deep patterns for @outfitter/cli including output modes, pagination, input parsing, and Commander.js integration. Use when building CLI commands, handling output formatting, implementing pagination, or when "CLI output", "pagination", "exitWithError", or "@outfitter/cli" are mentioned.

Quellinformationen

Repository
outfitter-dev/outfitter
Letzte Quellaktivität
24. Februar 2026 um 23:09
Erkannte Sprache von SKILL.md
Englisch
Sterne
6
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
outfitter-cli
version
0.1.0
description
Deep patterns for @outfitter/cli including output modes, pagination, input parsing, and Commander.js integration. Use when building CLI commands, handling output formatting, implementing pagination, or when "CLI output", "pagination", "exitWithError", or "@outfitter/cli" are mentioned.
allowed-tools
Read Write Edit Glob Grep
# CLI Patterns Deep dive into @outfitter/cli patterns. ## Creating a CLI ```typescript import { createCLI } from "@outfitter/cli"; const cli = createCLI({ name: "myapp", version: "1.0.0", description: "My CLI application", }); cli.program.addCommand(listCommand); cli.program.addCommand(getCommand); cli.program.parse(); ``` ## Command Builder Type-safe command construction: ```typescript import { command } from "@outfitter/cli"; export const myCommand = command("my-command") .description("What this command does") .argument("<id>", "Required resource ID") .argument("[name]", "Optional name") .option("-l, --limit <n>", "Limit results", parseInt) .option("-v, --verbose", "Enable verbose output") .option("-t, --tags <tags...>", "Filter by tags") .action(async ({ args, flags }) => { // args.id: string // args.name: string | undefined // flags.limit: number | undefined // flags.verbose: boolean // flags.tags: string[] | undefined }) .build(); ``` ## Output Modes ### Automatic Detection ```typescript import { output } from "@outfitter/cli"; await output(data); // Human by default ``` ### Mode Priority 1. Explicit `mode` option 2. `OUTFITTER_JSONL=1` env var 3. `OUTFITTER_JSON=1` env var 4. `OUTFITTER_JSON=0` forces human 5. Default fallback: human mode ### Forcing Modes ```typescript // Force JSON await output(data, { mode: "json" }); // Force human await output(data, { mode: "human" }); // JSONL for streaming for await (const item of items) { await output(item, { mode: "jsonl" }); } // Output to stderr await output(errorData, { stream: process.stderr }); ``` ### Custom Formatters ```typescript await output(data, { formatters: { human: (data) => formatTable(data), json: (data) => JSON.stringify(data, null, 2), }, }); ``` ## Error Handling ### Exit with Error ```typescript import { exitWithError } from "@outfitter/cli"; const result = await handler(input, ctx); if (result.isErr()) { exitWithError(result.error); // Exit code from error category } ``` ### Exit Code Mapping | Category | Exit Code | | ---------- | --------- | | validation | 1 | | not_found | 2 | | conflict | 3 | | permission | 4 | | timeout | 5 | | rate_limit | 6 | | network | 7 | | internal | 8 | | auth | 9 | | cancelled | 130 | ### Custom Error Output ```typescript import { formatError, getExitCode } from "@outfitter/cli"; if (result.isErr()) { const formatted = formatError(result.error, { verbose: flags.verbose }); await output(formatted, { stream: process.stderr }); process.exit(getExitCode(result.error.category)); } ``` ## Pagination ### Cursor State Cursors persist in XDG state directory: ``` $XDG_STATE_HOME/{toolName}/cursors/{command}/cursor.json ``` ### Using Pagination ```typescript import { loadCursor, saveCursor, clearCursor } from "@outfitter/cli"; const options = { command: "list", toolName: "myapp" }; // Load previous cursor const state = loadCursor(options); // Fetch data with cursor const results = await listItems({ cursor: state?.cursor, limit: 20, }); // Save for --next if (results.hasMore) { saveCursor(results.nextCursor, options); } // Clear on --reset if (flags.reset) { clearCursor(options); } ``` ### Cursor Expiration ```typescript const state = loadCursor({ ...options, maxAgeMs: 30 * 60 * 1000, // 30 minutes }); ``` ### Pagination Command Pattern ```typescript export const listCommand = command("list") .option("-n, --next", "Continue from previous position") .option("--reset", "Reset pagination cursor") .option("-l, --limit <n>", "Results per page", parseInt, 20) .action(async ({ flags }) => { const paginationOpts = { command: "list", toolName: "myapp" }; if (flags.reset) { clearCursor(paginationOpts); console.log("Cursor reset"); return; } const cursor = flags.next ? loadCursor(paginationOpts)?.cursor : undefined; const result = await listHandler({ cursor, limit: flags.limit }, ctx); if (result.isErr()) { exitWithError(result.error); } await output(result.value.items); if (result.value.nextCursor) { saveCursor(result.value.nextCursor, paginationOpts); console.log("\nUse --next for more results"); } }) .build(); ``` ## Input Parsing ### Stdin Reading ```typescript import { readStdin } from "@outfitter/cli"; const input = await readStdin(); // Returns string or null if no stdin ``` ### Piped Detection ```typescript import { isPiped } from "@outfitter/cli"; if (isPiped()) { const data = await readStdin(); } else { // Interactive mode } ``` ## Progress Indicators ```typescript import { createSpinner, createProgressBar } from "@outfitter/tui/render"; // Spinner const spinner = createSpinner("Loading..."); spinner.start(); // ... work spinner.succeed("Done!"); // Progress bar const progress = createProgressBar({ total: 100 }); for (let i = 0; i <= 100; i++) { progress.update(i); } progress.stop(); ``` ## Best Practices 1. **Handler first** — Business logic in handler, CLI is thin adapter 2. **Output modes** — Support both human and JSON output 3. **Exit codes** — Use `exitWithError` for consistent codes 4. **Pagination** — Use cursor state for `--next` functionality 5. **Stdin support** — Handle piped input gracefully 6. **TTY detection** — Adapt behavior for interactive vs piped ## Related Skills - `stack:patterns` — Handler contract - `stack:scaffold` — CLI command template - `stack:debug` — Troubleshooting CLI issues
Auf GitHub ansehen